summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml46
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml5
-rw-r--r--.github/stale.yml69
-rw-r--r--.github/workflows/bisects.yml31
-rw-r--r--.github/workflows/ci_docs.yml13
-rw-r--r--.github/workflows/ci_gcc14.yml76
-rw-r--r--.github/workflows/ci_packages.yml17
-rw-r--r--.github/workflows/ci_publish.yml10
-rw-r--r--.github/workflows/stale.yml25
-rw-r--r--.gitignore9
-rw-r--r--.gitlab-ci.yml62
-rw-r--r--azure-pipelines.yml18
-rwxr-xr-xbin/nim-gdb4
-rw-r--r--bin/nim-gdb.bat2
-rw-r--r--changelog.md184
-rw-r--r--changelogs/changelog.md33
-rw-r--r--changelogs/changelog_2_0_0.md330
-rw-r--r--changelogs/changelog_2_0_0_details.md560
-rw-r--r--changelogs/changelog_2_2_0.md248
-rw-r--r--changelogs/changelog_X_XX_X.md2
-rw-r--r--ci/action.nim2
-rw-r--r--ci/funs.sh6
-rw-r--r--compiler/aliasanalysis.nim128
-rw-r--r--compiler/aliases.nim31
-rw-r--r--compiler/ast.nim812
-rw-r--r--compiler/astalgo.nim374
-rw-r--r--compiler/astmsgs.nim8
-rw-r--r--compiler/astyaml.nim154
-rw-r--r--compiler/bitsets.nim6
-rw-r--r--compiler/btrees.nim5
-rw-r--r--compiler/cbuilder.nim174
-rw-r--r--compiler/ccgcalls.nim287
-rw-r--r--compiler/ccgexprs.nim1176
-rw-r--r--compiler/ccgliterals.nim2
-rw-r--r--compiler/ccgmerge_unused.nim7
-rw-r--r--compiler/ccgreset.nim44
-rw-r--r--compiler/ccgstmts.nim326
-rw-r--r--compiler/ccgthreadvars.nim4
-rw-r--r--compiler/ccgtrav.nim63
-rw-r--r--compiler/ccgtypes.nim1111
-rw-r--r--compiler/ccgutils.nim127
-rw-r--r--compiler/cgen.nim780
-rw-r--r--compiler/cgendata.nim37
-rw-r--r--compiler/cgmeth.nim101
-rw-r--r--compiler/closureiters.nim348
-rw-r--r--compiler/cmdlinehelper.nim11
-rw-r--r--compiler/commands.nim185
-rw-r--r--compiler/compiler.nimble28
-rw-r--r--compiler/concepts.nim97
-rw-r--r--compiler/condsyms.nim82
-rw-r--r--compiler/depends.nim39
-rw-r--r--compiler/dfa.nim573
-rw-r--r--compiler/docgen.nim448
-rw-r--r--compiler/docgen2.nim31
-rw-r--r--compiler/enumtostr.nim23
-rw-r--r--compiler/errorhandling.nim9
-rw-r--r--compiler/evalffi.nim75
-rw-r--r--compiler/evaltempl.nim50
-rw-r--r--compiler/expanddefaults.nim131
-rw-r--r--compiler/extccomp.nim127
-rw-r--r--compiler/filter_tmpl.nim24
-rw-r--r--compiler/filters.nim17
-rw-r--r--compiler/gorgeimpl.nim21
-rw-r--r--compiler/guards.nim169
-rw-r--r--compiler/hlo.nim15
-rw-r--r--compiler/ic/bitabs.nim17
-rw-r--r--compiler/ic/cbackend.nim12
-rw-r--r--compiler/ic/dce.nim16
-rw-r--r--compiler/ic/ic.nim508
-rw-r--r--compiler/ic/iclineinfos.nim84
-rw-r--r--compiler/ic/integrity.nim30
-rw-r--r--compiler/ic/navigator.nim79
-rw-r--r--compiler/ic/packed_ast.nim241
-rw-r--r--compiler/ic/replayer.nim37
-rw-r--r--compiler/ic/rodfiles.nim53
-rw-r--r--compiler/idents.nim4
-rw-r--r--compiler/importer.nim65
-rw-r--r--compiler/injectdestructors.nim757
-rw-r--r--compiler/installer.ini13
-rw-r--r--compiler/int128.nim108
-rw-r--r--compiler/isolation_check.nim99
-rw-r--r--compiler/jsgen.nim975
-rw-r--r--compiler/jstypes.nim20
-rw-r--r--compiler/lambdalifting.nim196
-rw-r--r--compiler/layouter.nim45
-rw-r--r--compiler/lexer.nim174
-rw-r--r--compiler/liftdestructors.nim399
-rw-r--r--compiler/liftlocals.nim5
-rw-r--r--compiler/lineinfos.nim65
-rw-r--r--compiler/linter.nim16
-rw-r--r--compiler/llstream.nim29
-rw-r--r--compiler/lookups.nim375
-rw-r--r--compiler/lowerings.nim58
-rw-r--r--compiler/magicsys.nim41
-rw-r--r--compiler/main.nim131
-rw-r--r--compiler/mangleutils.nim59
-rw-r--r--compiler/md5_old.nim297
-rw-r--r--compiler/modulegraphs.nim237
-rw-r--r--compiler/modulepaths.nim21
-rw-r--r--compiler/modules.nim104
-rw-r--r--compiler/msgs.nim108
-rw-r--r--compiler/ndi.nim2
-rw-r--r--compiler/nilcheck.nim60
-rw-r--r--compiler/nim.cfg40
-rw-r--r--compiler/nim.nim36
-rw-r--r--compiler/nimblecmd.nim20
-rw-r--r--compiler/nimconf.nim20
-rw-r--r--compiler/nimeval.nim36
-rw-r--r--compiler/nimfix/nimfix.nim111
-rw-r--r--compiler/nimfix/nimfix.nim.cfg17
-rw-r--r--compiler/nimfix/prettybase.nim45
-rw-r--r--compiler/nimlexbase.nim5
-rw-r--r--compiler/nimpaths.nim2
-rw-r--r--compiler/nimsets.nim6
-rw-r--r--compiler/nodejs.nim4
-rw-r--r--compiler/nodekinds.nim211
-rw-r--r--compiler/nversion.nim2
-rw-r--r--compiler/optimizer.nim25
-rw-r--r--compiler/options.nim203
-rw-r--r--compiler/packagehandling.nim1
-rw-r--r--compiler/packages.nim8
-rw-r--r--compiler/parampatterns.nim24
-rw-r--r--compiler/parser.nim666
-rw-r--r--compiler/passes.nim193
-rw-r--r--compiler/pathutils.nim51
-rw-r--r--compiler/patterns.nim49
-rw-r--r--compiler/pipelines.nim312
-rw-r--r--compiler/pipelineutils.nim26
-rw-r--r--compiler/platform.nim11
-rw-r--r--compiler/plugins/customast.nim136
-rw-r--r--compiler/plugins/itersgen.nim6
-rw-r--r--compiler/plugins/locals.nim2
-rw-r--r--compiler/plugins/plugins.nimble0
-rw-r--r--compiler/pragmas.nim329
-rw-r--r--compiler/prefixmatches.nim2
-rw-r--r--compiler/procfind.nim10
-rw-r--r--compiler/pushpoppragmas.nim54
-rw-r--r--compiler/readme.md2
-rw-r--r--compiler/renderer.nim390
-rw-r--r--compiler/renderverbatim.nim3
-rw-r--r--compiler/reorder.nim87
-rw-r--r--compiler/rodutils.nim5
-rw-r--r--compiler/ropes.nim29
-rw-r--r--compiler/scriptconfig.nim47
-rw-r--r--compiler/sem.nim364
-rw-r--r--compiler/semcall.nim652
-rw-r--r--compiler/semdata.nim135
-rw-r--r--compiler/semexprs.nim1029
-rw-r--r--compiler/semfields.nim34
-rw-r--r--compiler/semfold.nim193
-rw-r--r--compiler/semgnrc.nim269
-rw-r--r--compiler/seminst.nim182
-rw-r--r--compiler/semmacrosanity.nim28
-rw-r--r--compiler/semmagic.nim253
-rw-r--r--compiler/semobjconstr.nim234
-rw-r--r--compiler/semparallel.nim28
-rw-r--r--compiler/sempass2.nim610
-rw-r--r--compiler/semstmts.nim1155
-rw-r--r--compiler/semstrictfuncs.nim55
-rw-r--r--compiler/semtempl.nim365
-rw-r--r--compiler/semtypes.nim842
-rw-r--r--compiler/semtypinst.nim393
-rw-r--r--compiler/sighashes.nim138
-rw-r--r--compiler/sigmatch.nim1461
-rw-r--r--compiler/sinkparameter_inference.nim3
-rw-r--r--compiler/sizealignoffsetimpl.nim140
-rw-r--r--compiler/sourcemap.nim561
-rw-r--r--compiler/spawn.nim70
-rw-r--r--compiler/strutils2.nim57
-rw-r--r--compiler/suggest.nim309
-rw-r--r--compiler/suggestsymdb.nim212
-rw-r--r--compiler/syntaxes.nim15
-rw-r--r--compiler/tccgen.nim2
-rw-r--r--compiler/transf.nim251
-rw-r--r--compiler/trees.nim46
-rw-r--r--compiler/treetab.nim9
-rw-r--r--compiler/typeallowed.nim111
-rw-r--r--compiler/types.nim867
-rw-r--r--compiler/typesrenderer.nim7
-rw-r--r--compiler/varpartitions.nim96
-rw-r--r--compiler/vm.nim438
-rw-r--r--compiler/vmconv.nim18
-rw-r--r--compiler/vmdef.nim26
-rw-r--r--compiler/vmdeps.nim107
-rw-r--r--compiler/vmgen.nim451
-rw-r--r--compiler/vmhooks.nim9
-rw-r--r--compiler/vmmarshal.nim58
-rw-r--r--compiler/vmops.nim163
-rw-r--r--compiler/vmprofiler.nim12
-rw-r--r--compiler/vtables.nim167
-rw-r--r--compiler/wordrecg.nim61
-rw-r--r--config/build_config.txt6
-rw-r--r--config/config.nims9
-rw-r--r--config/nim.cfg55
-rw-r--r--config/nimdoc.cfg22
-rw-r--r--copying.txt2
-rw-r--r--doc/advopt.txt15
-rw-r--r--doc/astspec.txt48
-rw-r--r--doc/backends.md10
-rw-r--r--doc/basicopt.txt1
-rw-r--r--doc/contributing.md46
-rw-r--r--doc/destructors.md215
-rw-r--r--doc/docgen.md523
-rw-r--r--doc/docgen_sample.nim2
-rw-r--r--doc/drnim.md42
-rw-r--r--doc/estp.md271
-rw-r--r--doc/filelist.txt6
-rw-r--r--doc/filters.md136
-rw-r--r--doc/grammar.txt105
-rw-r--r--doc/idetools.md38
-rw-r--r--doc/intern.md11
-rw-r--r--doc/lib.md333
-rw-r--r--doc/manual.md845
-rw-r--r--doc/manual_experimental.md1005
-rw-r--r--doc/manual_experimental_strictnotnil.md15
-rw-r--r--doc/markdown_rst.md181
-rw-r--r--doc/mm.md27
-rw-r--r--doc/nep1.md40
-rw-r--r--doc/nimc.md113
-rw-r--r--doc/nimdoc.css29
-rw-r--r--doc/nimfix.md58
-rw-r--r--doc/nimgrep.md2
-rw-r--r--doc/nimgrep_cmdline.txt26
-rw-r--r--doc/niminst.md8
-rw-r--r--doc/nims.md43
-rw-r--r--doc/nimsuggest.md2
-rw-r--r--doc/packaging.md7
-rw-r--r--doc/pegdocs.txt76
-rw-r--r--doc/readme.txt2
-rw-r--r--doc/refc.md1
-rw-r--r--doc/regexprs.txt8
-rw-r--r--doc/rstcommon.rst6
-rw-r--r--doc/sets_fragment.txt21
-rw-r--r--doc/testament.md111
-rw-r--r--doc/tools.md7
-rw-r--r--doc/tut1.md20
-rw-r--r--doc/tut2.md6
-rw-r--r--doc/tut3.md34
-rw-r--r--koch.nim144
-rw-r--r--lib/core/hotcodereloading.nim2
-rw-r--r--lib/core/locks.nim7
-rw-r--r--lib/core/macrocache.nim39
-rw-r--r--lib/core/macros.nim104
-rw-r--r--lib/core/rlocks.nim5
-rw-r--r--lib/core/typeinfo.nim49
-rw-r--r--lib/deprecated/pure/future.nim2
-rw-r--r--lib/deprecated/pure/mersenne.nim (renamed from lib/pure/mersenne.nim)0
-rw-r--r--lib/deprecated/pure/ospaths.nim2
-rw-r--r--lib/deprecated/pure/oswalkdir.nim (renamed from lib/pure/oswalkdir.nim)6
-rw-r--r--lib/deprecated/pure/securehash.nim6
-rw-r--r--lib/deprecated/pure/sums.nim (renamed from lib/std/sums.nim)0
-rw-r--r--lib/experimental/diff.nim2
-rw-r--r--lib/genode/alloc.nim4
-rw-r--r--lib/genode/constructibles.nim21
-rw-r--r--lib/genode/entrypoints.nim22
-rw-r--r--lib/genode/env.nim10
-rw-r--r--lib/genode/signals.nim77
-rw-r--r--lib/genode_cpp/signals.h39
-rw-r--r--lib/impure/db_mysql.nim425
-rw-r--r--lib/impure/db_odbc.nim532
-rw-r--r--lib/impure/db_postgres.nim647
-rw-r--r--lib/impure/db_sqlite.nim945
-rw-r--r--lib/impure/nre.nim20
-rw-r--r--lib/impure/nre/private/util.nim2
-rw-r--r--lib/impure/rdstdin.nim6
-rw-r--r--lib/impure/re.nim24
-rw-r--r--lib/js/asyncjs.nim37
-rw-r--r--lib/js/dom.nim166
-rw-r--r--lib/js/jscore.nim26
-rw-r--r--lib/js/jsffi.nim124
-rw-r--r--lib/js/jsre.nim7
-rw-r--r--lib/nimbase.h87
-rw-r--r--lib/nimhcr.nim21
-rw-r--r--lib/nimrtl.nim4
-rw-r--r--lib/packages/docutils/dochelpers.nim34
-rw-r--r--lib/packages/docutils/highlite.nim45
-rw-r--r--lib/packages/docutils/rst.nim718
-rw-r--r--lib/packages/docutils/rstast.nim16
-rw-r--r--lib/packages/docutils/rstgen.nim233
-rw-r--r--lib/packages/docutils/rstidx.nim141
-rw-r--r--lib/posix/epoll.nim2
-rw-r--r--lib/posix/inotify.nim40
-rw-r--r--lib/posix/kqueue.nim2
-rw-r--r--lib/posix/linux.nim4
-rw-r--r--lib/posix/posix.nim104
-rw-r--r--lib/posix/posix_haiku.nim4
-rw-r--r--lib/posix/posix_linux_amd64.nim10
-rw-r--r--lib/posix/posix_linux_amd64_consts.nim2
-rw-r--r--lib/posix/posix_macos_amd64.nim39
-rw-r--r--lib/posix/posix_nintendoswitch.nim10
-rw-r--r--lib/posix/posix_openbsd_amd64.nim18
-rw-r--r--lib/posix/posix_other.nim13
-rw-r--r--lib/posix/posix_other_consts.nim15
-rw-r--r--lib/posix/posix_utils.nim38
-rw-r--r--lib/posix/termios.nim2
-rw-r--r--lib/pure/algorithm.nim18
-rw-r--r--lib/pure/async.nim7
-rw-r--r--lib/pure/asyncdispatch.nim130
-rw-r--r--lib/pure/asyncfile.nim74
-rw-r--r--lib/pure/asyncftpclient.nim451
-rw-r--r--lib/pure/asyncfutures.nim46
-rw-r--r--lib/pure/asynchttpserver.nim37
-rw-r--r--lib/pure/asyncmacro.nim92
-rw-r--r--lib/pure/asyncnet.nim33
-rw-r--r--lib/pure/asyncstreams.nim4
-rw-r--r--lib/pure/base64.nim65
-rw-r--r--lib/pure/bitops.nim16
-rw-r--r--lib/pure/browsers.nim66
-rw-r--r--lib/pure/cgi.nim52
-rw-r--r--lib/pure/collections/deques.nim127
-rw-r--r--lib/pure/collections/hashcommon.nim18
-rw-r--r--lib/pure/collections/heapqueue.nim17
-rw-r--r--lib/pure/collections/lists.nim83
-rw-r--r--lib/pure/collections/sequtils.nim43
-rw-r--r--lib/pure/collections/setimpl.nim5
-rw-r--r--lib/pure/collections/sets.nim43
-rw-r--r--lib/pure/collections/sharedlist.nim2
-rw-r--r--lib/pure/collections/sharedtables.nim6
-rw-r--r--lib/pure/collections/tableimpl.nim51
-rw-r--r--lib/pure/collections/tables.nim158
-rw-r--r--lib/pure/colors.nim15
-rw-r--r--lib/pure/complex.nim89
-rw-r--r--lib/pure/concurrency/atomics.nim72
-rw-r--r--lib/pure/concurrency/cpuinfo.nim141
-rw-r--r--lib/pure/concurrency/cpuload.nim6
-rw-r--r--lib/pure/concurrency/threadpool.nim13
-rw-r--r--lib/pure/cookies.nim2
-rw-r--r--lib/pure/coro.nim18
-rw-r--r--lib/pure/cstrutils.nim4
-rw-r--r--lib/pure/db_common.nim100
-rw-r--r--lib/pure/distros.nim20
-rw-r--r--lib/pure/dynlib.nim4
-rw-r--r--lib/pure/encodings.nim8
-rw-r--r--lib/pure/endians.nim2
-rw-r--r--lib/pure/fenv.nim2
-rw-r--r--lib/pure/hashes.nim281
-rw-r--r--lib/pure/htmlgen.nim11
-rw-r--r--lib/pure/htmlparser.nim20
-rw-r--r--lib/pure/httpclient.nim180
-rw-r--r--lib/pure/httpcore.nim29
-rw-r--r--lib/pure/includes/osenv.nim207
-rw-r--r--lib/pure/includes/oserr.nim119
-rw-r--r--lib/pure/includes/unicode_ranges.nim3913
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim11
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim10
-rw-r--r--lib/pure/ioselects/ioselectors_poll.nim2
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim8
-rw-r--r--lib/pure/json.nim642
-rw-r--r--lib/pure/lexbase.nim10
-rw-r--r--lib/pure/logging.nim124
-rw-r--r--lib/pure/marshal.nim9
-rw-r--r--lib/pure/math.nim255
-rw-r--r--lib/pure/md5.nim25
-rw-r--r--lib/pure/memfiles.nim198
-rw-r--r--lib/pure/mimetypes.nim2576
-rw-r--r--lib/pure/nativesockets.nim86
-rw-r--r--lib/pure/net.nim119
-rw-r--r--lib/pure/nimprof.nim18
-rw-r--r--lib/pure/nimtracker.nim88
-rw-r--r--lib/pure/oids.nim19
-rw-r--r--lib/pure/options.nim23
-rw-r--r--lib/pure/os.nim2743
-rw-r--r--lib/pure/osproc.nim298
-rw-r--r--lib/pure/parsecfg.nim11
-rw-r--r--lib/pure/parsecsv.nim10
-rw-r--r--lib/pure/parsejson.nim2
-rw-r--r--lib/pure/parseopt.nim130
-rw-r--r--lib/pure/parsesql.nim15
-rw-r--r--lib/pure/parseutils.nim654
-rw-r--r--lib/pure/parsexml.nim174
-rw-r--r--lib/pure/pathnorm.nim2
-rw-r--r--lib/pure/pegs.nim183
-rw-r--r--lib/pure/prelude.nim2
-rw-r--r--lib/pure/punycode.nim209
-rw-r--r--lib/pure/random.nim72
-rw-r--r--lib/pure/rationals.nim34
-rw-r--r--lib/pure/reservedmem.nim11
-rw-r--r--lib/pure/ropes.nim2
-rw-r--r--lib/pure/segfaults.nim4
-rw-r--r--lib/pure/selectors.nim42
-rw-r--r--lib/pure/smtp.nim358
-rw-r--r--lib/pure/smtp.nim.cfg1
-rw-r--r--lib/pure/ssl_certs.nim8
-rw-r--r--lib/pure/stats.nim2
-rw-r--r--lib/pure/streams.nim128
-rw-r--r--lib/pure/streamwrapper.nim6
-rw-r--r--lib/pure/strformat.nim196
-rw-r--r--lib/pure/strmisc.nim15
-rw-r--r--lib/pure/strscans.nim40
-rw-r--r--lib/pure/strtabs.nim9
-rw-r--r--lib/pure/strutils.nim388
-rw-r--r--lib/pure/sugar.nim25
-rw-r--r--lib/pure/terminal.nim121
-rw-r--r--lib/pure/times.nim245
-rw-r--r--lib/pure/typetraits.nim55
-rw-r--r--lib/pure/unicode.nim480
-rw-r--r--lib/pure/unittest.nim113
-rw-r--r--lib/pure/uri.nim99
-rw-r--r--lib/pure/volatile.nim12
-rw-r--r--lib/pure/xmlparser.nim4
-rw-r--r--lib/pure/xmltree.nim237
-rw-r--r--lib/std/appdirs.nim94
-rw-r--r--lib/std/assertions.nim23
-rw-r--r--lib/std/cmdline.nim313
-rw-r--r--lib/std/decls.nim13
-rw-r--r--lib/std/dirs.nim135
-rw-r--r--lib/std/editdistance.nim4
-rw-r--r--lib/std/effecttraits.nim2
-rw-r--r--lib/std/enumerate.nim4
-rw-r--r--lib/std/enumutils.nim40
-rw-r--r--lib/std/envvars.nim49
-rw-r--r--lib/std/exitprocs.nim36
-rw-r--r--lib/std/files.nim46
-rw-r--r--lib/std/formatfloat.nim55
-rw-r--r--lib/std/genasts.nim6
-rw-r--r--lib/std/isolation.nim6
-rw-r--r--lib/std/jsbigints.nim14
-rw-r--r--lib/std/jsfetch.nim11
-rw-r--r--lib/std/jsformdata.nim16
-rw-r--r--lib/std/jsonutils.nim119
-rw-r--r--lib/std/monotimes.nim4
-rw-r--r--lib/std/objectdollar.nim2
-rw-r--r--lib/std/oserrors.nim19
-rw-r--r--lib/std/outparams.nim38
-rw-r--r--lib/std/packedsets.nim33
-rw-r--r--lib/std/paths.nim302
-rw-r--r--lib/std/private/dbutils.nim15
-rw-r--r--lib/std/private/digitsutils.nim15
-rw-r--r--lib/std/private/dragonbox.nim52
-rw-r--r--lib/std/private/gitutils.nim16
-rw-r--r--lib/std/private/globs.nim10
-rw-r--r--lib/std/private/jsutils.nim23
-rw-r--r--lib/std/private/miscdollars.nim16
-rw-r--r--lib/std/private/osappdirs.nim176
-rw-r--r--lib/std/private/oscommon.nim186
-rw-r--r--lib/std/private/osdirs.nim570
-rw-r--r--lib/std/private/osfiles.nim416
-rw-r--r--lib/std/private/ospaths2.nim1030
-rw-r--r--lib/std/private/osseps.nim (renamed from lib/pure/includes/osseps.nim)2
-rw-r--r--lib/std/private/ossymlinks.nim78
-rw-r--r--lib/std/private/schubfach.nim26
-rw-r--r--lib/std/private/since.nim10
-rw-r--r--lib/std/private/strimpl.nim37
-rw-r--r--lib/std/private/syslocks.nim234
-rw-r--r--lib/std/private/threadtypes.nim176
-rw-r--r--lib/std/private/underscored_calls.nim22
-rw-r--r--lib/std/private/win_setenv.nim33
-rw-r--r--lib/std/setutils.nim2
-rw-r--r--lib/std/sha1.nim9
-rw-r--r--lib/std/socketstreams.nim63
-rw-r--r--lib/std/staticos.nim13
-rw-r--r--lib/std/strbasics.nim4
-rw-r--r--lib/std/symlinks.nim33
-rw-r--r--lib/std/syncio.nim150
-rw-r--r--lib/std/sysatomics.nim (renamed from lib/system/atomics.nim)90
-rw-r--r--lib/std/sysrand.nim36
-rw-r--r--lib/std/tasks.nim110
-rw-r--r--lib/std/tempfiles.nim8
-rw-r--r--lib/std/time_t.nim4
-rw-r--r--lib/std/typedthreads.nim (renamed from lib/system/threads.nim)275
-rw-r--r--lib/std/varints.nim12
-rw-r--r--lib/std/widestrs.nim239
-rw-r--r--lib/std/with.nim11
-rw-r--r--lib/std/wordwrap.nim2
-rw-r--r--lib/std/wrapnils.nim24
-rw-r--r--lib/system.nim1817
-rw-r--r--lib/system/alloc.nim734
-rw-r--r--lib/system/ansi_c.nim15
-rw-r--r--lib/system/arc.nim146
-rw-r--r--lib/system/arithm.nim425
-rw-r--r--lib/system/arithmetics.nim179
-rw-r--r--lib/system/assign.nim36
-rw-r--r--lib/system/basic_types.nim61
-rw-r--r--lib/system/bitmasks.nim2
-rw-r--r--lib/system/cellseqs_v1.nim19
-rw-r--r--lib/system/cellseqs_v2.nim23
-rw-r--r--lib/system/cellsets.nim4
-rw-r--r--lib/system/cgprocs.nim10
-rw-r--r--lib/system/channels_builtin.nim41
-rw-r--r--lib/system/chcks.nim8
-rw-r--r--lib/system/comparisons.nim35
-rw-r--r--lib/system/compilation.nim209
-rw-r--r--lib/system/ctypes.nim84
-rw-r--r--lib/system/cyclebreaker.nim5
-rw-r--r--lib/system/deepcopy.nim16
-rw-r--r--lib/system/dollars.nim27
-rw-r--r--lib/system/dyncalls.nim14
-rw-r--r--lib/system/embedded.nim5
-rw-r--r--lib/system/exceptions.nim96
-rw-r--r--lib/system/excpt.nim109
-rw-r--r--lib/system/fatal.nim46
-rw-r--r--lib/system/gc.nim36
-rw-r--r--lib/system/gc2.nim749
-rw-r--r--lib/system/gc_common.nim29
-rw-r--r--lib/system/gc_hooks.nim4
-rw-r--r--lib/system/gc_ms.nim22
-rw-r--r--lib/system/gc_regions.nim1
-rw-r--r--lib/system/hti.nim2
-rw-r--r--lib/system/inclrtl.nim5
-rw-r--r--lib/system/indices.nim164
-rw-r--r--lib/system/iterators.nim66
-rw-r--r--lib/system/iterators_1.nim17
-rw-r--r--lib/system/jssys.nim268
-rw-r--r--lib/system/memalloc.nim18
-rw-r--r--lib/system/memory.nim1
-rw-r--r--lib/system/memtracker.nim8
-rw-r--r--lib/system/mm/go.nim2
-rw-r--r--lib/system/mm/malloc.nim4
-rw-r--r--lib/system/mmdisp.nim2
-rw-r--r--lib/system/nimscript.nim59
-rw-r--r--lib/system/orc.nim96
-rw-r--r--lib/system/osalloc.nim12
-rw-r--r--lib/system/platforms.nim6
-rw-r--r--lib/system/profiler.nim4
-rw-r--r--lib/system/rawquits.nim27
-rw-r--r--lib/system/repr.nim12
-rw-r--r--lib/system/repr_v2.nim64
-rw-r--r--lib/system/reprjs.nim12
-rw-r--r--lib/system/seqs_v2.nim140
-rw-r--r--lib/system/setops.nim11
-rw-r--r--lib/system/sets.nim12
-rw-r--r--lib/system/stacktraces.nim12
-rw-r--r--lib/system/strmantle.nim49
-rw-r--r--lib/system/strs_v2.nim125
-rw-r--r--lib/system/syslocks.nim234
-rw-r--r--lib/system/sysstr.nim77
-rw-r--r--lib/system/threadimpl.nim111
-rw-r--r--lib/system/threadlocalstorage.nim176
-rw-r--r--lib/system/widestrs.nim229
-rw-r--r--lib/system_overview.rst4
-rw-r--r--lib/windows/registry.nim5
-rw-r--r--lib/windows/winlean.nim291
-rw-r--r--lib/wrappers/mysql.nim1115
-rw-r--r--lib/wrappers/odbcsql.nim841
-rw-r--r--lib/wrappers/openssl.nim180
-rw-r--r--lib/wrappers/postgres.nim378
-rw-r--r--lib/wrappers/sqlite3.nim392
-rw-r--r--nim.nimble13
-rw-r--r--nimdoc/extlinks/project/doc/manual.md17
-rw-r--r--nimdoc/extlinks/project/expected/_._/util.html105
-rw-r--r--nimdoc/extlinks/project/expected/_._/util.idx2
-rw-r--r--nimdoc/extlinks/project/expected/doc/manual.html45
-rw-r--r--nimdoc/extlinks/project/expected/doc/manual.idx3
-rw-r--r--nimdoc/extlinks/project/expected/main.html142
-rw-r--r--nimdoc/extlinks/project/expected/main.idx4
-rw-r--r--nimdoc/extlinks/project/expected/sub/submodule.html131
-rw-r--r--nimdoc/extlinks/project/expected/sub/submodule.idx3
-rw-r--r--nimdoc/extlinks/project/expected/theindex.html59
-rw-r--r--nimdoc/extlinks/project/main.nim23
-rw-r--r--nimdoc/extlinks/project/sub/submodule.nim13
-rw-r--r--nimdoc/extlinks/util.nim2
-rw-r--r--nimdoc/rst2html/expected/rst_examples.html14
-rw-r--r--nimdoc/rsttester.nim6
-rw-r--r--nimdoc/test_doctype/expected/test_doctype.html17
-rw-r--r--nimdoc/test_out_index_dot_html/expected/foo.idx3
-rw-r--r--nimdoc/test_out_index_dot_html/expected/index.html16
-rw-r--r--nimdoc/test_out_index_dot_html/expected/theindex.html12
-rw-r--r--nimdoc/tester.nim69
-rw-r--r--nimdoc/testproject/expected/nimdoc.out.css29
-rw-r--r--nimdoc/testproject/expected/subdir/subdir_b/utils.html76
-rw-r--r--nimdoc/testproject/expected/subdir/subdir_b/utils.idx88
-rw-r--r--nimdoc/testproject/expected/testproject.html169
-rw-r--r--nimdoc/testproject/expected/testproject.idx137
-rw-r--r--nimdoc/testproject/expected/theindex.html224
-rw-r--r--nimdoc/testproject/subdir/subdir_b/utils_helpers.nim5
-rw-r--r--nimdoc/testproject/testproject.nim37
-rw-r--r--nimpretty/nimpretty.nim50
-rw-r--r--nimpretty/tests/exhaustive.nim12
-rw-r--r--nimpretty/tests/expected/exhaustive.nim12
-rw-r--r--nimpretty/tests/expected/simple.nim11
-rw-r--r--nimpretty/tests/simple.nim11
-rw-r--r--nimsuggest/nimsuggest.nim585
-rw-r--r--nimsuggest/nimsuggest.nimble12
-rw-r--r--nimsuggest/procmonitor.nim34
-rw-r--r--nimsuggest/sexp.nim5
-rw-r--r--nimsuggest/tester.nim23
-rw-r--r--nimsuggest/tests/module_20265.nim6
-rw-r--r--nimsuggest/tests/t20265_1.nim8
-rw-r--r--nimsuggest/tests/t20265_2.nim8
-rw-r--r--nimsuggest/tests/t20440.nim7
-rw-r--r--nimsuggest/tests/t20440.nims1
-rw-r--r--nimsuggest/tests/t21185.nim18
-rw-r--r--nimsuggest/tests/t22448.nim11
-rw-r--r--nimsuggest/tests/tarrowcrash.nim20
-rw-r--r--nimsuggest/tests/tchk1.nim2
-rw-r--r--nimsuggest/tests/tchk2.nim35
-rw-r--r--nimsuggest/tests/tconcept1.nim12
-rw-r--r--nimsuggest/tests/tconcept2.nim15
-rw-r--r--nimsuggest/tests/tdef1.nim10
-rw-r--r--nimsuggest/tests/tdot4.nim2
-rw-r--r--nimsuggest/tests/tenum_field.nim17
-rw-r--r--nimsuggest/tests/tfatal1.nim15
-rw-r--r--nimsuggest/tests/tgeneric_highlight.nim7
-rw-r--r--nimsuggest/tests/tgenerics.nim18
-rw-r--r--nimsuggest/tests/tic.nim20
-rw-r--r--nimsuggest/tests/timport_highlight.nim12
-rw-r--r--nimsuggest/tests/tinclude.nim2
-rw-r--r--nimsuggest/tests/tsug_pragmas.nim40
-rw-r--r--nimsuggest/tests/tsug_recursive.nim8
-rw-r--r--nimsuggest/tests/tsug_template.nim2
-rw-r--r--nimsuggest/tests/tsug_typedecl.nim4
-rw-r--r--nimsuggest/tests/ttype_decl.nim4
-rw-r--r--nimsuggest/tests/tuse.nim8
-rw-r--r--nimsuggest/tests/tuse_enum.nim15
-rw-r--r--nimsuggest/tests/tv3.nim6
-rw-r--r--nimsuggest/tests/tv3_con.nim13
-rw-r--r--nimsuggest/tests/tv3_forward_definition.nim23
-rw-r--r--nimsuggest/tests/tv3_generics.nim18
-rw-r--r--nimsuggest/tests/tv3_globalSymbols.nim14
-rw-r--r--nimsuggest/tests/tv3_outline.nim45
-rw-r--r--nimsuggest/tests/twithin_macro.nim4
-rw-r--r--nimsuggest/tests/twithin_macro_prefix.nim2
-rw-r--r--readme.md21
-rw-r--r--testament/categories.nim20
-rw-r--r--testament/important_packages.nim76
-rw-r--r--testament/lib/stdtest/specialpaths.nim2
-rw-r--r--testament/lib/stdtest/testutils.nim7
-rw-r--r--testament/specs.nim22
-rw-r--r--testament/testament.nim59
-rw-r--r--tests/alias/t19349.nim19
-rw-r--r--tests/align/talign.nim16
-rw-r--r--tests/alloc/tmembug.nim54
-rw-r--r--tests/alloc/tmembug2.nim58
-rw-r--r--tests/arc/t17812.nim12
-rw-r--r--tests/arc/t18645.nim18
-rw-r--r--tests/arc/t18977.nim26
-rw-r--r--tests/arc/t19231.nim18
-rw-r--r--tests/arc/t19364.nim30
-rw-r--r--tests/arc/t19862.nim2
-rw-r--r--tests/arc/t20456.nim7
-rw-r--r--tests/arc/t20588.nim25
-rw-r--r--tests/arc/t21184.nim77
-rw-r--r--tests/arc/t22218.nim25
-rw-r--r--tests/arc/t22237.nim55
-rw-r--r--tests/arc/t22478.nim46
-rw-r--r--tests/arc/t22787.nim37
-rw-r--r--tests/arc/t23247.nim52
-rw-r--r--tests/arc/taliased_reassign.nim41
-rw-r--r--tests/arc/tarc_macro.nim57
-rw-r--r--tests/arc/tarc_orc.nim151
-rw-r--r--tests/arc/tarcmisc.nim349
-rw-r--r--tests/arc/tasyncleak.nim2
-rw-r--r--tests/arc/tcaseobj.nim115
-rw-r--r--tests/arc/tcaseobjcopy.nim23
-rw-r--r--tests/arc/tcomputedgoto.nim15
-rw-r--r--tests/arc/tcomputedgotocopy.nim11
-rw-r--r--tests/arc/tcontrolflow.nim2
-rw-r--r--tests/arc/tdup.nim71
-rw-r--r--tests/arc/texplicit_sink.nim29
-rw-r--r--tests/arc/thard_alignment.nim4
-rw-r--r--tests/arc/titeration_doesnt_copy.nim67
-rw-r--r--tests/arc/tmalloc.nim16
-rw-r--r--tests/arc/tmove_regression.nim22
-rw-r--r--tests/arc/tmovebug.nim25
-rw-r--r--tests/arc/topenarray.nim62
-rw-r--r--tests/arc/topt_no_cursor.nim72
-rw-r--r--tests/arc/topt_refcursors.nim8
-rw-r--r--tests/arc/topt_wasmoved_destroy_pairs.nim14
-rw-r--r--tests/arc/trepr.nim8
-rw-r--r--tests/arc/tstringliteral.nim17
-rw-r--r--tests/arc/tweave.nim3
-rw-r--r--tests/arc/twrong_sinkinference.nim2
-rw-r--r--tests/array/t15117.nim27
-rw-r--r--tests/array/t20248.nim14
-rw-r--r--tests/array/tinvalidarrayaccess.nim (renamed from tests/misc/tinvalidarrayaccess.nim)0
-rw-r--r--tests/array/tinvalidarrayaccess2.nim (renamed from tests/misc/tinvalidarrayaccess2.nim)0
-rw-r--r--tests/array/tlargeindex.nim18
-rw-r--r--tests/assert/panicoverride.nim15
-rw-r--r--tests/assert/t21195.nim6
-rw-r--r--tests/assert/tassert2.nim6
-rw-r--r--tests/assert/tassert_c.nim6
-rw-r--r--tests/assign/tvariantasgn.nim13
-rw-r--r--tests/async/t11558.nim13
-rw-r--r--tests/async/t17045.nim28
-rw-r--r--tests/async/t20111.nim19
-rw-r--r--tests/async/t21447.nim6
-rw-r--r--tests/async/t21893.nim13
-rw-r--r--tests/async/t22210.nim41
-rw-r--r--tests/async/t22210_2.nim73
-rw-r--r--tests/async/tasync_gcunsafe.nim2
-rw-r--r--tests/async/tasync_traceback.nim47
-rw-r--r--tests/async/tasyncssl.nim1
-rw-r--r--tests/async/tioselectors.nim21
-rw-r--r--tests/async/tnewasyncudp.nim4
-rw-r--r--tests/async/twinasyncrw.nim12
-rw-r--r--tests/bind/tdatabind.nim95
-rw-r--r--tests/borrow/tinvalidborrow.nim25
-rw-r--r--tests/c/tcompile.c (renamed from tests/compilepragma/test.c)0
-rw-r--r--tests/c/tcompile.nim (renamed from tests/compilepragma/test.nim)2
-rw-r--r--tests/c/temit.nim (renamed from tests/misc/temit.nim)1
-rw-r--r--tests/c/treservedcidentsasfields.nim (renamed from tests/misc/treservedcidentsasfields.nim)78
-rw-r--r--tests/casestmt/t18964.nim12
-rw-r--r--tests/casestmt/tcase_issues.nim7
-rw-r--r--tests/casestmt/tcasestmt.nim19
-rw-r--r--tests/casestmt/trangeexhaustiveness.nim7
-rw-r--r--tests/cast/tcast.nim21
-rw-r--r--tests/ccgbugs/m1/defs.nim4
-rw-r--r--tests/ccgbugs/m2/defs.nim4
-rw-r--r--tests/ccgbugs/t10964.nim7
-rw-r--r--tests/ccgbugs/t13062.nim7
-rw-r--r--tests/ccgbugs/t15428.nim22
-rw-r--r--tests/ccgbugs/t20139.nim10
-rw-r--r--tests/ccgbugs/t20141.nim4
-rw-r--r--tests/ccgbugs/t20787.nim4
-rw-r--r--tests/ccgbugs/t21116.nim10
-rw-r--r--tests/ccgbugs/t21972.nim33
-rw-r--r--tests/ccgbugs/t21995.nim9
-rw-r--r--tests/ccgbugs/t22462.nim20
-rw-r--r--tests/ccgbugs/t23796.nim25
-rw-r--r--tests/ccgbugs/tbug21505.nim39
-rw-r--r--tests/ccgbugs/tcgbug.nim39
-rw-r--r--tests/ccgbugs/tderefblock.nim55
-rw-r--r--tests/ccgbugs/tforward_decl_only.nim4
-rw-r--r--tests/ccgbugs/tnoalias.nim2
-rw-r--r--tests/ccgbugs/tsamename3.nim9
-rw-r--r--tests/ccgbugs/twrong_tupleconv.nim15
-rw-r--r--tests/ccgbugs2/tcodegen.nim75
-rw-r--r--tests/closure/t19095.nim35
-rw-r--r--tests/closure/t20152.nim20
-rw-r--r--tests/closure/t8550.nim1
-rw-r--r--tests/closure/t9334.nim19
-rw-r--r--tests/closure/tclosure.nim23
-rw-r--r--tests/closure/tclosure_issues.nim6
-rw-r--r--tests/closure/tinvalidclosure.nim2
-rw-r--r--tests/closure/tinvalidclosure5.nim2
-rw-r--r--tests/closure/tnested.nim37
-rw-r--r--tests/closure/tstmtlist.nim9
-rw-r--r--tests/codegen/titaniummangle.nim193
-rw-r--r--tests/collections/tactiontable.nim (renamed from tests/actiontable/tactiontable.nim)0
-rw-r--r--tests/collections/tseq.nim31
-rw-r--r--tests/collections/ttables.nim7
-rw-r--r--tests/compileoption/texperimental.nim20
-rw-r--r--tests/compileoption/texperimental.nims19
-rw-r--r--tests/compiler/tbtrees.nim (renamed from tests/compiler/tbrees.nim)2
-rw-r--r--tests/compiler/tgrammar.nim7
-rw-r--r--tests/compilerapi/tcompilerapi.nim29
-rw-r--r--tests/concepts/t20237.nim3
-rw-r--r--tests/concepts/t976.nim57
-rw-r--r--tests/concepts/tconcepts.nim11
-rw-r--r--tests/concepts/tconcepts_issues.nim154
-rw-r--r--tests/concepts/texplain.nim38
-rw-r--r--tests/concepts/tusertypeclasses2.nim19
-rw-r--r--tests/config.nims13
-rw-r--r--tests/constr/tglobal.nim7
-rw-r--r--tests/constructors/a.nim2
-rw-r--r--tests/constructors/b.nim2
-rw-r--r--tests/constructors/t18990.nim3
-rw-r--r--tests/constructors/tconstr1.nim (renamed from tests/constr/tconstr1.nim)0
-rw-r--r--tests/constructors/tconstr2.nim (renamed from tests/constr/tconstr2.nim)0
-rw-r--r--tests/controlflow/tcontrolflow.nim21
-rw-r--r--tests/controlflow/tunamedbreak.nim15
-rw-r--r--tests/controlflow/tunreachable.nim79
-rw-r--r--tests/controlflow/tunreachable2.nim12
-rw-r--r--tests/converter/t21531.nim10
-rw-r--r--tests/converter/t7097.nim4
-rw-r--r--tests/converter/t7098.nim4
-rw-r--r--tests/converter/tconverter_unique_ptr.nim15
-rw-r--r--tests/coroutines/tgc.nim2
-rw-r--r--tests/coroutines/twait.nim2
-rw-r--r--tests/cpp/23962.h17
-rw-r--r--tests/cpp/fam.h4
-rw-r--r--tests/cpp/t12946.nim8
-rw-r--r--tests/cpp/t22679.nim50
-rw-r--r--tests/cpp/t22680.nim50
-rw-r--r--tests/cpp/t22712.nim15
-rw-r--r--tests/cpp/t23306.nim12
-rw-r--r--tests/cpp/t23434.nim17
-rw-r--r--tests/cpp/t23657.nim54
-rw-r--r--tests/cpp/t23962.nim32
-rw-r--r--tests/cpp/tasync_cpp.nim2
-rw-r--r--tests/cpp/tcasts.nim3
-rw-r--r--tests/cpp/tcodegendecl.nim17
-rw-r--r--tests/cpp/tconstructor.nim131
-rw-r--r--tests/cpp/tfam.nim7
-rw-r--r--tests/cpp/tinitializers.nim60
-rw-r--r--tests/cpp/tmember.nim75
-rw-r--r--tests/cpp/tmember_forward_declaration.nim27
-rw-r--r--tests/cpp/tnoinitfield.nim30
-rw-r--r--tests/cpp/torc.nim50
-rw-r--r--tests/cpp/tpassbypragmas.nim27
-rw-r--r--tests/cpp/tretvar.nim4
-rw-r--r--tests/cpp/tvirtual.nim126
-rw-r--r--tests/cpp/virtualptr.nim9
-rw-r--r--tests/defer/t22309.nim11
-rw-r--r--tests/deprecated/tannot.nim9
-rw-r--r--tests/deprecated/tmessages.nim10
-rw-r--r--tests/deprecated/tmodule1.nim33
-rw-r--r--tests/destructor/const_smart_ptr.nim10
-rw-r--r--tests/destructor/t16607.nim3
-rw-r--r--tests/destructor/t23748.nim31
-rw-r--r--tests/destructor/t23837.nim51
-rw-r--r--tests/destructor/tatomicptrs.nim12
-rw-r--r--tests/destructor/tbintree2.nim8
-rw-r--r--tests/destructor/tdistinctseq.nim8
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim35
-rw-r--r--tests/destructor/tgotoexc_leak.nim19
-rw-r--r--tests/destructor/tgotoexceptions7.nim3
-rw-r--r--tests/destructor/tmatrix.nim5
-rw-r--r--tests/destructor/tmove.nim18
-rw-r--r--tests/destructor/tmove_objconstr.nim37
-rw-r--r--tests/destructor/tnonvardestructor.nim247
-rw-r--r--tests/destructor/topttree.nim1
-rw-r--r--tests/destructor/tprevent_assign2.nim11
-rw-r--r--tests/destructor/tprevent_assign3.nim11
-rw-r--r--tests/destructor/tsink.nim70
-rw-r--r--tests/destructor/tuse_ownedref_after_move.nim2
-rw-r--r--tests/destructor/tv2_cast.nim70
-rw-r--r--tests/destructor/twasmoved.nim14
-rw-r--r--tests/destructor/twasmoved_error.nim37
-rw-r--r--tests/discard/t23677.nim12
-rw-r--r--tests/discard/tdiscardable.nim108
-rw-r--r--tests/discard/tfinallyerrmsg.nim19
-rw-r--r--tests/discard/tillegaldiscardtypes.nim14
-rw-r--r--tests/distinct/tborrow.nim (renamed from tests/borrow/tborrow.nim)43
-rw-r--r--tests/distinct/tcomplexaddressableconv.nim21
-rw-r--r--tests/distinct/tdistinct.nim26
-rw-r--r--tests/distinct/tinvalidborrow.nim42
-rw-r--r--tests/distinct/typeclassborrow.nim (renamed from tests/borrow/typeclassborrow.nim)26
-rw-r--r--tests/dll/nimhcr_basic.nim8
-rw-r--r--tests/dll/nimhcr_unit.nim6
-rw-r--r--tests/effects/tdiagnostic_messages.nim2
-rw-r--r--tests/effects/teffects1.nim9
-rw-r--r--tests/effects/teffects11.nim2
-rw-r--r--tests/effects/teffects19.nim2
-rw-r--r--tests/effects/teffects6.nim2
-rw-r--r--tests/effects/teffectsmisc.nim39
-rw-r--r--tests/effects/tfuncs_cannot_mutate.nim10
-rw-r--r--tests/effects/tfuncs_cannot_mutate2.nim7
-rw-r--r--tests/effects/tfuncs_cannot_mutate3.nim35
-rw-r--r--tests/effects/tfuncs_cannot_mutate_simple.nim5
-rw-r--r--tests/effects/tgcsafe.nim4
-rw-r--r--tests/effects/tgcsafe2.nim2
-rw-r--r--tests/effects/thooks.nim16
-rw-r--r--tests/effects/tlaxeffects.nim11
-rw-r--r--tests/effects/tnosideeffect.nim2
-rw-r--r--tests/effects/toutparam.nim28
-rw-r--r--tests/effects/tstrict_caseobjects.nim47
-rw-r--r--tests/effects/tstrict_effects3.nim11
-rw-r--r--tests/effects/tstrict_funcs_imports.nim19
-rw-r--r--tests/effects/tstrictfuncs_misc.nim65
-rw-r--r--tests/enum/t21863.nim28
-rw-r--r--tests/enum/tambiguousoverloads.nim26
-rw-r--r--tests/enum/tcrossmodule.nim5
-rw-r--r--tests/enum/tenum.nim106
-rw-r--r--tests/enum/tenum_duplicate.nim10
-rw-r--r--tests/enum/tenum_invalid.nim8
-rw-r--r--tests/enum/toverloadedname.nim7
-rw-r--r--tests/enum/tpure_enums_conflict.nim14
-rw-r--r--tests/enum/tpure_enums_conflict_legacy.nim25
-rw-r--r--tests/enum/tredefinition.nim9
-rw-r--r--tests/enum/ttypenameconflict.nim13
-rw-r--r--tests/errmsgs/mambparam1.nim1
-rw-r--r--tests/errmsgs/mambparam2.nim2
-rw-r--r--tests/errmsgs/mambparam3.nim1
-rw-r--r--tests/errmsgs/mb.nim (renamed from tests/clearmsg/mb.nim)0
-rw-r--r--tests/errmsgs/mc.nim (renamed from tests/clearmsg/mc.nim)0
-rw-r--r--tests/errmsgs/t10489_a.nim2
-rw-r--r--tests/errmsgs/t10489_b.nim2
-rw-r--r--tests/errmsgs/t10542.nim24
-rw-r--r--tests/errmsgs/t10735.nim59
-rw-r--r--tests/errmsgs/t14444.nim14
-rw-r--r--tests/errmsgs/t16654.nim12
-rw-r--r--tests/errmsgs/t17460.nim4
-rw-r--r--tests/errmsgs/t18886.nim18
-rw-r--r--tests/errmsgs/t19224.nim12
-rw-r--r--tests/errmsgs/t21257.nim20
-rw-r--r--tests/errmsgs/t22097.nim9
-rw-r--r--tests/errmsgs/t22284.nim25
-rw-r--r--tests/errmsgs/t22753.nim52
-rw-r--r--tests/errmsgs/t22852.nim9
-rw-r--r--tests/errmsgs/t22996.nim7
-rw-r--r--tests/errmsgs/t23060.nim5
-rw-r--r--tests/errmsgs/t23419.nim5
-rw-r--r--tests/errmsgs/t23435.nim12
-rw-r--r--tests/errmsgs/t23536.nim26
-rw-r--r--tests/errmsgs/t2614.nim4
-rw-r--r--tests/errmsgs/t5167_4.nim2
-rw-r--r--tests/errmsgs/t5167_5.nim22
-rw-r--r--tests/errmsgs/t6499.nim6
-rw-r--r--tests/errmsgs/t8064.nim11
-rw-r--r--tests/errmsgs/t8794.nim28
-rw-r--r--tests/errmsgs/t9768.nim11
-rw-r--r--tests/errmsgs/ta.nim (renamed from tests/clearmsg/ta.nim)0
-rw-r--r--tests/errmsgs/tambparam.nim16
-rw-r--r--tests/errmsgs/tambparam_legacy.nim14
-rw-r--r--tests/errmsgs/tassignunpack.nim3
-rw-r--r--tests/errmsgs/tcall_with_default_arg.nim2
-rw-r--r--tests/errmsgs/tcase_stmt.nim29
-rw-r--r--tests/errmsgs/tconcisetypemismatch.nim23
-rw-r--r--tests/errmsgs/tconstinfo.nim7
-rw-r--r--tests/errmsgs/tconsttypemismatch.nim (renamed from tests/clearmsg/tconsttypemismatch.nim)0
-rw-r--r--tests/errmsgs/tgcsafety.nim6
-rw-r--r--tests/errmsgs/tgenericmismatchsegfault.nim13
-rw-r--r--tests/errmsgs/tgenericmismatchsegfault_legacy.nim10
-rw-r--r--tests/errmsgs/tinconsistentgensym.nim25
-rw-r--r--tests/errmsgs/tmacroerrorproc.nim (renamed from tests/clearmsg/tmacroerrorproc.nim)0
-rw-r--r--tests/errmsgs/tmetaobjectfields.nim75
-rw-r--r--tests/errmsgs/tnoop.nim (renamed from tests/misc/tnoop.nim)0
-rw-r--r--tests/errmsgs/tproc_mismatch.nim25
-rw-r--r--tests/errmsgs/tproper_stacktrace.nim31
-rw-r--r--tests/errmsgs/tproper_stacktrace2.nim3
-rw-r--r--tests/errmsgs/tproper_stacktrace3.nim3
-rw-r--r--tests/errmsgs/tsigmatch.nim30
-rw-r--r--tests/errmsgs/tsigmatch2.nim4
-rw-r--r--tests/errmsgs/tsimpletypecheck.nim (renamed from tests/misc/tsimtych.nim)2
-rw-r--r--tests/errmsgs/tsubscriptmismatch.nim11
-rw-r--r--tests/errmsgs/tsubscriptmismatch_legacy.nim10
-rw-r--r--tests/errmsgs/ttupleindexoutofbounds.nim2
-rw-r--r--tests/errmsgs/ttypeAllowed.nim8
-rw-r--r--tests/errmsgs/tundeclared_routine.nim2
-rw-r--r--tests/errmsgs/tunresolvedinnerproc.nim (renamed from tests/misc/tnoinst.nim)10
-rw-r--r--tests/errmsgs/tuntypedoverload.nim37
-rw-r--r--tests/errmsgs/twrong_explicit_typeargs.nim26
-rw-r--r--tests/errmsgs/twrong_explicit_typeargs_legacy.nim25
-rw-r--r--tests/exception/m21261.nim1
-rw-r--r--tests/exception/m22469.nim4
-rw-r--r--tests/exception/t13115.nim7
-rw-r--r--tests/exception/t20613.nim8
-rw-r--r--tests/exception/t21261.nim9
-rw-r--r--tests/exception/t22008.nim8
-rw-r--r--tests/exception/t22469.nim16
-rw-r--r--tests/exception/texceptionbreak.nim8
-rw-r--r--tests/exprs/t22604.nim36
-rw-r--r--tests/float/tfloat4.nim12
-rw-r--r--tests/float/tfloats.nim8
-rw-r--r--tests/gc/closureleak.nim14
-rw-r--r--tests/gc/tdisable_orc.nim9
-rw-r--r--tests/generics/m14509.nim16
-rw-r--r--tests/generics/m22373a.nim7
-rw-r--r--tests/generics/m22373b.nim18
-rw-r--r--tests/generics/m3770.nim11
-rw-r--r--tests/generics/mdotlookup.nim12
-rw-r--r--tests/generics/mfriends.nim (renamed from tests/friends/mfriends.nim)0
-rw-r--r--tests/generics/mopensymimport1.nim34
-rw-r--r--tests/generics/mopensymimport2.nim16
-rw-r--r--tests/generics/mtypenodes.nim6
-rw-r--r--tests/generics/muninstantiatedgenericcalls.nim26
-rw-r--r--tests/generics/t12938.nim9
-rw-r--r--tests/generics/t14193.nim6
-rw-r--r--tests/generics/t14509.nim4
-rw-r--r--tests/generics/t1500.nim8
-rw-r--r--tests/generics/t17509.nim25
-rw-r--r--tests/generics/t18823.nim6
-rw-r--r--tests/generics/t19848.nim16
-rw-r--r--tests/generics/t20996.nim15
-rw-r--r--tests/generics/t21742.nim10
-rw-r--r--tests/generics/t21760.nim8
-rw-r--r--tests/generics/t21958.nim11
-rw-r--r--tests/generics/t22373.nim16
-rw-r--r--tests/generics/t22826.nim8
-rw-r--r--tests/generics/t23186.nim155
-rw-r--r--tests/generics/t23790.nim14
-rw-r--r--tests/generics/t23853.nim91
-rw-r--r--tests/generics/t23854.nim71
-rw-r--r--tests/generics/t23855.nim61
-rw-r--r--tests/generics/t3770.nim13
-rw-r--r--tests/generics/t500.nim8
-rw-r--r--tests/generics/t6137.nim4
-rw-r--r--tests/generics/t6637.nim9
-rw-r--r--tests/generics/t7446.nim10
-rw-r--r--tests/generics/t7839.nim9
-rw-r--r--tests/generics/taliashijack.nim8
-rw-r--r--tests/generics/tbadcache.nim26
-rw-r--r--tests/generics/tbaddeprecated.nim55
-rw-r--r--tests/generics/tbracketinstantiation.nim86
-rw-r--r--tests/generics/tcalltype.nim26
-rw-r--r--tests/generics/tconstraints.nim (renamed from tests/constraints/tconstraints.nim)0
-rw-r--r--tests/generics/tfriends.nim (renamed from tests/friends/tfriends.nim)0
-rw-r--r--tests/generics/tgeneric0.nim43
-rw-r--r--tests/generics/tgeneric3.nim8
-rw-r--r--tests/generics/tgenerics_issues.nim20
-rw-r--r--tests/generics/tgenerics_various.nim71
-rw-r--r--tests/generics/tgenericwhen.nim58
-rw-r--r--tests/generics/tgensyminst.nim29
-rw-r--r--tests/generics/timpl_ast.nim80
-rw-r--r--tests/generics/timplicit_and_explicit.nim28
-rw-r--r--tests/generics/timports.nim22
-rw-r--r--tests/generics/tmacroinjectedsym.nim186
-rw-r--r--tests/generics/tmacroinjectedsymwarning.nim59
-rw-r--r--tests/generics/tnestedissues.nim24
-rw-r--r--tests/generics/tnestedtemplate.nim9
-rw-r--r--tests/generics/tobjecttyperel.nim1
-rw-r--r--tests/generics/topensymimport.nim5
-rw-r--r--tests/generics/toverloading_typedesc.nim5
-rw-r--r--tests/generics/tprevent_double_bind.nim2
-rw-r--r--tests/generics/treentranttypes.nim11
-rw-r--r--tests/generics/treturn_inference.nim184
-rw-r--r--tests/generics/tstatic_constrained.nim6
-rw-r--r--tests/generics/tuninstantiated_failure.nim16
-rw-r--r--tests/generics/tuninstantiatedgenericcalls.nim517
-rw-r--r--tests/generics/twrong_explicit_typeargs.nim16
-rw-r--r--tests/generics/twrong_generic_object.nim4
-rw-r--r--tests/global/a_module.nim (renamed from tests/constr/a_module.nim)0
-rw-r--r--tests/global/t15005.nim18
-rw-r--r--tests/global/t21896.nim9
-rw-r--r--tests/global/t3505.nim41
-rw-r--r--tests/global/tglobal2.nim9
-rw-r--r--tests/ic/config.nims2
-rw-r--r--tests/ic/tgenericinst.nim11
-rw-r--r--tests/implicit/timplicit.nim49
-rw-r--r--tests/import/buzz/m21496.nim1
-rw-r--r--tests/import/fizz/m21496.nim1
-rw-r--r--tests/import/t21496.nim9
-rw-r--r--tests/import/t22065.nim5
-rw-r--r--tests/import/t22208.nim6
-rw-r--r--tests/import/t23167.nim5
-rw-r--r--tests/importalls/m4.nim1
-rw-r--r--tests/init/tcompiles.nim101
-rw-r--r--tests/init/tinitchecks_v2.nim83
-rw-r--r--tests/init/tlet.nim55
-rw-r--r--tests/init/tlet_uninit2.nim5
-rw-r--r--tests/init/tlet_uninit3.nim24
-rw-r--r--tests/init/tlet_uninit4.nim12
-rw-r--r--tests/init/toutparam_subtype.nim24
-rw-r--r--tests/init/toutparams.nim14
-rw-r--r--tests/init/tproveinit.nim (renamed from tests/misc/tproveinit.nim)0
-rw-r--r--tests/init/treturns.nim106
-rw-r--r--tests/init/tuninit1.nim2
-rw-r--r--tests/int/t1.nim61
-rw-r--r--tests/int/tarithm.nim (renamed from tests/arithm/tarithm.nim)0
-rw-r--r--tests/int/tashr.nim (renamed from tests/arithm/tashr.nim)0
-rw-r--r--tests/int/tdiv.nim (renamed from tests/arithm/tdiv.nim)0
-rw-r--r--tests/int/tints.nim (renamed from tests/misc/tints.nim)75
-rw-r--r--tests/int/tunsigned64mod.nim (renamed from tests/misc/tunsigned64mod.nim)0
-rw-r--r--tests/int/tunsignedcmp.nim (renamed from tests/misc/tunsignedcmp.nim)0
-rw-r--r--tests/int/tunsignedcomp.nim (renamed from tests/misc/tunsignedcomp.nim)0
-rw-r--r--tests/int/tunsignedconv.nim (renamed from tests/misc/tunsignedconv.nim)43
-rw-r--r--tests/int/tunsignedinc.nim (renamed from tests/misc/tunsignedinc.nim)6
-rw-r--r--tests/int/tunsignedmisc.nim (renamed from tests/misc/tunsignedmisc.nim)0
-rw-r--r--tests/int/twrongexplicitvarconv.nim16
-rw-r--r--tests/int/twrongvarconv.nim9
-rw-r--r--tests/isolate/tisolated_lock.nim67
-rw-r--r--tests/iter/t1550.nim4
-rw-r--r--tests/iter/t20891.nim28
-rw-r--r--tests/iter/t21306.nim118
-rw-r--r--tests/iter/t21737.nim22
-rw-r--r--tests/iter/t22148.nim15
-rw-r--r--tests/iter/t22548.nim21
-rw-r--r--tests/iter/t22619.nim83
-rw-r--r--tests/iter/t2771.nim4
-rw-r--r--tests/iter/tanoniter1.nim1
-rw-r--r--tests/iter/tclosureiters.nim22
-rw-r--r--tests/iter/tgeniteratorinblock.nim54
-rw-r--r--tests/iter/titer.nim33
-rw-r--r--tests/iter/titer11.nim1
-rw-r--r--tests/iter/titer12.nim1
-rw-r--r--tests/iter/titer_issues.nim47
-rw-r--r--tests/iter/titervaropenarray.nim2
-rw-r--r--tests/iter/tyieldintry.nim24
-rw-r--r--tests/js/t20235.nim11
-rw-r--r--tests/js/t21209.nim6
-rw-r--r--tests/js/t21209.nims1
-rw-r--r--tests/js/t21247.nim15
-rw-r--r--tests/js/t21439.nim11
-rw-r--r--tests/js/t7109.nim8
-rw-r--r--tests/js/t8821.nim5
-rw-r--r--tests/js/tasyncjs.nim10
-rw-r--r--tests/js/tcodegendeclproc.nim2
-rw-r--r--tests/js/tdanger.nim17
-rw-r--r--tests/js/tjsffi.nim11
-rw-r--r--tests/js/tjsffi_old.nim6
-rw-r--r--tests/js/tjsnimscombined.nim1
-rw-r--r--tests/js/tjsnimscombined.nims1
-rw-r--r--tests/js/tnativeexc.nim2
-rw-r--r--tests/js/tneginthash.nim21
-rw-r--r--tests/js/tos.nim10
-rw-r--r--tests/js/trepr.nim4
-rw-r--r--tests/js/tsourcemap.nim96
-rw-r--r--tests/js/tstdlib_imports.nim11
-rw-r--r--tests/js/tstdlib_various.nim7
-rw-r--r--tests/js/ttypedarray.nim11
-rw-r--r--tests/lent/t16898.nim2
-rw-r--r--tests/lent/tbasic_lent_check.nim2
-rw-r--r--tests/lent/tlent_from_var.nim75
-rw-r--r--tests/lent/tvm.nim21
-rw-r--r--tests/lexer/nim.cfg1
-rw-r--r--tests/lexer/tcustom_numeric_literals.nim38
-rw-r--r--tests/lexer/tlexerspaces.nim2
-rw-r--r--tests/lexer/trawstr.nim (renamed from tests/misc/trawstr.nim)0
-rw-r--r--tests/lexer/tunary_minus.nim4
-rw-r--r--tests/lookups/issue_23032/deep_scope.nim2
-rw-r--r--tests/lookups/issue_23172/m23172.nim6
-rw-r--r--tests/lookups/mambsym1.nim (renamed from tests/ambsym/mambsym1.nim)0
-rw-r--r--tests/lookups/mambsym2.nim (renamed from tests/ambsym/mambsym2.nim)0
-rw-r--r--tests/lookups/mambsym3.nim4
-rw-r--r--tests/lookups/mambsym4.nim4
-rw-r--r--tests/lookups/mambsys1.nim (renamed from tests/ambsym/mambsys1.nim)0
-rw-r--r--tests/lookups/mambsys2.nim (renamed from tests/ambsym/mambsys2.nim)0
-rw-r--r--tests/lookups/mambtype1.nim1
-rw-r--r--tests/lookups/mambtype2.nim4
-rw-r--r--tests/lookups/mbind3.nim (renamed from tests/bind/mbind3.nim)0
-rw-r--r--tests/lookups/mdisambsym1.nim2
-rw-r--r--tests/lookups/mdisambsym2.nim1
-rw-r--r--tests/lookups/mdisambsym3.nim1
-rw-r--r--tests/lookups/mmacroamb.nim10
-rw-r--r--tests/lookups/t23032.nim13
-rw-r--r--tests/lookups/t23172.nim9
-rw-r--r--tests/lookups/t23749.nim37
-rw-r--r--tests/lookups/tambiguousemit.nim12
-rw-r--r--tests/lookups/tambprocvar.nim19
-rw-r--r--tests/lookups/tambsym.nim (renamed from tests/ambsym/tambsym.nim)0
-rw-r--r--tests/lookups/tambsym2.nim (renamed from tests/ambsym/tambsym2.nim)0
-rw-r--r--tests/lookups/tambsym3.nim (renamed from tests/ambsym/tambsym3.nim)2
-rw-r--r--tests/lookups/tambsymmanual.nim25
-rw-r--r--tests/lookups/tambsys.nim (renamed from tests/ambsym/tambsys.nim)0
-rw-r--r--tests/lookups/tambtype.nim20
-rw-r--r--tests/lookups/tbind.nim (renamed from tests/bind/tbind.nim)0
-rw-r--r--tests/lookups/tdisambsym.nim8
-rw-r--r--tests/lookups/tenumlocalsym.nim22
-rw-r--r--tests/lookups/tgensym.nim (renamed from tests/gensym/tgensym.nim)0
-rw-r--r--tests/lookups/tgensymgeneric.nim (renamed from tests/gensym/tgensymgeneric.nim)0
-rw-r--r--tests/lookups/tinvalidbindtypedesc.nim (renamed from tests/bind/tinvalidbindtypedesc.nim)0
-rw-r--r--tests/lookups/tmacroamb.nim5
-rw-r--r--tests/lookups/tmoduleclash1.nim13
-rw-r--r--tests/lookups/tmoduleclash2.nim6
-rw-r--r--tests/lookups/tnicerrorforsymchoice.nim (renamed from tests/bind/tnicerrorforsymchoice.nim)2
-rw-r--r--tests/lookups/told_bind_expr.nim (renamed from tests/bind/told_bind_expr.nim)0
-rw-r--r--tests/lookups/tundeclaredmixin.nim17
-rw-r--r--tests/macros/t14329.nim4
-rw-r--r--tests/macros/t15691.nim22
-rw-r--r--tests/macros/t15751.nim11
-rw-r--r--tests/macros/t16758.nim38
-rw-r--r--tests/macros/t19766_20114.nim16
-rw-r--r--tests/macros/t20435.nim30
-rw-r--r--tests/macros/t21593.nim13
-rw-r--r--tests/macros/t23032_1.nim19
-rw-r--r--tests/macros/t23032_2.nim20
-rw-r--r--tests/macros/t23547.nim23
-rw-r--r--tests/macros/t23784.nim157
-rw-r--r--tests/macros/tastrepr.nim16
-rw-r--r--tests/macros/tfail_parse.nim15
-rw-r--r--tests/macros/tgetimpl.nim15
-rw-r--r--tests/macros/tgettypeinst7737.nim61
-rw-r--r--tests/macros/tmacros_various.nim61
-rw-r--r--tests/macros/tmacrostmt.nim2
-rw-r--r--tests/macros/tmacrotypes.nim6
-rw-r--r--tests/macros/tprocgettype.nim28
-rw-r--r--tests/macros/tstructuredlogging.nim4
-rw-r--r--tests/macros/ttemplatesymbols.nim2
-rw-r--r--tests/macros/ttryparseexpr.nim4
-rw-r--r--tests/manyloc/argument_parser/argument_parser.nim4
-rw-r--r--tests/manyloc/keineschweine/dependencies/nake/nake.nim2
-rw-r--r--tests/manyloc/keineschweine/enet_server/enet_server.nim2
-rw-r--r--tests/manyloc/keineschweine/enet_server/server_utils.nim4
-rw-r--r--tests/manyloc/keineschweine/keineschweine.nim2
-rw-r--r--tests/manyloc/keineschweine/lib/client_helpers.nim4
-rw-r--r--tests/manyloc/keineschweine/lib/sg_assets.nim4
-rw-r--r--tests/manyloc/keineschweine/lib/sg_packets.nim4
-rw-r--r--tests/manyloc/keineschweine/server/old_dirserver.nim6
-rw-r--r--tests/manyloc/keineschweine/server/old_server_utils.nim4
-rw-r--r--tests/manyloc/keineschweine/server/old_sg_server.nim2
-rw-r--r--tests/manyloc/keineschweine/server/sg_lobby.nim3
-rw-r--r--tests/manyloc/nake/nake.nim2
-rw-r--r--tests/metatype/tcompositetypeclasses.nim2
-rw-r--r--tests/metatype/tmetatype_issues.nim9
-rw-r--r--tests/metatype/tmetatype_various.nim7
-rw-r--r--tests/metatype/tstatic_generic_typeclass.nim30
-rw-r--r--tests/metatype/ttypedescnotnimnode.nim21
-rw-r--r--tests/metatype/ttypeselectors.nim13
-rw-r--r--tests/metatype/ttypetraits.nim37
-rw-r--r--tests/metatype/twrong_same_type.nim28
-rw-r--r--tests/method/nim.cfg1
-rw-r--r--tests/method/t20515.nim20
-rw-r--r--tests/method/t22673.nim21
-rw-r--r--tests/method/tcompilegenerics.nim24
-rw-r--r--tests/method/tgeneric_methods.nim84
-rw-r--r--tests/method/tmethod_various.nim7
-rw-r--r--tests/method/tmethods_old.nim12
-rw-r--r--tests/method/tmultim.nim193
-rw-r--r--tests/method/tvtable.nim24
-rw-r--r--tests/misc/m20456.nims1
-rw-r--r--tests/misc/mjsondoc.nim3
-rw-r--r--tests/misc/t11634.nim17
-rw-r--r--tests/misc/t12869.nim14
-rw-r--r--tests/misc/t14667.nim12
-rw-r--r--tests/misc/t15351.nim5
-rw-r--r--tests/misc/t16244.nim9
-rw-r--r--tests/misc/t16264.nim2
-rw-r--r--tests/misc/t16541.nim12
-rw-r--r--tests/misc/t18079.nim15
-rw-r--r--tests/misc/t20253.nim10
-rw-r--r--tests/misc/t20289.nim15
-rw-r--r--tests/misc/t20456_2.nim14
-rw-r--r--tests/misc/t20883.nim13
-rw-r--r--tests/misc/t21109.nim13
-rw-r--r--tests/misc/t21443.nim6
-rw-r--r--tests/misc/t23240.nim6
-rw-r--r--tests/misc/t3907.nim10
-rw-r--r--tests/misc/t6549.nim4
-rw-r--r--tests/misc/t8545.nim3
-rw-r--r--tests/misc/taddr.nim10
-rw-r--r--tests/misc/tcast.nim7
-rw-r--r--tests/misc/tconv.nim23
-rw-r--r--tests/misc/tcsharpusingstatement.nim26
-rw-r--r--tests/misc/tdefine.nim64
-rw-r--r--tests/misc/tgenconstraints.nim31
-rw-r--r--tests/misc/tradix.nim54
-rw-r--r--tests/misc/trfc405.nim28
-rw-r--r--tests/misc/trunner.nim28
-rw-r--r--tests/misc/trunner_special.nim8
-rw-r--r--tests/misc/tsizeof.nim8
-rw-r--r--tests/misc/tsizeof3.nim29
-rw-r--r--tests/misc/tspellsuggest.nim45
-rw-r--r--tests/misc/tspellsuggest2.nim45
-rw-r--r--tests/misc/tstrtabs.nim20
-rw-r--r--tests/misc/ttlsemulation.nim1
-rw-r--r--tests/misc/tvarnums.nim26
-rw-r--r--tests/misc/tvcc.nim9
-rw-r--r--tests/modules/mincludeprefix.nim1
-rw-r--r--tests/modules/mincludetemplate.nim1
-rw-r--r--tests/modules/timportas.nim11
-rw-r--r--tests/modules/tincludeprefix.nim3
-rw-r--r--tests/modules/tincludetemplate.nim5
-rw-r--r--tests/modules/tmodulesymtype.nim22
-rw-r--r--tests/modules/treorder.nim6
-rw-r--r--tests/msgs/mdeprecated3.nim (renamed from tests/deprecated/importme.nim)0
-rw-r--r--tests/msgs/mspellsuggest.nim (renamed from tests/misc/mspellsuggest.nim)0
-rw-r--r--tests/msgs/mused2a.nim (renamed from tests/pragmas/mused2a.nim)0
-rw-r--r--tests/msgs/mused2b.nim (renamed from tests/pragmas/mused2b.nim)0
-rw-r--r--tests/msgs/mused2c.nim (renamed from tests/pragmas/mused2c.nim)0
-rw-r--r--tests/msgs/mused3.nim (renamed from tests/pragmas/mused3.nim)0
-rw-r--r--tests/msgs/mused3a.nim (renamed from tests/pragmas/mused3a.nim)0
-rw-r--r--tests/msgs/mused3b.nim (renamed from tests/pragmas/mused3b.nim)0
-rw-r--r--tests/msgs/tdeprecated1.nim15
-rw-r--r--tests/msgs/tdeprecated2.nim (renamed from tests/deprecated/tdeprecated.nim)8
-rw-r--r--tests/msgs/tdeprecated3.nim33
-rw-r--r--tests/msgs/tdeprecatedequalhook.nim11
-rw-r--r--tests/msgs/texpandmacro.nim (renamed from tests/compilerfeatures/texpandmacro.nim)0
-rw-r--r--tests/msgs/thints_off.nim (renamed from tests/misc/thints_off.nim)0
-rw-r--r--tests/msgs/tspellsuggest.nim45
-rw-r--r--tests/msgs/tspellsuggest2.nim45
-rw-r--r--tests/msgs/tspellsuggest3.nim21
-rw-r--r--tests/msgs/tused2.nim (renamed from tests/pragmas/tused2.nim)8
-rw-r--r--tests/msgs/twarningaserror.nim (renamed from tests/misc/twarningaserror.nim)0
-rw-r--r--tests/navigator/tincludefile.nim2
-rw-r--r--tests/navigator/tnav1.nim2
-rw-r--r--tests/newconfig/tfoo.nims2
-rw-r--r--tests/nimdoc/m13129.nim1
-rw-r--r--tests/nimdoc/trunnableexamples.nim23
-rw-r--r--tests/nimdoc/trunnableexamples2.nim11
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim1
-rw-r--r--tests/niminaction/Chapter7/Tweeter/src/tweeter.nim1
-rw-r--r--tests/niminaction/Chapter7/Tweeter/tests/database_test.nim1
-rw-r--r--tests/niminaction/Chapter8/sdl/sdl_test.nim2
-rw-r--r--tests/notnil/tparse.nim18
-rw-r--r--tests/objects/mobject_default_value.nim15
-rw-r--r--tests/objects/t20972.nim15
-rw-r--r--tests/objects/t22301.nim17
-rw-r--r--tests/objects/tdefaultfieldscheck.nim16
-rw-r--r--tests/objects/tdefaultrangetypescheck.nim11
-rw-r--r--tests/objects/tillegal_recursion.nim2
-rw-r--r--tests/objects/tillegal_recursion2.nim6
-rw-r--r--tests/objects/tillegal_recursion3.nim6
-rw-r--r--tests/objects/tobject.nim20
-rw-r--r--tests/objects/tobject_default_value.nim780
-rw-r--r--tests/objects/tobjects_various.nim13
-rw-r--r--tests/objects/trequireinit.nim10
-rw-r--r--tests/objects/tunsafenew.nim10
-rw-r--r--tests/objects/tunsafenew2.nim15
-rw-r--r--tests/objects/twhen1.nim32
-rw-r--r--tests/objvariant/tconstobjvariant.nim18
-rw-r--r--tests/openarray/topenarray.nim37
-rw-r--r--tests/openarray/tuncheckedarray.nim19
-rw-r--r--tests/options/tnimbasepattern.nim26
-rw-r--r--tests/osproc/tnoexe.nim27
-rw-r--r--tests/osproc/twaitforexit.nim38
-rw-r--r--tests/overflow/tdistinct_range.nim (renamed from tests/overflw/tdistinct_range.nim)0
-rw-r--r--tests/overflow/toverflow.nim82
-rw-r--r--tests/overflow/toverflow2.nim (renamed from tests/overflw/toverflw2.nim)0
-rw-r--r--tests/overflow/toverflow_reorder.nim (renamed from tests/overflw/toverflw.nim)4
-rw-r--r--tests/overflow/tovfint.nim (renamed from tests/overflw/tovfint.nim)0
-rw-r--r--tests/overflow/trangechecks.nim (renamed from tests/misc/trangechecks.nim)0
-rw-r--r--tests/overflow/twronginference.nim10
-rw-r--r--tests/overload/issue22142/tfail_implicit_ambiguous.nim10
-rw-r--r--tests/overload/issue22142/tfail_nested_pointers.nim12
-rw-r--r--tests/overload/issue22142/tfail_object_is_generic.nim16
-rw-r--r--tests/overload/issue22142/tfail_typeclass_var_invar.nim9
-rw-r--r--tests/overload/issue22142/tissue22142_shouldpass.nim68
-rw-r--r--tests/overload/m19737.nim10
-rw-r--r--tests/overload/mvaruintconv.nim145
-rw-r--r--tests/overload/t19737.nim15
-rw-r--r--tests/overload/t23249.nim17
-rw-r--r--tests/overload/t23755.nim62
-rw-r--r--tests/overload/t8829.nim4
-rw-r--r--tests/overload/tgenericalias.nim13
-rw-r--r--tests/overload/tor_isnt_better.nim47
-rw-r--r--tests/overload/toverl4.nim26
-rw-r--r--tests/overload/toverload_issues.nim45
-rw-r--r--tests/overload/toverload_various.nim60
-rw-r--r--tests/overload/tproc_types_dont_like_subtypes.nim2
-rw-r--r--tests/overload/tstatic_with_converter.nim6
-rw-r--r--tests/overload/tuntypedarg.nim19
-rw-r--r--tests/overload/tvartypeclass.nim11
-rw-r--r--tests/overload/tvaruintconv.nim207
-rw-r--r--tests/parallel/t10913.nim2
-rw-r--r--tests/parallel/t7535.nim2
-rw-r--r--tests/parallel/t9691.nim2
-rw-r--r--tests/parallel/tblocking_channel.nim4
-rw-r--r--tests/parallel/tguard1.nim2
-rw-r--r--tests/parallel/tlet_spawn.nim11
-rw-r--r--tests/parallel/tsendtwice.nim21
-rw-r--r--tests/parallel/tsimple_array_checks.nim12
-rw-r--r--tests/parallel/tuseafterdef.nim1
-rw-r--r--tests/parser/t19430.nim3
-rw-r--r--tests/parser/t20922.nim35
-rw-r--r--tests/parser/tbinarynotindented.nim3
-rw-r--r--tests/parser/tbinarynotsameline.nim10
-rw-r--r--tests/parser/tcommand_as_expr.nim9
-rw-r--r--tests/parser/tcommandequals.nim17
-rw-r--r--tests/parser/tcommandindent.nim16
-rw-r--r--tests/parser/tdotlikeoperators.nim (renamed from tests/misc/trfc_341.nim)3
-rw-r--r--tests/parser/tdoublenotnil.nim3
-rw-r--r--tests/parser/tifextracolon.nim8
-rw-r--r--tests/parser/tpostexprblocks.nim11
-rw-r--r--tests/parser/tprocexprasstmt.nim14
-rw-r--r--tests/parser/ttry.nim27
-rw-r--r--tests/parser/ttupleunpack.nim65
-rw-r--r--tests/parser/ttypeexprobject.nim10
-rw-r--r--tests/parser/ttypeexprs.nim25
-rw-r--r--tests/pragmas/monoff1.nim1
-rw-r--r--tests/pragmas/mqualifiedmacro.nim10
-rw-r--r--tests/pragmas/t22713.nim12
-rw-r--r--tests/pragmas/t8741.nim2
-rw-r--r--tests/pragmas/tcompile_missing_file.nim5
-rw-r--r--tests/pragmas/tcustom_pragma.nim101
-rw-r--r--tests/pragmas/tinvalid_user_pragma.nim9
-rw-r--r--tests/pragmas/tinvalidcustompragma.nim23
-rw-r--r--tests/pragmas/tlocks.nim4
-rw-r--r--tests/pragmas/tonoff1.nim8
-rw-r--r--tests/pragmas/tonoff2.nim14
-rw-r--r--tests/pragmas/tpragmas_misc.nim5
-rw-r--r--tests/pragmas/tpragmas_reorder.nim19
-rw-r--r--tests/pragmas/tpush.nim129
-rw-r--r--tests/pragmas/tpushnotes.nim13
-rw-r--r--tests/pragmas/tqualifiedmacro.nim14
-rw-r--r--tests/pragmas/tuserpragmaargs.nim5
-rw-r--r--tests/proc/mdefaultprocparam.nim (renamed from tests/defaultprocparam/mdefaultprocparam.nim)0
-rw-r--r--tests/proc/t15949.nim16
-rw-r--r--tests/proc/t17157.nim1
-rw-r--r--tests/proc/t19795.nim19
-rw-r--r--tests/proc/t23874.nim26
-rw-r--r--tests/proc/tcolonisproc.nim (renamed from tests/misc/tcolonisproc.nim)0
-rw-r--r--tests/proc/tdefaultprocparam.nim (renamed from tests/defaultprocparam/tdefaultprocparam.nim)0
-rw-r--r--tests/proc/texplicitgenericcount.nim24
-rw-r--r--tests/proc/texplicitgenericcountverbose.nim22
-rw-r--r--tests/proc/texplicitgenerics.nim55
-rw-r--r--tests/proc/tgenericdefaultparam.nim98
-rw-r--r--tests/proc/tillegalreturntype.nim2
-rw-r--r--tests/proc/tinferlambdareturn.nim36
-rw-r--r--tests/proc/tlambdadonotation.nim (renamed from tests/misc/tlambdadonotation.nim)2
-rw-r--r--tests/proc/tnamedparams.nim (renamed from tests/namedparams/tnamedparams.nim)0
-rw-r--r--tests/proc/tnamedparams2.nim (renamed from tests/namedparams/tnamedparams2.nim)0
-rw-r--r--tests/proc/tnamedparams3.nim (renamed from tests/namedparams/tnamedparams3.nim)0
-rw-r--r--tests/proc/tparamsindefault.nim (renamed from tests/misc/tparamsindefault.nim)0
-rw-r--r--tests/proc/tprocvar.nim (renamed from tests/procvar/tprocvar.nim)0
-rw-r--r--tests/proc/tprocvarmismatch.nim (renamed from tests/procvar/tprocvarmismatch.nim)0
-rw-r--r--tests/proc/tstaticsignature.nim268
-rw-r--r--tests/proc/tunderscoreparam.nim122
-rw-r--r--tests/proc/twrongdefaultvalue.nim25
-rw-r--r--tests/range/t19678.nim17
-rw-r--r--tests/range/tcompiletime_range_checks.nim4
-rw-r--r--tests/range/texplicitvarconv.nim13
-rw-r--r--tests/range/toutofrangevarconv.nim14
-rw-r--r--tests/range/trange.nim22
-rw-r--r--tests/readme.md4
-rw-r--r--tests/refc/tsinkbug.nim26
-rw-r--r--tests/sets/t13764.nim6
-rw-r--r--tests/sets/t20997.nim18
-rw-r--r--tests/sets/thugeset.nim10
-rw-r--r--tests/sets/tnewsets.nim (renamed from tests/misc/tnewsets.nim)0
-rw-r--r--tests/sets/trangeincompatible.nim32
-rw-r--r--tests/sets/tsets.nim42
-rw-r--r--tests/sets/twrongenumrange.nim50
-rw-r--r--tests/showoff/tgenericmacrotypes.nim55
-rw-r--r--tests/slice/tdistinct.nim2
-rw-r--r--tests/specialops/tcallops.nim10
-rw-r--r--tests/specialops/terrmsgs.nim76
-rw-r--r--tests/specialops/tnewseq.nim22
-rw-r--r--tests/specialops/tsetter.nim10
-rw-r--r--tests/statictypes/t20148.nim8
-rw-r--r--tests/statictypes/t5780.nim3
-rw-r--r--tests/statictypes/t9255.nim2
-rw-r--r--tests/statictypes/tgenericcomputedrange.nim125
-rw-r--r--tests/statictypes/tstaticgenericparam.nim24
-rw-r--r--tests/statictypes/tstaticprocparams.nim16
-rw-r--r--tests/statictypes/tstatictypes.nim68
-rw-r--r--tests/stdlib/concurrency/tatomics.nim6
-rw-r--r--tests/stdlib/concurrency/tatomics_size.nim4
-rw-r--r--tests/stdlib/config.nims4
-rw-r--r--tests/stdlib/mimportutils.nim6
-rw-r--r--tests/stdlib/t15663.nim4
-rw-r--r--tests/stdlib/t19304.nim7
-rw-r--r--tests/stdlib/t20023.nim10
-rw-r--r--tests/stdlib/t21251.nim6
-rw-r--r--tests/stdlib/t21406.nim7
-rw-r--r--tests/stdlib/t21564.nim32
-rw-r--r--tests/stdlib/talgorithm.nim1
-rw-r--r--tests/stdlib/tarithmetics.nim1
-rw-r--r--tests/stdlib/tasynchttpserver.nim1
-rw-r--r--tests/stdlib/tasynchttpserver_transferencoding.nim2
-rw-r--r--tests/stdlib/tbase64.nim7
-rw-r--r--tests/stdlib/tbitops.nim1
-rw-r--r--tests/stdlib/tbitops_utils.nim4
-rw-r--r--tests/stdlib/tcasts.nim26
-rw-r--r--tests/stdlib/tcgi.nim4
-rw-r--r--tests/stdlib/tclosures.nim47
-rw-r--r--tests/stdlib/tcmdline.nim1
-rw-r--r--tests/stdlib/tcomplex.nim7
-rw-r--r--tests/stdlib/tcookies.nim1
-rw-r--r--tests/stdlib/tcritbits.nim1
-rw-r--r--tests/stdlib/tcstrutils.nim1
-rw-r--r--tests/stdlib/tdb.nim0
-rw-r--r--tests/stdlib/tdb.nims1
-rw-r--r--tests/stdlib/tdb_mysql.nim5
-rw-r--r--tests/stdlib/tdecls.nim25
-rw-r--r--tests/stdlib/tdeques.nim57
-rw-r--r--tests/stdlib/tdiff.nim1
-rw-r--r--tests/stdlib/tdistros_detect.nim (renamed from tests/distros/tdistros_detect.nim)7
-rw-r--r--tests/stdlib/tdochelpers.nim17
-rw-r--r--tests/stdlib/teditdistance.nim4
-rw-r--r--tests/stdlib/tencodings.nim8
-rw-r--r--tests/stdlib/tenumerate.nim4
-rw-r--r--tests/stdlib/tenumutils.nim11
-rw-r--r--tests/stdlib/tenvvars.nim11
-rw-r--r--tests/stdlib/texitprocs.nim1
-rw-r--r--tests/stdlib/tfdleak.nim2
-rw-r--r--tests/stdlib/tfilesanddirs.nim36
-rw-r--r--tests/stdlib/tfrexp1.nim1
-rw-r--r--tests/stdlib/tgetaddrinfo.nim1
-rw-r--r--tests/stdlib/tgetfileinfo.nim30
-rw-r--r--tests/stdlib/tgetprotobyname.nim4
-rw-r--r--tests/stdlib/tglobs.nim4
-rw-r--r--tests/stdlib/thashes.nim30
-rw-r--r--tests/stdlib/theapqueue.nim4
-rw-r--r--tests/stdlib/thighlite.nim13
-rw-r--r--tests/stdlib/thtmlparser.nim1
-rw-r--r--tests/stdlib/thttpclient.nim29
-rw-r--r--tests/stdlib/thttpclient_ssl.nim5
-rw-r--r--tests/stdlib/thttpcore.nim4
-rw-r--r--tests/stdlib/timportutils.nim15
-rw-r--r--tests/stdlib/tio.nim22
-rw-r--r--tests/stdlib/tjson.nim9
-rw-r--r--tests/stdlib/tjsonmacro.nim7
-rw-r--r--tests/stdlib/tjsonutils.nim37
-rw-r--r--tests/stdlib/tlists.nim1
-rw-r--r--tests/stdlib/tlocks.nim2
-rw-r--r--tests/stdlib/tmacros.nim190
-rw-r--r--tests/stdlib/tmarshal.nim42
-rw-r--r--tests/stdlib/tmarshalsegfault.nim54
-rw-r--r--tests/stdlib/tmath.nim35
-rw-r--r--tests/stdlib/tmd5.nim17
-rw-r--r--tests/stdlib/tmemfiles2.nim5
-rw-r--r--tests/stdlib/tmget.nim17
-rw-r--r--tests/stdlib/tmimetypes.nim9
-rw-r--r--tests/stdlib/tmisc_issues.nim20
-rw-r--r--tests/stdlib/tmitems.nim1
-rw-r--r--tests/stdlib/tmonotimes.nim1
-rw-r--r--tests/stdlib/tnativesockets.nim4
-rw-r--r--tests/stdlib/tnet.nim1
-rw-r--r--tests/stdlib/tnet_ll.nim5
-rw-r--r--tests/stdlib/tnetbind.nim1
-rw-r--r--tests/stdlib/tnetconnect.nim5
-rw-r--r--tests/stdlib/tnetdial.nim2
-rw-r--r--tests/stdlib/tnre.nim1
-rw-r--r--tests/stdlib/tntpath.nim4
-rw-r--r--tests/stdlib/toids.nim2
-rw-r--r--tests/stdlib/topenssl.nim4
-rw-r--r--tests/stdlib/toptions.nim9
-rw-r--r--tests/stdlib/tos.nim138
-rw-r--r--tests/stdlib/tos_unc.nim1
-rw-r--r--tests/stdlib/tosenv.nim9
-rw-r--r--tests/stdlib/tosproc.nim14
-rw-r--r--tests/stdlib/tosprocterminate.nim2
-rw-r--r--tests/stdlib/tpackedsets.nim4
-rw-r--r--tests/stdlib/tparsecfg.nim1
-rw-r--r--tests/stdlib/tparsecsv.nim4
-rw-r--r--tests/stdlib/tparseipv6.nim1
-rw-r--r--tests/stdlib/tparsesql.nim1
-rw-r--r--tests/stdlib/tparseuints.nim4
-rw-r--r--tests/stdlib/tparseutils.nim137
-rw-r--r--tests/stdlib/tpathnorm.nim1
-rw-r--r--tests/stdlib/tpaths.nim238
-rw-r--r--tests/stdlib/tpegs.nim18
-rw-r--r--tests/stdlib/tposix.nim72
-rw-r--r--tests/stdlib/tpunycode.nim206
-rw-r--r--tests/stdlib/tquit.nim17
-rw-r--r--tests/stdlib/trandom.nim86
-rw-r--r--tests/stdlib/trat_float.nim (renamed from tests/rational/trat_float.nim)0
-rw-r--r--tests/stdlib/trat_init.nim (renamed from tests/rational/trat_init.nim)0
-rw-r--r--tests/stdlib/trationals.nim13
-rw-r--r--tests/stdlib/tre.nim4
-rw-r--r--tests/stdlib/treadln.nim (renamed from tests/misc/treadln.nim)4
-rw-r--r--tests/stdlib/tregex.nim1
-rw-r--r--tests/stdlib/tregistry.nim2
-rw-r--r--tests/stdlib/trepr.nim57
-rw-r--r--tests/stdlib/tropes.nim1
-rw-r--r--tests/stdlib/trst.nim258
-rw-r--r--tests/stdlib/trstgen.nim18
-rw-r--r--tests/stdlib/tsequtils.nim6
-rw-r--r--tests/stdlib/tsetutils.nim1
-rw-r--r--tests/stdlib/tsha1.nim24
-rw-r--r--tests/stdlib/tsharedlist.nim2
-rw-r--r--tests/stdlib/tsharedtable.nim2
-rw-r--r--tests/stdlib/tsince.nim7
-rw-r--r--tests/stdlib/tsocketstreams.nim1
-rw-r--r--tests/stdlib/tsortcall.nim2
-rw-r--r--tests/stdlib/tsqlitebindatas.nim79
-rw-r--r--tests/stdlib/tsqlparser.nim1
-rw-r--r--tests/stdlib/tssl.nim10
-rw-r--r--tests/stdlib/tstaticos.nim8
-rw-r--r--tests/stdlib/tstats.nim4
-rw-r--r--tests/stdlib/tstdlib_issues.nim1
-rw-r--r--tests/stdlib/tstdlib_various.nim7
-rw-r--r--tests/stdlib/tstrbasics.nim2
-rw-r--r--tests/stdlib/tstreams.nim12
-rw-r--r--tests/stdlib/tstrformat.nim46
-rw-r--r--tests/stdlib/tstrformatlineinfo.nim8
-rw-r--r--tests/stdlib/tstrimpl.nim12
-rw-r--r--tests/stdlib/tstring.nim2
-rw-r--r--tests/stdlib/tstrmiscs.nim4
-rw-r--r--tests/stdlib/tstrscans.nim2
-rw-r--r--tests/stdlib/tstrset.nim12
-rw-r--r--tests/stdlib/tstrtabs.nim1
-rw-r--r--tests/stdlib/tstrtabs2.nim16
-rw-r--r--tests/stdlib/tstrutils.nim69
-rw-r--r--tests/stdlib/tstrutils2.nim38
-rw-r--r--tests/stdlib/tsugar.nim118
-rw-r--r--tests/stdlib/tsums.nim4
-rw-r--r--tests/stdlib/tsysrand.nim2
-rw-r--r--tests/stdlib/tsystem.nim60
-rw-r--r--tests/stdlib/ttables.nim4
-rw-r--r--tests/stdlib/ttasks.nim54
-rw-r--r--tests/stdlib/ttempfiles.nim1
-rw-r--r--tests/stdlib/tthreadpool.nim2
-rw-r--r--tests/stdlib/ttimes.nim48
-rw-r--r--tests/stdlib/ttypeinfo.nim21
-rw-r--r--tests/stdlib/ttypetraits.nim1
-rw-r--r--tests/stdlib/tunicode.nim6
-rw-r--r--tests/stdlib/tunittest.nim2
-rw-r--r--tests/stdlib/tunittestpass.nim1
-rw-r--r--tests/stdlib/tunixsocket.nim35
-rw-r--r--tests/stdlib/turi.nim1
-rw-r--r--tests/stdlib/tuserlocks.nim2
-rw-r--r--tests/stdlib/tvarargs.nim2
-rw-r--r--tests/stdlib/tvarints.nim6
-rw-r--r--tests/stdlib/tvmutils.nim2
-rw-r--r--tests/stdlib/tvolatile.nim15
-rw-r--r--tests/stdlib/twchartoutf8.nim2
-rw-r--r--tests/stdlib/twith.nim20
-rw-r--r--tests/stdlib/twordwrap.nim4
-rw-r--r--tests/stdlib/twrapnils.nim4
-rw-r--r--tests/stdlib/twrongstattype.nim14
-rw-r--r--tests/stdlib/txmltree.nim37
-rw-r--r--tests/stdlib/tyield.nim1
-rw-r--r--tests/stdlib/unixsockettest.nim26
-rw-r--r--tests/stmt/tforloop_tuple_multiple_underscore.nim4
-rw-r--r--tests/stmt/tforloop_tuple_underscore.nim2
-rw-r--r--tests/stmt/tforloop_underscore.nim2
-rw-r--r--tests/stmt/tgenericsunderscore.nim4
-rw-r--r--tests/stmt/tmiscunderscore.nim15
-rw-r--r--tests/stylecheck/foreign_package/foreign_package.nim1
-rw-r--r--tests/stylecheck/foreign_package/foreign_package.nimble2
-rw-r--r--tests/stylecheck/t20397_1.nim4
-rw-r--r--tests/stylecheck/tforeign_package.nim16
-rw-r--r--tests/stylecheck/thint.nim43
-rw-r--r--tests/stylecheck/treject.nim4
-rw-r--r--tests/stylecheck/tusages.nim6
-rw-r--r--tests/system/t10307.nim (renamed from tests/magics/t10307.nim)0
-rw-r--r--tests/system/t20938.nim12
-rw-r--r--tests/system/tcomparisons.nim51
-rw-r--r--tests/system/tconcat.nim (renamed from tests/concat/tconcat.nim)0
-rw-r--r--tests/system/tdollars.nim19
-rw-r--r--tests/system/temptyecho.nim (renamed from tests/misc/temptyecho.nim)0
-rw-r--r--tests/system/tensuremove.nim131
-rw-r--r--tests/system/tensuremove1.nim16
-rw-r--r--tests/system/tensuremove2.nim15
-rw-r--r--tests/system/tensuremove3.nim28
-rw-r--r--tests/system/tfielditerator.nim (renamed from tests/fields/tfielditerator.nim)16
-rw-r--r--tests/system/tfields.nim (renamed from tests/fields/tfields.nim)0
-rw-r--r--tests/system/tgcnone.nim3
-rw-r--r--tests/system/tgcregions.nim (renamed from tests/misc/tgcregions.nim)0
-rw-r--r--tests/system/timmutableinc.nim (renamed from tests/misc/tinc.nim)2
-rw-r--r--tests/system/tinvalidnot.nim (renamed from tests/misc/tnot.nim)2
-rw-r--r--tests/system/tlocals.nim (renamed from tests/misc/tlocals.nim)0
-rw-r--r--tests/system/tlowhigh.nim (renamed from tests/misc/tlowhigh.nim)0
-rw-r--r--tests/system/tmagics.nim (renamed from tests/magics/tmagics.nim)4
-rw-r--r--tests/system/tmemory.nim (renamed from tests/stdlib/tmemory.nim)0
-rw-r--r--tests/system/tnew.nim (renamed from tests/misc/tnew.nim)8
-rw-r--r--tests/system/tnewderef.nim (renamed from tests/misc/tnewderef.nim)0
-rw-r--r--tests/system/tnewstring_uninitialized.nim11
-rw-r--r--tests/system/tnilconcats.nim3
-rw-r--r--tests/system/tostring.nim2
-rw-r--r--tests/system/trefs.nim15
-rw-r--r--tests/system/tslices.nim (renamed from tests/misc/tslices.nim)6
-rw-r--r--tests/system/tslimsystem.nim2
-rw-r--r--tests/system/tsystem_misc.nim13
-rw-r--r--tests/system/tuse_version.nim49
-rw-r--r--tests/system/tuse_version16.nim4
-rw-r--r--tests/system/tvarargslen.nim6
-rw-r--r--tests/template/m1027a.nim1
-rw-r--r--tests/template/m1027b.nim1
-rw-r--r--tests/template/m19277_1.nim2
-rw-r--r--tests/template/m19277_2.nim2
-rw-r--r--tests/template/mdotcall.nim82
-rw-r--r--tests/template/mdotcall2.nim7
-rw-r--r--tests/template/mqualifiedtype1.nim2
-rw-r--r--tests/template/mqualifiedtype2.nim2
-rw-r--r--tests/template/t1027.nim22
-rw-r--r--tests/template/t11705.nim17
-rw-r--r--tests/template/t13426.nim87
-rw-r--r--tests/template/t13515.nim16
-rw-r--r--tests/template/t19149.nim19
-rw-r--r--tests/template/t19277.nim19
-rw-r--r--tests/template/t19700.nim10
-rw-r--r--tests/template/t21231.nim10
-rw-r--r--tests/template/t21532.nim8
-rw-r--r--tests/template/t24112.nim19
-rw-r--r--tests/template/taliassyntax.nim74
-rw-r--r--tests/template/taliassyntaxerrors.nim28
-rw-r--r--tests/template/tcallsitelineinfo.nim17
-rw-r--r--tests/template/tcallsitelineinfo2.nim20
-rw-r--r--tests/template/tdefaultparam.nim56
-rw-r--r--tests/template/tdotcall.nim32
-rw-r--r--tests/template/template_issues.nim6
-rw-r--r--tests/template/template_various.nim51
-rw-r--r--tests/template/tgenericparam.nim93
-rw-r--r--tests/template/tgensymhijack.nim37
-rw-r--r--tests/template/tidentconcatenations.nim (renamed from tests/misc/tidentconcatenations.nim)0
-rw-r--r--tests/template/tinnerouterproc.nim12
-rw-r--r--tests/template/tnested.nim38
-rw-r--r--tests/template/tobjectdeclfield.nim21
-rw-r--r--tests/template/topensym.nim209
-rw-r--r--tests/template/topensymoverride.nim39
-rw-r--r--tests/template/topensymwarning.nim60
-rw-r--r--tests/template/tparams_gensymed.nim9
-rw-r--r--tests/template/tqualifiedident.nim8
-rw-r--r--tests/template/tqualifiedtype.nim25
-rw-r--r--tests/template/tredefinition_override.nim2
-rw-r--r--tests/template/tunderscore1.nim11
-rw-r--r--tests/template/twrongmapit.nim7
-rw-r--r--tests/test_nimscript.nims8
-rw-r--r--tests/threads/t7172.nim6
-rw-r--r--tests/threads/t8535.nim1
-rw-r--r--tests/threads/threadex.nim1
-rw-r--r--tests/threads/tjsthreads.nim2
-rw-r--r--tests/threads/tmanyjoin.nim1
-rw-r--r--tests/threads/tmembug.nim51
-rw-r--r--tests/threads/tonthreadcreation.nim1
-rw-r--r--tests/threads/tracy_allocator.nim1
-rw-r--r--tests/threads/treusetvar.nim1
-rw-r--r--tests/threads/tthreadvars.nim1
-rw-r--r--tests/threads/ttryrecv.nim1
-rw-r--r--tests/tools/compile/tkoch.nim5
-rw-r--r--tests/tools/compile/tniminst.nim5
-rw-r--r--tests/tools/config.nims3
-rw-r--r--tests/tools/tlinter.nim2
-rw-r--r--tests/tools/tnimgrep.nim4
-rw-r--r--tests/tools/tunused_imports.nim11
-rw-r--r--tests/trmacros/tnorewrite.nim20
-rw-r--r--tests/trmacros/tor.nim24
-rw-r--r--tests/trmacros/trmacros_various.nim3
-rw-r--r--tests/trmacros/trmacros_various2.nim4
-rw-r--r--tests/tuples/mnimsconstunpack.nim4
-rw-r--r--tests/tuples/t18125_1.nim14
-rw-r--r--tests/tuples/t18125_2.nim20
-rw-r--r--tests/tuples/tnimsconstunpack.nim8
-rw-r--r--tests/tuples/ttuples_issues.nim12
-rw-r--r--tests/tuples/ttuples_various.nim38
-rw-r--r--tests/typerel/t7600_1.nim2
-rw-r--r--tests/typerel/t7600_2.nim2
-rw-r--r--tests/typerel/tnoargopenarray.nim8
-rw-r--r--tests/typerel/tproctypeclass.nim89
-rw-r--r--tests/typerel/tregionptrs.nim16
-rw-r--r--tests/typerel/tsigmatch.nim6
-rw-r--r--tests/typerel/ttynilinstantiation.nim7
-rw-r--r--tests/typerel/ttypedesc_as_genericparam1_orc.nim6
-rw-r--r--tests/typerel/ttypelessemptyset.nim2
-rw-r--r--tests/typerel/tuncheckedarray_eq.nim2
-rw-r--r--tests/typerel/typedescs.nim15
-rw-r--r--tests/types/t15836.nim11
-rw-r--r--tests/types/t15836_2.nim21
-rw-r--r--tests/types/t21027.nim5
-rw-r--r--tests/types/t21260.nim13
-rw-r--r--tests/types/t5648.nim32
-rw-r--r--tests/types/tcyclic.nim135
-rw-r--r--tests/types/texportgeneric.nim8
-rw-r--r--tests/types/tinheritgenericparameter.nim39
-rw-r--r--tests/types/tinheritref.nim4
-rw-r--r--tests/types/tisop.nim48
-rw-r--r--tests/types/tisopr.nim34
-rw-r--r--tests/types/tissues_types.nim62
-rw-r--r--tests/types/ttopdowninference.nim138
-rw-r--r--tests/untestable/gdb/gdb_pretty_printer_test.py35
-rw-r--r--tests/untestable/gdb/gdb_pretty_printer_test_output.txt3
-rw-r--r--tests/untestable/gdb/gdb_pretty_printer_test_program.nim29
-rwxr-xr-x[-rw-r--r--]tests/untestable/gdb/gdb_pretty_printer_test_run.sh20
-rw-r--r--tests/untestable/thttpclient_ssl_remotenetwork.nim142
-rw-r--r--tests/untestable/tpostgres.nim327
-rw-r--r--tests/varres/tprevent_forloopvar_mutations.nim4
-rw-r--r--tests/varstmt/tvardecl.nim7
-rw-r--r--tests/views/tconst_views.nim11
-rw-r--r--tests/views/tviews1.nim57
-rw-r--r--tests/views/tviews2.nim90
-rw-r--r--tests/vm/mscriptcompiletime.nim7
-rw-r--r--tests/vm/t17121.nim9
-rw-r--r--tests/vm/t18103.nim35
-rw-r--r--tests/vm/t19075.nim19
-rw-r--r--tests/vm/t20746.nim13
-rw-r--r--tests/vm/t21704.nim69
-rw-r--r--tests/vm/t9622.nim2
-rw-r--r--tests/vm/tcnstseq.nim (renamed from tests/cnstseq/tcnstseq.nim)0
-rw-r--r--tests/vm/tconst.nim10
-rw-r--r--tests/vm/tconstarrayresem.nim29
-rw-r--r--tests/vm/tconstresem.nim10
-rw-r--r--tests/vm/tconstscope1.nim5
-rw-r--r--tests/vm/tconstscope2.nim5
-rw-r--r--tests/vm/tconsttable.nim15
-rw-r--r--tests/vm/tconvaddr.nim49
-rw-r--r--tests/vm/tfibconst.nim (renamed from tests/constr/tconexpr.nim)0
-rw-r--r--tests/vm/tgenericcompiletimeproc.nim36
-rw-r--r--tests/vm/tmisc_vm.nim12
-rw-r--r--tests/vm/tnilclosurecall.nim8
-rw-r--r--tests/vm/tnilclosurecallstacktrace.nim23
-rw-r--r--tests/vm/tnocompiletimefunc.nim (renamed from tests/constr/tnocompiletimefunc.nim)0
-rw-r--r--tests/vm/tnocompiletimefunclambda.nim (renamed from tests/constr/tnocompiletimefunclambda.nim)0
-rw-r--r--tests/vm/tnoreturn.nim32
-rw-r--r--tests/vm/topenarrays.nim89
-rw-r--r--tests/vm/tscriptcompiletime.nims9
-rw-r--r--tests/vm/tsignaturehash.nim2
-rw-r--r--tests/vm/ttouintconv.nim7
-rw-r--r--tests/vm/ttypedesc.nim31
-rw-r--r--tests/vm/tunsupportedintfloatcast.nim3
-rw-r--r--tests/vm/tvmmisc.nim270
-rw-r--r--tests/vm/tvmopsDanger.nim13
-rw-r--r--tests/vm/tzero_extend.nim8
-rw-r--r--tests/whenstmt/t12517.nim21
-rw-r--r--tests/whenstmt/t19426.nim16
-rw-r--r--tests/xml/ttree_add.nim51
-rw-r--r--tests/xml/ttree_add1.nim53
-rw-r--r--tests/xml/ttree_delete.nim47
-rw-r--r--tests/xml/ttree_delete1.nim48
-rw-r--r--tests/xml/ttree_insert.nim53
-rw-r--r--tests/xml/ttree_insert1.nim51
-rw-r--r--tests/xml/ttree_replace.nim46
-rw-r--r--tests/xml/ttree_replace1.nim53
-rw-r--r--tools/atlas/atlas.md77
-rw-r--r--tools/atlas/atlas.nim509
-rw-r--r--tools/atlas/osutils.nim51
-rw-r--r--tools/atlas/packagesjson.nim114
-rw-r--r--tools/atlas/parse_requires.nim2
-rw-r--r--tools/atlas/testdata.nim63
-rw-r--r--tools/atlas/tests/balls.nimble32
-rw-r--r--tools/atlas/tests/grok.nimble5
-rw-r--r--tools/atlas/tests/nim-bytes2human.nimble7
-rw-r--r--tools/atlas/tests/nim.cfg11
-rw-r--r--tools/atlas/tests/npeg.nimble48
-rw-r--r--tools/atlas/tests/packages/packages.json36
-rw-r--r--tools/atlas/tests/sync.nimble10
-rw-r--r--tools/atlas/tests/testes.nimble23
-rw-r--r--tools/atlas/tests/ups.nimble13
-rw-r--r--tools/debug/customdebugtype.nim72
-rw-r--r--tools/debug/nim-gdb.py (renamed from tools/nim-gdb.py)409
-rw-r--r--tools/debug/nimlldb.py1380
-rw-r--r--tools/deps.nim5
-rw-r--r--tools/detect/detect.nim7
-rw-r--r--tools/dochack/dochack.nim52
-rw-r--r--tools/grammar_nanny.nim1
-rw-r--r--tools/kochdocs.nim161
-rw-r--r--tools/nimgrab.nim13
-rw-r--r--tools/nimgrep.nim34
-rw-r--r--tools/nimgrep.nim.cfg4
-rw-r--r--tools/niminst/buildsh.nimf1
-rw-r--r--tools/niminst/debcreation.nim4
-rw-r--r--tools/niminst/makefile.nimf16
-rw-r--r--tools/niminst/niminst.nim38
-rw-r--r--tools/officialpackages.nim21
-rw-r--r--tools/unicode_parsedata.nim70
-rw-r--r--tools/vccexe/vccexe.nim20
-rw-r--r--tools/vccexe/vcvarsall.nim7
1779 files changed, 67752 insertions, 39352 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 30b3d3351..1e46e0544 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -7,17 +7,16 @@ body:
 - type: markdown
   attributes:
     value: |
-      - **Please provide a minimal code example that reproduces the Bug!** :bug:
-        Reports with a reproducible example and descriptive detailed information will likely receive fixes faster.
+      Please provide a minimal code example that reproduces the bug if possible.
+      Reports with a reproducible example or detailed information will likely receive fixes faster.
 
 - type: textarea
-  id: what-happened
+  id: description
   attributes:
-    label: What happened?
+    label: Description
     description: |
-      Use DETAILED DESCRIPTIVE information about the problem.
-      Here, you go into more details about your Bug report. This section can be a few paragraphs long.
-    placeholder: Bug reports with full repro code and detailed information will be fixed faster.
+      Describe the problem. Code example can be given here.
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
   validations:
     required: true
     
@@ -25,31 +24,33 @@ body:
   id: nim-version
   attributes:
     label: Nim Version
-    description: Please run `nim -v` on the command line.
+    description: |
+      Can be obtained from `nim -v` on the command line along with the OS/architecture.
+      For development versions, make sure to include the commit hash.
   validations:
     required: true
 
 - type: textarea
   id: current-logs
   attributes:
-    label: Current Standard Output Logs
+    label: Current Output
     description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
-    placeholder: Bug reports with full repro code and detailed information will be fixed faster.
-    render: shell
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
+    render: text
 
 - type: textarea
   id: expected-logs
   attributes:
-    label: Expected Standard Output Logs
+    label: Expected Output
     description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
-    placeholder: Bug reports with full repro code and detailed information will be fixed faster.
-    render: shell
+    placeholder: Bug reports with reproducible code or detailed information will be fixed faster.
+    render: text
 
 - type: textarea
-  id: possible-solution
+  id: known-workarounds
   attributes:
-    label: Possible Solution
-    description: Propose a possible solution.
+    label: Known Workarounds
+    description: Provide any known workarounds.
   validations:
     required: false
 
@@ -64,12 +65,9 @@ body:
 - type: markdown
   attributes:
     value: |
-      - Thanks for your contributions!, your Bug report will receive feedback from the community soon...
-      - Please check whether the problem still exists in the devel branch, see [rebuilding the compiler](https://nim-lang.github.io/Nim/intern.html#rebuilding-the-compiler).
+      - Please check whether the problem still exists in the devel branch, which can be installed via [choosenim](https://github.com/nim-lang/choosenim/), [nightlies](https://github.com/nim-lang/nightlies/), or by [creating a temporary build of the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler-building-an-instrumented-compiler).
       - Consider writing a PR targetting devel branch after filing this, see [contributing](https://nim-lang.github.io/Nim/contributing.html).
-      - If it's a pre-existing compiler bug, see [Debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler)
-        which should give more context on a compiler crash.
-      - If it's a regression, you can help us by identifying which version introduced the bug,
-        see [Bisecting for regressions](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions),
-        or at least try known past releases (eg `choosenim 1.2.0`).
+      - If it's a pre-existing compiler bug, see [debugging the compiler](https://nim-lang.github.io/Nim/intern.html#debugging-the-compiler) which should give more context on a compiler crash.
+      - If it's a regression, you can help us by identifying which version introduced the bug by [bisecting](https://nim-lang.github.io/Nim/intern.html#bisecting-for-regressions) or at least trying known past releases (e.g. `choosenim 2.0.0`).
+        The Nim repo also supports bisecting in issue comments by adding `!nim c`, `!nim js` etc. before a code block, see [nimrun-action](https://github.com/juancarlospaco/nimrun-action).
       - [Please, consider a Donation for the Nim project.](https://nim-lang.org/donate.html)
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index 55b4836fd..5cb458626 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -43,10 +43,9 @@ body:
 - type: textarea
   id: Examples
   attributes:
-    label: Standard Output Examples
-    description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
+    label: Examples
+    description: Provide examples for your feature request here.
     placeholder: Example code here.
-    render: shell
 
 - type: textarea
   id: incompatibility
diff --git a/.github/stale.yml b/.github/stale.yml
deleted file mode 100644
index c175f64a0..000000000
--- a/.github/stale.yml
+++ /dev/null
@@ -1,69 +0,0 @@
-# Configuration for probot-stale - https://github.com/probot/stale
-
-# Number of days of inactivity before an Issue or Pull Request becomes stale
-daysUntilStale: 365
-
-# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
-# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
-daysUntilClose: 30
-
-# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
-onlyLabels: []
-
-# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
-exemptLabels:
-  - ARC
-  - bounty
-  - Codegen
-  - Crash
-  - Generics
-  - High Priority
-  - Macros
-  - Next release
-  - Showstopper
-  - Static[T]
-
-# Set to true to ignore issues in a project (defaults to false)
-exemptProjects: false
-
-# Set to true to ignore issues in a milestone (defaults to false)
-exemptMilestones: false
-
-# Set to true to ignore issues with an assignee (defaults to false)
-exemptAssignees: false
-
-# Label to use when marking as stale
-staleLabel: stale
-
-# Comment to post when marking as stale. Set to `false` to disable
-markComment: >
-  This pull request has been automatically marked as stale because it has not had
-  recent activity.
-  If you think it is still a valid PR, please rebase it on the latest devel;
-  otherwise it will be closed. Thank you for your contributions.
-
-# Comment to post when removing the stale label.
-# unmarkComment: >
-#   Your comment here.
-
-# Comment to post when closing a stale Issue or Pull Request.
-# closeComment: >
-#   Your comment here.
-
-# Limit the number of actions per hour, from 1-30. Default is 30
-limitPerRun: 20
-
-# Limit to only `issues` or `pulls`
-only: pulls
-
-# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
-# pulls:
-#   daysUntilStale: 30
-#   markComment: >
-#     This pull request has been automatically marked as stale because it has not had
-#     recent activity. It will be closed if no further activity occurs. Thank you
-#     for your contributions.
-
-# issues:
-#   exemptLabels:
-#     - confirmed
diff --git a/.github/workflows/bisects.yml b/.github/workflows/bisects.yml
new file mode 100644
index 000000000..4a6a92f01
--- /dev/null
+++ b/.github/workflows/bisects.yml
@@ -0,0 +1,31 @@
+# See https://github.com/juancarlospaco/nimrun-action/issues/3#issuecomment-1607344901
+name: issue comments bisects
+on:
+  issue_comment:
+    types: created
+
+jobs:
+  bisects:
+    if: |
+      github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '!nim ') && github.event.issue.pull_request == null && github.event.comment.author_association != 'NONE'
+    strategy:
+      fail-fast: false
+      matrix:
+        platform: [ubuntu-latest, windows-latest, macos-latest]
+    name: ${{ matrix.platform }}-bisects
+    runs-on: ${{ matrix.platform }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: jiro4989/setup-nim-action@v1
+        with:
+          nim-version: 'devel'
+
+      - uses: juancarlospaco/nimrun-action@nim
+        if: |
+          runner.os == 'Linux'   && contains(github.event.comment.body, '-d:linux'  ) ||
+          runner.os == 'Windows' && contains(github.event.comment.body, '-d:windows') ||
+          runner.os == 'macOS'   && contains(github.event.comment.body, '-d:osx'    ) ||
+          runner.os == 'Linux' && !contains(github.event.comment.body, '-d:linux') && !contains(github.event.comment.body, '-d:windows') && !contains(github.event.comment.body, '-d:osx')
+        with:
+          github-token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml
index 6228c48c8..2faa37ce0 100644
--- a/.github/workflows/ci_docs.yml
+++ b/.github/workflows/ci_docs.yml
@@ -2,8 +2,7 @@ name: Nim Docs CI
 on:
   push:
     paths:
-      - 'compiler/docgen.nim'
-      - 'compiler/renderverbatim.nim'
+      - 'compiler/**.nim'
       - 'config/nimdoc.cfg'
       - 'doc/**.rst'
       - 'doc/**.md'
@@ -14,11 +13,11 @@ on:
       - 'tools/kochdocs.nim'
       - '.github/workflows/ci_docs.yml'
       - 'koch.nim'
+
   pull_request:
     # Run only on changes on these files.
     paths:
-      - 'compiler/docgen.nim'
-      - 'compiler/renderverbatim.nim'
+      - 'compiler/**.nim'
       - 'config/nimdoc.cfg'
       - 'doc/**.rst'
       - 'doc/**.md'
@@ -46,7 +45,7 @@ jobs:
           - target: windows
             os: windows-2019
           - target: osx
-            os: macos-11
+            os: macos-12
 
     name: ${{ matrix.target }}
     runs-on: ${{ matrix.os }}
@@ -54,7 +53,7 @@ jobs:
 
     steps:
       - name: 'Checkout'
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 2
 
@@ -110,7 +109,7 @@ jobs:
         if: |
           github.event_name == 'push' && github.ref == 'refs/heads/devel' &&
           matrix.target == 'linux'
-        uses: crazy-max/ghaction-github-pages@v3
+        uses: crazy-max/ghaction-github-pages@v4
         with:
           build_dir: doc/html
         env:
diff --git a/.github/workflows/ci_gcc14.yml b/.github/workflows/ci_gcc14.yml
new file mode 100644
index 000000000..fb286887a
--- /dev/null
+++ b/.github/workflows/ci_gcc14.yml
@@ -0,0 +1,76 @@
+name: GCC 14
+on:
+  pull_request:
+  push:
+    branches:
+      - 'devel'
+
+
+jobs:
+  build:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ubuntu-24.04]
+        cpu: [amd64]
+    name: '${{ matrix.os }}'
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 60 # refs bug #18178
+    steps:
+      - name: 'Checkout'
+        uses: actions/checkout@v4
+        with:
+          fetch-depth: 2
+
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
+        with:
+          node-version: '20.x'
+
+      - name: 'Install dependencies (Linux amd64)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo apt update -qq
+          sudo apt remove needrestart
+          DEBIAN_FRONTEND='noninteractive' \
+            sudo apt install --no-install-recommends -yq \
+              libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \
+              valgrind libc6-dbg libblas-dev xorg-dev
+      - name: 'Install dependencies (Linux amd64 gcc 14)'
+        if: runner.os == 'Linux' && matrix.cpu == 'amd64'
+        run: |
+          sudo add-apt-repository universe
+          sudo apt update -qq
+          sudo apt install -y gcc-14 g++-14 libpcre3 liblapack-dev
+          sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 60 --slave /usr/bin/g++ g++ /usr/bin/g++-14
+      - name: 'Install dependencies (macOS)'
+        if: runner.os == 'macOS'
+        run: brew install boehmgc make sfml gtk+3
+      - name: 'Install dependencies (Windows)'
+        if: runner.os == 'Windows'
+        shell: bash
+        run: |
+          set -e
+          . ci/funs.sh
+          nimInternalInstallDepsWindows
+          echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}"
+
+      - name: 'Add build binaries to PATH'
+        shell: bash
+        run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}"
+
+      - name: 'NIM_TESTAMENT_DISABLE_SSL'
+        shell: bash
+        run: echo "NIM_TESTAMENT_DISABLE_SSL=1" >> $GITHUB_ENV
+
+      - name: 'System information'
+        shell: bash
+        run: . ci/funs.sh && nimCiSystemInfo
+
+      - name: 'Build csourcesAny'
+        shell: bash
+        run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}'
+
+      - name: 'koch, Run CI'
+        shell: bash
+        run: . ci/funs.sh && nimInternalBuildKochAndRunCI
diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml
index 86843d420..2ea5092e3 100644
--- a/.github/workflows/ci_packages.yml
+++ b/.github/workflows/ci_packages.yml
@@ -4,6 +4,7 @@ on:
   push:
     branches:
       - 'devel'
+      - 'version-2-0'
       - 'version-1-6'
       - 'version-1-2'
 
@@ -16,7 +17,7 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        os: [ubuntu-20.04, macos-11]
+        os: [ubuntu-20.04, macos-12]
         cpu: [amd64]
         batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num`
     name: '${{ matrix.os }} (batch: ${{ matrix.batch }})'
@@ -27,14 +28,14 @@ jobs:
       NIM_TESTAMENT_BATCH: ${{ matrix.batch }}
     steps:
       - name: 'Checkout'
-        uses: actions/checkout@v2
+        uses: actions/checkout@v4
         with:
           fetch-depth: 2
 
-      - name: 'Install node.js 16.x'
-        uses: actions/setup-node@v2
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
         with:
-          node-version: '16.x'
+          node-version: '20.x'
 
       - name: 'Install dependencies (Linux amd64)'
         if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -46,8 +47,7 @@ jobs:
               valgrind libc6-dbg libblas-dev xorg-dev
       - name: 'Install dependencies (macOS)'
         if: runner.os == 'macOS'
-        run: |
-          brew install boehmgc make sfml gtk+3
+        run: brew install boehmgc make sfml gtk+3
       - name: 'Install dependencies (Windows)'
         if: runner.os == 'Windows'
         shell: bash
@@ -71,5 +71,4 @@ jobs:
 
       - name: 'koch, Run CI'
         shell: bash
-        run: |
-          . ci/funs.sh && nimInternalBuildKochAndRunCI
+        run: . ci/funs.sh && nimInternalBuildKochAndRunCI
diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml
index a56ff9a7e..decfe953e 100644
--- a/.github/workflows/ci_publish.yml
+++ b/.github/workflows/ci_publish.yml
@@ -17,14 +17,14 @@ jobs:
     runs-on: ${{ matrix.os }}
     steps:
       - name: 'Checkout'
-        uses: actions/checkout@v2
+        uses: actions/checkout@v4
         with:
           fetch-depth: 2
 
-      - name: 'Install node.js 16.x'
-        uses: actions/setup-node@v2
+      - name: 'Install node.js 20.x'
+        uses: actions/setup-node@v4
         with:
-          node-version: '16.x'
+          node-version: '20.x'
 
       - name: 'Install dependencies (Linux amd64)'
         if: runner.os == 'Linux' && matrix.cpu == 'amd64'
@@ -71,7 +71,7 @@ jobs:
         run: nim c -r -d:release ci/action.nim
 
       - name: 'Comment'
-        uses: actions/github-script@v6
+        uses: actions/github-script@v7
         with:
           script: |
             const fs = require('fs');
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 000000000..0c5a533e1
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,25 @@
+# https://github.com/actions/stale#usage
+name: Stale pull requests
+
+on:
+  schedule:
+    - cron: '0 0 * * *'  # Midnight.
+
+jobs:
+  stale:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/stale@v9
+        with:
+          days-before-pr-stale:    365
+          days-before-pr-close:    30
+          days-before-issue-stale: -1
+          days-before-issue-close: -1
+          exempt-pr-labels:    "ARC,bounty,Codegen,Crash,Generics,High Priority,Macros,Next release,Showstopper,Static[T]"
+          exempt-issue-labels: "Showstopper,Severe,bounty,Compiler Crash,Medium Priority"
+          stale-pr-message: >
+            This pull request is stale because it has been open for 1 year with no activity.
+            Contribute more commits on the pull request and rebase it on the latest devel,
+            or it will be closed in 30 days. Thank you for your contributions.
+          close-pr-message: >
+            This pull request has been marked as stale and closed due to inactivity after 395 days.
diff --git a/.gitignore b/.gitignore
index d22b8316a..fad7909bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,9 +3,9 @@
 !*.*
 
 # Cache
-nimcache/
-rnimcache/
-dnimcache/
+nimcache*/
+rnimcache*/
+dnimcache*/
 
 *.o
 !/icons/*.o
@@ -67,6 +67,7 @@ testament.db
 
 /csources
 /csources_v1
+/csources_v2
 
 /dist/
 # /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it
@@ -109,3 +110,5 @@ htmldocs
 nimdoc.out.css
 # except here:
 !/nimdoc/testproject/expected/*
+pkgs/
+/compiler/compiler/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 31908d223..000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,62 +0,0 @@
-# xxx unused, out of date
-
-image: ubuntu:18.04
-
-stages:
-  - pre-build
-  - build
-  - deploy
-  - test
-
-.linux_set_path: &linux_set_path_def
-  before_script:
-    - export PATH=$(pwd)/bin${PATH:+:$PATH}
-  tags:
-    - linux
-
-.windows_set_path: &win_set_path_def
-  before_script:
-    - set PATH=%CD%\bin;%PATH%
-  tags:
-    - windows
-
-
-build-windows:
-  stage: build
-  script:
-    - ci\build.bat
-  artifacts:
-    paths:
-      - bin\nim.exe
-      - bin\nimd.exe
-      - compiler\nim.exe
-      - koch.exe
-    expire_in: 1 week
-  tags:
-    - windows
-
-deploy-windows:
-  stage: deploy
-  script:
-    - koch.exe winrelease
-  artifacts:
-    paths:
-      - build/*.exe
-      - build/*.zip
-    expire_in: 1 week
-  tags:
-    - windows
-    - fast
-
-
-
-test-windows:
-  stage: test
-  <<: *win_set_path_def
-  script:
-    - call ci\deps.bat
-    - nim c testament\tester
-    - testament\tester.exe all
-  tags:
-    - windows
-    - fast
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index d034e3b9b..9ef202948 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -23,16 +23,16 @@ jobs:
         vmImage: 'ubuntu-20.04'
         CPU: amd64
       # regularly breaks, refs bug #17325
-      Linux_i386:
-        # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
-        # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
-        vmImage: 'ubuntu-18.04'
-        CPU: i386
+#       Linux_i386:
+#         # on 'ubuntu-16.04' (not supported anymore anyways) it errored with:
+#         # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed
+#         vmImage: 'ubuntu-18.04'
+#         CPU: i386
       OSX_amd64:
-        vmImage: 'macOS-11'
+        vmImage: 'macOS-12'
         CPU: amd64
       OSX_amd64_cpp:
-        vmImage: 'macOS-11'
+        vmImage: 'macOS-12'
         CPU: amd64
         NIM_COMPILE_TO_CPP: true
       Windows_amd64_batch0_3:
@@ -73,8 +73,8 @@ jobs:
 
     - task: NodeTool@0
       inputs:
-        versionSpec: '16.x'
-      displayName: 'Install node.js 16.x'
+        versionSpec: '20.x'
+      displayName: 'Install node.js 20.x'
       condition: and(succeeded(), eq(variables['skipci'], 'false'))
 
     - bash: |
diff --git a/bin/nim-gdb b/bin/nim-gdb
index f7f9a79c6..d4d60e432 100755
--- a/bin/nim-gdb
+++ b/bin/nim-gdb
@@ -7,14 +7,14 @@ which nim > /dev/null || (echo "nim not in PATH"; exit 1)
 which gdb > /dev/null || (echo "gdb not in PATH"; exit 1)
 which readlink > /dev/null || (echo "readlink not in PATH."; exit 1)
 
-if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then
+if [[ $(uname -s) == Darwin || $(uname -s) == *BSD ]]; then
   NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim))))
 else
   NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim))))
 fi
 
 # Find out where the pretty printer Python module is
-GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py"
+GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py"
 
 # Run GDB with the additional arguments that load the pretty printers
 # Set the environment variable `NIM_GDB` to overwrite the call to a
diff --git a/bin/nim-gdb.bat b/bin/nim-gdb.bat
index e98a2063c..b5537dd9d 100644
--- a/bin/nim-gdb.bat
+++ b/bin/nim-gdb.bat
@@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i)
 
 for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi)
 
-set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py
+set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py
 set @NIM_GDB=gdb.exe
 
 @echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt
diff --git a/changelog.md b/changelog.md
index 6d4ccd4b0..8acd2120a 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,198 +1,18 @@
-# v1.8.x - yyyy-mm-dd
+# v2.x.x - yyyy-mm-dd
 
 
 ## Changes affecting backward compatibility
-- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously raise `ValueError`.  
 
-- `addr` is now available for all addressable locations,
-  `unsafeAddr` is now deprecated and an alias for `addr`.
-
-- `io`, `assertions`, `formatfloat`, and `` dollars.`$` `` for objects are about to move out of the `system` module. You may instead import `std/syncio`, `std/assertions`, `std/formatfloat` and `std/objectdollar`.
-  The `-d:nimPreviewSlimSystem` option makes these imports required.
-
-- The `gc:v2` option is removed.
-
-- The `mainmodule` and `m` options are removed.
-
-- The `threads:on` option is now the default.
-
-- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via
-  `experimental:flexibleOptionalParams`.
-
-- The `Math.trunc` polyfill for targeting Internet Explorer was
-  previously included in most JavaScript output files.
-  Now, it is only included with `-d:nimJsMathTruncPolyfill`.
-  If you are targeting Internet Explorer, you may choose to enable this option
-  or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
-  Nim uses `Math.trunc` for the division and modulo operators for integers.
-
-- `shallowCopy` is removed for ARC/ORC. Use `move` when possible or combine assignment and
-`sink` for optimization purposes.
-
-- The `nimPreviewDotLikeOps` define is going to be removed or deprecated.
-
-- The `{.this.}` pragma, deprecated since 0.19, has been removed.
-- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
-  ```nim
-  type Foo = distinct ptr int
-
-  # Before:
-  var x: Foo = nil
-  # After:
-  var x: Foo = Foo(nil)
-  ```
-- Removed two type pragma syntaxes deprecated since 0.20, namely
-  `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`.
-
-- [Overloadable enums](https://nim-lang.github.io/Nim/manual_experimental.html#overloadable-enum-value-names)
-  are no longer experimental.
-
-- Removed the `nimIncrSeqV3` define.
-
-- Static linking against OpenSSL versions below 1.1, previously done by
-  setting `-d:openssl10`, is no longer supported.
-
-- ORC is now the default memory management strategy. Use
-  `--mm:refc` for a transition period.
 
 ## Standard library additions and changes
 
-[//]: # "Changes:"
-- OpenSSL version 3 is now supported by setting either `-d:sslVersion=3` or `-d:useOpenssl3`.
-- `macros.parseExpr` and `macros.parseStmt` now accept an optional
-  filename argument for more informative errors.
-- Module `colors` expanded with missing colors from the CSS color standard.
-  `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
-- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
-- The `md5` module now works at compile time and in JavaScript.
-- `std/smtp` sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback.
-- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables.
-- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
-  making limiting it to just the first char (`last = 0`) valid.
-- `random.rand` now works with `Ordinal`s.
-- `std/oids` now uses `int64` to store time internally (before it was int32), the length of
-  the string form of `Oid` changes from 24 to 32.
-
-[//]: # "Additions:"
-- Added ISO 8601 week date utilities in `times`:
-  - Added `IsoWeekRange`, a range type for weeks in a week-based year.
-  - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
-  - Added a `initDateTime` overload to create a datetime from an ISO week date.
-  - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
-  - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
-- Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling.
-- Added `sep` parameter in `std/uri` to specify the query separator.
-- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
-  and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
-  in `jscore` for JavaScript targets.
-- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
-- Added `complex.sgn` for obtaining the phase of complex numbers.
-- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
-  `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
-  `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
-  `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
-  `toggleAttribute`, and `matches` to `std/dom`.
-- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices)
-- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460
-
-[//]: # "Deprecations:"
-- Deprecated `selfExe` for Nimscript.
-- Deprecated `std/sums`.
-
-[//]: # "Removals:"
-- Removed deprecated module `parseopt2`.
-- Removed deprecated module `sharedstrings`.
-- Removed deprecated module `dom_extensions`.
-- Removed deprecated module `LockFreeHash`.
-- Removed deprecated module `events`.
-- Removed deprecated `oids.oidToString`.
-- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
-- Removed deprecated `jsre.test` and `jsre.toString`.
-- Removed deprecated `math.c_frexp`.
-- Removed deprecated `` httpcore.`==` ``.
-- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types.
-- Removed deprecated `osproc.poDemon`, symbol with typo.
 
 ## Language changes
 
-- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma
-  which can be used to disable certain effects in proc types.
-- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
-  meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
-- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
-  for the right-hand side of type definitions in type sections. Previously
-  they would error with "invalid indentation".
-- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
-  In the command line, this is defined as `-d:a.b.c`. Older versions can
-  use accents as in ``defined(`a.b.c`)`` to access such defines.
-- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
-  - Templates now accept macro pragmas.
-  - Macro pragmas for var/let/const sections have been redesigned in a way that works
-    similarly to routine macro pragmas. The new behavior is documented in the
-    [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
-  - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
-    allowing multiple type definitions to be injected in place of the original type definition.
-
-    ```nim
-    import macros
-    macro multiply(amount: static int, s: untyped): untyped =
-      let name = $s[0].basename
-      result = newNimNode(nnkTypeSection)
-      for i in 1 .. amount:
-        result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
-    type
-      Foo = object
-      Bar {.multiply: 3.} = object
-        x, y, z: int
-      Baz = object
-    # becomes
-    type
-      Foo = object
-      Bar1 = object
-        x, y, z: int
-      Bar2 = object
-        x, y, z: int
-      Bar3 = object
-        x, y, z: int
-      Baz = object
-    ```
-
-- Redefining templates with the same signature implicitly was previously
-  allowed to support certain macro code. A `{.redefine.}` pragma has been
-  added to make this work explicitly, and a warning is generated in the case
-  where it is implicit. This behavior only applies to templates, redefinition
-  is generally disallowed for other symbols.
-
-- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
-  has been implemented for a variety of basic cases. For example, code like the following now compiles:
-
-  ```nim
-  let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
-  ```
-
-- `cstring` is now accepted as a selector in `case` statements, removing the
-  need to convert to `string`. On the JS backend, this is translated directly
-  to a `switch` statement.
 
 ## Compiler changes
 
-- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
-  reality better. (Nim moved away from all techniques based on "tracing".)
-
-- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
-
-- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
-  without requiring `-d:nimVersion140` which is now a noop.
-
-- `--styleCheck`, `--hintAsError` and `--warningAsError` now only applies to the current package.
-
-- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
-  related functions produced on the backend. This prevents conflicts with other Nim
-  static libraries.
-
 
 ## Tool changes
 
-- Nim now supports Nimble version 0.14 which added support for lock-files. This is done by
-  a simple configuration change setting that you can do yourself too. In `$nim/config/nim.cfg`
-  replace `pkgs` by `pkgs2`.
+
diff --git a/changelogs/changelog.md b/changelogs/changelog.md
deleted file mode 100644
index d793c4313..000000000
--- a/changelogs/changelog.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# v1.xx.x - yyyy-mm-dd
-
-## Changes affecting backward compatibility
-
-## Standard library additions and changes
-
-- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433).
-
-### New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall
-
-This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
-
-On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
-
-When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
-```sh
-$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
-$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
-$ sh build.sh  # per unix install instructions
-$ bin/nim c koch  # per unix install instructions
-$ ./koch boot -d:release  # per unix install instructions
-$ ./koch tools -d:nimNoGetRandom  # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
-```
-
-This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular).
-
-## Language changes
-
-
-## Compiler changes
-
-
-## Tool changes
diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md
new file mode 100644
index 000000000..457cc62a6
--- /dev/null
+++ b/changelogs/changelog_2_0_0.md
@@ -0,0 +1,330 @@
+# v2.0.0 - 2023-08-01
+
+Version 2.0 is a big milestone with too many changes to list them all here.
+
+For a full list see [details](changelog_2_0_0_details.html).
+
+
+## New features
+
+### Better tuple unpacking
+
+Tuple unpacking for variables is now treated as syntax sugar that directly
+expands into multiple assignments. Along with this, tuple unpacking for
+variables can now be nested.
+
+```nim
+proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+# Now nesting is supported!
+let (x, (_, y), _, z) = returnsNestedTuple()
+
+```
+
+### Improved type inference
+
+A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) has been implemented for a variety of basic cases.
+
+For example, code like the following now compiles:
+
+```nim
+let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
+```
+
+### Forbidden Tags
+
+[Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition
+of forbidden tags by the `.forbids` pragma which can be used to disable certain effects in proc types.
+
+For example:
+
+```nim
+
+type IO = object ## input/output effect
+proc readLine(): string {.tags: [IO].} = discard
+proc echoLine(): void = discard
+
+proc no_IO_please() {.forbids: [IO].} =
+  # this is OK because it didn't define any tag:
+  echoLine()
+  # the compiler prevents this:
+  let y = readLine()
+
+```
+
+### New standard library modules
+
+The famous `os` module got an overhaul. Several of its features are available
+under a new interface that introduces a `Path` abstraction. A `Path` is
+a `distinct string`, which improves the type safety when dealing with paths, files
+and directories.
+
+Use:
+
+- `std/oserrors` for OS error reporting.
+- `std/envvars` for environment variables handling.
+- `std/paths` for path handling.
+- `std/dirs` for directory creation/deletion/traversal.
+- `std/files` for file existence checking, file deletions and moves.
+- `std/symlinks` for symlink handling.
+- `std/appdirs` for accessing configuration/home/temp directories.
+- `std/cmdline` for reading command line parameters.
+
+### Consistent underscore handling
+
+The underscore identifier (`_`) is now generally not added to scope when
+used as the name of a definition. While this was already the case for
+variables, it is now also the case for routine parameters, generic
+parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
+
+```nim
+proc foo(_: int): int = _ + 1
+echo foo(1)
+
+proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+echo foo[int]()
+
+proc _() = echo "_"
+_()
+
+type _ = int
+let x: _ = 3
+```
+
+Whereas the following code now compiles:
+
+```nim
+proc foo(_, _: int): int = 123
+echo foo(1, 2)
+
+proc foo[_, _](): int = 123
+echo foo[int, bool]()
+
+proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
+echo foo(int, bool)
+
+proc _() = echo "one"
+proc _() = echo "two"
+
+type _ = int
+type _ = float
+```
+
+### JavaScript codegen improvement
+
+The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
+for 64-bit integer types (`int64` and `uint64`) by default. As this affects
+JS code generation, code using these types to interface with the JS backend
+may need to be updated. Note that `int` and `uint` are not affected.
+
+For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
+and in the case of potential bugs with the new implementation, the
+old behavior is currently still supported with the command line option
+`--jsbigint64:off`.
+
+
+## Docgen improvements
+
+`Markdown` is now the default markup language of doc comments (instead
+of the legacy `RstMarkdown` mode). In this release we begin to separate
+RST and Markdown features to better follow specification of each
+language, with the focus on Markdown development.
+See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
+
+* Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
+  select the markup language mode in the doc comments of the current `.nim`
+  file for processing by `nim doc`:
+
+    1. `Markdown` (default) is basically CommonMark (standard Markdown) +
+        some Pandoc Markdown features + some RST features that are missing
+        in our current implementation of CommonMark and Pandoc Markdown.
+    2. `RST` closely follows the RST spec with few additional Nim features.
+    3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
+        is kept for the sake of compatibility and ease of migration.
+
+* Added separate `md2html` and `rst2html` commands for processing
+  standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
+
+* Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
+* Docgen now supports concise syntax for referencing Nim symbols:
+  instead of specifying HTML anchors directly one can use original
+  Nim symbol declarations (adding the aforementioned link brackets
+  `[...]` around them).
+  * To use this feature across modules, a new `importdoc` directive was added.
+    Using this feature for referencing also helps to ensure that links
+    (inside one module or the whole project) are not broken.
+* Added support for RST & Markdown quote blocks (blocks starting with `>`).
+* Added a popular Markdown definition lists extension.
+* Added Markdown indented code blocks (blocks indented by >= 4 spaces).
+* Added syntax for additional parameters to Markdown code blocks:
+
+       ```nim test="nim c $1"
+       ...
+       ```
+
+
+## C++ interop enhancements
+
+Nim 2.0 takes C++ interop to the next level. With the new [virtual](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma and the extended [constructor](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
+Now one can define constructors and virtual procs that maps to C++ constructors and virtual methods, allowing one to further customize
+the interoperability. There is also extended support for the [codeGenDecl](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-codegendecl-pragma) pragma, so that it works on types.
+
+It's a common pattern in C++ to use inheritance to extend a library. Some even use multiple inheritance as a mechanism to make interfaces.
+
+Consider the following example:
+
+```cpp
+
+struct Base {
+  int someValue;
+  Base(int inValue)  {
+    someValue = inValue;
+  };
+};
+
+class IPrinter {
+public:
+  virtual void print() = 0;
+};
+```
+
+```nim
+
+type
+  Base* {.importcpp, inheritable.} = object
+    someValue*: int32
+  IPrinter* {.importcpp.} = object
+
+const objTemplate = """
+  struct $1 : public $3, public IPrinter {
+    $2
+  };
+""";
+
+type NimChild {.codegenDecl: objTemplate .} = object of Base
+
+proc makeNimChild(val: int32): NimChild {.constructor: "NimClass('1 #1) : Base(#1)".} =
+  echo "It calls the base constructor passing " & $this.someValue
+  this.someValue = val * 2 # Notice how we can access `this` inside the constructor. It's of the type `ptr NimChild`.
+
+proc print*(self: NimChild) {.virtual.} =
+  echo "Some value is " & $self.someValue
+
+let child = makeNimChild(10)
+child.print()
+```
+
+It outputs:
+
+```
+It calls the base constructor passing 10
+Some value is 20
+```
+
+
+## ARC/ORC refinements
+
+With the 2.0 release, the ARC/ORC model got refined once again and is now finally complete:
+
+1. Programmers now have control over the "item was moved from" state as `=wasMoved` is overridable.
+2. There is a new `=dup` hook which is more efficient than the old combination of `=wasMoved(tmp); =copy(tmp, x)` operations.
+3. Destructors now take a parameter of the attached object type `T` directly and don't have to take a `var T` parameter.
+
+With these important optimizations we improved the runtime of the compiler and important benchmarks by 0%! Wait ... what?
+Yes, unfortunately it turns out that for a modern optimizer like in GCC or LLVM there is no difference.
+
+But! This refined model is more efficient once separate compilation enters the picture. In other words, as we think of
+providing a stable ABI it is important not to lose any efficiency in the calling conventions.
+
+
+## Tool changes
+
+- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
+- nimgrep now offers the option `--inContext` (and `--notInContext`), which
+  allows to filter only matches with the context block containing a given pattern.
+- nimgrep: names of options containing "include/exclude" are deprecated,
+  e.g. instead of `--includeFile` and `--excludeFile` we have
+  `--filename` and `--notFilename` respectively.
+  Also, the semantics are now consistent for such positive/negative filters.
+- Nim now ships with an alternative package manager called Atlas. More on this in upcoming versions.
+
+
+## Porting guide
+
+### Block and Break
+
+Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. In other words, turn:
+
+```nim
+
+block:
+  a()
+  if cond:
+    break
+  b()
+
+```
+
+Into:
+
+```nim
+
+block maybePerformB:
+  a()
+  if cond:
+    break maybePerformB
+  b()
+
+```
+
+### Strict funcs
+
+The definition of `"strictFuncs"` was changed.
+The old definition was roughly: "A store to a ref/ptr deref is forbidden unless it's coming from a `var T` parameter".
+The new definition is: "A store to a ref/ptr deref is forbidden."
+
+This new definition is much easier to understand, the price is some expressitivity. The following code used to be
+accepted:
+
+```nim
+
+{.experimental: "strictFuncs".}
+
+type Node = ref object
+  s: string
+
+func create(s: string): Node =
+  result = Node()
+  result.s = s # store to result[]
+
+```
+
+Now it has to be rewritten to:
+
+```nim
+
+{.experimental: "strictFuncs".}
+
+type Node = ref object
+  s: string
+
+func create(s: string): Node =
+  result = Node(s: s)
+
+```
+
+### Standard library
+
+Several standard library modules have been moved to nimble packages, use `nimble` or `atlas` to install them:
+
+- `std/punycode` => `punycode`
+- `std/asyncftpclient` => `asyncftpclient`
+- `std/smtp` => `smtp`
+- `std/db_common` => `db_connector/db_common`
+- `std/db_sqlite` => `db_connector/db_sqlite`
+- `std/db_mysql` => `db_connector/db_mysql`
+- `std/db_postgres` => `db_connector/db_postgres`
+- `std/db_odbc` => `db_connector/db_odbc`
+- `std/md5` => `checksums/md5`
+- `std/sha1` => `checksums/sha1`
+- `std/sums` => `sums`
diff --git a/changelogs/changelog_2_0_0_details.md b/changelogs/changelog_2_0_0_details.md
new file mode 100644
index 000000000..24dc4edad
--- /dev/null
+++ b/changelogs/changelog_2_0_0_details.md
@@ -0,0 +1,560 @@
+# v2.0.0 - 2023-08-01
+
+
+## Changes affecting backward compatibility
+
+- ORC is now the default memory management strategy. Use
+  `--mm:refc` for a transition period.
+
+- The `threads:on` option is now the default.
+
+- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache's `HttpClient` (Java), `http` (go) and .NET `HttpWebResponse` (C#) behaviors. Previously it raised a `ValueError`.
+
+- `addr` is now available for all addressable locations,
+  `unsafeAddr` is now deprecated and an alias for `addr`.
+
+- Certain definitions from the default `system` module have been moved to
+  the following new modules:
+
+  - `std/syncio`
+  - `std/assertions`
+  - `std/formatfloat`
+  - `std/objectdollar`
+  - `std/widestrs`
+  - `std/typedthreads`
+  - `std/sysatomics`
+
+  In the future, these definitions will be removed from the `system` module,
+  and their respective modules will have to be imported to use them.
+  Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option
+  may be used.
+
+- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated
+  symbols in the `system` module:
+  - Aliases with an `Error` suffix to exception types that have a `Defect` suffix
+    (see [exceptions](https://nim-lang.github.io/Nim/exceptions.html)):
+    `ArithmeticError`, `DivByZeroError`, `OverflowError`,
+    `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`,
+    `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`,
+    `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`,
+    `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`,
+    `DeadThreadError`, `NilAccessError`
+  - `addQuitProc`, replaced by `exitprocs.addExitProc`
+  - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32`
+  - `TaintedString`, formerly a distinct alias to `string`
+  - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to
+    `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64`
+
+- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in
+  in the `system` module, which is replaced by [threading/channels](https://github.com/nim-lang/threading/blob/master/threading/channels.nim). Use the command `nimble install threading` and import `threading/channels`.
+
+- Enabling `-d:nimPreviewCstringConversion` causes `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` to not support conversion to `cstring` anymore.
+
+- Enabling `-d:nimPreviewProcConversion` causes `proc` to not support conversion to
+  `pointer` anymore. `cast` may be used instead.
+
+- The `gc:v2` option is removed.
+
+- The `mainmodule` and `m` options are removed.
+
+- Optional parameters in combination with `: body` syntax ([RFC #405](https://github.com/nim-lang/RFCs/issues/405))
+  are now opt-in via `experimental:flexibleOptionalParams`.
+
+- Automatic dereferencing (experimental feature) is removed.
+
+- The `Math.trunc` polyfill for targeting Internet Explorer was
+  previously included in most JavaScript output files.
+  Now, it is only included with `-d:nimJsMathTruncPolyfill`.
+  If you are targeting Internet Explorer, you may choose to enable this option
+  or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma).
+  Nim uses `Math.trunc` for the division and modulo operators for integers.
+
+- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and
+`sink` for optimization purposes.
+
+- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fulfill its promises.
+
+- The `{.this.}` pragma, deprecated since 0.19, has been removed.
+- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead.
+  ```nim
+  type Foo = distinct ptr int
+
+  # Before:
+  var x: Foo = nil
+  # After:
+  var x: Foo = Foo(nil)
+  ```
+- Removed two type pragma syntaxes deprecated since 0.20, namely
+  `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. Instead,
+  use `type Foo[T] {.final.} = object`.
+
+- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent
+  with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`.
+  This decision was made with the assumption that the old syntax was used rarely;
+  if your code used the old syntax, please be aware of this change.
+
+- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators
+  are no longer experimental.
+
+- `macros.getImpl` for `const` symbols now returns the full definition node
+  (as `nnkConstDef`) rather than the AST of the constant value.
+
+- Lock levels are deprecated, now a noop.
+
+- `strictEffects` are no longer experimental.
+  Use `legacy:laxEffects` to keep backward compatibility.
+
+- The `gorge`/`staticExec` calls will now return a descriptive message in the output
+  if the execution fails for whatever reason. To get back legacy behaviour, use `-d:nimLegacyGorgeErrors`.
+
+- Pointer to `cstring` conversions now trigger a `[PtrToCstringConv]` warning.
+  This warning will become an error in future versions! Use a `cast` operation
+  like `cast[cstring](x)` instead.
+
+- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`.
+
+- Redefining templates with the same signature was previously
+  allowed to support certain macro code. To do this explicitly, the
+  `{.redefine.}` pragma has been added. Note that this is only for templates.
+  Implicit redefinition of templates is now deprecated and will give an error in the future.
+
+- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead.
+
+- Several Standard libraries have been moved to nimble packages, use `nimble` to install them:
+  - `std/punycode` => `punycode`
+  - `std/asyncftpclient` => `asyncftpclient`
+  - `std/smtp` => `smtp`
+  - `std/db_common` => `db_connector/db_common`
+  - `std/db_sqlite` => `db_connector/db_sqlite`
+  - `std/db_mysql` => `db_connector/db_mysql`
+  - `std/db_postgres` => `db_connector/db_postgres`
+  - `std/db_odbc` => `db_connector/db_odbc`
+  - `std/md5` => `checksums/md5`
+  - `std/sha1` => `checksums/sha1`
+  - `std/sums` => `std/sums`
+
+- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of
+  `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`.
+  This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead.
+
+- When `--warning[BareExcept]:on` is enabled, if an `except` specifies no exception or any exception not inheriting from `Defect` or `CatchableError`, a `warnBareExcept` warning will be triggered. For example, the following code will emit a warning:
+  ```nim
+  try:
+    discard
+  except: # Warning: The bare except clause is deprecated; use `except CatchableError:` instead [BareExcept]
+    discard
+  ```
+
+- The experimental `strictFuncs` feature now disallows a store to the heap via a `ref` or `ptr` indirection.
+
+- The underscore identifier (`_`) is now generally not added to scope when
+  used as the name of a definition. While this was already the case for
+  variables, it is now also the case for routine parameters, generic
+  parameters, routine declarations, type declarations, etc. This means that the following code now does not compile:
+
+  ```nim
+  proc foo(_: int): int = _ + 1
+  echo foo(1)
+
+  proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+  echo foo[int]()
+
+  proc _() = echo "_"
+  _()
+
+  type _ = int
+  let x: _ = 3
+  ```
+
+  Whereas the following code now compiles:
+
+  ```nim
+  proc foo(_, _: int): int = 123
+  echo foo(1, 2)
+
+  proc foo[_, _](): int = 123
+  echo foo[int, bool]()
+
+  proc foo[T, U](_: typedesc[T], _: typedesc[U]): (T, U) = (default(T), default(U))
+  echo foo(int, bool)
+
+  proc _() = echo "one"
+  proc _() = echo "two"
+
+  type _ = int
+  type _ = float
+  ```
+
+- Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages.
+
+- The JavaScript backend now uses [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
+  for 64-bit integer types (`int64` and `uint64`) by default. As this affects
+  JS code generation, code using these types to interface with the JS backend
+  may need to be updated. Note that `int` and `uint` are not affected.
+
+  For compatibility with [platforms that do not support BigInt](https://caniuse.com/bigint)
+  and in the case of potential bugs with the new implementation, the
+  old behavior is currently still supported with the command line option
+  `--jsbigint64:off`.
+
+- The `proc` and `iterator` type classes now respectively only match
+  procs and iterators. Previously both type classes matched any of
+  procs or iterators.
+
+  ```nim
+  proc prc(): int =
+    123
+
+  iterator iter(): int {.closure.} =
+    yield 123
+
+  proc takesProc[T: proc](x: T) = discard
+  proc takesIter[T: iterator](x: T) = discard
+
+  # always compiled:
+  takesProc(prc)
+  takesIter(iter)
+  # no longer compiles:
+  takesProc(iter)
+  takesIter(prc)
+  ```
+
+- The `proc` and `iterator` type classes now accept a calling convention pragma
+  (i.e. `proc {.closure.}`) that must be shared by matching proc or iterator
+  types. Previously, pragmas were parsed but discarded if no parameter list
+  was given.
+
+  This is represented in the AST by an `nnkProcTy`/`nnkIteratorTy` node with
+  an `nnkEmpty` node in the place of the `nnkFormalParams` node, and the pragma
+  node in the same place as in a concrete `proc` or `iterator` type node. This
+  state of the AST may be unexpected to existing code, both due to the
+  replacement of the `nnkFormalParams` node as well as having child nodes
+  unlike other type class AST.
+
+- Signed integer literals in `set` literals now default to a range type of
+  `0..255` instead of `0..65535` (the maximum size of sets).
+
+- `case` statements with `else` branches put before `elif`/`of` branches in macros
+  are rejected with "invalid order of case branches".
+
+- Destructors now default to `.raises: []` (i.e. destructors must not raise
+  unlisted exceptions) and explicitly raising destructors are implementation
+  defined behavior.
+
+- The very old, undocumented `deprecated` pragma statement syntax for
+  deprecated aliases is now a no-op. The regular deprecated pragma syntax is
+  generally sufficient instead.
+
+  ```nim
+  # now does nothing:
+  {.deprecated: [OldName: NewName].}
+
+  # instead use:
+  type OldName* {.deprecated: "use NewName instead".} = NewName
+  const oldName* {.deprecated: "use newName instead".} = newName
+  ```
+
+  `defined(nimalias)` can be used to check for versions when this syntax was
+  available; however since code that used this syntax is usually very old,
+  these deprecated aliases are likely not used anymore and it may make sense
+  to simply remove these statements.
+
+- `getProgramResult` and `setProgramResult` in `std/exitprocs` are no longer
+  declared when they are not available on the backend. Previously it would call
+  `doAssert false` at runtime despite the condition being checkable at compile-time.
+
+- Custom destructors now supports non-var parameters, e.g. ``proc `=destroy`[T: object](x: T)`` is valid. ``proc `=destroy`[T: object](x: var T)`` is deprecated.
+
+- Relative imports will not resolve to searched paths anymore, e.g. `import ./tables` now reports an error properly.
+
+## Standard library additions and changes
+
+[//]: # "Changes:"
+- OpenSSL 3 is now supported.
+- `macros.parseExpr` and `macros.parseStmt` now accept an optional
+  `filename` argument for more informative errors.
+- The `colors` module is expanded with missing colors from the CSS color standard.
+  `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard.
+- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)).
+- The `md5` module now works at compile time and in JavaScript.
+- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef`, to support `const` tables.
+- `strutils.find` now uses and defaults to `last = -1` for whole string searches,
+  making limiting it to just the first char (`last = 0`) valid.
+- `strutils.split` and `strutils.rsplit` now return the source string as a single element for an empty separator.
+- `random.rand` now works with `Ordinal`s.
+- Undeprecated `os.isvalidfilename`.
+- `std/oids` now uses `int64` to store time internally (before, it was int32).
+- `std/uri.Uri` dollar (`$`) improved, precalculates the `string` result length from the `Uri`.
+- `std/uri.Uri.isIpv6` is now exported.
+- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2.
+
+
+- `std/jsfetch.newFetchOptions` now has default values for all parameters.
+- `std/jsformdata` now accepts the `Blob` data type.
+
+- `std/sharedlist` and `std/sharedtables` are now deprecated, see [RFC #433](https://github.com/nim-lang/RFCs/issues/433).
+
+- There is a new compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove the dependency on the Linux `getrandom` syscall.
+
+  This compile flag only affects Linux builds and is necessary if either compiling on a Linux kernel version < 3.17, or if code built will be executing on kernel < 3.17.
+
+  On Linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since Linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall.
+
+  When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be:
+  ```sh
+  $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details
+  $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8
+  $ sh build.sh  # per unix install instructions
+  $ bin/nim c koch  # per unix install instructions
+  $ ./koch boot -d:release  # per unix install instructions
+  $ ./koch tools -d:nimNoGetRandom  # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall
+  ```
+
+  This is necessary to pass when building Nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for the stdlib (`sysrand` in particular).
+
+[//]: # "Additions:"
+- Added ISO 8601 week date utilities in `times`:
+  - Added `IsoWeekRange`, a range type for weeks in a week-based year.
+  - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year.
+  - Added an `initDateTime` overload to create a `DateTime` from an ISO week date.
+  - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime.
+  - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year.
+- Added new modules which were previously part of `std/os`:
+  - Added `std/oserrors` for OS error reporting.
+  - Added `std/envvars` for environment variables handling.
+  - Added `std/cmdline` for reading command line parameters.
+  - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`.
+- Added `sep` parameter in `std/uri` to specify the query separator.
+- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`.
+- Added `complex.sgn` for obtaining the phase of complex numbers.
+- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`,
+  `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`,
+  `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`,
+  `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`,
+  `toggleAttribute`, and `matches` to `std/dom`.
+- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices).
+- Added `capacity` for `string` and `seq` to return the current capacity, see [RFC #460](https://github.com/nim-lang/RFCs/issues/460).
+- Added `openArray[char]` overloads for `std/parseutils` and `std/unicode`, allowing for more code reuse.
+- Added a `safe` parameter to `base64.encodeMime`.
+- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes.
+- Added `minmax` to `sequtils`, as a more efficient `(min(_), max(_))` over sequences.
+- `std/jscore` for the JavaScript target:
+  + Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift)
+  and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask).
+  + Added `toDateString`, `toISOString`, `toJSON`, `toTimeString`, `toUTCString` converters for `DateTime`.
+- Added `BackwardsIndex` overload for `CacheSeq`.
+- Added support for nested `with` blocks in `std/with`.
+- Added `ensureMove` to the system module. It ensures that the passed argument is moved, otherwise an error is given at the compile time.
+
+
+[//]: # "Deprecations:"
+- Deprecated `selfExe` for Nimscript.
+- Deprecated `std/base64.encode` for collections of arbitrary integer element type.
+  Now only `byte` and `char` are supported.
+
+[//]: # "Removals:"
+- Removed deprecated module `parseopt2`.
+- Removed deprecated module `sharedstrings`.
+- Removed deprecated module `dom_extensions`.
+- Removed deprecated module `LockFreeHash`.
+- Removed deprecated module `events`.
+- Removed deprecated `oids.oidToString`.
+- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`.
+- Removed deprecated `jsre.test` and `jsre.toString`.
+- Removed deprecated `math.c_frexp`.
+- Removed deprecated `` httpcore.`==` ``.
+- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that take wrong argument types.
+- Removed deprecated `osproc.poDemon`, symbol with typo.
+- Removed deprecated `tables.rightSize`.
+
+
+- Removed deprecated `posix.CLONE_STOPPED`.
+
+
+## Language changes
+
+- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) now supports the definition of forbidden tags by the `.forbids` pragma
+  which can be used to disable certain effects in proc types.
+- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental,
+  meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them.
+- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed
+  for the right-hand side of type definitions in type sections. Previously
+  they would error with "invalid indentation".
+
+- Compile-time define changes:
+  - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`.
+    In the command line, this is defined as `-d:a.b.c`. Older versions can
+    use backticks as in ``defined(`a.b.c`)`` to access such defines.
+  - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas)
+    now support a string argument for qualified define names.
+
+    ```nim
+    # -d:package.FooBar=42
+    const FooBar {.intdefine: "package.FooBar".}: int = 5
+    echo FooBar # 42
+    ```
+
+    This was added to help disambiguate similar define names for different packages.
+    In older versions, this could only be achieved with something like the following:
+
+    ```nim
+    const FooBar = block:
+      const `package.FooBar` {.intdefine.}: int = 5
+      `package.FooBar`
+    ```
+  - A generic `define` pragma for constants has been added that interprets
+    the value of the define based on the type of the constant value.
+    See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-nimdefine-pragma)
+    for a list of supported types.
+
+- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes:
+  - Templates now accept macro pragmas.
+  - Macro pragmas for var/let/const sections have been redesigned in a way that works
+    similarly to routine macro pragmas. The new behavior is documented in the
+    [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas).
+  - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`,
+    allowing multiple type definitions to be injected in place of the original type definition.
+
+    ```nim
+    import macros
+
+    macro multiply(amount: static int, s: untyped): untyped =
+      let name = $s[0].basename
+      result = newNimNode(nnkTypeSection)
+      for i in 1 .. amount:
+        result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2]))
+
+    type
+      Foo = object
+      Bar {.multiply: 3.} = object
+        x, y, z: int
+      Baz = object
+    # becomes
+    type
+      Foo = object
+      Bar1 = object
+        x, y, z: int
+      Bar2 = object
+        x, y, z: int
+      Bar3 = object
+        x, y, z: int
+      Baz = object
+    ```
+
+- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference)
+  has been implemented for a variety of basic cases. For example, code like the following now compiles:
+
+  ```nim
+  let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")]
+  ```
+
+- `cstring` is now accepted as a selector in `case` statements, removing the
+  need to convert to `string`. On the JS backend, this is translated directly
+  to a `switch` statement.
+
+- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters).
+- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`.
+
+- IBM Z architecture and macOS m1 arm64 architecture are supported.
+
+- `=wasMoved` can now be overridden by users.
+
+- There is a new pragma called [quirky](https://nim-lang.github.io/Nim/manual_experimental.html#quirky-routines) that can be used to affect the code
+  generation of goto based exception handling. It can improve the produced code size but its effects can be subtle so use it with care.
+
+- Tuple unpacking for variables is now treated as syntax sugar that directly
+  expands into multiple assignments. Along with this, tuple unpacking for
+  variables can now be nested.
+
+  ```nim
+  proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+  let (x, (_, y), _, z) = returnsNestedTuple()
+  # roughly becomes
+  let
+    tmpTup1 = returnsNestedTuple()
+    x = tmpTup1[0]
+    tmpTup2 = tmpTup1[1]
+    y = tmpTup2[1]
+    z = tmpTup1[3]
+  ```
+
+  As a result `nnkVarTuple` nodes in variable sections will no longer be
+  reflected in `typed` AST.
+
+- C++ interoperability:
+  - New [`virtual`](https://nim-lang.github.io/Nim/manual_experimental.html#virtual-pragma) pragma added.
+  - Improvements to [`constructor`](https://nim-lang.github.io/Nim/manual_experimental.html#constructor-pragma) pragma.
+
+## Compiler changes
+
+- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the
+  reality better. (Nim moved away from all techniques based on "tracing".)
+
+- Defines the `gcRefc` symbol which allows writing specific code for the refc GC.
+
+- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`,
+  without requiring `-d:nimVersion140` which is now a noop.
+
+- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package.
+
+- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and
+  related functions produced on the backend. This prevents conflicts with other Nim
+  static libraries.
+
+- When compiling for release, the flag `-fno-math-errno` is used for GCC.
+- Removed deprecated `LineTooLong` hint.
+- Line numbers and file names of source files work correctly inside templates for JavaScript targets.
+
+- Removed support for LCC (Local C), Pelles C, Digital Mars and Watcom compilers.
+
+
+## Docgen
+
+- `Markdown` is now the default markup language of doc comments (instead
+  of the legacy `RstMarkdown` mode). In this release we begin to separate
+  RST and Markdown features to better follow specification of each
+  language, with the focus on Markdown development.
+  See also [the docs](https://nim-lang.github.io/Nim/markdown_rst.html).
+
+  * Added a `{.doctype: Markdown | RST | RstMarkdown.}` pragma allowing to
+    select the markup language mode in the doc comments of the current `.nim`
+    file for processing by `nim doc`:
+
+      1. `Markdown` (default) is basically CommonMark (standard Markdown) +
+         some Pandoc Markdown features + some RST features that are missing
+         in our current implementation of CommonMark and Pandoc Markdown.
+      2. `RST` closely follows the RST spec with few additional Nim features.
+      3. `RstMarkdown` is a maximum mix of RST and Markdown features, which
+          is kept for the sake of compatibility and ease of migration.
+
+  * Added separate `md2html` and `rst2html` commands for processing
+    standalone `.md` and `.rst` files respectively (and also `md2tex`/`rst2tex`).
+
+- Added Pandoc Markdown bracket syntax `[...]` for making anchor-less links.
+- Docgen now supports concise syntax for referencing Nim symbols:
+  instead of specifying HTML anchors directly one can use original
+  Nim symbol declarations (adding the aforementioned link brackets
+  `[...]` around them).
+    * To use this feature across modules, a new `importdoc` directive was added.
+      Using this feature for referencing also helps to ensure that links
+      (inside one module or the whole project) are not broken.
+- Added support for RST & Markdown quote blocks (blocks starting with `>`).
+- Added a popular Markdown definition lists extension.
+- Added Markdown indented code blocks (blocks indented by >= 4 spaces).
+- Added syntax for additional parameters to Markdown code blocks:
+
+       ```nim test="nim c $1"
+       ...
+       ```
+
+## Tool changes
+
+- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs` before). Use `nimble develop --global` to create an old style link file in the special links directory documented at https://github.com/nim-lang/nimble#nimble-develop.
+- nimgrep added the option `--inContext` (and `--notInContext`), which
+  allows to filter only matches with the context block containing a given pattern.
+- nimgrep: names of options containing "include/exclude" are deprecated,
+  e.g. instead of `--includeFile` and `--excludeFile` we have
+  `--filename` and `--notFilename` respectively.
+  Also the semantics are now consistent for such positive/negative filters.
+- koch now supports the `--skipIntegrityCheck` option. The command `koch --skipIntegrityCheck boot -d:release` always builds the compiler twice.
diff --git a/changelogs/changelog_2_2_0.md b/changelogs/changelog_2_2_0.md
new file mode 100644
index 000000000..b50cbeb27
--- /dev/null
+++ b/changelogs/changelog_2_2_0.md
@@ -0,0 +1,248 @@
+# v2.2.0 - 2024-10-02
+
+
+## Changes affecting backward compatibility
+
+- `-d:nimStrictDelete` becomes the default. An index error is produced when the index passed to `system.delete` is out of bounds. Use `-d:nimAuditDelete` to mimic the old behavior for backward compatibility.
+
+- The default user-agent in `std/httpclient` has been changed to `Nim-httpclient/<version>` instead of `Nim httpclient/<version>` which was incorrect according to the HTTP spec.
+
+- Methods now support implementations based on a VTable by using `--experimental:vtables`. Methods are then confined to the same module where their type has been defined.
+
+- With `-d:nimPreviewNonVarDestructor`, non-var destructors become the default.
+
+- A bug where tuple unpacking assignment with a longer tuple on the RHS than the LHS was allowed has been fixed, i.e. code like:
+  ```nim
+  var a, b: int
+  (a, b) = (1, 2, 3, 4)
+  ```
+  will no longer compile.
+
+- `internalNew` is removed from the `system` module, use `new` instead.
+
+- `bindMethod` in `std/jsffi` is deprecated, don't use it with closures.
+
+- JS backend now supports lambda lifting for closures. Use `--legacy:jsNoLambdaLifting` to emulate old behaviors.
+
+- JS backend now supports closure iterators.
+
+- `owner` in `std/macros` is deprecated.
+
+- Ambiguous type symbols in generic procs and templates now generate symchoice nodes.
+  Previously; in templates they would error immediately at the template definition,
+  and in generic procs a type symbol would arbitrarily be captured, losing the
+  information of the other symbols. This means that generic code can now give
+  errors for ambiguous type symbols, and macros operating on generic proc AST
+  may encounter symchoice nodes instead of the arbitrarily resolved type symbol nodes.
+
+- Partial generic instantiation of routines is no longer allowed. Previously
+  it compiled in niche situations due to bugs in the compiler.
+
+  ```nim
+  proc foo[T, U](x: T, y: U) = echo (x, y)
+  proc foo[T, U](x: var T, y: U) = echo "var ", (x, y)
+
+  proc bar[T]() =
+    foo[float](1, "abc")
+
+  bar[int]() # before: (1.0, "abc"), now: type mismatch, missing generic parameter
+  ```
+
+- `const` values now open a new scope for each constant, meaning symbols
+  declared in them can no longer be used outside or in the value of
+  other constants.
+
+  ```nim
+  const foo = (var a = 1; a)
+  const bar = a # error
+  let baz = a # error
+  ```
+
+- The following POSIX wrappers have had their types changed from signed to
+  unsigned types on OSX and FreeBSD/OpenBSD to correct codegen errors:
+  - `Gid` (was `int32`, is now `uint32`)
+  - `Uid` (was `int32`, is now `uint32`)
+  - `Dev` (was `int32`, is now `uint32` on FreeBSD)
+  - `Nlink` (was `int16`, is now `uint32` on OpenBSD and `uint16` on OSX/other BSD)
+  - `sin6_flowinfo` and `sin6_scope_id` fields of `Sockaddr_in6`
+    (were `int32`, are now `uint32`)
+  - `n_net` field of `Tnetent` (was `int32`, is now `uint32`)
+
+- The `Atomic[T]` type on C++ now uses C11 primitives by default instead of
+  `std::atomic`. To use `std::atomic` instead, `-d:nimUseCppAtomics` can be defined.
+
+
+
+
+
+
+## Standard library additions and changes
+
+[//]: # "Changes:"
+
+- Changed `std/osfiles.copyFile` to allow specifying `bufferSize` instead of a hard-coded one.
+- Changed `std/osfiles.copyFile` to use `POSIX_FADV_SEQUENTIAL` hints for kernel-level aggressive sequential read-aheads.
+- `std/htmlparser` has been moved to a nimble package, use `nimble` or `atlas` to install it.
+- Changed `std/os.copyDir` and `copyDirWithPermissions` to allow skipping special "file" objects like FIFOs, device files, etc on Unix by specifying a `skipSpecial` parameter.
+
+[//]: # "Additions:"
+
+- Added `newStringUninit` to the `system` module, which creates a new string of length `len` like `newString` but with uninitialized content.
+- Added `setLenUninit` to the `system` module, which doesn't initialize
+slots when enlarging a sequence.
+- Added `hasDefaultValue` to `std/typetraits` to check if a type has a valid default value.
+- Added `rangeBase` to `std/typetraits` to obtain the base type of a range type or
+  convert a value with a range type to its base type.
+- Added Viewport API for the JavaScript targets in the `dom` module.
+- Added `toSinglyLinkedRing` and `toDoublyLinkedRing` to `std/lists` to convert from `openArray`s.
+- ORC: To be enabled via `nimOrcStats` there is a new API called `GC_orcStats` that can be used to query how many
+  objects the cyclic collector did free. If the number is zero that is a strong indicator that you can use `--mm:arc`
+  instead of `--mm:orc`.
+- A `$` template is provided for `Path` in `std/paths`.
+- `std/hashes.hash(x:string)` changed to produce a 64-bit string `Hash` (based
+on Google's Farm Hash) which is also often faster than the present one.  Define
+`nimStringHash2` to get the old values back.  `--jsbigint=off` mode always only
+produces the old values.  This may impact your automated tests if they depend
+on hash order in some obvious or indirect way.  Using `sorted` or `OrderedTable`
+is often an easy workaround.
+
+[//]: # "Deprecations:"
+
+- Deprecates `system.newSeqUninitialized`, which is replaced by `newSeqUninit`.
+
+[//]: # "Removals:"
+
+
+
+
+
+
+## Language changes
+
+- `noInit` can be used in types and fields to disable member initializers in the C++ backend.
+
+- C++ custom constructors initializers see https://nim-lang.org/docs/manual_experimental.html#constructor-initializer
+
+- `member` can be used to attach a procedure to a C++ type.
+
+- C++ `constructor` now reuses `result` instead creating `this`.
+
+- Tuple unpacking changes:
+  - Tuple unpacking assignment now supports using underscores to discard values.
+    ```nim
+    var a, c: int
+    (a, _, c) = (1, 2, 3)
+    ```
+  - Tuple unpacking variable declarations now support type annotations, but
+    only for the entire tuple.
+    ```nim
+    let (a, b): (int, int) = (1, 2)
+    let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
+    ```
+
+- The experimental option `--experimental:openSym` has been added to allow
+  captured symbols in generic routine and template bodies respectively to be
+  replaced by symbols injected locally by templates/macros at instantiation
+  time. `bind` may be used to keep the captured symbols over the injected ones
+  regardless of enabling the option, but other methods like renaming the
+  captured symbols should be used instead so that the code is not affected by
+  context changes.
+
+  Since this change may affect runtime behavior, the experimental switch
+  `openSym` needs to be enabled; and a warning is given in the case where an
+  injected symbol would replace a captured symbol not bound by `bind` and
+  the experimental switch isn't enabled.
+
+  ```nim
+  const value = "captured"
+  template foo(x: int, body: untyped): untyped =
+    let value {.inject.} = "injected"
+    body
+
+  proc old[T](): string =
+    foo(123):
+      return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+  echo old[int]() # "captured"
+
+  template oldTempl(): string =
+    block:
+      foo(123):
+        value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+  echo oldTempl() # "captured"
+
+  {.experimental: "openSym".}
+
+  proc bar[T](): string =
+    foo(123):
+      return value
+  assert bar[int]() == "injected" # previously it would be "captured"
+
+  proc baz[T](): string =
+    bind value
+    foo(123):
+      return value
+  assert baz[int]() == "captured"
+
+  template barTempl(): string =
+    block:
+      foo(123):
+        value
+  assert barTempl() == "injected" # previously it would be "captured"
+
+  template bazTempl(): string =
+    bind value
+    block:
+      foo(123):
+        value
+  assert bazTempl() == "captured"
+  ```
+
+  This option also generates a new node kind `nnkOpenSym` which contains
+  exactly 1 `nnkSym` node. In the future this might be merged with a slightly
+  modified `nnkOpenSymChoice` node but macros that want to support the
+  experimental feature should still handle `nnkOpenSym`, as the node kind would
+  simply not be generated as opposed to being removed.
+
+  Another experimental switch `genericsOpenSym` exists that enables this behavior
+  at instantiation time, meaning templates etc can enable it specifically when
+  they are being called. However this does not generate `nnkOpenSym` nodes
+  (unless the other switch is enabled) and so doesn't reflect the regular
+  behavior of the switch.
+
+  ```nim
+  const value = "captured"
+  template foo(x: int, body: untyped): untyped =
+    let value {.inject.} = "injected"
+    {.push experimental: "genericsOpenSym".}
+    body
+    {.pop.}
+
+  proc bar[T](): string =
+    foo(123):
+      return value
+  echo bar[int]() # "injected"
+
+  template barTempl(): string =
+    block:
+      var res: string
+      foo(123):
+        res = value
+      res
+  assert barTempl() == "injected"
+  ```
+
+
+
+
+
+## Compiler changes
+
+- `--nimcache` using a relative path as the argument in a config file is now relative to the config file instead of the current directory.
+
+
+
+
+
+## Tool changes
+
+- koch now allows bootstrapping with `-d:nimHasLibFFI`, replacing the older option of building the compiler directly w/ the `libffi` nimble package.
diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md
index 524599b11..f8a09a535 100644
--- a/changelogs/changelog_X_XX_X.md
+++ b/changelogs/changelog_X_XX_X.md
@@ -1,4 +1,4 @@
-# v1.xx.x - yyyy-mm-dd
+# v2.xx.x - yyyy-mm-dd
 
 This is an example file.
 The changes should go to changelog.md!
diff --git a/ci/action.nim b/ci/action.nim
index 5d3a50fda..ad0f4df0b 100644
--- a/ci/action.nim
+++ b/ci/action.nim
@@ -3,7 +3,7 @@ import std/[strutils, os, osproc, parseutils, strformat]
 
 proc main() =
   var msg = ""
-  const cmd = "./koch boot --gc:orc -d:release"
+  const cmd = "./koch boot --mm:orc -d:release"
 
   let (output, exitCode) = execCmdEx(cmd)
 
diff --git a/ci/funs.sh b/ci/funs.sh
index 8e7f8faf7..026366198 100644
--- a/ci/funs.sh
+++ b/ci/funs.sh
@@ -58,7 +58,11 @@ _nimNumCpu(){
   # FreeBSD | macOS: $(sysctl -n hw.ncpu)
   # OpenBSD: $(sysctl -n hw.ncpuonline)
   # windows: $NUMBER_OF_PROCESSORS ?
-  echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
+  if env | grep -q '^NIMCORES='; then
+    echo $NIMCORES
+  else
+    echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1)
+  fi
 }
 
 _nimBuildCsourcesIfNeeded(){
diff --git a/compiler/aliasanalysis.nim b/compiler/aliasanalysis.nim
new file mode 100644
index 000000000..e24c6d8e2
--- /dev/null
+++ b/compiler/aliasanalysis.nim
@@ -0,0 +1,128 @@
+
+import ast
+
+import std / assertions
+
+const
+  PathKinds0* = {nkDotExpr, nkCheckedFieldExpr,
+                 nkBracketExpr, nkDerefExpr, nkHiddenDeref,
+                 nkAddr, nkHiddenAddr,
+                 nkObjDownConv, nkObjUpConv}
+  PathKinds1* = {nkHiddenStdConv, nkHiddenSubConv}
+
+proc skipConvDfa*(n: PNode): PNode =
+  result = n
+  while true:
+    case result.kind
+    of nkObjDownConv, nkObjUpConv:
+      result = result[0]
+    of PathKinds1:
+      result = result[1]
+    else: break
+
+proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
+  var n = orig
+  while true:
+    case n.kind
+    of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
+      n = n[0]
+    of PathKinds1:
+      n = n[1]
+    of nkHiddenDeref, nkDerefExpr:
+      # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
+      # pointer indirection.
+      # bug #14159, we cannot reason about sinkParam[].location as it can
+      # still be shared for tyRef.
+      n = n[0]
+      return n.kind == nkSym and n.sym.owner == owner and
+         (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
+    else: break
+  # XXX Allow closure deref operations here if we know
+  # the owner controlled the closure allocation?
+  result = n.kind == nkSym and n.sym.owner == owner and
+    {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
+    (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
+  # Note: There is a different move analyzer possible that checks for
+  # consume(param.key); param.key = newValue  for all paths. Then code like
+  #
+  #   let splited = split(move self.root, x)
+  #   self.root = merge(splited.lower, splited.greater)
+  #
+  # could be written without the ``move self.root``. However, this would be
+  # wrong! Then the write barrier for the ``self.root`` assignment would
+  # free the old data and all is lost! Lesson: Don't be too smart, trust the
+  # lower level C++ optimizer to specialize this code.
+
+type AliasKind* = enum
+  yes, no, maybe
+
+proc aliases*(obj, field: PNode): AliasKind =
+  # obj -> field:
+  # x -> x: true
+  # x -> x.f: true
+  # x.f -> x: false
+  # x.f -> x.f: true
+  # x.f -> x.v: false
+  # x -> x[]: true
+  # x[] -> x: false
+  # x -> x[0]: true
+  # x[0] -> x: false
+  # x[0] -> x[0]: true
+  # x[0] -> x[1]: false
+  # x -> x[i]: true
+  # x[i] -> x: false
+  # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
+  # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
+  template collectImportantNodes(result, n) =
+    var result: seq[PNode] = @[]
+    var n = n
+    while true:
+      case n.kind
+      of PathKinds0 - {nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref}:
+        n = n[0]
+      of PathKinds1:
+        n = n[1]
+      of nkDotExpr, nkBracketExpr, nkDerefExpr, nkHiddenDeref:
+        result.add n
+        n = n[0]
+      of nkSym:
+        result.add n
+        break
+      else: return no
+
+  collectImportantNodes(objImportantNodes, obj)
+  collectImportantNodes(fieldImportantNodes, field)
+
+  # If field is less nested than obj, then it cannot be part of/aliased by obj
+  if fieldImportantNodes.len < objImportantNodes.len: return no
+
+  result = yes
+  for i in 1..objImportantNodes.len:
+    # We compare the nodes leading to the location of obj and field
+    # with each other.
+    # We continue until they diverge, in which case we return no, or
+    # until we reach the location of obj, in which case we do not need
+    # to look further, since field must be part of/aliased by obj now.
+    # If we encounter an element access using an index which is a runtime value,
+    # we simply return maybe instead of yes; should further nodes not diverge.
+    let currFieldPath = fieldImportantNodes[^i]
+    let currObjPath = objImportantNodes[^i]
+
+    if currFieldPath.kind != currObjPath.kind:
+      return no
+
+    case currFieldPath.kind
+    of nkSym:
+      if currFieldPath.sym != currObjPath.sym: return no
+    of nkDotExpr:
+      if currFieldPath[1].sym != currObjPath[1].sym: return no
+    of nkDerefExpr, nkHiddenDeref:
+      discard
+    of nkBracketExpr:
+      if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
+        if currFieldPath[1].intVal != currObjPath[1].intVal:
+          return no
+      else:
+        result = maybe
+    else: assert false # unreachable
+
diff --git a/compiler/aliases.nim b/compiler/aliases.nim
index 4b50fdb28..fa1167753 100644
--- a/compiler/aliases.nim
+++ b/compiler/aliases.nim
@@ -10,7 +10,9 @@
 ## Simple alias analysis for the HLO and the code generators.
 
 import
-  ast, astalgo, types, trees, intsets
+  ast, astalgo, types, trees
+
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -49,14 +51,16 @@ proc isPartOfAux(a, b: PType, marker: var IntSet): TAnalysisResult =
   if compareTypes(a, b, dcEqIgnoreDistinct): return arYes
   case a.kind
   of tyObject:
-    if a[0] != nil:
-      result = isPartOfAux(a[0].skipTypes(skipPtrs), b, marker)
+    if a.baseClass != nil:
+      result = isPartOfAux(a.baseClass.skipTypes(skipPtrs), b, marker)
     if result == arNo: result = isPartOfAux(a.n, b, marker)
   of tyGenericInst, tyDistinct, tyAlias, tySink:
-    result = isPartOfAux(lastSon(a), b, marker)
-  of tyArray, tySet, tyTuple:
-    for i in 0..<a.len:
-      result = isPartOfAux(a[i], b, marker)
+    result = isPartOfAux(skipModifier(a), b, marker)
+  of tySet, tyArray:
+    result = isPartOfAux(a.elementType, b, marker)
+  of tyTuple:
+    for aa in a.kids:
+      result = isPartOfAux(aa, b, marker)
       if result == arYes: return
   else: discard
 
@@ -114,6 +118,8 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         # use expensive type check:
         if isPartOf(a.sym.typ, b.sym.typ) != arNo:
           result = arMaybe
+        else:
+          result = arNo
     of nkBracketExpr:
       result = isPartOf(a[0], b[0])
       if a.len >= 2 and b.len >= 2:
@@ -149,7 +155,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
       result = isPartOf(a[1], b[1])
     of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
       result = isPartOf(a[0], b[0])
-    else: discard
+    else: result = arNo
     # Calls return a new location, so a default of ``arNo`` is fine.
   else:
     # go down recursively; this is quite demanding:
@@ -165,6 +171,7 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
 
     of DerefKinds:
       # a* !<| b[] iff
+      result = arNo
       if isPartOf(a.typ, b.typ) != arNo:
         result = isPartOf(a, b[0])
         if result == arNo: result = arMaybe
@@ -186,7 +193,9 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
         if isPartOf(a.typ, b.typ) != arNo:
           result = isPartOf(a[0], b)
           if result == arNo: result = arMaybe
-      else: discard
+        else:
+          result = arNo
+      else: result = arNo
     of nkObjConstr:
       result = arNo
       for i in 1..<b.len:
@@ -204,4 +213,6 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
     of nkBracket:
       if b.len > 0:
         result = isPartOf(a, b[0])
-    else: discard
+      else:
+        result = arNo
+    else: result = arNo
diff --git a/compiler/ast.nim b/compiler/ast.nim
index f81dc443d..a342e1ea7 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,14 +10,19 @@
 # abstract syntax tree + symbol table
 
 import
-  lineinfos, hashes, options, ropes, idents, int128, tables
-from strutils import toLowerAscii
+  lineinfos, options, ropes, idents, int128, wordrecg
+
+import std/[tables, hashes]
+from std/strutils import toLowerAscii
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 export int128
 
+import nodekinds
+export nodekinds
+
 type
   TCallingConvention* = enum
     ccNimCall = "nimcall"           # nimcall, also the default
@@ -31,208 +36,12 @@ type
     ccThisCall = "thiscall"         # thiscall (parameters are pushed right-to-left)
     ccClosure  = "closure"          # proc has a closure
     ccNoConvention = "noconv"       # needed for generating proper C procs sometimes
-
-type
-  TNodeKind* = enum # order is extremely important, because ranges are used
-                    # to check whether a node belongs to a certain class
-    nkNone,               # unknown node kind: indicates an error
-                          # Expressions:
-                          # Atoms:
-    nkEmpty,              # the node is empty
-    nkIdent,              # node is an identifier
-    nkSym,                # node is a symbol
-    nkType,               # node is used for its typ field
-
-    nkCharLit,            # a character literal ''
-    nkIntLit,             # an integer literal
-    nkInt8Lit,
-    nkInt16Lit,
-    nkInt32Lit,
-    nkInt64Lit,
-    nkUIntLit,            # an unsigned integer literal
-    nkUInt8Lit,
-    nkUInt16Lit,
-    nkUInt32Lit,
-    nkUInt64Lit,
-    nkFloatLit,           # a floating point literal
-    nkFloat32Lit,
-    nkFloat64Lit,
-    nkFloat128Lit,
-    nkStrLit,             # a string literal ""
-    nkRStrLit,            # a raw string literal r""
-    nkTripleStrLit,       # a triple string literal """
-    nkNilLit,             # the nil literal
-                          # end of atoms
-    nkComesFrom,          # "comes from" template/macro information for
-                          # better stack trace generation
-    nkDotCall,            # used to temporarily flag a nkCall node;
-                          # this is used
-                          # for transforming ``s.len`` to ``len(s)``
-
-    nkCommand,            # a call like ``p 2, 4`` without parenthesis
-    nkCall,               # a call like p(x, y) or an operation like +(a, b)
-    nkCallStrLit,         # a call with a string literal
-                          # x"abc" has two sons: nkIdent, nkRStrLit
-                          # x"""abc""" has two sons: nkIdent, nkTripleStrLit
-    nkInfix,              # a call like (a + b)
-    nkPrefix,             # a call like !a
-    nkPostfix,            # something like a! (also used for visibility)
-    nkHiddenCallConv,     # an implicit type conversion via a type converter
-
-    nkExprEqExpr,         # a named parameter with equals: ''expr = expr''
-    nkExprColonExpr,      # a named parameter with colon: ''expr: expr''
-    nkIdentDefs,          # a definition like `a, b: typeDesc = expr`
-                          # either typeDesc or expr may be nil; used in
-                          # formal parameters, var statements, etc.
-    nkVarTuple,           # a ``var (a, b) = expr`` construct
-    nkPar,                # syntactic (); may be a tuple constructor
-    nkObjConstr,          # object constructor: T(a: 1, b: 2)
-    nkCurly,              # syntactic {}
-    nkCurlyExpr,          # an expression like a{i}
-    nkBracket,            # syntactic []
-    nkBracketExpr,        # an expression like a[i..j, k]
-    nkPragmaExpr,         # an expression like a{.pragmas.}
-    nkRange,              # an expression like i..j
-    nkDotExpr,            # a.b
-    nkCheckedFieldExpr,   # a.b, but b is a field that needs to be checked
-    nkDerefExpr,          # a^
-    nkIfExpr,             # if as an expression
-    nkElifExpr,
-    nkElseExpr,
-    nkLambda,             # lambda expression
-    nkDo,                 # lambda block appering as trailing proc param
-    nkAccQuoted,          # `a` as a node
-
-    nkTableConstr,        # a table constructor {expr: expr}
-    nkBind,               # ``bind expr`` node
-    nkClosedSymChoice,    # symbol choice node; a list of nkSyms (closed)
-    nkOpenSymChoice,      # symbol choice node; a list of nkSyms (open)
-    nkHiddenStdConv,      # an implicit standard type conversion
-    nkHiddenSubConv,      # an implicit type conversion from a subtype
-                          # to a supertype
-    nkConv,               # a type conversion
-    nkCast,               # a type cast
-    nkStaticExpr,         # a static expr
-    nkAddr,               # a addr expression
-    nkHiddenAddr,         # implicit address operator
-    nkHiddenDeref,        # implicit ^ operator
-    nkObjDownConv,        # down conversion between object types
-    nkObjUpConv,          # up conversion between object types
-    nkChckRangeF,         # range check for floats
-    nkChckRange64,        # range check for 64 bit ints
-    nkChckRange,          # range check for ints
-    nkStringToCString,    # string to cstring
-    nkCStringToString,    # cstring to string
-                          # end of expressions
-
-    nkAsgn,               # a = b
-    nkFastAsgn,           # internal node for a fast ``a = b``
-                          # (no string copy)
-    nkGenericParams,      # generic parameters
-    nkFormalParams,       # formal parameters
-    nkOfInherit,          # inherited from symbol
-
-    nkImportAs,           # a 'as' b in an import statement
-    nkProcDef,            # a proc
-    nkMethodDef,          # a method
-    nkConverterDef,       # a converter
-    nkMacroDef,           # a macro
-    nkTemplateDef,        # a template
-    nkIteratorDef,        # an iterator
-
-    nkOfBranch,           # used inside case statements
-                          # for (cond, action)-pairs
-    nkElifBranch,         # used in if statements
-    nkExceptBranch,       # an except section
-    nkElse,               # an else part
-    nkAsmStmt,            # an assembler block
-    nkPragma,             # a pragma statement
-    nkPragmaBlock,        # a pragma with a block
-    nkIfStmt,             # an if statement
-    nkWhenStmt,           # a when expression or statement
-    nkForStmt,            # a for statement
-    nkParForStmt,         # a parallel for statement
-    nkWhileStmt,          # a while statement
-    nkCaseStmt,           # a case statement
-    nkTypeSection,        # a type section (consists of type definitions)
-    nkVarSection,         # a var section
-    nkLetSection,         # a let section
-    nkConstSection,       # a const section
-    nkConstDef,           # a const definition
-    nkTypeDef,            # a type definition
-    nkYieldStmt,          # the yield statement as a tree
-    nkDefer,              # the 'defer' statement
-    nkTryStmt,            # a try statement
-    nkFinally,            # a finally section
-    nkRaiseStmt,          # a raise statement
-    nkReturnStmt,         # a return statement
-    nkBreakStmt,          # a break statement
-    nkContinueStmt,       # a continue statement
-    nkBlockStmt,          # a block statement
-    nkStaticStmt,         # a static statement
-    nkDiscardStmt,        # a discard statement
-    nkStmtList,           # a list of statements
-    nkImportStmt,         # an import statement
-    nkImportExceptStmt,   # an import x except a statement
-    nkExportStmt,         # an export statement
-    nkExportExceptStmt,   # an 'export except' statement
-    nkFromStmt,           # a from * import statement
-    nkIncludeStmt,        # an include statement
-    nkBindStmt,           # a bind statement
-    nkMixinStmt,          # a mixin statement
-    nkUsingStmt,          # an using statement
-    nkCommentStmt,        # a comment statement
-    nkStmtListExpr,       # a statement list followed by an expr; this is used
-                          # to allow powerful multi-line templates
-    nkBlockExpr,          # a statement block ending in an expr; this is used
-                          # to allow powerful multi-line templates that open a
-                          # temporary scope
-    nkStmtListType,       # a statement list ending in a type; for macros
-    nkBlockType,          # a statement block ending in a type; for macros
-                          # types as syntactic trees:
-
-    nkWith,               # distinct with `foo`
-    nkWithout,            # distinct without `foo`
-
-    nkTypeOfExpr,         # type(1+2)
-    nkObjectTy,           # object body
-    nkTupleTy,            # tuple body
-    nkTupleClassTy,       # tuple type class
-    nkTypeClassTy,        # user-defined type class
-    nkStaticTy,           # ``static[T]``
-    nkRecList,            # list of object parts
-    nkRecCase,            # case section of object
-    nkRecWhen,            # when section of object
-    nkRefTy,              # ``ref T``
-    nkPtrTy,              # ``ptr T``
-    nkVarTy,              # ``var T``
-    nkConstTy,            # ``const T``
-    nkMutableTy,          # ``mutable T``
-    nkDistinctTy,         # distinct type
-    nkProcTy,             # proc type
-    nkIteratorTy,         # iterator type
-    nkSharedTy,           # 'shared T'
-                          # we use 'nkPostFix' for the 'not nil' addition
-    nkEnumTy,             # enum body
-    nkEnumFieldDef,       # `ident = expr` in an enumeration
-    nkArgList,            # argument list
-    nkPattern,            # a special pattern; used for matching
-    nkHiddenTryStmt,      # a hidden try statement
-    nkClosure,            # (prc, env)-pair (internally used for code gen)
-    nkGotoState,          # used for the state machine (for iterators)
-    nkState,              # give a label to a code section (for iterators)
-    nkBreakState,         # special break statement for easier code generation
-    nkFuncDef,            # a func
-    nkTupleConstr         # a tuple constructor
-    nkError               # erroneous AST node
-    nkModuleRef           # for .rod file support: A (moduleId, itemId) pair
-    nkReplayAction        # for .rod file support: A replay action
-    nkNilRodNode          # for .rod file support: a 'nil' PNode
+    ccMember = "member"             # proc is a (cpp) member
 
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # 49 flags!
+  TSymFlag* = enum    # 63 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
     sfFromGeneric,    # symbol is instantiation of a generic; this is needed
@@ -284,7 +93,7 @@ type
     sfNamedParamCall, # symbol needs named parameter call syntax in target
                       # language; for interfacing with Objective C
     sfDiscardable,    # returned value may be discarded implicitly
-    sfOverriden,      # proc is overridden
+    sfOverridden,     # proc is overridden
     sfCallsite        # A flag for template symbols to tell the
                       # compiler it should use line information from
                       # the calling side of the macro, not from the
@@ -300,9 +109,12 @@ type
     sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation
     sfNeverRaises     # proc can never raise an exception, not even OverflowDefect
                       # or out-of-memory
+    sfSystemRaisesDefect # proc in the system can raise defects
     sfUsedInFinallyOrExcept  # symbol is used inside an 'except' or 'finally'
     sfSingleUsedTemp  # For temporaries that we know will only be used once
     sfNoalias         # 'noalias' annotation, means C's 'restrict'
+                      # for templates and macros, means cannot be called
+                      # as a lone symbol (cannot use alias syntax)
     sfEffectsDelayed  # an 'effectsDelayed' parameter
     sfGeneratedType   # A anonymous generic type that is generated by the compiler for
                       # objects that do not have generic parameters in case one of the
@@ -310,24 +122,29 @@ type
                       #
                       # This is disallowed but can cause the typechecking to go into
                       # an infinite loop, this flag is used as a sentinel to stop it.
+    sfVirtual         # proc is a C++ virtual function
+    sfByCopy          # param is marked as pass bycopy
+    sfMember          # proc is a C++ member of a type
+    sfCodegenDecl     # type, proc, global or proc param is marked as codegenDecl
+    sfWasGenSym       # symbol was 'gensym'ed
+    sfForceLift       # variable has to be lifted into closure environment
+
+    sfDirty           # template is not hygienic (old styled template) module,
+                      # compiled from a dirty-buffer
+    sfCustomPragma    # symbol is custom pragma template
+    sfBase,           # a base method
+    sfGoto            # var is used for 'goto' code generation
+    sfAnon,           # symbol name that was generated by the compiler
+                      # the compiler will avoid printing such names
+                      # in user messages.
+    sfAllUntyped      # macro or template is immediately expanded in a generic context
+    sfTemplateRedefinition # symbol is a redefinition of an earlier template
 
   TSymFlags* = set[TSymFlag]
 
 const
   sfNoInit* = sfMainModule       # don't generate code to init the variable
 
-  sfAllUntyped* = sfVolatile # macro or template is immediately expanded \
-    # in a generic context
-
-  sfDirty* = sfPure
-    # template is not hygienic (old styled template)
-    # module, compiled from a dirty-buffer
-
-  sfAnon* = sfDiscardable
-    # symbol name that was generated by the compiler
-    # the compiler will avoid printing such names
-    # in user messages.
-
   sfNoForward* = sfRegister
     # forward declarations are not required (per module)
   sfReorder* = sfForward
@@ -335,13 +152,10 @@ const
 
   sfCompileToCpp* = sfInfixCall       # compile the module as C++ code
   sfCompileToObjc* = sfNamedParamCall # compile the module as Objective-C code
-  sfExperimental* = sfOverriden       # module uses the .experimental switch
-  sfGoto* = sfOverriden               # var is used for 'goto' code generation
+  sfExperimental* = sfOverridden       # module uses the .experimental switch
   sfWrittenTo* = sfBorrow             # param is assigned to
                                       # currently unimplemented
-  sfBase* = sfDiscriminant
-  sfCustomPragma* = sfRegister        # symbol is custom pragma template
-  sfTemplateRedefinition* = sfExportc # symbol is a redefinition of an earlier template
+  sfCppMember* = { sfVirtual, sfMember, sfConstructor } # proc is a C++ member, meaning it will be attached to the type definition
 
 const
   # getting ready for the future expr/stmt merge
@@ -400,7 +214,8 @@ type
     tyUncheckedArray
       # An array with boundaries [0,+∞]
 
-    tyProxy # used as errornous type (for idetools)
+    tyError # used as erroneous type (for idetools)
+      # as an erroneous node should match everything
 
     tyBuiltInTypeClass
       # Type such as the catch-all object, tuple, seq, etc
@@ -425,9 +240,9 @@ type
     tyInferred
       # In the initial state `base` stores a type class constraining
       # the types that can be inferred. After a candidate type is
-      # selected, it's stored in `lastSon`. Between `base` and `lastSon`
+      # selected, it's stored in `last`. Between `base` and `last`
       # there may be 0, 2 or more types that were also considered as
-      # possible candidates in the inference process (i.e. lastSon will
+      # possible candidates in the inference process (i.e. last will
       # be updated to store a type best conforming to all candidates)
 
     tyAnd, tyOr, tyNot
@@ -462,10 +277,6 @@ static:
 const
   tyPureObject* = tyTuple
   GcTypeKinds* = {tyRef, tySequence, tyString}
-  tyError* = tyProxy # as an errornous node should match everything
-  tyUnknown* = tyFromExpr
-
-  tyUnknownTypes* = {tyError, tyFromExpr}
 
   tyTypeClasses* = {tyBuiltInTypeClass, tyCompositeTypeClass,
                     tyUserTypeClass, tyUserTypeClassInst,
@@ -508,11 +319,15 @@ type
                        # the flag is applied to proc default values and to calls
     nfExecuteOnReload  # A top-level statement that will be executed during reloads
     nfLastRead  # this node is a last read
-    nfFirstWrite# this node is a first write
+    nfFirstWrite # this node is a first write
     nfHasComment # node has a comment
+    nfSkipFieldChecking # node skips field visable checking
+    nfDisabledOpenSym # temporary: node should be nkOpenSym but cannot
+                      # because openSym experimental switch is disabled
+                      # gives warning instead
 
   TNodeFlags* = set[TNodeFlag]
-  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 45)
+  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 47)
     tfVarargs,        # procedure has C styled varargs
                       # tyArray type represeting a varargs list
     tfNoSideEffect,   # procedure type does not allow side effects
@@ -542,7 +357,7 @@ type
     tfIterator,       # type is really an iterator, not a tyProc
     tfPartial,        # type is declared as 'partial'
     tfNotNil,         # type cannot be 'nil'
-    tfRequiresInit,   # type constains a "not nil" constraint somewhere or
+    tfRequiresInit,   # type contains a "not nil" constraint somewhere or
                       # a `requiresInit` field, so the default zero init
                       # is not appropriate
     tfNeedsFullInit,  # object type marked with {.requiresInit.}
@@ -581,6 +396,9 @@ type
     tfExplicitCallConv
     tfIsConstructor
     tfEffectSystemWorkaround
+    tfIsOutParam
+    tfSendable
+    tfImplicitStatic
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -616,22 +434,23 @@ type
                           # file (it is loaded on demand, which may
                           # mean: never)
     skPackage,            # symbol is a package (used for canonicalization)
-    skAlias               # an alias (needs to be resolved immediately)
   TSymKinds* = set[TSymKind]
 
 const
   routineKinds* = {skProc, skFunc, skMethod, skIterator,
                    skConverter, skMacro, skTemplate}
-  ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds
+  ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub} + routineKinds
 
   tfUnion* = tfNoSideEffect
   tfGcSafe* = tfThread
   tfObjHasKids* = tfEnumHasHoles
   tfReturnsNew* = tfInheritable
+  tfNonConstExpr* = tfExplicitCallConv
+    ## tyFromExpr where the expression shouldn't be evaluated as a static value
   skError* = skUnknown
 
 var
-  eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect}
+  eqTypeFlags* = {tfIterator, tfNotNil, tfVarIsPtr, tfGcSafe, tfNoSideEffect, tfIsOutParam}
     ## type flags that are essential for type equality.
     ## This is now a variable because for emulation of version:1.0 we
     ## might exclude {tfGcSafe, tfNoSideEffect}.
@@ -667,7 +486,6 @@ type
     mUnaryPlusI, mBitnotI,
     mUnaryPlusF64, mUnaryMinusF64,
     mCharToStr, mBoolToStr,
-    mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping)
     mCStrToStr,
     mStrToStr, mEnumToStr,
     mAnd, mOr,
@@ -683,8 +501,8 @@ type
     mIsPartOf, mAstToStr, mParallel,
     mSwap, mIsNil, mArrToSeq, mOpenArrayToSeq,
     mNewString, mNewStringOfCap, mParseBiggestFloat,
-    mMove, mWasMoved, mDestroy, mTrace,
-    mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset,
+    mMove, mEnsureMove, mWasMoved, mDup, mDestroy, mTrace,
+    mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField,
     mArray, mOpenArray, mRange, mSet, mSeq, mVarargs,
     mRef, mPtr, mVar, mDistinct, mVoid, mTuple,
     mOrdinal, mIterableType,
@@ -704,15 +522,15 @@ type
     mNctPut, mNctLen, mNctGet, mNctHasNext, mNctNext,
 
     mNIntVal, mNFloatVal, mNSymbol, mNIdent, mNGetType, mNStrVal, mNSetIntVal,
-    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetType, mNSetStrVal, mNLineInfo,
+    mNSetFloatVal, mNSetSymbol, mNSetIdent, mNSetStrVal, mNLineInfo,
     mNNewNimNode, mNCopyNimNode, mNCopyNimTree, mStrToIdent, mNSigHash, mNSizeOf,
     mNBindSym, mNCallSite,
     mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym,
     mNHint, mNWarning, mNError,
     mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2,
-    mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples,
+    mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples,
     mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf,
-    mSymIsInstantiationOf, mNodeId, mPrivateAccess
+    mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault
 
 
 const
@@ -737,7 +555,6 @@ const
     mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI,
     mUnaryPlusF64, mUnaryMinusF64,
     mCharToStr, mBoolToStr,
-    mIntToStr, mInt64ToStr, mFloatToStr,
     mCStrToStr,
     mStrToStr, mEnumToStr,
     mAnd, mOr,
@@ -767,10 +584,6 @@ proc hash*(x: ItemId): Hash =
 
 
 type
-  TIdObj* {.acyclic.} = object of RootObj
-    itemId*: ItemId
-  PIdObj* = ref TIdObj
-
   PNode* = ref TNode
   TNodeSeq* = seq[PNode]
   PType* = ref TType
@@ -794,6 +607,8 @@ type
       ident*: PIdent
     else:
       sons*: TNodeSeq
+    when defined(nimsuggest):
+      endInfo*: TLineInfo
 
   TStrTable* = object         # a table[PIdent] of PSym
     counter*: int
@@ -814,9 +629,6 @@ type
     locOther                  # location is something other
   TLocFlag* = enum
     lfIndirect,               # backend introduced a pointer
-    lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates
-      # that the symbol has been imported via 'importc: "fullname"' and
-      # no format string.
     lfNoDeepCopy,             # no need for a deep copy
     lfNoDecl,                 # do not declare it in C
     lfDynamicLib,             # link symbol to dynamic library
@@ -840,7 +652,7 @@ type
     storage*: TStorageLoc
     flags*: TLocFlags         # location's flags
     lode*: PNode              # Node where the location came from; can be faked
-    r*: Rope                  # rope value of location (code generators)
+    snippet*: Rope            # C code snippet of location (code generators)
 
   # ---------------- end of backend information ------------------------------
 
@@ -851,7 +663,7 @@ type
                               # keep in sync with PackedLib
     kind*: TLibKind
     generated*: bool          # needed for the backends:
-    isOverriden*: bool
+    isOverridden*: bool
     name*: Rope
     path*: PNode              # can be a string literal!
 
@@ -874,7 +686,8 @@ type
   PScope* = ref TScope
 
   PLib* = ref TLib
-  TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym
+  TSym* {.acyclic.} = object # Keep in sync with PackedSym
+    itemId*: ItemId
     # proc and type instantiations are cached in the generic symbol
     case kind*: TSymKind
     of routineKinds:
@@ -890,6 +703,9 @@ type
     typ*: PType
     name*: PIdent
     info*: TLineInfo
+    when defined(nimsuggest):
+      endInfo*: TLineInfo
+      hasUserSpecifiedType*: bool  # used for determining whether to display inlay type hints
     owner*: PSym
     flags*: TSymFlags
     ast*: PNode               # syntax tree of proc, iterator, etc.:
@@ -911,7 +727,9 @@ type
                               # for modules, an unique index corresponding
                               # to the module's fileIdx
                               # for variables a slot index for the evaluator
-    offset*: int              # offset of record field
+    offset*: int32            # offset of record field
+    disamb*: int32            # disambiguation number; the basic idea is that
+                              # `<procname>__<module>_<disamb>` is unique
     loc*: TLoc
     annex*: PLib              # additional fields (seldom used, so we use a
                               # reference to another object to save space)
@@ -919,32 +737,35 @@ type
       cname*: string          # resolved C declaration name in importc decl, e.g.:
                               # proc fun() {.importc: "$1aux".} => cname = funaux
     constraint*: PNode        # additional constraints like 'lit|result'; also
-                              # misused for the codegenDecl pragma in the hope
+                              # misused for the codegenDecl and virtual pragmas in the hope
                               # it won't cause problems
                               # for skModule the string literal to output for
                               # deprecated modules.
+    instantiatedFrom*: PSym   # for instances, the generic symbol where it came from.
     when defined(nimsuggest):
       allUsages*: seq[TLineInfo]
 
   TTypeSeq* = seq[PType]
-  TLockLevel* = distinct int16
 
   TTypeAttachedOp* = enum ## as usual, order is important here
+    attachedWasMoved,
     attachedDestructor,
     attachedAsgn,
+    attachedDup,
     attachedSink,
     attachedTrace,
     attachedDeepCopy
 
-  TType* {.acyclic.} = object of TIdObj # \
+  TType* {.acyclic.} = object # \
                               # types are identical iff they have the
                               # same id; there may be multiple copies of a type
                               # in memory!
                               # Keep in sync with PackedType
+    itemId*: ItemId
     kind*: TTypeKind          # kind of type
     callConv*: TCallingConvention # for procs
     flags*: TTypeFlags        # flags of the type
-    sons*: TTypeSeq           # base types, etc.
+    sons: TTypeSeq           # base types, etc.
     n*: PNode                 # node for types:
                               # for range types a nkRange node
                               # for record types a nkRecord node
@@ -961,7 +782,6 @@ type
                               # -1 means that the size is unkwown
     align*: int16             # the type's alignment requirements
     paddingAtEnd*: int16      #
-    lockLevel*: TLockLevel    # lock level as required for deadlock checking
     loc*: TLoc
     typeInst*: PType          # for generic instantiations the tyGenericInst that led to this
                               # type.
@@ -973,24 +793,6 @@ type
 
   TPairSeq* = seq[TPair]
 
-  TIdPair* = object
-    key*: PIdObj
-    val*: RootRef
-
-  TIdPairSeq* = seq[TIdPair]
-  TIdTable* = object # the same as table[PIdent] of PObject
-    counter*: int
-    data*: TIdPairSeq
-
-  TIdNodePair* = object
-    key*: PIdObj
-    val*: PNode
-
-  TIdNodePairSeq* = seq[TIdNodePair]
-  TIdNodeTable* = object # the same as table[PIdObj] of PNode
-    counter*: int
-    data*: TIdNodePairSeq
-
   TNodePair* = object
     h*: Hash                 # because it is expensive to compute!
     key*: PNode
@@ -1026,6 +828,8 @@ proc comment*(n: PNode): string =
   if nfHasComment in n.flags and not gconfig.useIc:
     # IC doesn't track comments, see `packed_ast`, so this could fail
     result = gconfig.comments[n.nodeId]
+  else:
+    result = ""
 
 proc `comment=`*(n: PNode, a: string) =
   let id = n.nodeId
@@ -1075,7 +879,9 @@ const
                                       nfDotSetter, nfDotField,
                                       nfIsRef, nfIsPtr, nfPreventCg, nfLL,
                                       nfFromTemplate, nfDefaultRefsParam,
-                                      nfExecuteOnReload, nfLastRead, nfFirstWrite}
+                                      nfExecuteOnReload, nfLastRead,
+                                      nfFirstWrite, nfSkipFieldChecking,
+                                      nfDisabledOpenSym}
   namePos* = 0
   patternPos* = 1    # empty except for term rewriting macros
   genericParamsPos* = 2
@@ -1088,10 +894,8 @@ const
 
   nfAllFieldsSet* = nfBase2
 
-  nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
-                  nkCommand, nkCallStrLit, nkHiddenCallConv}
   nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
-                   nkClosedSymChoice}
+                   nkClosedSymChoice, nkOpenSym}
 
   nkPragmaCallKinds* = {nkExprColonExpr, nkCall, nkCallStrLit}
   nkLiterals* = {nkCharLit..nkTripleStrLit}
@@ -1115,17 +919,17 @@ const
 
 proc getPIdent*(a: PNode): PIdent {.inline.} =
   ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`.
-  # xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice}
-  # which may simplify code.
   case a.kind
   of nkSym: a.sym.name
   of nkIdent: a.ident
+  of nkOpenSymChoice, nkClosedSymChoice: a.sons[0].sym.name
+  of nkOpenSym: getPIdent(a.sons[0])
   else: nil
 
 const
   moduleShift = when defined(cpu32): 20 else: 24
 
-template id*(a: PIdObj): int =
+template id*(a: PType | PSym): int =
   let x = a
   (x.itemId.module.int shl moduleShift) + x.itemId.item.int
 
@@ -1135,15 +939,19 @@ type
     symId*: int32
     typeId*: int32
     sealed*: bool
+    disambTable*: CountTable[PIdent]
 
 const
   PackageModuleId* = -3'i32
 
 proc idGeneratorFromModule*(m: PSym): IdGenerator =
   assert m.kind == skModule
-  result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
+  result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0, disambTable: initCountTable[PIdent]())
+
+proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator =
+  result = IdGenerator(module: PackageModuleId, symId: nextIdWillBe - 1'i32, typeId: 0, disambTable: initCountTable[PIdent]())
 
-proc nextSymId*(x: IdGenerator): ItemId {.inline.} =
+proc nextSymId(x: IdGenerator): ItemId {.inline.} =
   assert(not x.sealed)
   inc x.symId
   result = ItemId(module: x.module, item: x.symId)
@@ -1173,9 +981,7 @@ proc isCallExpr*(n: PNode): bool =
 
 proc discardSons*(father: PNode)
 
-type Indexable = PNode | PType
-
-proc len*(n: Indexable): int {.inline.} =
+proc len*(n: PNode): int {.inline.} =
   result = n.sons.len
 
 proc safeLen*(n: PNode): int {.inline.} =
@@ -1189,18 +995,31 @@ proc safeArrLen*(n: PNode): int {.inline.} =
   elif n.kind in {nkNone..nkFloat128Lit}: result = 0
   else: result = n.len
 
-proc add*(father, son: Indexable) =
+proc add*(father, son: PNode) =
   assert son != nil
   father.sons.add(son)
 
-proc addAllowNil*(father, son: Indexable) {.inline.} =
+proc addAllowNil*(father, son: PNode) {.inline.} =
   father.sons.add(son)
 
-template `[]`*(n: Indexable, i: int): Indexable = n.sons[i]
-template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x
+template `[]`*(n: PNode, i: int): PNode = n.sons[i]
+template `[]=`*(n: PNode, i: int; x: PNode) = n.sons[i] = x
+
+template `[]`*(n: PNode, i: BackwardsIndex): PNode = n[n.len - i.int]
+template `[]=`*(n: PNode, i: BackwardsIndex; x: PNode) = n[n.len - i.int] = x
 
-template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int]
-template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x
+proc add*(father, son: PType) =
+  assert son != nil
+  father.sons.add(son)
+
+proc addAllowNil*(father, son: PType) {.inline.} =
+  father.sons.add(son)
+
+template `[]`*(n: PType, i: int): PType = n.sons[i]
+template `[]=`*(n: PType, i: int; x: PType) = n.sons[i] = x
+
+template `[]`*(n: PType, i: BackwardsIndex): PType = n[n.len - i.int]
+template `[]=`*(n: PType, i: BackwardsIndex; x: PType) = n[n.len - i.int] = x
 
 proc getDeclPragma*(n: PNode): PNode =
   ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found.
@@ -1208,6 +1027,7 @@ proc getDeclPragma*(n: PNode): PNode =
   case n.kind
   of routineDefs:
     if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos]
+    else: result = nil
   of nkTypeDef:
     #[
     type F3*{.deprecated: "x3".} = int
@@ -1227,12 +1047,48 @@ proc getDeclPragma*(n: PNode): PNode =
     ]#
     if n[0].kind == nkPragmaExpr:
       result = n[0][1]
+    else:
+      result = nil
   else:
     # support as needed for `nkIdentDefs` etc.
     result = nil
   if result != nil:
     assert result.kind == nkPragma, $(result.kind, n.kind)
 
+proc extractPragma*(s: PSym): PNode =
+  ## gets the pragma node of routine/type/var/let/const symbol `s`
+  if s.kind in routineKinds: # bug #24167
+    if s.ast[pragmasPos] != nil and s.ast[pragmasPos].kind != nkEmpty:
+      result = s.ast[pragmasPos]
+    else:
+      result = nil
+  elif s.kind in {skType, skVar, skLet, skConst}:
+    if s.ast != nil and s.ast.len > 0:
+      if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
+        # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
+        result = s.ast[0][1]
+      else:
+        result = nil
+    else:
+      result = nil
+  else:
+    result = nil
+  assert result == nil or result.kind == nkPragma
+
+proc skipPragmaExpr*(n: PNode): PNode =
+  ## if pragma expr, give the node the pragmas are applied to,
+  ## otherwise give node itself
+  if n.kind == nkPragmaExpr:
+    result = n[0]
+  else:
+    result = n
+
+proc setInfoRecursive*(n: PNode, info: TLineInfo) =
+  ## set line info recursively
+  if n != nil:
+    for i in 0..<n.safeLen: setInfoRecursive(n[i], info)
+    n.info = info
+
 when defined(useNodeIds):
   const nodeIdToDebug* = -1 # 2322968
   var gNodeId: int
@@ -1278,6 +1134,33 @@ proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode =
   result.info = info
   result.typ = typ
 
+proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
+  ## new node with line info, no type, and no children
+  newNodeImpl(info)
+  setIdMaybe()
+
+proc newAtom*(ident: PIdent, info: TLineInfo): PNode =
+  result = newNode(nkIdent, info)
+  result.ident = ident
+
+proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.intVal = intVal
+
+proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.floatVal = floatVal
+
+proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.strVal = strVal
+
+proc newTree*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode =
+  result = newNodeI(kind, info)
+  if children.len > 0:
+    result.info = children[0].info
+  result.sons = @children
+
 proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode =
   result = newNode(kind)
   if children.len > 0:
@@ -1297,7 +1180,7 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[
   result.sons = @children
 
 template previouslyInferred*(t: PType): PType =
-  if t.sons.len > 1: t.lastSon else: nil
+  if t.sons.len > 1: t.last else: nil
 
 when false:
   import tables, strutils
@@ -1308,11 +1191,15 @@ when false:
       echo k
       echo v
 
-proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
+proc newSym*(symKind: TSymKind, name: PIdent, idgen: IdGenerator; owner: PSym,
              info: TLineInfo; options: TOptions = {}): PSym =
   # generates a symbol and initializes the hash field too
+  assert not name.isNil
+  let id = nextSymId idgen
   result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id,
-                options: options, owner: owner, offset: defaultOffset)
+                options: options, owner: owner, offset: defaultOffset,
+                disamb: getOrDefault(idgen.disambTable, name).int32)
+  idgen.disambTable.inc name
   when false:
     if id.module == 48 and id.item == 39:
       writeStackTrace()
@@ -1321,7 +1208,7 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym,
 
 proc astdef*(s: PSym): PNode =
   # get only the definition (initializer) portion of the ast
-  if s.ast != nil and s.ast.kind == nkIdentDefs:
+  if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
     s.ast[2]
   else:
     s.ast
@@ -1371,11 +1258,6 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
   setLen(dest.data, src.data.len)
   for i in 0..high(src.data): dest.data[i] = src.data[i]
 
-proc copyIdTable*(dest: var TIdTable, src: TIdTable) =
-  dest.counter = src.counter
-  newSeq(dest.data, src.data.len)
-  for i in 0..high(src.data): dest.data[i] = src.data[i]
-
 proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
   dest.counter = src.counter
   setLen(dest.data, src.data.len)
@@ -1405,6 +1287,9 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode =
   result.typ = sym.typ
   result.info = info
 
+proc newOpenSym*(n: PNode): PNode {.inline.} =
+  result = newTreeI(nkOpenSym, n.info, n)
+
 proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode =
   result = newNode(kind)
   result.intVal = intVal
@@ -1413,7 +1298,42 @@ proc newIntNode*(kind: TNodeKind, intVal: Int128): PNode =
   result = newNode(kind)
   result.intVal = castToInt64(intVal)
 
-proc lastSon*(n: Indexable): Indexable = n.sons[^1]
+proc lastSon*(n: PNode): PNode {.inline.} = n.sons[^1]
+template setLastSon*(n: PNode, s: PNode) = n.sons[^1] = s
+
+template firstSon*(n: PNode): PNode = n.sons[0]
+template secondSon*(n: PNode): PNode = n.sons[1]
+
+template hasSon*(n: PNode): bool = n.len > 0
+template has2Sons*(n: PNode): bool = n.len > 1
+
+proc replaceFirstSon*(n, newson: PNode) {.inline.} =
+  n.sons[0] = newson
+
+proc replaceSon*(n: PNode; i: int; newson: PNode) {.inline.} =
+  n.sons[i] = newson
+
+proc last*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc elementType*(n: PType): PType {.inline.} = n.sons[^1]
+proc skipModifier*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc indexType*(n: PType): PType {.inline.} = n.sons[0]
+proc baseClass*(n: PType): PType {.inline.} = n.sons[0]
+
+proc base*(t: PType): PType {.inline.} =
+  result = t.sons[0]
+
+proc returnType*(n: PType): PType {.inline.} = n.sons[0]
+proc setReturnType*(n, r: PType) {.inline.} = n.sons[0] = r
+proc setIndexType*(n, idx: PType) {.inline.} = n.sons[0] = idx
+
+proc firstParamType*(n: PType): PType {.inline.} = n.sons[1]
+proc firstGenericParam*(n: PType): PType {.inline.} = n.sons[1]
+
+proc typeBodyImpl*(n: PType): PType {.inline.} = n.sons[^1]
+
+proc genericHead*(n: PType): PType {.inline.} = n.sons[0]
 
 proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
   ## Used throughout the compiler code to test whether a type tree contains or
@@ -1421,7 +1341,7 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
   ## last child nodes of a type tree need to be searched. This is a really hot
   ## path within the compiler!
   result = t
-  while result.kind in kinds: result = lastSon(result)
+  while result.kind in kinds: result = last(result)
 
 proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
   let kind = skipTypes(typ, abstractVarRange).kind
@@ -1442,7 +1362,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode =
     result = newNode(nkIntLit)
   of tyStatic: # that's a pre-existing bug, will fix in another PR
     result = newNode(nkIntLit)
-  else: doAssert false, $kind
+  else: raiseAssert $kind
   result.intVal = intVal
   result.typ = typ
 
@@ -1471,16 +1391,8 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
                   pragmas, exceptions, body]
 
 const
-  UnspecifiedLockLevel* = TLockLevel(-1'i16)
-  MaxLockLevel* = 1000'i16
-  UnknownLockLevel* = TLockLevel(1001'i16)
   AttachedOpToStr*: array[TTypeAttachedOp, string] = [
-    "=destroy", "=copy", "=sink", "=trace", "=deepcopy"]
-
-proc `$`*(x: TLockLevel): string =
-  if x.ord == UnspecifiedLockLevel.ord: result = "<unspecified>"
-  elif x.ord == UnknownLockLevel.ord: result = "<unknown>"
-  else: result = $int16(x)
+    "=wasMoved", "=destroy", "=copy", "=dup", "=sink", "=trace", "=deepcopy"]
 
 proc `$`*(s: PSym): string =
   if s != nil:
@@ -1488,27 +1400,140 @@ proc `$`*(s: PSym): string =
   else:
     result = "<nil>"
 
-proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType =
+when false:
+  iterator items*(t: PType): PType =
+    for i in 0..<t.sons.len: yield t.sons[i]
+
+  iterator pairs*(n: PType): tuple[i: int, n: PType] =
+    for i in 0..<n.sons.len: yield (i, n.sons[i])
+
+when true:
+  proc len*(n: PType): int {.inline.} =
+    result = n.sons.len
+
+proc sameTupleLengths*(a, b: PType): bool {.inline.} =
+  result = a.sons.len == b.sons.len
+
+iterator tupleTypePairs*(a, b: PType): (int, PType, PType) =
+  for i in 0 ..< a.sons.len:
+    yield (i, a.sons[i], b.sons[i])
+
+iterator underspecifiedPairs*(a, b: PType; start = 0; without = 0): (PType, PType) =
+  # XXX Figure out with what typekinds this is called.
+  for i in start ..< min(a.sons.len, b.sons.len) + without:
+    yield (a.sons[i], b.sons[i])
+
+proc signatureLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc paramsLen*(t: PType): int {.inline.} =
+  result = t.sons.len - 1
+
+proc genericParamsLen*(t: PType): int {.inline.} =
+  assert t.kind == tyGenericInst
+  result = t.sons.len - 2 # without 'head' and 'body'
+
+proc genericInvocationParamsLen*(t: PType): int {.inline.} =
+  assert t.kind == tyGenericInvocation
+  result = t.sons.len - 1 # without 'head'
+
+proc kidsLen*(t: PType): int {.inline.} =
+  result = t.sons.len
+
+proc genericParamHasConstraints*(t: PType): bool {.inline.} = t.sons.len > 0
+
+proc hasElementType*(t: PType): bool {.inline.} = t.sons.len > 0
+proc isEmptyTupleType*(t: PType): bool {.inline.} = t.sons.len == 0
+proc isSingletonTupleType*(t: PType): bool {.inline.} = t.sons.len == 1
+
+proc genericConstraint*(t: PType): PType {.inline.} = t.sons[0]
+
+iterator genericInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator genericInstParamPairs*(a, b: PType): (int, PType, PType) =
+  for i in 1..<min(a.sons.len, b.sons.len)-1:
+    yield (i-1, a.sons[i], b.sons[i])
+
+iterator genericInvocationParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len:
+    yield (i!=1, t.sons[i])
+
+iterator genericInvocationAndBodyElements*(a, b: PType): (PType, PType) =
+  for i in 1..<a.sons.len:
+    yield (a.sons[i], b.sons[i-1])
+
+iterator genericInvocationParamPairs*(a, b: PType): (bool, PType, PType) =
+  for i in 1..<a.sons.len:
+    if i >= b.sons.len:
+      yield (false, nil, nil)
+    else:
+      yield (true, a.sons[i], b.sons[i])
+
+iterator genericBodyParams*(t: PType): (int, PType) =
+  for i in 0..<t.sons.len-1:
+    yield (i, t.sons[i])
+
+iterator userTypeClassInstParams*(t: PType): (bool, PType) =
+  for i in 1..<t.sons.len-1:
+    yield (i!=1, t.sons[i])
+
+iterator ikids*(t: PType): (int, PType) =
+  for i in 0..<t.sons.len: yield (i, t.sons[i])
+
+const
+  FirstParamAt* = 1
+  FirstGenericParamAt* = 1
+
+iterator paramTypes*(t: PType): (int, PType) =
+  for i in FirstParamAt..<t.sons.len: yield (i, t.sons[i])
+
+iterator paramTypePairs*(a, b: PType): (PType, PType) =
+  for i in FirstParamAt..<a.sons.len: yield (a.sons[i], b.sons[i])
+
+template paramTypeToNodeIndex*(x: int): int = x
+
+iterator kids*(t: PType): PType =
+  for i in 0..<t.sons.len: yield t.sons[i]
+
+iterator signature*(t: PType): PType =
+  # yields return type + parameter types
+  for i in 0..<t.sons.len: yield t.sons[i]
+
+proc newType*(kind: TTypeKind; idgen: IdGenerator; owner: PSym; son: sink PType = nil): PType =
+  let id = nextTypeId idgen
   result = PType(kind: kind, owner: owner, size: defaultSize,
                  align: defaultAlignment, itemId: id,
-                 lockLevel: UnspecifiedLockLevel,
-                 uniqueId: id)
+                 uniqueId: id, sons: @[])
+  if son != nil: result.sons.add son
   when false:
     if result.itemId.module == 55 and result.itemId.item == 2:
       echo "KNID ", kind
       writeStackTrace()
 
+proc setSons*(dest: PType; sons: sink seq[PType]) {.inline.} = dest.sons = sons
+proc setSon*(dest: PType; son: sink PType) {.inline.} = dest.sons = @[son]
+proc setSonsLen*(dest: PType; len: int) {.inline.} = setLen(dest.sons, len)
 
 proc mergeLoc(a: var TLoc, b: TLoc) =
   if a.k == low(typeof(a.k)): a.k = b.k
   if a.storage == low(typeof(a.storage)): a.storage = b.storage
   a.flags.incl b.flags
   if a.lode == nil: a.lode = b.lode
-  if a.r == "": a.r = b.r
+  if a.snippet == "": a.snippet = b.snippet
+
+proc newSons*(father: PNode, length: int) =
+  setLen(father.sons, length)
 
-proc newSons*(father: Indexable, length: int) =
+proc newSons*(father: PType, length: int) =
   setLen(father.sons, length)
 
+proc truncateInferredTypeCandidates*(t: PType) {.inline.} =
+  assert t.kind == tyInferred
+  if t.sons.len > 1:
+    setLen(t.sons, 1)
+
 proc assignType*(dest, src: PType) =
   dest.kind = src.kind
   dest.flags = src.flags
@@ -1516,7 +1541,6 @@ proc assignType*(dest, src: PType) =
   dest.n = src.n
   dest.size = src.size
   dest.align = src.align
-  dest.lockLevel = src.lockLevel
   # this fixes 'type TLock = TSysLock':
   if src.sym != nil:
     if dest.sym != nil:
@@ -1525,19 +1549,23 @@ proc assignType*(dest, src: PType) =
       mergeLoc(dest.sym.loc, src.sym.loc)
     else:
       dest.sym = src.sym
-  newSons(dest, src.len)
-  for i in 0..<src.len: dest[i] = src[i]
+  newSons(dest, src.sons.len)
+  for i in 0..<src.sons.len: dest[i] = src[i]
 
-proc copyType*(t: PType, id: ItemId, owner: PSym): PType =
-  result = newType(t.kind, id, owner)
+proc copyType*(t: PType, idgen: IdGenerator, owner: PSym): PType =
+  result = newType(t.kind, idgen, owner)
   assignType(result, t)
   result.sym = t.sym          # backend-info should not be copied
 
 proc exactReplica*(t: PType): PType =
-  result = copyType(t, t.itemId, t.owner)
+  result = PType(kind: t.kind, owner: t.owner, size: defaultSize,
+                 align: defaultAlignment, itemId: t.itemId,
+                 uniqueId: t.uniqueId)
+  assignType(result, t)
+  result.sym = t.sym          # backend-info should not be copied
 
-proc copySym*(s: PSym; id: ItemId): PSym =
-  result = newSym(s.kind, s.name, id, s.owner, s.info, s.options)
+proc copySym*(s: PSym; idgen: IdGenerator): PSym =
+  result = newSym(s.kind, s.name, idgen, s.owner, s.info, s.options)
   #result.ast = nil            # BUGFIX; was: s.ast which made problems
   result.typ = s.typ
   result.flags = s.flags
@@ -1552,9 +1580,9 @@ proc copySym*(s: PSym; id: ItemId): PSym =
     result.bitsize = s.bitsize
     result.alignment = s.alignment
 
-proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
+proc createModuleAlias*(s: PSym, idgen: IdGenerator, newIdent: PIdent, info: TLineInfo;
                         options: TOptions): PSym =
-  result = newSym(s.kind, newIdent, id, s.owner, info, options)
+  result = newSym(s.kind, newIdent, idgen, s.owner, info, options)
   # keep ID!
   result.ast = s.ast
   #result.id = s.id # XXX figure out what to do with the ID.
@@ -1564,43 +1592,23 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo;
   result.loc = s.loc
   result.annex = s.annex
 
-proc initStrTable*(x: var TStrTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc newStrTable*: TStrTable =
-  initStrTable(result)
+proc initStrTable*(): TStrTable =
+  result = TStrTable(counter: 0)
+  newSeq(result.data, StartSize)
 
-proc initIdTable*(x: var TIdTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
+proc initObjectSet*(): TObjectSet =
+  result = TObjectSet(counter: 0)
+  newSeq(result.data, StartSize)
 
-proc newIdTable*: TIdTable =
-  initIdTable(result)
-
-proc resetIdTable*(x: var TIdTable) =
-  x.counter = 0
-  # clear and set to old initial size:
-  setLen(x.data, 0)
-  setLen(x.data, StartSize)
-
-proc initObjectSet*(x: var TObjectSet) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc initIdNodeTable*(x: var TIdNodeTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
-
-proc initNodeTable*(x: var TNodeTable) =
-  x.counter = 0
-  newSeq(x.data, StartSize)
+proc initNodeTable*(): TNodeTable =
+  result = TNodeTable(counter: 0)
+  newSeq(result.data, StartSize)
 
 proc skipTypes*(t: PType, kinds: TTypeKinds; maxIters: int): PType =
   result = t
   var i = maxIters
   while result.kind in kinds:
-    result = lastSon(result)
+    result = last(result)
     dec i
     if i == 0: return nil
 
@@ -1608,8 +1616,8 @@ proc skipTypesOrNil*(t: PType, kinds: TTypeKinds): PType =
   ## same as skipTypes but handles 'nil'
   result = t
   while result != nil and result.kind in kinds:
-    if result.len == 0: return nil
-    result = lastSon(result)
+    if result.sons.len == 0: return nil
+    result = last(result)
 
 proc isGCedMem*(t: PType): bool {.inline.} =
   result = t.kind in {tyString, tyRef, tySequence} or
@@ -1644,9 +1652,6 @@ proc rawAddSon*(father, son: PType; propagateHasAsgn = true) =
   father.sons.add(son)
   if not son.isNil: propagateToOwner(father, son, propagateHasAsgn)
 
-proc rawAddSonNoPropagationOfTypeFlags*(father, son: PType) =
-  father.sons.add(son)
-
 proc addSonNilAllowed*(father, son: PNode) =
   father.sons.add(son)
 
@@ -1674,6 +1679,8 @@ proc copyNode*(src: PNode): PNode =
   of nkIdent: result.ident = src.ident
   of nkStrLit..nkTripleStrLit: result.strVal = src.strVal
   else: discard
+  when defined(nimsuggest):
+    result.endInfo = src.endInfo
 
 template transitionNodeKindCommon(k: TNodeKind) =
   let obj {.inject.} = n[]
@@ -1726,6 +1733,8 @@ template copyNodeImpl(dst, src, processSonsStmt) =
   if src == nil: return
   dst = newNode(src.kind)
   dst.info = src.info
+  when defined(nimsuggest):
+    result.endInfo = src.endInfo
   dst.typ = src.typ
   dst.flags = src.flags * PersistentNodeFlags
   dst.comment = src.comment
@@ -1774,6 +1783,7 @@ proc hasNilSon*(n: PNode): bool =
   result = false
 
 proc containsNode*(n: PNode, kinds: TNodeKinds): bool =
+  result = false
   if n == nil: return
   case n.kind
   of nkEmpty..nkNilLit: result = n.kind in kinds
@@ -1874,13 +1884,15 @@ proc skipGenericOwner*(s: PSym): PSym =
   ## Generic instantiations are owned by their originating generic
   ## symbol. This proc skips such owners and goes straight to the owner
   ## of the generic itself (the module or the enclosing proc).
-  result = if s.kind in skProcKinds and sfFromGeneric in s.flags:
+  result = if s.kind == skModule:
+            s
+           elif s.kind in skProcKinds and sfFromGeneric in s.flags and s.owner.kind != skModule:
              s.owner.owner
            else:
              s.owner
 
 proc originatingModule*(s: PSym): PSym =
-  result = s.owner
+  result = s
   while result.kind != skModule: result = result.owner
 
 proc isRoutine*(s: PSym): bool {.inline.} =
@@ -1890,20 +1902,6 @@ proc isCompileTimeProc*(s: PSym): bool {.inline.} =
   result = s.kind == skMacro or
            s.kind in {skProc, skFunc} and sfCompileTime in s.flags
 
-proc isRunnableExamples*(n: PNode): bool =
-  # Templates and generics don't perform symbol lookups.
-  result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
-    n.kind == nkIdent and n.ident.s == "runnableExamples"
-
-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[patternPos].kind != nkEmpty
 
@@ -1940,23 +1938,21 @@ proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType =
   ## returned. Otherwise ``typ`` is simply returned as-is.
   result = typ
   if typ.kind != kind:
-    result = newType(kind, nextTypeId(idgen), typ.owner)
-    rawAddSon(result, typ)
+    result = newType(kind, idgen, typ.owner, typ)
 
 proc toRef*(typ: PType; idgen: IdGenerator): PType =
   ## If ``typ`` is a tyObject then it is converted into a `ref <typ>` and
   ## returned. Otherwise ``typ`` is simply returned as-is.
   result = typ
   if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject:
-    result = newType(tyRef, nextTypeId(idgen), typ.owner)
-    rawAddSon(result, typ)
+    result = newType(tyRef, idgen, typ.owner, typ)
 
 proc toObject*(typ: PType): PType =
   ## If ``typ`` is a tyRef then its immediate son is returned (which in many
   ## cases should be a ``tyObject``).
   ## Otherwise ``typ`` is simply returned as-is.
   let t = typ.skipTypes({tyAlias, tyGenericInst})
-  if t.kind == tyRef: t.lastSon
+  if t.kind == tyRef: t.elementType
   else: typ
 
 proc toObjectFromRefPtrGeneric*(typ: PType): PType =
@@ -1973,11 +1969,11 @@ proc toObjectFromRefPtrGeneric*(typ: PType): PType =
   result = typ
   while true:
     case result.kind
-    of tyGenericBody: result = result.lastSon
+    of tyGenericBody: result = result.last
     of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0]
       # automatic dereferencing is deep, refs #18298.
     else: break
-  assert result.sym != nil
+  # result does not have to be object type
 
 proc isImportedException*(t: PType; conf: ConfigRef): bool =
   assert t != nil
@@ -1986,12 +1982,10 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool =
     return false
 
   let base = t.skipTypes({tyAlias, tyPtr, tyDistinct, tyGenericInst})
-
-  if base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}:
-    result = true
+  result = base.sym != nil and {sfCompileToCpp, sfImportc} * base.sym.flags != {}
 
 proc isInfixAs*(n: PNode): bool =
-  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as"
+  return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.id == ord(wAs)
 
 proc skipColon*(n: PNode): PNode =
   result = n
@@ -1999,10 +1993,12 @@ proc skipColon*(n: PNode): PNode =
     result = n[1]
 
 proc findUnresolvedStatic*(n: PNode): PNode =
-  # n.typ == nil: see issue #14802
   if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil:
     return n
-
+  if n.typ != nil and n.typ.kind == tyTypeDesc:
+    let t = skipTypes(n.typ, {tyTypeDesc})
+    if t.kind == tyGenericParam and not t.genericParamHasConstraints:
+      return n
   for son in n:
     let n = son.findUnresolvedStatic
     if n != nil: return n
@@ -2031,20 +2027,29 @@ template detailedInfo*(sym: PSym): string =
 proc isInlineIterator*(typ: PType): bool {.inline.} =
   typ.kind == tyProc and tfIterator in typ.flags and typ.callConv != ccClosure
 
+proc isIterator*(typ: PType): bool {.inline.} =
+  typ.kind == tyProc and tfIterator in typ.flags
+
 proc isClosureIterator*(typ: PType): bool {.inline.} =
   typ.kind == tyProc and tfIterator in typ.flags and typ.callConv == ccClosure
 
 proc isClosure*(typ: PType): bool {.inline.} =
   typ.kind == tyProc and typ.callConv == ccClosure
 
+proc isNimcall*(s: PSym): bool {.inline.} =
+  s.typ.callConv == ccNimCall
+
+proc isExplicitCallConv*(s: PSym): bool {.inline.} =
+  tfExplicitCallConv in s.typ.flags
+
 proc isSinkParam*(s: PSym): bool {.inline.} =
   s.kind == skParam and (s.typ.kind == tySink or tfHasOwned in s.typ.flags)
 
 proc isSinkType*(t: PType): bool {.inline.} =
   t.kind == tySink or tfHasOwned in t.flags
 
-proc newProcType*(info: TLineInfo; id: ItemId; owner: PSym): PType =
-  result = newType(tyProc, id, owner)
+proc newProcType*(info: TLineInfo; idgen: IdGenerator; owner: PSym): PType =
+  result = newType(tyProc, idgen, owner)
   result.n = newNodeI(nkFormalParams, info)
   rawAddSon(result, nil) # return type
   # result.n[0] used to be `nkType`, but now it's `nkEffectList` because
@@ -2053,7 +2058,7 @@ proc newProcType*(info: TLineInfo; id: ItemId; owner: PSym): PType =
   result.n.add newNodeI(nkEffectList, info)
 
 proc addParam*(procType: PType; param: PSym) =
-  param.position = procType.len-1
+  param.position = procType.sons.len-1
   procType.n.add newSymNode(param)
   rawAddSon(procType, param.typ)
 
@@ -2095,13 +2100,15 @@ proc toHumanStr*(kind: TTypeKind): string =
   ## strips leading `tk`
   result = toHumanStrImpl(kind, 2)
 
-proc skipAddr*(n: PNode): PNode {.inline.} =
+proc skipHiddenAddr*(n: PNode): PNode {.inline.} =
   (if n.kind == nkHiddenAddr: n[0] else: n)
 
 proc isNewStyleConcept*(n: PNode): bool {.inline.} =
   assert n.kind == nkTypeClassTy
   result = n[0].kind == nkEmpty
 
+proc isOutParam*(t: PType): bool {.inline.} = tfIsOutParam in t.flags
+
 const
   nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit,
     nkTypeSection, nkProcDef, nkConverterDef,
@@ -2109,3 +2116,20 @@ const
     nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt,
     nkExportStmt, nkPragma, nkCommentStmt, nkBreakState,
     nkTypeOfExpr, nkMixinStmt, nkBindStmt}
+
+proc isTrue*(n: PNode): bool =
+  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
+    n.kind == nkIntLit and n.intVal != 0
+
+type
+  TypeMapping* = Table[ItemId, PType]
+  SymMapping* = Table[ItemId, PSym]
+
+template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
+template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
+
+template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
+template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
+
+template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
+template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 28c38129e..7a9892f78 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -12,26 +12,18 @@
 # the data structures here are used in various places of the compiler.
 
 import
-  ast, hashes, intsets, options, lineinfos, ropes, idents, rodutils,
+  ast, astyaml, options, lineinfos, idents, rodutils,
   msgs
 
-import strutils except addf
+import std/[hashes, intsets]
+import std/strutils except addf
+
+export astyaml.treeToYaml, astyaml.typeToYaml, astyaml.symToYaml, astyaml.lineInfoToStr
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-when not defined(nimHasCursor):
-  {.pragma: cursor.}
-
 proc hashNode*(p: RootRef): Hash
-proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope
-  # Convert a tree into its YAML representation; this is used by the
-  # YAML code generator and it is invaluable for debugging purposes.
-  # If maxRecDepht <> -1 then it won't print the whole graph.
-proc typeToYaml*(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc symToYaml*(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope
-proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): Rope
-
 
 # these are for debugging only: They are not really deprecated, but I want
 # the warning so that release versions do not contain debugging statements:
@@ -39,15 +31,6 @@ proc debug*(n: PSym; conf: ConfigRef = nil) {.exportc: "debugSym", deprecated.}
 proc debug*(n: PType; conf: ConfigRef = nil) {.exportc: "debugType", deprecated.}
 proc debug*(n: PNode; conf: ConfigRef = nil) {.exportc: "debugNode", deprecated.}
 
-proc typekinds*(t: PType) {.deprecated.} =
-  var t = t
-  var s = ""
-  while t != nil and t.len > 0:
-    s.add $t.kind
-    s.add " "
-    t = t.lastSon
-  echo s
-
 template debug*(x: PSym|PType|PNode) {.deprecated.} =
   when compiles(c.config):
     debug(c.config, x)
@@ -82,16 +65,6 @@ template mdbg*: bool {.deprecated.} =
   else:
     error()
 
-# --------------------------- ident tables ----------------------------------
-proc idTableGet*(t: TIdTable, key: PIdObj): RootRef
-proc idTableGet*(t: TIdTable, key: int): RootRef
-proc idTablePut*(t: var TIdTable, key: PIdObj, val: RootRef)
-proc idTableHasObjectAsKey*(t: TIdTable, key: PIdObj): bool
-  # checks if `t` contains the `key` (compared by the pointer value, not only
-  # `key`'s id)
-proc idNodeTableGet*(t: TIdNodeTable, key: PIdObj): PNode
-proc idNodeTablePut*(t: var TIdNodeTable, key: PIdObj, val: PNode)
-
 # ---------------------------------------------------------------------------
 
 proc lookupInRecord*(n: PNode, field: PIdent): PSym
@@ -112,7 +85,7 @@ type
     data*: TIIPairSeq
 
 
-proc initIiTable*(x: var TIITable)
+proc initIITable*(x: var TIITable)
 proc iiTableGet*(t: TIITable, key: int): int
 proc iiTablePut*(t: var TIITable, key, val: int)
 
@@ -200,6 +173,7 @@ proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
   result = nil
 
 proc sameIgnoreBacktickGensymInfo(a, b: string): bool =
+  result = false
   if a[0] != b[0]: return false
   var alen = a.len - 1
   while alen > 0 and a[alen] != '`': dec(alen)
@@ -229,10 +203,11 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym =
   ## Named parameters are special because a named parameter can be
   ## gensym'ed and then they have '\`<number>' suffix that we need to
   ## ignore, see compiler / evaltempl.nim, snippet:
-  ##   ```
+  ##   ```nim
   ##   result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id),
   ##            if c.instLines: actual.info else: templ.info)
   ##   ```
+  result = nil
   for i in 1..<list.len:
     let it = list[i].sym
     if it.name.id == ident.id or
@@ -245,169 +220,7 @@ proc mustRehash(length, counter: int): bool =
   assert(length > counter)
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
-proc rspaces(x: int): Rope =
-  # returns x spaces
-  result = rope(spaces(x))
-
-proc toYamlChar(c: char): string =
-  case c
-  of '\0'..'\x1F', '\x7F'..'\xFF': result = "\\u" & strutils.toHex(ord(c), 4)
-  of '\'', '\"', '\\': result = '\\' & c
-  else: result = $c
-
-proc makeYamlString*(s: string): Rope =
-  # We have to split long strings into many ropes. Otherwise
-  # this could trigger InternalError(111). See the ropes module for
-  # further information.
-  const MaxLineLength = 64
-  result = ""
-  var res = "\""
-  for i in 0..<s.len:
-    if (i + 1) mod MaxLineLength == 0:
-      res.add('\"')
-      res.add("\n")
-      result.add(rope(res))
-      res = "\""              # reset
-    res.add(toYamlChar(s[i]))
-  res.add('\"')
-  result.add(rope(res))
-
-proc flagsToStr[T](flags: set[T]): Rope =
-  if flags == {}:
-    result = rope("[]")
-  else:
-    result = ""
-    for x in items(flags):
-      if result != "": result.add(", ")
-      result.add(makeYamlString($x))
-    result = "[" & result & "]"
-
-proc lineInfoToStr(conf: ConfigRef; info: TLineInfo): Rope =
-  result = "[$1, $2, $3]" % [makeYamlString(toFilename(conf, info)),
-                             rope(toLinenumber(info)),
-                             rope(toColumn(info))]
-
-proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet,
-                   indent, maxRecDepth: int): Rope
-proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet,
-                  indent, maxRecDepth: int): Rope
-proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet,
-                   indent, maxRecDepth: int): Rope
-
-proc symToYamlAux(conf: ConfigRef; n: PSym, marker: var IntSet, indent: int,
-                  maxRecDepth: int): Rope =
-  if n == nil:
-    result = rope("null")
-  elif containsOrIncl(marker, n.id):
-    result = "\"$1\"" % [rope(n.name.s)]
-  else:
-    var ast = treeToYamlAux(conf, n.ast, marker, indent + 2, maxRecDepth - 1)
-                                 #rope("typ"), typeToYamlAux(conf, n.typ, marker,
-                                 #  indent + 2, maxRecDepth - 1),
-    let istr = rspaces(indent + 2)
-    result = rope("{")
-    result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
-    result.addf("$N$1\"name\": $2", [istr, makeYamlString(n.name.s)])
-    result.addf("$N$1\"typ\": $2", [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth - 1)])
-    if conf != nil:
-      # if we don't pass the config, we probably don't care about the line info
-      result.addf("$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
-    if card(n.flags) > 0:
-      result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
-    result.addf("$N$1\"magic\": $2", [istr, makeYamlString($n.magic)])
-    result.addf("$N$1\"ast\": $2", [istr, ast])
-    result.addf("$N$1\"options\": $2", [istr, flagsToStr(n.options)])
-    result.addf("$N$1\"position\": $2", [istr, rope(n.position)])
-    result.addf("$N$1\"k\": $2", [istr, makeYamlString($n.loc.k)])
-    result.addf("$N$1\"storage\": $2", [istr, makeYamlString($n.loc.storage)])
-    if card(n.loc.flags) > 0:
-      result.addf("$N$1\"flags\": $2", [istr, makeYamlString($n.loc.flags)])
-    result.addf("$N$1\"r\": $2", [istr, n.loc.r])
-    result.addf("$N$1\"lode\": $2", [istr, treeToYamlAux(conf, n.loc.lode, marker, indent + 2, maxRecDepth - 1)])
-    result.addf("$N$1}", [rspaces(indent)])
-
-proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int,
-                   maxRecDepth: int): Rope =
-  var sonsRope: Rope
-  if n == nil:
-    sonsRope = rope("null")
-  elif containsOrIncl(marker, n.id):
-    sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope(
-        strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))]
-  else:
-    if n.len > 0:
-      sonsRope = rope("[")
-      for i in 0..<n.len:
-        if i > 0: sonsRope.add(",")
-        sonsRope.addf("$N$1$2", [rspaces(indent + 4), typeToYamlAux(conf, n[i],
-            marker, indent + 4, maxRecDepth - 1)])
-      sonsRope.addf("$N$1]", [rspaces(indent + 2)])
-    else:
-      sonsRope = rope("null")
-
-    let istr = rspaces(indent + 2)
-    result = rope("{")
-    result.addf("$N$1\"kind\": $2", [istr, makeYamlString($n.kind)])
-    result.addf("$N$1\"sym\": $2",  [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth - 1)])
-    result.addf("$N$1\"n\": $2",     [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)])
-    if card(n.flags) > 0:
-      result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)])
-    result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)])
-    result.addf("$N$1\"size\": $2", [istr, rope(n.size)])
-    result.addf("$N$1\"align\": $2", [istr, rope(n.align)])
-    result.addf("$N$1\"sons\": $2", [istr, sonsRope])
-
-proc treeToYamlAux(conf: ConfigRef; n: PNode, marker: var IntSet, indent: int,
-                   maxRecDepth: int): Rope =
-  if n == nil:
-    result = rope("null")
-  else:
-    var istr = rspaces(indent + 2)
-    result = "{$N$1\"kind\": $2" % [istr, makeYamlString($n.kind)]
-    if maxRecDepth != 0:
-      if conf != nil:
-        result.addf(",$N$1\"info\": $2", [istr, lineInfoToStr(conf, n.info)])
-      case n.kind
-      of nkCharLit..nkInt64Lit:
-        result.addf(",$N$1\"intVal\": $2", [istr, rope(n.intVal)])
-      of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
-        result.addf(",$N$1\"floatVal\": $2",
-            [istr, rope(n.floatVal.toStrMaxPrecision)])
-      of nkStrLit..nkTripleStrLit:
-        result.addf(",$N$1\"strVal\": $2", [istr, makeYamlString(n.strVal)])
-      of nkSym:
-        result.addf(",$N$1\"sym\": $2",
-             [istr, symToYamlAux(conf, n.sym, marker, indent + 2, maxRecDepth)])
-      of nkIdent:
-        if n.ident != nil:
-          result.addf(",$N$1\"ident\": $2", [istr, makeYamlString(n.ident.s)])
-        else:
-          result.addf(",$N$1\"ident\": null", [istr])
-      else:
-        if n.len > 0:
-          result.addf(",$N$1\"sons\": [", [istr])
-          for i in 0..<n.len:
-            if i > 0: result.add(",")
-            result.addf("$N$1$2", [rspaces(indent + 4), treeToYamlAux(conf, n[i],
-                marker, indent + 4, maxRecDepth - 1)])
-          result.addf("$N$1]", [istr])
-      result.addf(",$N$1\"typ\": $2",
-           [istr, typeToYamlAux(conf, n.typ, marker, indent + 2, maxRecDepth)])
-    result.addf("$N$1}", [rspaces(indent)])
-
-proc treeToYaml(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = treeToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-proc typeToYaml(conf: ConfigRef; n: PType, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = typeToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-proc symToYaml(conf: ConfigRef; n: PSym, indent: int = 0, maxRecDepth: int = - 1): Rope =
-  var marker = initIntSet()
-  result = symToYamlAux(conf, n, marker, indent, maxRecDepth)
-
-import tables
+import std/tables
 
 const backrefStyle = "\e[90m"
 const enumStyle = "\e[34m"
@@ -571,14 +384,12 @@ proc value(this: var DebugPrinter; value: PType) =
     this.key "n"
     this.value value.n
 
-  if value.len > 0:
-    this.key "sons"
-    this.openBracket
-    for i in 0..<value.len:
-      this.value value[i]
-      if i != value.len - 1:
-        this.comma
-    this.closeBracket
+  this.key "sons"
+  this.openBracket
+  for i, a in value.ikids:
+    if i > 0: this.comma
+    this.value a
+  this.closeBracket
 
   if value.n != nil:
     this.key "n"
@@ -647,30 +458,33 @@ proc value(this: var DebugPrinter; value: PNode) =
 
 
 proc debug(n: PSym; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
 proc debug(n: PType; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: true,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
 proc debug(n: PNode; conf: ConfigRef) =
-  var this: DebugPrinter
-  this.visited = initTable[pointer, int]()
-  #this.renderSymType = true
-  this.useColor = not defined(windows)
+  var this = DebugPrinter(
+    visited: initTable[pointer, int](),
+    renderSymType: false,
+    useColor: not defined(windows)
+  )
   this.value(n)
   echo($this.res)
 
-proc nextTry(h, maxHash: Hash): Hash =
+proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = ((5 * h) + 1) and maxHash
   # For any initial h in range(maxHash), repeating that maxHash times
   # generates each int in range(maxHash) exactly once (see any text on
@@ -893,125 +707,12 @@ proc initTabIter*(ti: var TTabIter, tab: TStrTable): PSym =
     result = nextIter(ti, tab)
 
 iterator items*(tab: TStrTable): PSym =
-  var it: TTabIter
+  var it: TTabIter = default(TTabIter)
   var s = initTabIter(it, tab)
   while s != nil:
     yield s
     s = nextIter(it, tab)
 
-proc hasEmptySlot(data: TIdPairSeq): bool =
-  for h in 0..high(data):
-    if data[h].key == nil:
-      return true
-  result = false
-
-proc idTableRawGet(t: TIdTable, key: int): int =
-  var h: Hash
-  h = key and high(t.data)    # start with real hash value
-  while t.data[h].key != nil:
-    if t.data[h].key.id == key:
-      return h
-    h = nextTry(h, high(t.data))
-  result = - 1
-
-proc idTableHasObjectAsKey(t: TIdTable, key: PIdObj): bool =
-  var index = idTableRawGet(t, key.id)
-  if index >= 0: result = t.data[index].key == key
-  else: result = false
-
-proc idTableGet(t: TIdTable, key: PIdObj): RootRef =
-  var index = idTableRawGet(t, key.id)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-proc idTableGet(t: TIdTable, key: int): RootRef =
-  var index = idTableRawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-iterator pairs*(t: TIdTable): tuple[key: int, value: RootRef] =
-  for i in 0..high(t.data):
-    if t.data[i].key != nil:
-      yield (t.data[i].key.id, t.data[i].val)
-
-proc idTableRawInsert(data: var TIdPairSeq, key: PIdObj, val: RootRef) =
-  var h: Hash
-  h = key.id and high(data)
-  while data[h].key != nil:
-    assert(data[h].key.id != key.id)
-    h = nextTry(h, high(data))
-  assert(data[h].key == nil)
-  data[h].key = key
-  data[h].val = val
-
-proc idTablePut(t: var TIdTable, key: PIdObj, val: RootRef) =
-  var
-    index: int
-    n: TIdPairSeq
-  index = idTableRawGet(t, key.id)
-  if index >= 0:
-    assert(t.data[index].key != nil)
-    t.data[index].val = val
-  else:
-    if mustRehash(t.data.len, t.counter):
-      newSeq(n, t.data.len * GrowthFactor)
-      for i in 0..high(t.data):
-        if t.data[i].key != nil:
-          idTableRawInsert(n, t.data[i].key, t.data[i].val)
-      assert(hasEmptySlot(n))
-      swap(t.data, n)
-    idTableRawInsert(t.data, key, val)
-    inc(t.counter)
-
-iterator idTablePairs*(t: TIdTable): tuple[key: PIdObj, val: RootRef] =
-  for i in 0..high(t.data):
-    if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
-
-proc idNodeTableRawGet(t: TIdNodeTable, key: PIdObj): int =
-  var h: Hash
-  h = key.id and high(t.data) # start with real hash value
-  while t.data[h].key != nil:
-    if t.data[h].key.id == key.id:
-      return h
-    h = nextTry(h, high(t.data))
-  result = - 1
-
-proc idNodeTableGet(t: TIdNodeTable, key: PIdObj): PNode =
-  var index: int
-  index = idNodeTableRawGet(t, key)
-  if index >= 0: result = t.data[index].val
-  else: result = nil
-
-proc idNodeTableRawInsert(data: var TIdNodePairSeq, key: PIdObj, val: PNode) =
-  var h: Hash
-  h = key.id and high(data)
-  while data[h].key != nil:
-    assert(data[h].key.id != key.id)
-    h = nextTry(h, high(data))
-  assert(data[h].key == nil)
-  data[h].key = key
-  data[h].val = val
-
-proc idNodeTablePut(t: var TIdNodeTable, key: PIdObj, val: PNode) =
-  var index = idNodeTableRawGet(t, key)
-  if index >= 0:
-    assert(t.data[index].key != nil)
-    t.data[index].val = val
-  else:
-    if mustRehash(t.data.len, t.counter):
-      var n: TIdNodePairSeq
-      newSeq(n, t.data.len * GrowthFactor)
-      for i in 0..high(t.data):
-        if t.data[i].key != nil:
-          idNodeTableRawInsert(n, t.data[i].key, t.data[i].val)
-      swap(t.data, n)
-    idNodeTableRawInsert(t.data, key, val)
-    inc(t.counter)
-
-iterator pairs*(t: TIdNodeTable): tuple[key: PIdObj, val: PNode] =
-  for i in 0..high(t.data):
-    if not isNil(t.data[i].key): yield (t.data[i].key, t.data[i].val)
-
 proc initIITable(x: var TIITable) =
   x.counter = 0
   newSeq(x.data, StartSize)
@@ -1057,15 +758,8 @@ proc iiTablePut(t: var TIITable, key, val: int) =
     iiTableRawInsert(t.data, key, val)
     inc(t.counter)
 
-proc isAddrNode*(n: PNode): bool =
-  case n.kind
-    of nkAddr, nkHiddenAddr: true
-    of nkCallKinds:
-      if n[0].kind == nkSym and n[0].sym.magic == mAddr: true
-      else: false
-    else: false
-
 proc listSymbolNames*(symbols: openArray[PSym]): string =
+  result = ""
   for sym in symbols:
     if result.len > 0:
       result.add ", "
diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim
index a9027126a..aeeff1fd0 100644
--- a/compiler/astmsgs.nim
+++ b/compiler/astmsgs.nim
@@ -5,7 +5,7 @@ import options, ast, msgs
 proc typSym*(t: PType): PSym =
   result = t.sym
   if result == nil and t.kind == tyGenericInst: # this might need to be refined
-    result = t[0].sym
+    result = t.genericHead.sym
 
 proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) =
   result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)]
@@ -24,6 +24,12 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
     result.add " declared in " & toFileLineCol(conf, typ.sym.info)
   result.add "]"
 
+proc addTypeNodeDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) =
+  result.add " [$1" % typ.kind.toHumanStr
+  if typ.sym != nil:
+    result.add " declared in " & toFileLineCol(conf, typ.sym.info)
+  result.add "]"
+
 proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) =
   if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ)
 
diff --git a/compiler/astyaml.nim b/compiler/astyaml.nim
new file mode 100644
index 000000000..b0fa2bfb2
--- /dev/null
+++ b/compiler/astyaml.nim
@@ -0,0 +1,154 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# AST YAML printing
+
+import "."/[ast, lineinfos, msgs, options, rodutils]
+import std/[intsets, strutils]
+
+proc addYamlString*(res: var string; s: string) =
+  res.add "\""
+  for c in s:
+    case c
+    of '\0' .. '\x1F', '\x7F' .. '\xFF':
+      res.add("\\u" & strutils.toHex(ord(c), 4))
+    of '\"', '\\':
+      res.add '\\' & c
+    else:
+      res.add c
+
+  res.add('\"')
+
+proc makeYamlString(s: string): string =
+  result = ""
+  result.addYamlString(s)
+
+proc flagsToStr[T](flags: set[T]): string =
+  if flags == {}:
+    result = "[]"
+  else:
+    result = ""
+    for x in items(flags):
+      if result != "":
+        result.add(", ")
+      result.addYamlString($x)
+    result = "[" & result & "]"
+
+proc lineInfoToStr*(conf: ConfigRef; info: TLineInfo): string =
+  result = "["
+  result.addYamlString(toFilename(conf, info))
+  result.addf ", $1, $2]", [toLinenumber(info), toColumn(info)]
+
+proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent, maxRecDepth: int)
+proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent, maxRecDepth: int)
+proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent, maxRecDepth: int)
+
+proc symToYamlAux(res: var string; conf: ConfigRef; n: PSym; marker: var IntSet; indent: int; maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  elif containsOrIncl(marker, n.id):
+    res.addYamlString(n.name.s)
+  else:
+    let istr = spaces(indent * 4)
+
+    res.addf("kind: $1", [makeYamlString($n.kind)])
+    res.addf("\n$1name: $2", [istr, makeYamlString(n.name.s)])
+    res.addf("\n$1typ: ", [istr])
+    res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth - 1)
+    if conf != nil:
+      # if we don't pass the config, we probably don't care about the line info
+      res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
+    if card(n.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
+    res.addf("\n$1magic: $2", [istr, makeYamlString($n.magic)])
+    res.addf("\n$1ast: ", [istr])
+    res.treeToYamlAux(conf, n.ast, marker, indent + 1, maxRecDepth - 1)
+    res.addf("\n$1options: $2", [istr, flagsToStr(n.options)])
+    res.addf("\n$1position: $2", [istr, $n.position])
+    res.addf("\n$1k: $2", [istr, makeYamlString($n.loc.k)])
+    res.addf("\n$1storage: $2", [istr, makeYamlString($n.loc.storage)])
+    if card(n.loc.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, makeYamlString($n.loc.flags)])
+    res.addf("\n$1snippet: $2", [istr, n.loc.snippet])
+    res.addf("\n$1lode: $2", [istr])
+    res.treeToYamlAux(conf, n.loc.lode, marker, indent + 1, maxRecDepth - 1)
+
+proc typeToYamlAux(res: var string; conf: ConfigRef; n: PType; marker: var IntSet; indent: int; maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  elif containsOrIncl(marker, n.id):
+    res.addf "\"$1 @$2\"" % [$n.kind, strutils.toHex(cast[uint](n), sizeof(n) * 2)]
+  else:
+    let istr = spaces(indent * 4)
+    res.addf("kind: $2", [istr, makeYamlString($n.kind)])
+    res.addf("\n$1sym: ")
+    res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth - 1)
+    res.addf("\n$1n: ")
+    res.treeToYamlAux(conf, n.n, marker, indent + 1, maxRecDepth - 1)
+    if card(n.flags) > 0:
+      res.addf("\n$1flags: $2", [istr, flagsToStr(n.flags)])
+    res.addf("\n$1callconv: $2", [istr, makeYamlString($n.callConv)])
+    res.addf("\n$1size: $2", [istr, $(n.size)])
+    res.addf("\n$1align: $2", [istr, $(n.align)])
+    if n.hasElementType:
+      res.addf("\n$1sons:")
+      for a in n.kids:
+        res.addf("\n  - ")
+        res.typeToYamlAux(conf, a, marker, indent + 1, maxRecDepth - 1)
+
+proc treeToYamlAux(res: var string; conf: ConfigRef; n: PNode; marker: var IntSet; indent: int;
+                   maxRecDepth: int) =
+  if n == nil:
+    res.add("null")
+  else:
+    var istr = spaces(indent * 4)
+    res.addf("kind: $1" % [makeYamlString($n.kind)])
+
+    if maxRecDepth != 0:
+      if conf != nil:
+        res.addf("\n$1info: $2", [istr, lineInfoToStr(conf, n.info)])
+      case n.kind
+      of nkCharLit .. nkInt64Lit:
+        res.addf("\n$1intVal: $2", [istr, $(n.intVal)])
+      of nkFloatLit, nkFloat32Lit, nkFloat64Lit:
+        res.addf("\n$1floatVal: $2", [istr, n.floatVal.toStrMaxPrecision])
+      of nkStrLit .. nkTripleStrLit:
+        res.addf("\n$1strVal: $2", [istr, makeYamlString(n.strVal)])
+      of nkSym:
+        res.addf("\n$1sym: ", [istr])
+        res.symToYamlAux(conf, n.sym, marker, indent + 1, maxRecDepth)
+      of nkIdent:
+        if n.ident != nil:
+          res.addf("\n$1ident: $2", [istr, makeYamlString(n.ident.s)])
+        else:
+          res.addf("\n$1ident: null", [istr])
+      else:
+        if n.len > 0:
+          res.addf("\n$1sons: ", [istr])
+          for i in 0 ..< n.len:
+            res.addf("\n$1  - ", [istr])
+            res.treeToYamlAux(conf, n[i], marker, indent + 1, maxRecDepth - 1)
+      if n.typ != nil:
+        res.addf("\n$1typ: ", [istr])
+        res.typeToYamlAux(conf, n.typ, marker, indent + 1, maxRecDepth)
+
+proc treeToYaml*(conf: ConfigRef; n: PNode; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.treeToYamlAux(conf, n, marker, indent, maxRecDepth)
+
+proc typeToYaml*(conf: ConfigRef; n: PType; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.typeToYamlAux(conf, n, marker, indent, maxRecDepth)
+
+proc symToYaml*(conf: ConfigRef; n: PSym; indent: int = 0; maxRecDepth: int = -1): string =
+  var marker = initIntSet()
+  result = newStringOfCap(1024)
+  result.symToYamlAux(conf, n, marker, indent, maxRecDepth)
diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim
index 67598f9ca..7d142b01d 100644
--- a/compiler/bitsets.nim
+++ b/compiler/bitsets.nim
@@ -87,5 +87,11 @@ const populationCount: array[uint8, uint8] = block:
     arr
 
 proc bitSetCard*(x: TBitSet): BiggestInt =
+  result = 0
   for it in x:
     result.inc int(populationCount[it])
+
+proc bitSetToWord*(s: TBitSet; size: int): BiggestUInt =
+  result = 0
+  for j in 0..<size:
+    if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
diff --git a/compiler/btrees.nim b/compiler/btrees.nim
index c79442249..3b737b1bc 100644
--- a/compiler/btrees.nim
+++ b/compiler/btrees.nim
@@ -38,6 +38,7 @@ template less(a, b): bool = cmp(a, b) < 0
 template eq(a, b): bool = cmp(a, b) == 0
 
 proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val =
+  result = default(Val)
   var x = b.root
   while x.isInternal:
     for j in 0..<x.entries:
@@ -68,7 +69,7 @@ proc copyHalf[Key, Val](h, result: Node[Key, Val]) =
       result.links[j] = h.links[Mhalf + j]
   else:
     for j in 0..<Mhalf:
-      when defined(gcArc) or defined(gcOrc):
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
         result.vals[j] = move h.vals[Mhalf + j]
       else:
         shallowCopy(result.vals[j], h.vals[Mhalf + j])
@@ -91,7 +92,7 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] =
       if less(key, h.keys[j]): break
       inc j
     for i in countdown(h.entries, j+1):
-      when defined(gcArc) or defined(gcOrc):
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
         h.vals[i] = move h.vals[i-1]
       else:
         shallowCopy(h.vals[i], h.vals[i-1])
diff --git a/compiler/cbuilder.nim b/compiler/cbuilder.nim
new file mode 100644
index 000000000..bb1bdfe27
--- /dev/null
+++ b/compiler/cbuilder.nim
@@ -0,0 +1,174 @@
+type
+  Snippet = string
+  Builder = string
+
+template newBuilder(s: string): Builder =
+  s
+
+proc addField(obj: var Builder; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  obj.add('\t')
+  obj.add(typ)
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+proc addField(obj: var Builder; field: PSym; name, typ: Snippet; isFlexArray: bool = false; initializer: Snippet = "") =
+  ## for fields based on an `skField` symbol
+  obj.add('\t')
+  if field.alignment > 0:
+    obj.add("NIM_ALIGN(")
+    obj.addInt(field.alignment)
+    obj.add(") ")
+  obj.add(typ)
+  if sfNoalias in field.flags:
+    obj.add(" NIM_NOALIAS")
+  obj.add(" ")
+  obj.add(name)
+  if isFlexArray:
+    obj.add("[SEQ_DECL_SIZE]")
+  if field.bitsize != 0:
+    obj.add(":")
+    obj.addInt(field.bitsize)
+  if initializer.len != 0:
+    obj.add(initializer)
+  obj.add(";\n")
+
+type
+  BaseClassKind = enum
+    bcNone, bcCppInherit, bcSupField, bcNoneRtti, bcNoneTinyRtti
+  StructBuilderInfo = object
+    baseKind: BaseClassKind
+    preFieldsLen: int
+
+proc structOrUnion(t: PType): Snippet =
+  let t = t.skipTypes({tyAlias, tySink})
+  if tfUnion in t.flags: "union"
+  else: "struct"
+
+proc ptrType(t: Snippet): Snippet =
+  t & "*"
+
+proc startSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  obj.add("struct ")
+  obj.add(name)
+  if baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  if result.baseKind == bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishSimpleStruct(obj: var Builder; m: BModule; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+
+template addSimpleStruct(obj: var Builder; m: BModule; name: string; baseType: Snippet; body: typed) =
+  ## for independent structs, not directly based on a Nim type
+  let info = startSimpleStruct(obj, m, name, baseType)
+  body
+  finishSimpleStruct(obj, m, info)
+
+proc startStruct(obj: var Builder; m: BModule; t: PType; name: string; baseType: Snippet): StructBuilderInfo =
+  result = StructBuilderInfo(baseKind: bcNone)
+  if tfPacked in t.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add(structOrUnion(t))
+      obj.add(" __attribute__((__packed__))")
+    else:
+      obj.add("#pragma pack(push, 1)\n")
+      obj.add(structOrUnion(t))
+  else:
+    obj.add(structOrUnion(t))
+  obj.add(" ")
+  obj.add(name)
+  if t.kind == tyObject:
+    if t.baseClass == nil:
+      if lacksMTypeField(t):
+        result.baseKind = bcNone
+      elif optTinyRtti in m.config.globalOptions:
+        result.baseKind = bcNoneTinyRtti
+      else:
+        result.baseKind = bcNoneRtti
+    elif m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  elif baseType.len != 0:
+    if m.compileToCpp:
+      result.baseKind = bcCppInherit
+    else:
+      result.baseKind = bcSupField
+  if result.baseKind == bcCppInherit:
+    obj.add(" : public ")
+    obj.add(baseType)
+  obj.add(" ")
+  obj.add("{\n")
+  result.preFieldsLen = obj.len
+  case result.baseKind
+  of bcNone:
+    # rest of the options add a field or don't need it due to inheritance,
+    # we need to add the dummy field for uncheckedarray ahead of time
+    # so that it remains trailing
+    if t.itemId notin m.g.graph.memberProcsPerType and
+        t.n != nil and t.n.len == 1 and t.n[0].kind == nkSym and
+        t.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+      # only consists of flexible array field, add *initial* dummy field
+      obj.addField(name = "dummy", typ = "char")
+  of bcCppInherit: discard
+  of bcNoneRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimType")))
+  of bcNoneTinyRtti:
+    obj.addField(name = "m_type", typ = ptrType(cgsymValue(m, "TNimTypeV2")))
+  of bcSupField:
+    obj.addField(name = "Sup", typ = baseType)
+
+proc finishStruct(obj: var Builder; m: BModule; t: PType; info: StructBuilderInfo) =
+  if info.baseKind == bcNone and info.preFieldsLen == obj.len and
+      t.itemId notin m.g.graph.memberProcsPerType:
+    # no fields were added, add dummy field
+    obj.addField(name = "dummy", typ = "char")
+  obj.add("};\n")
+  if tfPacked in t.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    obj.add("#pragma pack(pop)\n")
+
+template addStruct(obj: var Builder; m: BModule; typ: PType; name: string; baseType: Snippet; body: typed) =
+  ## for structs built directly from a Nim type
+  let info = startStruct(obj, m, typ, name, baseType)
+  body
+  finishStruct(obj, m, typ, info)
+
+template addFieldWithStructType(obj: var Builder; m: BModule; parentTyp: PType; fieldName: string, body: typed) =
+  ## adds a field with a `struct { ... }` type, building it according to `body`
+  obj.add('\t')
+  if tfPacked in parentTyp.flags:
+    if hasAttribute in CC[m.config.cCompiler].props:
+      obj.add("struct __attribute__((__packed__)) {\n")
+    else:
+      obj.add("#pragma pack(push, 1)\nstruct {")
+  else:
+    obj.add("struct {\n")
+  body
+  obj.add("} ")
+  obj.add(fieldName)
+  obj.add(";\n")
+  if tfPacked in parentTyp.flags and hasAttribute notin CC[m.config.cCompiler].props:
+    result.add("#pragma pack(pop)\n")
+
+template addAnonUnion(obj: var Builder; body: typed) =
+  obj.add "union{\n"
+  body
+  obj.add("};\n")
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index ed5a5b079..ac607e3ad 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -14,7 +14,8 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
   if n.kind == nkSym and {sfNeverRaises, sfImportc, sfCompilerProc} * n.sym.flags != {}:
     result = false
   elif optPanics in p.config.globalOptions or
-      (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags):
+      (n.kind == nkSym and sfSystemModule in getModule(n.sym).flags and
+       sfSystemRaisesDefect notin n.sym.flags):
     # we know we can be strict:
     result = canRaise(n)
   else:
@@ -23,6 +24,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
 
 proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
   proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool =
+    result = false
     var n = le
     while true:
       # do NOT follow nkHiddenDeref here!
@@ -45,6 +47,7 @@ proc preventNrvo(p: BProc; dest, le, ri: PNode): bool =
         # cannot analyse the location; assume the worst
         return true
 
+  result = false
   if le != nil:
     for i in 1..<ri.len:
       let r = ri[i]
@@ -73,6 +76,23 @@ proc isHarmlessStore(p: BProc; canRaise: bool; d: TLoc): bool =
   else:
     result = false
 
+proc cleanupTemp(p: BProc; returnType: PType, tmp: TLoc): bool =
+  if returnType.kind in {tyVar, tyLent}:
+    # we don't need to worry about var/lent return types
+    result = false
+  elif hasDestructor(returnType) and getAttachedOp(p.module.g.graph, returnType, attachedDestructor) != nil:
+    let dtor = getAttachedOp(p.module.g.graph, returnType, attachedDestructor)
+    var op = initLocExpr(p, newSymNode(dtor))
+    var callee = rdLoc(op)
+    let destroy = if dtor.typ.firstParamType.kind == tyVar:
+        callee & "(&" & rdLoc(tmp) & ")"
+      else:
+        callee & "(" & rdLoc(tmp) & ")"
+    raiseExitCleanup(p, destroy)
+    result = true
+  else:
+    result = false
+
 proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
                callee, params: Rope) =
   let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
@@ -80,13 +100,17 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
   var pl = callee & "(" & params
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
-  if typ[0] != nil:
+  if typ.returnType != nil:
+    var flags: TAssignmentFlags = {}
+    if typ.returnType.kind in {tyOpenArray, tyVarargs}:
+      # perhaps generate no temp if the call doesn't have side effects
+      flags.incl needTempForOpenArray
     if isInvalidReturnType(p.config, typ):
       if params.len != 0: pl.add(", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
         # Great, we can use 'd':
-        if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
+        if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
@@ -94,8 +118,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
         pl.add(");\n")
         line(p, cpsStmts, pl)
       else:
-        var tmp: TLoc
-        getTemp(p, typ[0], tmp, needsInit=true)
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
         pl.add(addrLoc(p.config, tmp))
         pl.add(");\n")
         line(p, cpsStmts, pl)
@@ -109,61 +132,74 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
           # with them to prevent undefined behaviour and because the codegen
           # is free to emit expressions multiple times!
           d.k = locCall
-          d.r = pl
+          d.snippet = pl
           excl d.flags, lfSingleUse
         else:
           if d.k == locNone and p.splitDecls == 0:
-            getTempCpp(p, typ[0], d, pl)
+            d = getTempCpp(p, typ.returnType, pl)
           else:
-            if d.k == locNone: getTemp(p, typ[0], d)
-            var list: TLoc
-            initLoc(list, locCall, d.lode, OnUnknown)
-            list.r = pl
-            genAssignment(p, d, list, {}) # no need for deep copying
+            if d.k == locNone: d = getTemp(p, typ.returnType)
+            var list = initLoc(locCall, d.lode, OnUnknown)
+            list.snippet = pl
+            genAssignment(p, d, list, {needAssignCall}) # no need for deep copying
             if canRaise: raiseExit(p)
 
       elif isHarmlessStore(p, canRaise, d):
-        if d.k == locNone: getTemp(p, typ[0], d)
+        var useTemp = false
+        if d.k == locNone:
+          useTemp = true
+          d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
-        var list: TLoc
-        initLoc(list, locCall, d.lode, OnUnknown)
-        list.r = pl
-        genAssignment(p, d, list, {}) # no need for deep copying
-        if canRaise: raiseExit(p)
+        var list = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
+        genAssignment(p, d, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not (useTemp and cleanupTemp(p, typ.returnType, d)):
+            raiseExit(p)
       else:
-        var tmp: TLoc
-        getTemp(p, typ[0], tmp, needsInit=true)
-        var list: TLoc
-        initLoc(list, locCall, d.lode, OnUnknown)
-        list.r = pl
-        genAssignment(p, tmp, list, {}) # no need for deep copying
-        if canRaise: raiseExit(p)
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
+        var list = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
+        genAssignment(p, tmp, list, flags+{needAssignCall}) # no need for deep copying
+        if canRaise:
+          if not cleanupTemp(p, typ.returnType, tmp):
+            raiseExit(p)
         genAssignment(p, d, tmp, {})
   else:
     pl.add(");\n")
     line(p, cpsStmts, pl)
     if canRaise: raiseExit(p)
 
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc)
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType)
 
 proc reifiedOpenArray(n: PNode): bool {.inline.} =
   var x = n
-  while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}:
-    x = x[0]
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr, nkHiddenDeref}:
+      x = x[0]
+    of nkHiddenStdConv:
+      x = x[1]
+    else:
+      break
   if x.kind == nkSym and x.sym.kind == skParam:
     result = false
   else:
     result = true
 
-proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) =
-  var a, b, c: TLoc
-  initLocExpr(p, q[1], a)
-  initLocExpr(p, q[2], b)
-  initLocExpr(p, q[3], c)
+proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) =
+  var a = initLocExpr(p, q[1])
+  var b = initLocExpr(p, q[2])
+  var c = initLocExpr(p, q[3])
+  # bug #23321: In the function mapType, ptrs (tyPtr, tyVar, tyLent, tyRef)
+  # are mapped into ctPtrToArray, the dereference of which is skipped
+  # in the `genDeref`. We need to skip these ptrs here
+  let ty = skipTypes(a.t, abstractVar+{tyPtr, tyRef})
   # but first produce the required index checks:
   if optBoundsCheck in p.options:
-    genBoundsCheck(p, a, b, c)
-  let ty = skipTypes(a.t, abstractVar+{tyPtr})
+    genBoundsCheck(p, a, b, c, ty)
+  if prepareForMutation:
+    linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
   let dest = getTypeDesc(p.module, destType)
   let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)]
   case ty.kind
@@ -194,12 +230,15 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope,
         optSeqDestructors in p.config.globalOptions:
       linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
     if atyp.kind in {tyVar} and not compileToCpp(p.module):
-      result = ("($4*)(*$1)$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
+      result = ("(($5) ? (($4*)(*$1)$3+($2)) : NIM_NIL)" %
+                  [rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, "*" & rdLoc(a))],
                 lengthExpr)
     else:
-      result = ("($4*)$1$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest],
+      result = ("(($5) ? (($4*)$1$3+($2)) : NIM_NIL)" %
+                  [rdLoc(a), rdLoc(b), dataField(p), dest, dataFieldAccessor(p, rdLoc(a))],
                 lengthExpr)
   else:
+    result = ("", "")
     internalError(p.config, "openArrayLoc: " & typeToString(a.t))
 
 proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
@@ -216,11 +255,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
         for i in 0..<q.len-1:
           genStmts(p, q[i])
         q = q.lastSon
-    let (x, y) = genOpenArraySlice(p, q, formalType, n.typ[0])
+    let (x, y) = genOpenArraySlice(p, q, formalType, n.typ.elementType)
     result.add x & ", " & y
   else:
-    var a: TLoc
-    initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a)
+    var a = initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n)
     case skipTypes(a.t, abstractVar+{tyStatic}).kind
     of tyOpenArray, tyVarargs:
       if reifiedOpenArray(n):
@@ -236,21 +274,24 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode; result: var Rope) =
           optSeqDestructors in p.config.globalOptions:
         linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
       if ntyp.kind in {tyVar} and not compileToCpp(p.module):
-        var t: TLoc
-        t.r = "(*$1)" % [a.rdLoc]
-        result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
+        result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, t), dataField(p),
+                      dataFieldAccessor(p, "*" & a.rdLoc)]
       else:
-        result.add "$1$3, $2" % [a.rdLoc, lenExpr(p, a), dataField(p)]
+        result.add "($4) ? ($1$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)]
     of tyArray:
       result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))]
     of tyPtr, tyRef:
-      case lastSon(a.t).kind
+      case elementType(a.t).kind
       of tyString, tySequence:
-        var t: TLoc
-        t.r = "(*$1)" % [a.rdLoc]
-        result.add "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)]
+        var t = TLoc(snippet: "(*$1)" % [a.rdLoc])
+        result.add "($4) ? ((*$1)$3) : NIM_NIL, $2" %
+                     [a.rdLoc, lenExpr(p, t), dataField(p),
+                      dataFieldAccessor(p, "*" & a.rdLoc)]
       of tyArray:
-        result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, lastSon(a.t)))]
+        result.add "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, elementType(a.t)))]
       else:
         internalError(p.config, "openArrayLoc: " & typeToString(a.t))
     else: internalError(p.config, "openArrayLoc: " & typeToString(a.t))
@@ -259,20 +300,19 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc =
   # Bug https://github.com/status-im/nimbus-eth2/issues/1549
   # Aliasing is preferred over stack overflows.
   # Also don't regress for non ARC-builds, too risky.
-  if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and
+  if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
       getSize(p.config, a.lode.typ) < 1024:
-    getTemp(p, a.lode.typ, result, needsInit=false)
+    result = getTemp(p, a.lode.typ, needsInit=false)
     genAssignment(p, result, a, {})
   else:
     result = a
 
 proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc =
-  getTemp(p, a.lode.typ, result, needsInit=false)
+  result = getTemp(p, a.lode.typ, needsInit=false)
   genAssignment(p, result, a, {})
 
 proc genArgStringToCString(p: BProc, n: PNode; result: var Rope; needsTmp: bool) {.inline.} =
-  var a: TLoc
-  initLocExpr(p, n[0], a)
+  var a = initLocExpr(p, n[0])
   appcg(p.module, result, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc])
 
 proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; needsTmp = false) =
@@ -282,26 +322,42 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode; result: var Rope; need
   elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}:
     var n = if n.kind != nkHiddenAddr: n else: n[0]
     openArrayLoc(p, param.typ, n, result)
-  elif ccgIntroducedPtr(p.config, param, call[0].typ[0]):
-    initLocExpr(p, n, a)
+  elif ccgIntroducedPtr(p.config, param, call[0].typ.returnType) and
+    (optByRef notin param.options or not p.module.compileToCpp):
+    a = initLocExpr(p, n)
     if n.kind in {nkCharLit..nkNilLit}:
       addAddrLoc(p.config, literalsNeedsTmp(p, a), result)
     else:
       addAddrLoc(p.config, withTmpIfNeeded(p, a, needsTmp), result)
   elif p.module.compileToCpp and param.typ.kind in {tyVar} and
       n.kind == nkHiddenAddr:
-    initLocExprSingleUse(p, n[0], a)
+    # bug #23748: we need to introduce a temporary here. The expression type
+    # will be a reference in C++ and we cannot create a temporary reference
+    # variable. Thus, we create a temporary pointer variable instead.
+    let needsIndirect = mapType(p.config, n[0].typ, mapTypeChooser(n[0]) == skParam) != ctArray
+    if needsIndirect:
+      n.typ = n.typ.exactReplica
+      n.typ.flags.incl tfVarIsPtr
+    a = initLocExprSingleUse(p, n)
+    a = withTmpIfNeeded(p, a, needsTmp)
+    if needsIndirect: a.flags.incl lfIndirect
     # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still
     # means '*T'. See posix.nim for lots of examples that do that in the wild.
     let callee = call[0]
     if callee.kind == nkSym and
         {sfImportc, sfInfixCall, sfCompilerProc} * callee.sym.flags == {sfImportc} and
-        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {}:
+        {lfHeader, lfNoDecl} * callee.sym.loc.flags != {} and
+        needsIndirect:
       addAddrLoc(p.config, a, result)
     else:
       addRdLoc(a, result)
   else:
-    initLocExprSingleUse(p, n, a)
+    a = initLocExprSingleUse(p, n)
+    if param.typ.kind in {tyVar, tyPtr, tyRef, tySink}:
+      let typ = skipTypes(param.typ, abstractPtrs)
+      if not sameBackendTypePickyAliases(typ, n.typ.skipTypes(abstractPtrs)):
+        a.snippet = "(($1) ($2))" %
+          [getTypeDesc(p.module, param.typ), rdCharLoc(a)]
     addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
   #assert result != nil
 
@@ -310,12 +366,13 @@ proc genArgNoParam(p: BProc, n: PNode; result: var Rope; needsTmp = false) =
   if n.kind == nkStringToCString:
     genArgStringToCString(p, n, result, needsTmp)
   else:
-    initLocExprSingleUse(p, n, a)
+    a = initLocExprSingleUse(p, n)
     addRdLoc(withTmpIfNeeded(p, a, needsTmp), result)
 
-from dfa import aliases, AliasKind
+import aliasanalysis
 
 proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool =
+  result = false
   for p in potentialWrites:
     if p.aliases(n) != no or n.aliases(p) != no:
       return true
@@ -335,7 +392,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
   of nkLiterals, nkIdent, nkFormalParams: discard
   of nkSym:
     if mutate: result.add n
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     getPotentialWrites(n[0], true, result)
     getPotentialWrites(n[1], mutate, result)
   of nkAddr, nkHiddenAddr:
@@ -345,7 +402,7 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) =
   of nkCallKinds:
     case n.getMagic:
     of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem,
-        mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset:
+        mAddr, mNew, mNewFinalize, mWasMoved, mDestroy:
       getPotentialWrites(n[1], true, result)
       for i in 2..<n.len:
         getPotentialWrites(n[i], mutate, result)
@@ -371,25 +428,27 @@ proc genParams(p: BProc, ri: PNode, typ: PType; result: var Rope) =
   # We must generate temporaries in cases like #14396
   # to keep the strict Left-To-Right evaluation
   var needTmp = newSeq[bool](ri.len - 1)
-  var potentialWrites: seq[PNode]
+  var potentialWrites: seq[PNode] = @[]
   for i in countdown(ri.len - 1, 1):
     if ri[i].skipTrivialIndirections.kind == nkSym:
       needTmp[i - 1] = potentialAlias(ri[i], potentialWrites)
     else:
       #if not ri[i].typ.isCompileTimeOnly:
-      var potentialReads: seq[PNode]
+      var potentialReads: seq[PNode] = @[]
       getPotentialReads(ri[i], potentialReads)
       for n in potentialReads:
         if not needTmp[i - 1]:
           needTmp[i - 1] = potentialAlias(n, potentialWrites)
       getPotentialWrites(ri[i], false, potentialWrites)
-    if ri[i].kind in {nkHiddenAddr, nkAddr}:
-      # Optimization: don't use a temp, if we would only take the address anyway
-      needTmp[i - 1] = false
+    when false:
+      # this optimization is wrong, see bug #23748
+      if ri[i].kind in {nkHiddenAddr, nkAddr}:
+        # Optimization: don't use a temp, if we would only take the address anyway
+        needTmp[i - 1] = false
 
   var oldLen = result.len
   for i in 1..<ri.len:
-    if i < typ.len:
+    if i < typ.n.len:
       assert(typ.n[i].kind == nkSym)
       let paramType = typ.n[i]
       if not paramType.typ.isCompileTimeOnly:
@@ -409,13 +468,11 @@ proc addActualSuffixForHCR(res: var Rope, module: PSym, sym: PSym) =
     res = res & "_actual".rope
 
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  var op: TLoc
   # this is a hotspot in the compiler
-  initLocExpr(p, ri[0], op)
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   var params = newRopeAppender()
   genParams(p, ri, typ, params)
@@ -433,13 +490,11 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   const PatProc = "$1.ClE_0? $1.ClP_0($3$1.ClE_0):(($4)($1.ClP_0))($2)"
   const PatIter = "$1.ClP_0($3$1.ClE_0)" # we know the env exists
 
-  var op: TLoc
-  initLocExpr(p, ri[0], op)
+  var op = initLocExpr(p, ri[0])
 
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInstOwned)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   var pl = newRopeAppender()
   genParams(p, ri, typ, pl)
@@ -452,47 +507,44 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
   let rawProc = getClosureType(p.module, typ, clHalf)
   let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0])
-  if typ[0] != nil:
+  if typ.returnType != nil:
     if isInvalidReturnType(p.config, typ):
       if ri.len > 1: pl.add(", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
       if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri):
         # Great, we can use 'd':
         if d.k == locNone:
-          getTemp(p, typ[0], d, needsInit=true)
+          d = getTemp(p, typ.returnType, needsInit=true)
         elif d.k notin {locTemp} and not hasNoInit(ri):
           # reset before pass as 'result' var:
           discard "resetLoc(p, d)"
         pl.add(addrLoc(p.config, d))
         genCallPattern()
+        if canRaise: raiseExit(p)
       else:
-        var tmp: TLoc
-        getTemp(p, typ[0], tmp, needsInit=true)
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
         pl.add(addrLoc(p.config, tmp))
         genCallPattern()
         if canRaise: raiseExit(p)
         genAssignment(p, d, tmp, {}) # no need for deep copying
     elif isHarmlessStore(p, canRaise, d):
-      if d.k == locNone: getTemp(p, typ[0], d)
+      if d.k == locNone: d = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, d.lode, OnUnknown)
+      var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
       if tfIterator in typ.flags:
-        list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
       else:
-        list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
       genAssignment(p, d, list, {}) # no need for deep copying
       if canRaise: raiseExit(p)
     else:
-      var tmp: TLoc
-      getTemp(p, typ[0], tmp)
+      var tmp: TLoc = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, d.lode, OnUnknown)
+      var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
       if tfIterator in typ.flags:
-        list.r = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatIter % [rdLoc(op), pl, pl.addComma, rawProc]
       else:
-        list.r = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
+        list.snippet = PatProc % [rdLoc(op), pl, pl.addComma, rawProc]
       genAssignment(p, tmp, list, {})
       if canRaise: raiseExit(p)
       genAssignment(p, d, tmp, {})
@@ -502,14 +554,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
 
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope;
                  argsCounter: var int) =
-  if i < typ.len:
+  if i < typ.n.len:
     # 'var T' is 'T&' in C++. This means we ignore the request of
     # any nkHiddenAddr when it's a 'var T'.
     let paramType = typ.n[i]
     assert(paramType.kind == nkSym)
     if paramType.typ.isCompileTimeOnly:
       discard
-    elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr:
+    elif paramType.typ.kind in {tyVar} and ri[i].kind == nkHiddenAddr:
       if argsCounter > 0: result.add ", "
       genArgNoParam(p, ri[i][0], result)
       inc argsCounter
@@ -584,7 +636,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType; result: var 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 p.config, i < typ.len
+  internalAssert p.config, i < typ.n.len
   assert(typ.n[i].kind == nkSym)
   # if the parameter is lying (tyVar) and thus we required an additional deref,
   # skip the deref:
@@ -656,7 +708,7 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
       inc j
       inc i
     of '\'':
-      var idx, stars: int
+      var idx, stars: int = 0
       if scanCppGenericSlot(pat, i, idx, stars):
         var t = resolveStarsInCppType(typ, idx, stars)
         if t == nil: result.add("void")
@@ -670,34 +722,31 @@ proc genPatternCall(p: BProc; ri: PNode; pat: string; typ: PType; result: var Ro
         result.add(substr(pat, start, i - 1))
 
 proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
-  var op: TLoc
-  initLocExpr(p, ri[0], op)
+  var op = initLocExpr(p, ri[0])
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
   # don't call '$' here for efficiency:
-  let pat = $ri[0].sym.loc.r
+  let pat = $ri[0].sym.loc.snippet
   internalAssert p.config, pat.len > 0
   if pat.contains({'#', '(', '@', '\''}):
     var pl = newRopeAppender()
     genPatternCall(p, ri, pat, typ, pl)
     # simpler version of 'fixupCall' that works with the pl+params combination:
     var typ = skipTypes(ri[0].typ, abstractInst)
-    if typ[0] != nil:
+    if typ.returnType != nil:
       if p.module.compileToCpp and lfSingleUse in d.flags:
         # do not generate spurious temporaries for C++! For C we're better off
         # with them to prevent undefined behaviour and because the codegen
         # is free to emit expressions multiple times!
         d.k = locCall
-        d.r = pl
+        d.snippet = pl
         excl d.flags, lfSingleUse
       else:
-        if d.k == locNone: getTemp(p, typ[0], d)
+        if d.k == locNone: d = getTemp(p, typ.returnType)
         assert(d.t != nil)        # generate an assignment to d:
-        var list: TLoc
-        initLoc(list, locCall, d.lode, OnUnknown)
-        list.r = pl
+        var list: TLoc = initLoc(locCall, d.lode, OnUnknown)
+        list.snippet = pl
         genAssignment(p, d, list, {}) # no need for deep copying
     else:
       pl.add(";\n")
@@ -707,30 +756,27 @@ proc genInfixCall(p: BProc, le, ri: PNode, d: var TLoc) =
     var argsCounter = 0
     if 1 < ri.len:
       genThisArg(p, ri, 1, typ, pl)
-    pl.add(op.r)
+    pl.add(op.snippet)
     var params = newRopeAppender()
     for i in 2..<ri.len:
-      assert(typ.len == typ.n.len)
       genOtherArg(p, ri, i, typ, params, argsCounter)
     fixupCall(p, le, ri, d, pl, params)
 
 proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
   # generates a crappy ObjC call
-  var op: TLoc
-  initLocExpr(p, ri[0], op)
+  var op = initLocExpr(p, ri[0])
   var pl = "["
   # getUniqueType() is too expensive here:
   var typ = skipTypes(ri[0].typ, abstractInst)
   assert(typ.kind == tyProc)
-  assert(typ.len == typ.n.len)
 
   # don't call '$' here for efficiency:
-  let pat = $ri[0].sym.loc.r
+  let pat = $ri[0].sym.loc.snippet
   internalAssert p.config, pat.len > 0
   var start = 3
   if ' ' in pat:
     start = 1
-    pl.add(op.r)
+    pl.add(op.snippet)
     if ri.len > 1:
       pl.add(": ")
       genArg(p, ri[1], typ.n[1].sym, ri, pl)
@@ -739,13 +785,12 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     if ri.len > 1:
       genArg(p, ri[1], typ.n[1].sym, ri, pl)
       pl.add(" ")
-    pl.add(op.r)
+    pl.add(op.snippet)
     if ri.len > 2:
       pl.add(": ")
       genArg(p, ri[2], typ.n[2].sym, ri, pl)
   for i in start..<ri.len:
-    assert(typ.len == typ.n.len)
-    if i >= typ.len:
+    if i >= typ.n.len:
       internalError(p.config, ri.info, "varargs for objective C method?")
     assert(typ.n[i].kind == nkSym)
     var param = typ.n[i].sym
@@ -753,31 +798,29 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
     pl.add(param.name.s)
     pl.add(": ")
     genArg(p, ri[i], param, ri, pl)
-  if typ[0] != nil:
+  if typ.returnType != nil:
     if isInvalidReturnType(p.config, typ):
       if ri.len > 1: pl.add(" ")
       # beware of 'result = p(result)'. We always allocate a temporary:
       if d.k in {locTemp, locNone}:
         # We already got a temp. Great, special case it:
-        if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
+        if d.k == locNone: d = getTemp(p, typ.returnType, needsInit=true)
         pl.add("Result: ")
         pl.add(addrLoc(p.config, d))
         pl.add("];\n")
         line(p, cpsStmts, pl)
       else:
-        var tmp: TLoc
-        getTemp(p, typ[0], tmp, needsInit=true)
+        var tmp: TLoc = getTemp(p, typ.returnType, needsInit=true)
         pl.add(addrLoc(p.config, tmp))
         pl.add("];\n")
         line(p, cpsStmts, pl)
         genAssignment(p, d, tmp, {}) # no need for deep copying
     else:
       pl.add("]")
-      if d.k == locNone: getTemp(p, typ[0], d)
+      if d.k == locNone: d = getTemp(p, typ.returnType)
       assert(d.t != nil)        # generate an assignment to d:
-      var list: TLoc
-      initLoc(list, locCall, ri, OnUnknown)
-      list.r = pl
+      var list: TLoc = initLoc(locCall, ri, OnUnknown)
+      list.snippet = pl
       genAssignment(p, d, list, {}) # no need for deep copying
   else:
     pl.add("];\n")
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 9b085b8ed..545d43ae8 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -112,11 +112,6 @@ proc genLiteral(p: BProc, n: PNode, ty: PType; result: var Rope) =
 proc genLiteral(p: BProc, n: PNode; result: var Rope) =
   genLiteral(p, n, n.typ, result)
 
-proc bitSetToWord(s: TBitSet, size: int): BiggestUInt =
-  result = 0
-  for j in 0..<size:
-    if j < s.len: result = result or (BiggestUInt(s[j]) shl (j * 8))
-
 proc genRawSetData(cs: TBitSet, size: int; result: var Rope) =
   if size > 8:
     var res = "{\n"
@@ -173,7 +168,9 @@ proc getStorageLoc(n: PNode): TStorageLoc =
     of tyVar, tyLent: result = OnUnknown
     of tyPtr: result = OnStack
     of tyRef: result = OnHeap
-    else: doAssert(false, "getStorageLoc")
+    else:
+      result = OnUnknown
+      doAssert(false, "getStorageLoc")
   of nkBracketExpr, nkDotExpr, nkObjDownConv, nkObjUpConv:
     result = getStorageLoc(n[0])
   else: result = OnUnknown
@@ -215,16 +212,20 @@ proc asgnComplexity(n: PNode): int =
       # 'case objects' are too difficult to inline their assignment operation:
       result = 100
     of nkRecList:
+      result = 0
       for t in items(n):
         result += asgnComplexity(t)
-    else: discard
+    else: result = 0
+  else:
+    result = 0
 
 proc optAsgnLoc(a: TLoc, t: PType, field: Rope): TLoc =
   assert field != ""
-  result.k = locField
-  result.storage = a.storage
-  result.lode = lodeTyp t
-  result.r = rdLoc(a) & "." & field
+  result = TLoc(k: locField,
+    storage: a.storage,
+    lode: lodeTyp t,
+    snippet: rdLoc(a) & "." & field
+  )
 
 proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   let newflags =
@@ -235,8 +236,7 @@ proc genOptAsgnTuple(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       flags
   let t = skipTypes(dest.t, abstractInst).getUniqueType()
-  for i in 0..<t.len:
-    let t = t[i]
+  for i, t in t.ikids:
     let field = "Field$1" % [i.rope]
     genAssignment(p, optAsgnLoc(dest, t, field),
                      optAsgnLoc(src, t, field), newflags)
@@ -254,9 +254,9 @@ proc genOptAsgnObject(p: BProc, dest, src: TLoc, flags: TAssignmentFlags,
   case t.kind
   of nkSym:
     let field = t.sym
-    if field.loc.r == "": fillObjectFields(p.module, typ)
-    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.r),
-                     optAsgnLoc(src, field.typ, field.loc.r), newflags)
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
+    genAssignment(p, optAsgnLoc(dest, field.typ, field.loc.snippet),
+                     optAsgnLoc(src, field.typ, field.loc.snippet), newflags)
   of nkRecList:
     for child in items(t): genOptAsgnObject(p, dest, src, newflags, child, typ)
   else: discard
@@ -285,21 +285,28 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n",
             [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)])
 
-proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
+proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc; flags: TAssignmentFlags) =
   assert d.k != locNone
   #  getTemp(p, d.t, d)
 
   case a.t.skipTypes(abstractVar).kind
   of tyOpenArray, tyVarargs:
     if reifiedOpenArray(a.lode):
-      linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
-        [rdLoc(d), a.rdLoc])
+      if needTempForOpenArray in flags:
+        var tmp: TLoc = getTemp(p, a.t)
+        linefmt(p, cpsStmts, "$2 = $1; $n",
+                [a.rdLoc, tmp.rdLoc])
+        linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
+          [rdLoc(d), tmp.rdLoc])
+      else:
+        linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n",
+          [rdLoc(d), a.rdLoc])
     else:
       linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n",
         [rdLoc(d), a.rdLoc])
   of tySequence:
-    linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n",
-      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)])
+    linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)])
   of tyArray:
     linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n",
       [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))])
@@ -308,8 +315,8 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) =
     if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions:
       linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
 
-    linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n",
-      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)])
+    linefmt(p, cpsStmts, "$1.Field0 = ($5) ? ($2$3) : NIM_NIL; $1.Field1 = $4;$n",
+      [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a), dataFieldAccessor(p, a.rdLoc)])
   else:
     internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind)
 
@@ -336,15 +343,14 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
   of tyString:
     if optSeqDestructors in p.config.globalOptions:
       genGenericAsgn(p, dest, src, flags)
-    elif (needToCopy notin flags and src.storage != OnStatic) or canMove(p, src.lode, dest):
+    elif ({needToCopy, needToCopySinkParam} * flags == {} and src.storage != OnStatic) or canMove(p, src.lode, dest):
       genRefAssign(p, dest, src)
     else:
       if (dest.storage == OnStack and p.config.selectedGC != gcGo) or not usesWriteBarrier(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:
-        var tmp: TLoc
-        getTemp(p, ty, tmp)
+        var tmp: TLoc = getTemp(p, ty)
         linefmt(p, cpsStmts, "$3 = $1; $1 = #copyStringRC1($2);$n",
                 [dest.rdLoc, src.rdLoc, tmp.rdLoc])
         linefmt(p, cpsStmts, "if ($1) #nimGCunrefNoCycle($1);$n", [tmp.rdLoc])
@@ -362,7 +368,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
       linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyTuple:
     if containsGarbageCollectedRef(dest.t):
-      if dest.t.len <= 4: genOptAsgnTuple(p, dest, src, flags)
+      if dest.t.kidsLen <= 4: genOptAsgnTuple(p, dest, src, flags)
       else: genGenericAsgn(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
@@ -373,7 +379,8 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     elif not isObjLackingTypeField(ty):
       genGenericAsgn(p, dest, src, flags)
     elif containsGarbageCollectedRef(ty):
-      if ty[0].isNil and asgnComplexity(ty.n) <= 4:
+      if ty[0].isNil and asgnComplexity(ty.n) <= 4 and
+            needAssignCall notin flags: # calls might contain side effects
         discard getTypeDesc(p.module, ty)
         internalAssert p.config, ty.n != nil
         genOptAsgnObject(p, dest, src, flags, ty.n, ty)
@@ -382,7 +389,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)])
   of tyArray:
-    if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcOrc, gcHooks}:
+    if containsGarbageCollectedRef(dest.t) and p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
       genGenericAsgn(p, dest, src, flags)
     else:
       linefmt(p, cpsStmts,
@@ -392,7 +399,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     # open arrays are always on the stack - really? What if a sequence is
     # passed to an open array?
     if reifiedOpenArray(dest.lode):
-      genOpenArrayConv(p, dest, src)
+      genOpenArrayConv(p, dest, src, flags)
     elif containsGarbageCollectedRef(dest.t):
       linefmt(p, cpsStmts,     # XXX: is this correct for arrays?
            "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
@@ -401,7 +408,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     else:
       linefmt(p, cpsStmts,
            # bug #4799, keep the nimCopyMem for a while
-           #"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);$n",
+           #"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($1[0])*$1Len_0);\n",
            "$1 = $2;$n",
            [rdLoc(dest), rdLoc(src)])
   of tySet:
@@ -426,8 +433,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
 proc genDeepCopy(p: BProc; dest, src: TLoc) =
   template addrLocOrTemp(a: TLoc): Rope =
     if a.k == locExpr:
-      var tmp: TLoc
-      getTemp(p, a.t, tmp)
+      var tmp: TLoc = getTemp(p, a.t)
       genAssignment(p, tmp, a, {})
       addrLoc(p.config, tmp)
     else:
@@ -450,9 +456,10 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) =
               [addrLoc(p.config, dest), rdLoc(src),
               genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tyOpenArray, tyVarargs:
+    let source = addrLocOrTemp(src)
     linefmt(p, cpsStmts,
-         "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n",
-         [addrLoc(p.config, dest), addrLocOrTemp(src),
+         "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $2->Field1, $3);$n",
+         [addrLoc(p.config, dest), source,
          genTypeInfoV1(p.module, dest.t, dest.lode.info)])
   of tySet:
     if mapSetType(p.config, ty) == ctArray:
@@ -473,11 +480,10 @@ proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc) =
     d = s # ``d`` is free, so fill it with ``s``
 
 proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
-  var a: TLoc
   if d.k != locNone:
+    var a: TLoc = initLoc(locData, n, OnStatic)
     # need to generate an assignment here
-    initLoc(a, locData, n, OnStatic)
-    a.r = r
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -485,14 +491,13 @@ proc putDataIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope) =
     # the flags field!
     d.k = locData
     d.lode = n
-    d.r = r
+    d.snippet = r
 
 proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
-  var a: TLoc
   if d.k != locNone:
     # need to generate an assignment here
-    initLoc(a, locExpr, n, s)
-    a.r = r
+    var a: TLoc = initLoc(locExpr, n, s)
+    a.snippet = r
     if lfNoDeepCopy in d.flags: genAssignment(p, d, a, {})
     else: genAssignment(p, d, a, {needToCopy})
   else:
@@ -500,52 +505,45 @@ proc putIntoDest(p: BProc, d: var TLoc, n: PNode, r: Rope; s=OnUnknown) =
     # the flags field!
     d.k = locExpr
     d.lode = n
-    d.r = r
+    d.snippet = r
 
 proc binaryStmt(p: BProc, e: PNode, d: var TLoc, op: string) =
-  var a, b: TLoc
   if d.k != locNone: internalError(p.config, e.info, "binaryStmt")
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   lineCg(p, cpsStmts, "$1 $2 $3;$n", [rdLoc(a), op, rdLoc(b)])
 
 proc binaryStmtAddr(p: BProc, e: PNode, d: var TLoc, cpname: string) =
-  var a, b: TLoc
   if d.k != locNone: internalError(p.config, e.info, "binaryStmtAddr")
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   lineCg(p, cpsStmts, "#$1($2, $3);$n", [cpname, byRefLoc(p, a), rdLoc(b)])
 
 template unaryStmt(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
   if d.k != locNone: internalError(p.config, e.info, "unaryStmt")
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   lineCg(p, cpsStmts, frmt, [rdLoc(a)])
 
 template binaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
   assert(e[1].typ != nil)
   assert(e[2].typ != nil)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a), rdLoc(b)]))
 
 template binaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
   assert(e[1].typ != nil)
   assert(e[2].typ != nil)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [a.rdCharLoc, b.rdCharLoc]))
 
 template unaryExpr(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdLoc(a)]))
 
 template unaryExprChar(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   putIntoDest(p, d, e, ropecg(p.module, frmt, [rdCharLoc(a)]))
 
 template binaryArithOverflowRaw(p: BProc, t: PType, a, b: TLoc;
@@ -585,15 +583,14 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
       "nimAddInt64", "nimSubInt64"
     ]
     opr: array[mAddI..mPred, string] = ["+", "-", "*", "/", "%", "+", "-"]
-  var a, b: TLoc
   assert(e[1].typ != nil)
   assert(e[2].typ != nil)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   # skipping 'range' is correct here as we'll generate a proper range check
   # later via 'chckRange'
   let t = e.typ.skipTypes(abstractRange)
-  if optOverflowCheck notin p.options:
+  if optOverflowCheck notin p.options or (m in {mSucc, mPred} and t.kind in {tyUInt..tyUInt64}):
     let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
     putIntoDest(p, d, e, res)
   else:
@@ -615,15 +612,13 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
         if t.kind == tyInt64: prc64[m] else: prc[m])
       putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res])
     else:
-      let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
+      let res = "($1)(($2) $3 ($4))" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)]
       putIntoDest(p, d, e, res)
 
 proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
-  var
-    a: TLoc
-    t: PType
+  var t: PType
   assert(e[1].typ != nil)
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   t = skipTypes(e.typ, abstractRange)
   if optOverflowCheck in p.options:
     var first = newRopeAppender()
@@ -645,12 +640,11 @@ proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
 
 proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   var
-    a, b: TLoc
-    s, k: BiggestInt
+    s, k: BiggestInt = 0
   assert(e[1].typ != nil)
   assert(e[2].typ != nil)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   # BUGFIX: cannot use result-type here, as it may be a boolean
   s = max(getSize(p.config, a.t), getSize(p.config, b.t)) * 8
   k = getSize(p.config, a.t) * 8
@@ -704,11 +698,10 @@ proc binaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     assert(false, $op)
 
 proc genEqProc(p: BProc, e: PNode, d: var TLoc) =
-  var a, b: TLoc
   assert(e[1].typ != nil)
   assert(e[2].typ != nil)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   if a.t.skipTypes(abstractInstOwned).callConv == ccClosure:
     putIntoDest(p, d, e,
       "($1.ClP_0 == $2.ClP_0 && $1.ClE_0 == $2.ClE_0)" % [rdLoc(a), rdLoc(b)])
@@ -724,10 +717,9 @@ proc genIsNil(p: BProc, e: PNode, d: var TLoc) =
 
 proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   var
-    a: TLoc
     t: PType
   assert(e[1].typ != nil)
-  initLocExpr(p, e[1], a)
+  var a = initLocExpr(p, e[1])
   t = skipTypes(e.typ, abstractRange)
 
   template applyFormat(frmt: untyped) =
@@ -752,23 +744,8 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} =
       skipTypes(typ, abstractInstOwned).kind in {tyVar} and
       tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags
 
-proc derefBlock(p: BProc, e: PNode, d: var TLoc) =
-  # We transform (block: x)[] to (block: x[])
-  let e0 = e[0]
-  var n = shallowCopy(e0)
-  n.typ = e.typ
-  for i in 0 ..< e0.len - 1:
-    n[i] = e0[i]
-  n[e0.len-1] = newTreeIT(nkHiddenDeref, e.info, e.typ, e0[e0.len-1])
-  expr p, n, d
-
 proc genDeref(p: BProc, e: PNode, d: var TLoc) =
-  if e.kind == nkHiddenDeref and e[0].kind in {nkBlockExpr, nkBlockStmt}:
-    # bug #20107. Watch out to not deref the pointer too late.
-    derefBlock(p, e, d)
-    return
-
-  let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]))
+  let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam)
   if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags:
     # XXX the amount of hacks for C's arrays is incredible, maybe we should
     # simply wrap them in a struct? --> Losing auto vectorization then?
@@ -779,13 +756,13 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) =
     var a: TLoc
     var typ = e[0].typ
     if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass:
-      typ = typ.lastSon
+      typ = typ.last
     typ = typ.skipTypes(abstractInstOwned)
     if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr:
-      initLocExprSingleUse(p, e[0][0], d)
+      d = initLocExprSingleUse(p, e[0][0])
       return
     else:
-      initLocExprSingleUse(p, e[0], a)
+      a = initLocExprSingleUse(p, e[0])
     if d.k == locNone:
       # dest = *a;  <-- We do not know that 'dest' is on the heap!
       # It is completely wrong to set 'd.storage' here, unless it's not yet
@@ -822,8 +799,7 @@ proc cowBracket(p: BProc; n: PNode) =
   if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions:
     let strCandidate = n[0]
     if strCandidate.typ.skipTypes(abstractInst).kind == tyString:
-      var a: TLoc
-      initLocExpr(p, strCandidate, a)
+      var a: TLoc = initLocExpr(p, strCandidate)
       linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
 
 proc cow(p: BProc; n: PNode) {.inline.} =
@@ -832,31 +808,30 @@ proc cow(p: BProc; n: PNode) {.inline.} =
 proc genAddr(p: BProc, e: PNode, d: var TLoc) =
   # careful  'addr(myptrToArray)' needs to get the ampersand:
   if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}:
-    var a: TLoc
-    initLocExpr(p, e[0], a)
-    putIntoDest(p, d, e, "&" & a.r, a.storage)
+    var a: TLoc = initLocExpr(p, e[0])
+    putIntoDest(p, d, e, "&" & a.snippet, a.storage)
     #Message(e.info, warnUser, "HERE NEW &")
-  elif mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray or isCppRef(p, e.typ):
+  elif mapType(p.config, e[0].typ, mapTypeChooser(e[0]) == skParam) == ctArray or isCppRef(p, e.typ):
     expr(p, e[0], d)
+    # bug #19497
+    d.lode = e
   else:
-    var a: TLoc
-    initLocExpr(p, e[0], a)
+    var a: TLoc = initLocExpr(p, e[0])
     putIntoDest(p, d, e, addrLoc(p.config, a), a.storage)
 
 template inheritLocation(d: var TLoc, a: TLoc) =
   if d.k == locNone: d.storage = a.storage
 
-proc genRecordFieldAux(p: BProc, e: PNode, d, a: var TLoc) =
-  initLocExpr(p, e[0], a)
+proc genRecordFieldAux(p: BProc, e: PNode, d: var TLoc, a: var TLoc) =
+  a = initLocExpr(p, e[0])
   if e[1].kind != nkSym: internalError(p.config, e.info, "genRecordFieldAux")
   d.inheritLocation(a)
   discard getTypeDesc(p.module, a.t) # fill the record's fields.loc
 
 proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
   var
-    a: TLoc
-    i: int
-  initLocExpr(p, e[0], a)
+    i: int = 0
+  var a: TLoc = initLocExpr(p, e[0])
   let tupType = a.t.skipTypes(abstractInst+{tyVar})
   assert tupType.kind == tyTuple
   d.inheritLocation(a)
@@ -870,6 +845,7 @@ proc genTupleElem(p: BProc, e: PNode, d: var TLoc) =
 
 proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
                       resTyp: ptr PType = nil): PSym =
+  result = nil
   var ty = ty
   assert r != ""
   while ty != nil:
@@ -884,7 +860,13 @@ proc lookupFieldAgain(p: BProc, ty: PType; field: PSym; r: var Rope;
   if result == nil: internalError(p.config, field.info, "genCheckedRecordField")
 
 proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
-  var a: TLoc
+  var a: TLoc = default(TLoc)
+  if p.module.compileToCpp and e.kind == nkDotExpr and e[1].kind == nkSym and e[1].typ.kind == tyPtr:
+    # special case for C++: we need to pull the type of the field as member and friends require the complete type.
+    let typ = e[1].typ.elementType
+    if typ.itemId in p.module.g.graph.memberProcsPerType:
+      discard getTypeDesc(p.module, typ)
+
   genRecordFieldAux(p, e, d, a)
   var r = rdLoc(a)
   var f = e[1].sym
@@ -896,12 +878,12 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
     r.add rope(f.position)
     putIntoDest(p, d, e, r, a.storage)
   else:
-    var rtyp: PType
+    var rtyp: PType = nil
     let field = lookupFieldAgain(p, ty, f, r, addr rtyp)
-    if field.loc.r == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
-    if field.loc.r == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
+    if field.loc.snippet == "" and rtyp != nil: fillObjectFields(p.module, rtyp)
+    if field.loc.snippet == "": internalError(p.config, e.info, "genRecordField 3 " & typeToString(ty))
     r.add "."
-    r.add field.loc.r
+    r.add field.loc.snippet
     putIntoDest(p, d, e, r, a.storage)
   r.freeze
 
@@ -917,13 +899,13 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
     if op.magic == mNot: it = it[1]
     let disc = it[2].skipConv
     assert(disc.kind == nkSym)
-    initLoc(test, locNone, it, OnStack)
-    initLocExpr(p, it[1], u)
-    initLoc(v, locExpr, disc, OnUnknown)
-    v.r = newRopeAppender()
-    v.r.add obj
-    v.r.add(".")
-    v.r.add(disc.sym.loc.r)
+    test = initLoc(locNone, it, OnStack)
+    u = initLocExpr(p, it[1])
+    v = initLoc(locExpr, disc, OnUnknown)
+    v.snippet = newRopeAppender()
+    v.snippet.add obj
+    v.snippet.add(".")
+    v.snippet.add(disc.sym.loc.snippet)
     genInExprAux(p, it, u, v, test)
     var msg = ""
     if optDeclaredLocs in p.config.globalOptions:
@@ -945,10 +927,17 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
     var discIndex = newRopeAppender()
     rdSetElemLoc(p.config, v, u.t, discIndex)
     if optTinyRtti in p.config.globalOptions:
-      # not sure how to use `genEnumToStr` here
-      if p.config.getStdlibVersion < (1, 5, 1):
-        const code = "{ #raiseFieldError($1); "
-        linefmt(p, cpsStmts, code, [strLit])
+      let base = disc.typ.skipTypes(abstractInst+{tyRange})
+      case base.kind
+      of tyEnum:
+        const code = "{ #raiseFieldErrorStr($1, $2); "
+        let toStrProc = getToStringProc(p.module.g.graph, base)
+        # XXX need to modify this logic for IC.
+        # need to analyze nkFieldCheckedExpr and marks procs "used" like range checks in dce
+        var toStr: TLoc = default(TLoc)
+        expr(p, newSymNode(toStrProc), toStr)
+        let enumStr = "$1($2)" % [rdLoc(toStr), rdLoc(v)]
+        linefmt(p, cpsStmts, code, [strLit, enumStr])
       else:
         const code = "{ #raiseFieldError2($1, (NI)$2); "
         linefmt(p, cpsStmts, code, [strLit, discIndex])
@@ -959,12 +948,8 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
       var firstLit = newRopeAppender()
       int64Literal(cast[int](first), firstLit)
       let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info)
-      if p.config.getStdlibVersion < (1,5,1):
-        const code = "{ #raiseFieldError($1); "
-        linefmt(p, cpsStmts, code, [strLit])
-      else:
-        const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); "
-        linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName])
+      const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$2) + (NI)$3, $4)); "
+      linefmt(p, cpsStmts, code, [strLit, discIndex, firstLit, discName])
 
     raiseInstr(p, p.s(cpsStmts))
     linefmt p, cpsStmts, "}$n", []
@@ -972,35 +957,33 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) =
 proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
   assert e[0].kind == nkDotExpr
   if optFieldCheck in p.options:
-    var a: TLoc
+    var a: TLoc = default(TLoc)
     genRecordFieldAux(p, e[0], d, a)
     let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
     var r = rdLoc(a)
     let f = e[0][1].sym
     let field = lookupFieldAgain(p, ty, f, r)
-    if field.loc.r == "": fillObjectFields(p.module, ty)
-    if field.loc.r == "":
+    if field.loc.snippet == "": fillObjectFields(p.module, ty)
+    if field.loc.snippet == "":
       internalError(p.config, e.info, "genCheckedRecordField") # generate the checks:
     genFieldCheck(p, e, r, field)
     r.add(".")
-    r.add field.loc.r
+    r.add field.loc.snippet
     putIntoDest(p, d, e[0], r, a.storage)
     r.freeze
   else:
     genRecordField(p, e[0], d)
 
 proc genUncheckedArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   d.inheritLocation(a)
   putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]),
               a.storage)
 
 proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   var ty = skipTypes(a.t, abstractVarRange + abstractPtrs + tyUserTypeClasses)
   var first = newRopeAppender()
   intLiteral(firstOrd(p.config, ty), first)
@@ -1033,15 +1016,14 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
               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
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   inheritLocation(d, a)
   putIntoDest(p, d, n,
               ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
-proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
-  let ty = skipTypes(arr.t, abstractVarRange)
+proc genBoundsCheck(p: BProc; arr, a, b: TLoc; arrTyp: PType) =
+  let ty = arrTyp
   case ty.kind
   of tyOpenArray, tyVarargs:
     if reifiedOpenArray(arr.lode):
@@ -1081,9 +1063,8 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) =
   else: discard
 
 proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   if not reifiedOpenArray(x):
     # emit range check:
     if optBoundsCheck in p.options:
@@ -1107,12 +1088,12 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) =
                 ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage)
 
 proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
-  var a, b: TLoc
-  initLocExpr(p, x, a)
-  initLocExpr(p, y, b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, y)
   var ty = skipTypes(a.t, abstractVarRange)
   if ty.kind in {tyRef, tyPtr}:
-    ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check:
+    ty = skipTypes(ty.elementType, abstractVarRange)
+  # emit range check:
   if optBoundsCheck in p.options:
     linefmt(p, cpsStmts,
             "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); ",
@@ -1122,7 +1103,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
 
   if d.k == locNone: d.storage = OnHeap
   if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}:
-    a.r = ropecg(p.module, "(*$1)", [a.r])
+    a.snippet = ropecg(p.module, "(*$1)", [a.snippet])
 
   if lfPrepareForMutation in d.flags and ty.kind == tyString and
       optSeqDestructors in p.config.globalOptions:
@@ -1132,7 +1113,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) =
 
 proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) =
   var ty = skipTypes(n[0].typ, abstractVarRange + tyUserTypeClasses)
-  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange)
+  if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.elementType, abstractVarRange)
   case ty.kind
   of tyUncheckedArray: genUncheckedArrayElem(p, n, n[0], n[1], d)
   of tyArray: genArrayElem(p, n, n[0], n[1], d)
@@ -1157,8 +1138,7 @@ proc isSimpleExpr(n: PNode): bool =
       if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
     result = isSimpleExpr(n.lastSon)
   else:
-    if n.isAtom:
-      result = true
+    result = n.isAtom
 
 proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # how to generate code?
@@ -1183,16 +1163,15 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   # a = tmp
   when false:
     #if isSimpleExpr(e) and p.module.compileToCpp:
-    var tmpA, tmpB: TLoc
     #getTemp(p, e.typ, tmpA)
     #getTemp(p, e.typ, tmpB)
-    initLocExprSingleUse(p, e[1], tmpA)
-    initLocExprSingleUse(p, e[2], tmpB)
+    var tmpA = initLocExprSingleUse(p, e[1])
+    var tmpB = initLocExprSingleUse(p, e[2])
     tmpB.k = locExpr
     if m == mOr:
-      tmpB.r = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")||(" & rdLoc(tmpB) & "))"
     else:
-      tmpB.r = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
+      tmpB.snippet = "((" & rdLoc(tmpA) & ")&&(" & rdLoc(tmpB) & "))"
     if d.k == locNone:
       d = tmpB
     else:
@@ -1200,8 +1179,7 @@ proc genAndOr(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   else:
     var
       L: TLabel
-      tmp: TLoc
-    getTemp(p, e.typ, tmp)      # force it into a temp!
+    var tmp: TLoc = getTemp(p, e.typ)      # force it into a temp!
     inc p.splitDecls
     expr(p, e[1], tmp)
     L = getLabel(p)
@@ -1229,7 +1207,7 @@ proc genEcho(p: BProc, n: PNode) =
       if it.skipConv.kind == nkNilLit:
         args.add(", \"\"")
       elif n.len != 0:
-        initLocExpr(p, it, a)
+        a = initLocExpr(p, it)
         if i > 0:
           args.add(", ")
         case detectStrVersion(p.module)
@@ -1244,8 +1222,7 @@ proc genEcho(p: BProc, n: PNode) =
     if n.len == 0:
       linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len])
     else:
-      var a: TLoc
-      initLocExpr(p, n, a)
+      var a: TLoc = initLocExpr(p, n)
       linefmt(p, cpsStmts, "#echoBinSafe($1, $2);$n", [a.rdLoc, n.len])
     when false:
       p.module.includeHeader("<stdio.h>")
@@ -1279,14 +1256,14 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
   #    appendChar(tmp0, 'z');
   #    asgn(s, tmp0);
   #  }
-  var a, tmp: TLoc
-  getTemp(p, e.typ, tmp)
+  var a: TLoc
+  var tmp: TLoc = getTemp(p, e.typ)
   var L = 0
   var appends: Rope = ""
   var lens: Rope = ""
   for i in 0..<e.len - 1:
     # compute the length expression:
-    initLocExpr(p, e[i + 1], a)
+    a = initLocExpr(p, e[i + 1])
     if skipTypes(e[i + 1].typ, abstractVarRange).kind == tyChar:
       inc(L)
       appends.add(ropecg(p.module, "#appendChar($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
@@ -1297,7 +1274,7 @@ proc genStrConcat(p: BProc, e: PNode, d: var TLoc) =
         lens.add(lenExpr(p, a))
         lens.add(" + ")
       appends.add(ropecg(p.module, "#appendString($1, $2);$n", [strLoc(p, tmp), rdLoc(a)]))
-  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.r, lens, L])
+  linefmt(p, cpsStmts, "$1 = #rawNewString($2$3);$n", [tmp.snippet, lens, L])
   p.s(cpsStmts).add appends
   if d.k == locNone:
     d = tmp
@@ -1318,14 +1295,14 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
   #    appendChar(s, 'z');
   #  }
   var
-    a, dest, call: TLoc
-    appends, lens: Rope
+    a, call: TLoc
+    appends, lens: Rope = ""
   assert(d.k == locNone)
   var L = 0
-  initLocExpr(p, e[1], dest)
+  var dest = initLocExpr(p, e[1])
   for i in 0..<e.len - 2:
     # compute the length expression:
-    initLocExpr(p, e[i + 2], a)
+    a = initLocExpr(p, e[i + 2])
     if skipTypes(e[i + 2].typ, abstractVarRange).kind == tyChar:
       inc(L)
       appends.add(ropecg(p.module, "#appendChar($1, $2);$n",
@@ -1342,8 +1319,8 @@ proc genStrAppend(p: BProc, e: PNode, d: var TLoc) =
     linefmt(p, cpsStmts, "#prepareAdd($1, $2$3);$n",
             [byRefLoc(p, dest), lens, L])
   else:
-    initLoc(call, locCall, e, OnHeap)
-    call.r = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
+    call = initLoc(locCall, e, OnHeap)
+    call.snippet = ropecg(p.module, "#resizeString($1, $2$3)", [rdLoc(dest), lens, L])
     genAssignment(p, dest, call, {})
     gcUsage(p.config, e)
   p.s(cpsStmts).add appends
@@ -1352,19 +1329,18 @@ 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;
-  var a, b, dest, tmpL, call: TLoc
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   let seqType = skipTypes(e[1].typ, {tyVar})
-  initLoc(call, locCall, e, OnHeap)
+  var call = initLoc(locCall, e, OnHeap)
   if not p.module.compileToCpp:
     const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)"
-    call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
       getTypeDesc(p.module, e[1].typ),
       genTypeInfoV1(p.module, seqType, e.info)])
   else:
     const seqAppendPattern = "($2) #incrSeqV3($1, $3)"
-    call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a),
+    call.snippet = ropecg(p.module, seqAppendPattern, [rdLoc(a),
       getTypeDesc(p.module, e[1].typ),
       genTypeInfoV1(p.module, seqType, e.info)])
   # emit the write barrier if required, but we can always move here, so
@@ -1372,43 +1348,33 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
   genRefAssign(p, a, call)
   #if bt != b.t:
   #  echo "YES ", e.info, " new: ", typeToString(bt), " old: ", typeToString(b.t)
-  initLoc(dest, locExpr, e[2], OnHeap)
-  getIntTemp(p, tmpL)
-  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.r, rdLoc(a), lenField(p)])
-  dest.r = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.r, dataField(p)])
+  var dest = initLoc(locExpr, e[2], OnHeap)
+  var tmpL = getIntTemp(p)
+  lineCg(p, cpsStmts, "$1 = $2->$3++;$n", [tmpL.snippet, rdLoc(a), lenField(p)])
+  dest.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(a), tmpL.snippet, dataField(p)])
   genAssignment(p, dest, b, {needToCopy})
   gcUsage(p.config, e)
 
-proc genReset(p: BProc, n: PNode) =
-  var a: TLoc
-  initLocExpr(p, n[1], a)
-  specializeReset(p, a)
-  when false:
-    linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-            [addrLoc(p.config, a),
-            genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)])
-
 proc genDefault(p: BProc; n: PNode; d: var TLoc) =
-  if d.k == locNone: getTemp(p, n.typ, d, needsInit=true)
+  if d.k == locNone: d = getTemp(p, n.typ, needsInit=true)
   else: resetLoc(p, d)
 
 proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
   var sizeExpr = sizeExpr
   let typ = a.t
-  var b: TLoc
-  initLoc(b, locExpr, a.lode, OnHeap)
+  var b: TLoc = initLoc(locExpr, a.lode, OnHeap)
   let refType = typ.skipTypes(abstractInstOwned)
   assert refType.kind == tyRef
-  let bt = refType.lastSon
+  let bt = refType.elementType
   if sizeExpr == "":
     sizeExpr = "sizeof($1)" % [getTypeDesc(p.module, bt)]
 
   if optTinyRtti in p.config.globalOptions:
     if needsInit:
-      b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
+      b.snippet = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))",
           [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
     else:
-      b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
+      b.snippet = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))",
           [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)])
     genAssignment(p, a, b, {})
   else:
@@ -1422,38 +1388,35 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) =
         localError(p.module.config, a.lode.info,
           "the destructor that is turned into a finalizer needs " &
           "to have the 'nimcall' calling convention")
-      var f: TLoc
-      initLocExpr(p, newSymNode(op), f)
+      var f: TLoc = initLocExpr(p, newSymNode(op))
       p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
 
     if a.storage == OnHeap and usesWriteBarrier(p.config):
-      if canFormAcycle(a.t):
+      if canFormAcycle(p.module.g.graph, a.t):
         linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [a.rdLoc])
       else:
         linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [a.rdLoc])
       if p.config.selectedGC == gcGo:
         # newObjRC1() would clash with unsureAsgnRef() - which is used by gcGo to
         # implement the write barrier
-        b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n",
                 [addrLoc(p.config, a), b.rdLoc])
       else:
         # use newObjRC1 as an optimization
-        b.r = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+        b.snippet = ropecg(p.module, "($1) #newObjRC1($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
         linefmt(p, cpsStmts, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
-      b.r = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
+      b.snippet = ropecg(p.module, "($1) #newObj($2, $3)", [getTypeDesc(p.module, typ), ti, sizeExpr])
       genAssignment(p, a, b, {})
   # set the object type:
   genObjectInit(p, cpsStmts, bt, a, constructRefObj)
 
 proc genNew(p: BProc, e: PNode) =
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   # 'genNew' also handles 'unsafeNew':
   if e.len == 3:
-    var se: TLoc
-    initLocExpr(p, e[2], se)
+    var se: TLoc = initLocExpr(p, e[2])
     rawGenNew(p, a, se.rdLoc, needsInit = true)
   else:
     rawGenNew(p, a, "", needsInit = true)
@@ -1461,40 +1424,38 @@ proc genNew(p: BProc, e: PNode) =
 
 proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) =
   let seqtype = skipTypes(dest.t, abstractVarRange)
-  var call: TLoc
-  initLoc(call, locExpr, dest.lode, OnHeap)
+  var call: TLoc = initLoc(locExpr, dest.lode, OnHeap)
   if dest.storage == OnHeap and usesWriteBarrier(p.config):
-    if canFormAcycle(dest.t):
+    if canFormAcycle(p.module.g.graph, dest.t):
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefRC1($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
     else:
       linefmt(p, cpsStmts, "if ($1) { #nimGCunrefNoCycle($1); $1 = NIM_NIL; }$n", [dest.rdLoc])
     if not lenIsZero:
       if p.config.selectedGC == gcGo:
         # we need the write barrier
-        call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+        call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
         linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc])
       else:
-        call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
+        call.snippet = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
         linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc])
   else:
     if lenIsZero:
-      call.r = rope"NIM_NIL"
+      call.snippet = rope"NIM_NIL"
     else:
-      call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
+      call.snippet = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype),
               genTypeInfoV1(p.module, seqtype, dest.lode.info), length])
     genAssignment(p, dest, call, {})
 
 proc genNewSeq(p: BProc, e: PNode) =
-  var a, b: TLoc
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   if optSeqDestructors in p.config.globalOptions:
     let seqtype = skipTypes(e[1].typ, abstractVarRange)
     linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
       [a.rdLoc, b.rdLoc,
-       getTypeDesc(p.module, seqtype.lastSon),
+       getTypeDesc(p.module, seqtype.elementType),
        getSeqPayloadType(p.module, seqtype)])
   else:
     let lenIsZero = e[2].kind == nkIntLit and e[2].intVal == 0
@@ -1503,15 +1464,15 @@ proc genNewSeq(p: BProc, e: PNode) =
 
 proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) =
   let seqtype = skipTypes(e.typ, abstractVarRange)
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   if optSeqDestructors in p.config.globalOptions:
-    if d.k == locNone: getTemp(p, e.typ, d, needsInit=false)
-    linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
-      [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.lastSon),
+    if d.k == locNone: d = getTemp(p, e.typ, needsInit=false)
+    linefmt(p, cpsStmts, "$1.len = 0; $1.p = ($4*) #newSeqPayloadUninit($2, sizeof($3), NIM_ALIGNOF($3));$n",
+      [d.rdLoc, a.rdLoc, getTypeDesc(p.module, seqtype.elementType),
       getSeqPayloadType(p.module, seqtype),
     ])
   else:
+    if d.k == locNone: d = getTemp(p, e.typ, needsInit=false) # bug #22560
     putIntoDest(p, d, e, ropecg(p.module,
                 "($1)#nimNewSeqOfCap($2, $3)", [
                 getTypeDesc(p.module, seqtype),
@@ -1526,9 +1487,13 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) =
   if id == p.module.labels:
     # expression not found in the cache:
     inc(p.module.labels)
-    p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ", [getTypeDesc(p.module, t), d.r])
-    genBracedInit(p, n, isConst = true, t, p.module.s[cfsData])
-    p.module.s[cfsData].addf(";$n", [])
+    var data = "static NIM_CONST $1 $2 = " % [getTypeDesc(p.module, t), d.snippet]
+    # bug #23627; when generating const object fields, it's likely that
+    # we need to generate type infos for the object, which may be an object with
+    # custom hooks. We need to generate potential consts in the hooks first.
+    genBracedInit(p, n, isConst = true, t, data)
+    data.addf(";$n", [])
+    p.module.s[cfsData].add data
 
 proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
   if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr:
@@ -1537,8 +1502,31 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
   else:
     result = false
 
+
+proc genFieldObjConstr(p: BProc; ty: PType; useTemp, isRef: bool; nField, val, check: PNode; d: var TLoc; r: Rope; info: TLineInfo) =
+  var tmp2 = TLoc(snippet: r)
+  let field = lookupFieldAgain(p, ty, nField.sym, tmp2.snippet)
+  if field.loc.snippet == "": fillObjectFields(p.module, ty)
+  if field.loc.snippet == "": internalError(p.config, info, "genFieldObjConstr")
+  if check != nil and optFieldCheck in p.options:
+    genFieldCheck(p, check, r, field)
+  tmp2.snippet.add(".")
+  tmp2.snippet.add(field.loc.snippet)
+  if useTemp:
+    tmp2.k = locTemp
+    tmp2.storage = if isRef: OnHeap else: OnStack
+  else:
+    tmp2.k = d.k
+    tmp2.storage = if isRef: OnHeap else: d.storage
+  tmp2.lode = val
+  if nField.typ.skipTypes(abstractVar).kind in {tyOpenArray, tyVarargs}:
+    var tmp3 = getTemp(p, val.typ)
+    expr(p, val, tmp3)
+    genOpenArrayConv(p, tmp2, tmp3, {})
+  else:
+    expr(p, val, tmp2)
+
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
-  #echo renderTree e, " ", e.isDeepConstExpr
   # inheritance in C++ does not allow struct initialization so
   # we skip this step here:
   if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions:
@@ -1559,42 +1547,33 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
         (d.k notin {locTemp,locLocalVar,locGlobalVar,locParam,locField}) or
         (isPartOf(d.lode, e) != arNo)
 
-  var tmp: TLoc
+  var tmp: TLoc = default(TLoc)
   var r: Rope
+  let needsZeroMem = p.config.selectedGC notin {gcArc, gcAtomicArc, gcOrc} or nfAllFieldsSet notin e.flags
   if useTemp:
-    getTemp(p, t, tmp)
+    tmp = getTemp(p, t)
     r = rdLoc(tmp)
     if isRef:
       rawGenNew(p, tmp, "", needsInit = nfAllFieldsSet notin e.flags)
-      t = t.lastSon.skipTypes(abstractInstOwned)
+      t = t.elementType.skipTypes(abstractInstOwned)
       r = "(*$1)" % [r]
       gcUsage(p.config, e)
-    else:
+    elif needsZeroMem:
       constructLoc(p, tmp)
+    else:
+      genObjectInit(p, cpsStmts, t, tmp, constructObj)
   else:
-    resetLoc(p, d)
+    if needsZeroMem: resetLoc(p, d)
+    else: genObjectInit(p, cpsStmts, d.t, d, if isRef: constructRefObj else: constructObj)
     r = rdLoc(d)
   discard getTypeDesc(p.module, t)
   let ty = getUniqueType(t)
   for i in 1..<e.len:
-    let it = e[i]
-    var tmp2: TLoc
-    tmp2.r = r
-    let field = lookupFieldAgain(p, ty, it[0].sym, tmp2.r)
-    if field.loc.r == "": fillObjectFields(p.module, ty)
-    if field.loc.r == "": internalError(p.config, e.info, "genObjConstr")
-    if it.len == 3 and optFieldCheck in p.options:
-      genFieldCheck(p, it[2], r, field)
-    tmp2.r.add(".")
-    tmp2.r.add(field.loc.r)
-    if useTemp:
-      tmp2.k = locTemp
-      tmp2.storage = if isRef: OnHeap else: OnStack
-    else:
-      tmp2.k = d.k
-      tmp2.storage = if isRef: OnHeap else: d.storage
-    tmp2.lode = it[1]
-    expr(p, it[1], tmp2)
+    var check: PNode = nil
+    if e[i].len == 3 and optFieldCheck in p.options:
+      check = e[i][2]
+    genFieldObjConstr(p, ty, useTemp, isRef, e[i][0], e[i][1], check, d, r, e.info)
+
   if useTemp:
     if d.k == locNone:
       d = tmp
@@ -1602,34 +1581,36 @@ proc genObjConstr(p: BProc, e: PNode, d: var TLoc) =
       genAssignment(p, d, tmp, {})
 
 proc lhsDoesAlias(a, b: PNode): bool =
+  result = false
   for y in b:
     if isPartOf(a, y) != arNo: return true
 
 proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
-  var arr, tmp: TLoc
+  var arr: TLoc
+  var tmp: TLoc = default(TLoc)
   # bug #668
   let doesAlias = lhsDoesAlias(d.lode, n)
   let dest = if doesAlias: addr(tmp) else: addr(d)
   if doesAlias:
-    getTemp(p, n.typ, tmp)
+    tmp = getTemp(p, n.typ)
   elif d.k == locNone:
-    getTemp(p, n.typ, d)
+    d = getTemp(p, n.typ)
 
   var lit = newRopeAppender()
   intLiteral(n.len, lit)
   if optSeqDestructors in p.config.globalOptions:
     let seqtype = n.typ
     linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
-      [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.lastSon),
+      [rdLoc dest[], lit, getTypeDesc(p.module, seqtype.elementType),
       getSeqPayloadType(p.module, seqtype)])
   else:
     # generate call to newSeq before adding the elements per hand:
     genNewSeqAux(p, dest[], lit, n.len == 0)
   for i in 0..<n.len:
-    initLoc(arr, locExpr, n[i], OnHeap)
+    arr = initLoc(locExpr, n[i], OnHeap)
     var lit = newRopeAppender()
     intLiteral(i, lit)
-    arr.r = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
+    arr.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(dest[]), lit, dataField(p)])
     arr.storage = OnHeap            # we know that sequences are on the heap
     expr(p, n[i], arr)
   gcUsage(p.config, n)
@@ -1640,75 +1621,71 @@ proc genSeqConstr(p: BProc, n: PNode, d: var TLoc) =
       genAssignment(p, d, tmp, {})
 
 proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) =
-  var elem, a, arr: TLoc
+  var elem, arr: TLoc
   if n[1].kind == nkBracket:
     n[1].typ = n.typ
     genSeqConstr(p, n[1], d)
     return
   if d.k == locNone:
-    getTemp(p, n.typ, d)
+    d = getTemp(p, n.typ)
+  var a = initLocExpr(p, n[1])
   # generate call to newSeq before adding the elements per hand:
   let L = toInt(lengthOrd(p.config, n[1].typ))
   if optSeqDestructors in p.config.globalOptions:
     let seqtype = n.typ
     linefmt(p, cpsStmts, "$1.len = $2; $1.p = ($4*) #newSeqPayload($2, sizeof($3), NIM_ALIGNOF($3));$n",
-      [rdLoc d, L, getTypeDesc(p.module, seqtype.lastSon),
+      [rdLoc d, L, getTypeDesc(p.module, seqtype.elementType),
       getSeqPayloadType(p.module, seqtype)])
   else:
     var lit = newRopeAppender()
     intLiteral(L, lit)
     genNewSeqAux(p, d, lit, L == 0)
-  initLocExpr(p, n[1], a)
   # bug #5007; do not produce excessive C source code:
   if L < 10:
     for i in 0..<L:
-      initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
+      elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
       var lit = newRopeAppender()
       intLiteral(i, lit)
-      elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
+      elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), lit, dataField(p)])
       elem.storage = OnHeap # we know that sequences are on the heap
-      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
-      arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
+      arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
+      arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), lit])
       genAssignment(p, elem, arr, {needToCopy})
   else:
-    var i: TLoc
-    getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i)
-    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  [i.r, L])
-    initLoc(elem, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
-    elem.r = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
+    var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
+    linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",  [i.snippet, L])
+    elem = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), OnHeap)
+    elem.snippet = ropecg(p.module, "$1$3[$2]", [rdLoc(d), rdLoc(i), dataField(p)])
     elem.storage = OnHeap # we know that sequences are on the heap
-    initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
-    arr.r = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
+    arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n[1].typ, abstractInst)), a.storage)
+    arr.snippet = ropecg(p.module, "$1[$2]", [rdLoc(a), rdLoc(i)])
     genAssignment(p, elem, arr, {needToCopy})
     lineF(p, cpsStmts, "}$n", [])
 
 
 proc genNewFinalize(p: BProc, e: PNode) =
   var
-    a, b, f: TLoc
+    b: TLoc
     refType, bt: PType
     ti: Rope
   refType = skipTypes(e[1].typ, abstractVarRange)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], f)
-  initLoc(b, locExpr, a.lode, OnHeap)
+  var a = initLocExpr(p, e[1])
+  var f = initLocExpr(p, e[2])
+  b = initLoc(locExpr, a.lode, OnHeap)
   ti = genTypeInfo(p.config, p.module, refType, e.info)
   p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)])
-  b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
+  b.snippet = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [
       getTypeDesc(p.module, refType),
-      ti, getTypeDesc(p.module, skipTypes(refType.lastSon, abstractRange))])
+      ti, getTypeDesc(p.module, skipTypes(refType.elementType, abstractRange))])
   genAssignment(p, a, b, {})  # set the object type:
-  bt = skipTypes(refType.lastSon, abstractRange)
+  bt = skipTypes(refType.elementType, abstractRange)
   genObjectInit(p, cpsStmts, bt, a, constructRefObj)
   gcUsage(p.config, e)
 
 proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) =
   if optTinyRtti in p.config.globalOptions:
-    let ti = genTypeInfo2Name(p.module, dest)
-    inc p.module.labels
-    let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
-    p.module.s[cfsVars].addf("static TNimTypeV2* $#[2];$n", [cache])
-    appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
+    let token = $genDisplayElem(MD5Digest(hashType(dest, p.config)))
+    appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token])
   else:
     # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we
     # have to call it here first:
@@ -1722,14 +1699,9 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Ro
       let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope
       p.module.s[cfsVars].addf("static TNimType* $#[2];$n", [cache])
       appcg(p.module, result, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache])
-    when false:
-      # former version:
-      appcg(p.module, result, "#isObj($1.m_type, $2)",
-            [a, genTypeInfoV1(p.module, dest, info)])
 
 proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, x, a)
+  var a: TLoc = initLocExpr(p, x)
   var dest = skipTypes(typ, typedescPtrs)
   var r = rdLoc(a)
   var nilCheck: Rope = ""
@@ -1738,12 +1710,12 @@ proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) =
     if t.kind notin {tyVar, tyLent}: nilCheck = r
     if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
       r = ropecg(p.module, "(*$1)", [r])
-    t = skipTypes(t.lastSon, typedescInst+{tyOwned})
+    t = skipTypes(t.elementType, typedescInst+{tyOwned})
   discard getTypeDesc(p.module, t)
   if not p.module.compileToCpp:
-    while t.kind == tyObject and t[0] != nil:
+    while t.kind == tyObject and t.baseClass != nil:
       r.add(".Sup")
-      t = skipTypes(t[0], skipPtrs)
+      t = skipTypes(t.baseClass, skipPtrs)
   if isObjLackingTypeField(t):
     globalError(p.config, x.info,
       "no 'of' operator available for pure objects")
@@ -1770,8 +1742,7 @@ proc genOf(p: BProc, n: PNode, d: var TLoc) =
 proc genRepr(p: BProc, e: PNode, d: var TLoc) =
   if optTinyRtti in p.config.globalOptions:
     localError(p.config, e.info, "'repr' is not available for --newruntime")
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   var t = skipTypes(e[1].typ, abstractVarRange)
   case t.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
@@ -1793,13 +1764,15 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [
                 addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage)
   of tyOpenArray, tyVarargs:
-    var b: TLoc
+    var b: TLoc = default(TLoc)
     case skipTypes(a.t, abstractVarRange).kind
     of tyOpenArray, tyVarargs:
       putIntoDest(p, b, e, "$1, $1Len_0" % [rdLoc(a)], a.storage)
     of tyString, tySequence:
       putIntoDest(p, b, e,
-                  "$1$3, $2" % [rdLoc(a), lenExpr(p, a), dataField(p)], a.storage)
+                  "($4) ? ($1$3) : NIM_NIL, $2" %
+                    [rdLoc(a), lenExpr(p, a), dataField(p), dataFieldAccessor(p, a.rdLoc)],
+                  a.storage)
     of tyArray:
       putIntoDest(p, b, e,
                   "$1, $2" % [rdLoc(a), rope(lengthOrd(p.config, a.t))], a.storage)
@@ -1826,13 +1799,13 @@ proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; result: var Rope; enforceV1
     if t.kind notin {tyVar, tyLent}: nilCheck = derefs
     if t.kind notin {tyVar, tyLent} or not p.module.compileToCpp:
       derefs = "(*$1)" % [derefs]
-    t = skipTypes(t.lastSon, abstractInst)
+    t = skipTypes(t.elementType, abstractInst)
   result.add derefs
   discard getTypeDesc(p.module, t)
   if not p.module.compileToCpp:
-    while t.kind == tyObject and t[0] != nil:
+    while t.kind == tyObject and t.baseClass != nil:
       result.add(".Sup")
-      t = skipTypes(t[0], skipPtrs)
+      t = skipTypes(t.baseClass, skipPtrs)
   result.add ".m_type"
   if optTinyRtti in p.config.globalOptions and enforceV1:
     result.add "->typeInfoV1"
@@ -1849,8 +1822,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) =
     # ordinary static type information
     putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info))
   else:
-    var a: TLoc
-    initLocExpr(p, e[1], a)
+    var a: TLoc = initLocExpr(p, e[1])
     var nilCheck = ""
     # use the dynamic type stored at offset 0:
     var rt = newRopeAppender()
@@ -1858,8 +1830,7 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) =
     putIntoDest(p, d, e, rt)
 
 proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   var nilCheck = ""
   # use the dynamic type stored at offset 0:
   var rt = newRopeAppender()
@@ -1867,11 +1838,10 @@ proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) =
   putIntoDest(p, d, e, rt)
 
 template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) =
-  var a: TLoc
-  initLocExpr(p, n[1], a)
-  a.r = ropecg(p.module, frmt, [rdLoc(a)])
+  var a: TLoc = initLocExpr(p, n[1])
+  a.snippet = ropecg(p.module, frmt, [rdLoc(a)])
   a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR)
-  if d.k == locNone: getTemp(p, n.typ, d)
+  if d.k == locNone: d = getTemp(p, n.typ)
   genAssignment(p, d, a, {})
   gcUsage(p.config, n)
 
@@ -1884,78 +1854,75 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     # Bug #9279, len(toOpenArray()) has to work:
     if a.kind in nkCallKinds and a[0].kind == nkSym and a[0].sym.magic == mSlice:
       # magic: pass slice to openArray:
-      var b, c: TLoc
-      initLocExpr(p, a[2], b)
-      initLocExpr(p, a[3], c)
+      var m = initLocExpr(p, a[1])
+      var b = initLocExpr(p, a[2])
+      var c = initLocExpr(p, a[3])
+      if optBoundsCheck in p.options:
+        genBoundsCheck(p, m, b, c, skipTypes(m.t, abstractVarRange))
       if op == mHigh:
-        putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)", [rdLoc(b), rdLoc(c)]))
+        putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1))", [rdLoc(b), rdLoc(c)]))
       else:
-        putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)]))
+        putIntoDest(p, d, e, ropecg(p.module, "(($2)-($1)+1)", [rdLoc(b), rdLoc(c)]))
     else:
       if not reifiedOpenArray(a):
         if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)")
         else: unaryExpr(p, e, d, "$1Len_0")
       else:
-        if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)")
-        else: unaryExpr(p, e, d, "$1.Field1")
+        let isDeref = a.kind in {nkHiddenDeref, nkDerefExpr}
+        if op == mHigh:
+          if isDeref:
+            unaryExpr(p, e, d, "($1->Field1-1)")
+          else:
+            unaryExpr(p, e, d, "($1.Field1-1)")
+        else:
+          if isDeref:
+            unaryExpr(p, e, d, "$1->Field1")
+          else:
+            unaryExpr(p, e, d, "$1.Field1")
   of tyCstring:
-    if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)")
-    else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)")
+    if op == mHigh: unaryExpr(p, e, d, "(#nimCStrLen($1)-1)")
+    else: unaryExpr(p, e, d, "#nimCStrLen($1)")
   of tyString:
-    var a: TLoc
-    initLocExpr(p, e[1], a)
+    var a: TLoc = initLocExpr(p, e[1])
     var x = lenExpr(p, a)
     if op == mHigh: x = "($1-1)" % [x]
     putIntoDest(p, d, e, x)
   of tySequence:
     # we go through a temporary here because people write bullshit code.
-    var a, tmp: TLoc
-    initLocExpr(p, e[1], a)
-    getIntTemp(p, tmp)
+    var tmp: TLoc = getIntTemp(p)
+    var a = initLocExpr(p, e[1])
     var x = lenExpr(p, a)
     if op == mHigh: x = "($1-1)" % [x]
-    lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.r, x])
-    putIntoDest(p, d, e, tmp.r)
+    lineCg(p, cpsStmts, "$1 = $2;$n", [tmp.snippet, x])
+    putIntoDest(p, d, e, tmp.snippet)
   of tyArray:
     # YYY: length(sideeffect) is optimized away incorrectly?
     if op == mHigh: putIntoDest(p, d, e, rope(lastOrd(p.config, typ)))
     else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ)))
   else: internalError(p.config, e.info, "genArrayLen()")
 
-proc makePtrType(baseType: PType; idgen: IdGenerator): PType =
-  result = newType(tyPtr, nextTypeId idgen, baseType.owner)
-  addSonSkipIntLit(result, baseType, idgen)
-
-proc makeAddr(n: PNode; idgen: IdGenerator): PNode =
-  if n.kind == nkHiddenAddr:
-    result = n
-  else:
-    result = newTree(nkHiddenAddr, n)
-    result.typ = makePtrType(n.typ, idgen)
-
 proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) =
   if optSeqDestructors in p.config.globalOptions:
     e[1] = makeAddr(e[1], p.module.idgen)
     genCall(p, e, d)
     return
-  var a, b, call: TLoc
   assert(d.k == locNone)
   var x = e[1]
   if x.kind in {nkAddr, nkHiddenAddr}: x = x[0]
-  initLocExpr(p, x, a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, x)
+  var b = initLocExpr(p, e[2])
   let t = skipTypes(e[1].typ, {tyVar})
 
-  initLoc(call, locCall, e, OnHeap)
+  var call = initLoc(locCall, e, OnHeap)
   if not p.module.compileToCpp:
-    const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)"
-    call.r = ropecg(p.module, setLenPattern, [
+    const setLenPattern = "($3) #setLengthSeqV2(($1)?&($1)->Sup:NIM_NIL, $4, $2)"
+    call.snippet = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
       genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
 
   else:
     const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)"
-    call.r = ropecg(p.module, setLenPattern, [
+    call.snippet = ropecg(p.module, setLenPattern, [
       rdLoc(a), rdLoc(b), getTypeDesc(p.module, t),
       genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)])
 
@@ -1966,13 +1933,12 @@ proc genSetLengthStr(p: BProc, e: PNode, d: var TLoc) =
   if optSeqDestructors in p.config.globalOptions:
     binaryStmtAddr(p, e, d, "setLengthStrV2")
   else:
-    var a, b, call: TLoc
     if d.k != locNone: internalError(p.config, e.info, "genSetLengthStr")
-    initLocExpr(p, e[1], a)
-    initLocExpr(p, e[2], b)
+    var a = initLocExpr(p, e[1])
+    var b = initLocExpr(p, e[2])
 
-    initLoc(call, locCall, e, OnHeap)
-    call.r = ropecg(p.module, "#setLengthStr($1, $2)", [
+    var call = initLoc(locCall, e, OnHeap)
+    call.snippet = ropecg(p.module, "#setLengthStr($1, $2)", [
         rdLoc(a), rdLoc(b)])
     genAssignment(p, a, call, {})
     gcUsage(p.config, e)
@@ -1984,10 +1950,9 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) =
   # b = temp
   cowBracket(p, e[1])
   cowBracket(p, e[2])
-  var a, b, tmp: TLoc
-  getTemp(p, skipTypes(e[1].typ, abstractVar), tmp)
-  initLocExpr(p, e[1], a) # eval a
-  initLocExpr(p, e[2], b) # eval b
+  var tmp: TLoc = getTemp(p, skipTypes(e[1].typ, abstractVar))
+  var a = initLocExpr(p, e[1]) # eval a
+  var b = initLocExpr(p, e[2]) # eval b
   genAssignment(p, tmp, a, {})
   genAssignment(p, a, b, {})
   genAssignment(p, b, tmp, {})
@@ -2029,10 +1994,9 @@ proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) =
   else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)")
 
 template binaryStmtInExcl(p: BProc, e: PNode, d: var TLoc, frmt: string) =
-  var a, b: TLoc
   assert(d.k == locNone)
-  initLocExpr(p, e[1], a)
-  initLocExpr(p, e[2], b)
+  var a = initLocExpr(p, e[1])
+  var b = initLocExpr(p, e[2])
   var elem = newRopeAppender()
   rdSetElemLoc(p.config, b, a.t, elem)
   lineF(p, cpsStmts, frmt, [rdLoc(a), elem])
@@ -2048,31 +2012,31 @@ proc genInOp(p: BProc, e: PNode, d: var TLoc) =
                e[2][0]
              else:
                e[2]
-    initLocExpr(p, ea, a)
-    initLoc(b, locExpr, e, OnUnknown)
+    a = initLocExpr(p, ea)
+    b = initLoc(locExpr, e, OnUnknown)
     if e[1].len > 0:
-      b.r = rope("(")
+      b.snippet = rope("(")
       for i in 0..<e[1].len:
         let it = e[1][i]
         if it.kind == nkRange:
-          initLocExpr(p, it[0], x)
-          initLocExpr(p, it[1], y)
-          b.r.addf("$1 >= $2 && $1 <= $3",
+          x = initLocExpr(p, it[0])
+          y = initLocExpr(p, it[1])
+          b.snippet.addf("$1 >= $2 && $1 <= $3",
                [rdCharLoc(a), rdCharLoc(x), rdCharLoc(y)])
         else:
-          initLocExpr(p, it, x)
-          b.r.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
-        if i < e[1].len - 1: b.r.add(" || ")
-      b.r.add(")")
+          x = initLocExpr(p, it)
+          b.snippet.addf("$1 == $2", [rdCharLoc(a), rdCharLoc(x)])
+        if i < e[1].len - 1: b.snippet.add(" || ")
+      b.snippet.add(")")
     else:
       # handle the case of an empty set
-      b.r = rope("0")
-    putIntoDest(p, d, e, b.r)
+      b.snippet = rope("0")
+    putIntoDest(p, d, e, b.snippet)
   else:
     assert(e[1].typ != nil)
     assert(e[2].typ != nil)
-    initLocExpr(p, e[1], a)
-    initLocExpr(p, e[2], b)
+    a = initLocExpr(p, e[1])
+    b = initLocExpr(p, e[2])
     genInExprAux(p, e, a, b, d)
 
 proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
@@ -2088,7 +2052,8 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       "&",
       "|",
       "& ~"]
-  var a, b, i: TLoc
+  var a, b: TLoc
+  var i: TLoc
   var setType = skipTypes(e[1].typ, abstractVar)
   var size = int(getSize(p.config, setType))
   case size
@@ -2125,14 +2090,13 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     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:
-      var a: TLoc
-      initLocExpr(p, e[1], a)
+      var a: TLoc = initLocExpr(p, e[1])
       putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size]))
     of mLtSet, mLeSet:
-      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
-      initLocExpr(p, e[1], a)
-      initLocExpr(p, e[2], b)
-      if d.k == locNone: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool), d)
+      i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+      a = initLocExpr(p, e[1])
+      b = initLocExpr(p, e[2])
+      if d.k == locNone: d = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyBool))
       if op == mLtSet:
         linefmt(p, cpsStmts, lookupOpr[mLtSet],
            [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
@@ -2140,18 +2104,17 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
         linefmt(p, cpsStmts, lookupOpr[mLeSet],
            [rdLoc(i), size, rdLoc(d), rdLoc(a), rdLoc(b)])
     of mEqSet:
-      var a, b: TLoc
       assert(e[1].typ != nil)
       assert(e[2].typ != nil)
-      initLocExpr(p, e[1], a)
-      initLocExpr(p, e[2], b)
+      var a = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
       putIntoDest(p, d, e, ropecg(p.module, "(#nimCmpMem($1, $2, $3)==0)", [a.rdCharLoc, b.rdCharLoc, size]))
     of mMulSet, mPlusSet, mMinusSet:
       # we inline the simple for loop for better code generation:
-      getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter
-      initLocExpr(p, e[1], a)
-      initLocExpr(p, e[2], b)
-      if d.k == locNone: getTemp(p, setType, d)
+      i = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+      a = initLocExpr(p, e[1])
+      b = initLocExpr(p, e[2])
+      if d.k == locNone: d = getTemp(p, setType)
       lineF(p, cpsStmts,
            "for ($1 = 0; $1 < $2; $1++) $n" &
            "  $3[$1] = $4[$1] $6 $5[$1];$n", [
@@ -2168,8 +2131,7 @@ proc genSomeCast(p: BProc, e: PNode, d: var TLoc) =
     ValueTypes = {tyTuple, tyObject, tyArray, tyOpenArray, tyVarargs, tyUncheckedArray}
   # we use whatever C gives us. Except if we have a value-type, we need to go
   # through its address:
-  var a: TLoc
-  initLocExpr(p, e[1], a)
+  var a: TLoc = initLocExpr(p, e[1])
   let etyp = skipTypes(e.typ, abstractRange+{tyOwned})
   let srcTyp = skipTypes(e[1].typ, abstractRange)
   if etyp.kind in ValueTypes and lfIndirect notin a.flags:
@@ -2206,10 +2168,17 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     # 'cast' and some float type involved? --> use a union.
     inc(p.labels)
     var lbl = p.labels.rope
-    var tmp: TLoc
-    tmp.r = "LOC$1.source" % [lbl]
-    linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
-      [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
+    var tmp: TLoc = default(TLoc)
+    tmp.snippet = "LOC$1.source" % [lbl]
+    let destsize = getSize(p.config, destt)
+    let srcsize = getSize(p.config, srct)
+
+    if destsize > srcsize:
+      linefmt(p, cpsLocals, "union { $1 dest; $2 source; } LOC$3;$n #nimZeroMem(&LOC$3, sizeof(LOC$3));$n",
+        [getTypeDesc(p.module, e.typ), getTypeDesc(p.module, e[1].typ), lbl])
+    else:
+      linefmt(p, cpsLocals, "union { $1 source; $2 dest; } LOC$3;$n",
+        [getTypeDesc(p.module, e[1].typ), getTypeDesc(p.module, e.typ), lbl])
     tmp.k = locExpr
     tmp.lode = lodeTyp srct
     tmp.storage = OnStack
@@ -2222,9 +2191,8 @@ proc genCast(p: BProc, e: PNode, d: var TLoc) =
     genSomeCast(p, e, d)
 
 proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
+  var a: TLoc = initLocExpr(p, n[0])
   var dest = skipTypes(n.typ, abstractVar)
-  initLocExpr(p, n[0], a)
   if optRangeCheck notin p.options or (dest.kind in {tyUInt..tyUInt64} and
       checkUnsignedConversions notin p.config.legacyFeatures):
     discard "no need to generate a check because it was disabled"
@@ -2252,8 +2220,7 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
       cgsym(p.module, raiser)
 
       let boundaryCast =
-        if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or
-            (n0t.sym != nil and sfSystemModule in n0t.sym.owner.flags and n0t.sym.name.s == "csize"):
+        if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64}:
           "(NI64)"
         else:
           ""
@@ -2267,30 +2234,38 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) =
       raiseInstr(p, p.s(cpsStmts))
       linefmt p, cpsStmts, "}$n", []
 
-  putIntoDest(p, d, n, "(($1) ($2))" %
+  if sameBackendTypeIgnoreRange(dest, n[0].typ):
+    # don't cast so an address can be taken for `var` conversions
+    putIntoDest(p, d, n, "($1)" % [rdCharLoc(a)], a.storage)
+  else:
+    putIntoDest(p, d, n, "(($1) ($2))" %
       [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage)
 
 proc genConv(p: BProc, e: PNode, d: var TLoc) =
   let destType = e.typ.skipTypes({tyVar, tyLent, tyGenericInst, tyAlias, tySink})
-  if sameBackendType(destType, e[1].typ):
+  if sameBackendTypeIgnoreRange(destType, e[1].typ):
     expr(p, e[1], d)
   else:
     genSomeCast(p, e, d)
 
 proc convStrToCStr(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   putIntoDest(p, d, n,
               ropecg(p.module, "#nimToCStringConv($1)", [rdLoc(a)]),
 #                "($1 ? $1->data : (NCSTRING)\"\")" % [a.rdLoc],
               a.storage)
 
 proc convCStrToStr(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n[0], a)
-  putIntoDest(p, d, n,
-              ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
-              a.storage)
+  var a: TLoc = initLocExpr(p, n[0])
+  if p.module.compileToCpp:
+    # fixes for const qualifier; bug #12703; bug #19588
+    putIntoDest(p, d, n,
+            ropecg(p.module, "#cstrToNimstr((NCSTRING) $1)", [rdLoc(a)]),
+            a.storage)
+  else:
+    putIntoDest(p, d, n,
+                ropecg(p.module, "#cstrToNimstr($1)", [rdLoc(a)]),
+                a.storage)
   gcUsage(p.config, n)
 
 proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
@@ -2298,11 +2273,11 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
   var a = e[1]
   var b = e[2]
   if a.kind in {nkStrLit..nkTripleStrLit} and a.strVal == "":
-    initLocExpr(p, e[2], x)
+    x = initLocExpr(p, e[2])
     putIntoDest(p, d, e,
       ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
   elif b.kind in {nkStrLit..nkTripleStrLit} and b.strVal == "":
-    initLocExpr(p, e[1], x)
+    x = initLocExpr(p, e[1])
     putIntoDest(p, d, e,
       ropecg(p.module, "($1 == 0)", [lenExpr(p, x)]))
   else:
@@ -2311,11 +2286,10 @@ proc genStrEquals(p: BProc, e: PNode, d: var TLoc) =
 proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   if {optNaNCheck, optInfCheck} * p.options != {}:
     const opr: array[mAddF64..mDivF64, string] = ["+", "-", "*", "/"]
-    var a, b: TLoc
     assert(e[1].typ != nil)
     assert(e[2].typ != nil)
-    initLocExpr(p, e[1], a)
-    initLocExpr(p, e[2], b)
+    var a = initLocExpr(p, e[1])
+    var b = initLocExpr(p, e[2])
     putIntoDest(p, d, e, ropecg(p.module, "(($4)($2) $1 ($4)($3))",
                               [opr[m], rdLoc(a), rdLoc(b),
                               getSimpleTypeDesc(p.module, e[1].typ)]))
@@ -2332,34 +2306,60 @@ proc binaryFloatArith(p: BProc, e: PNode, d: var TLoc, m: TMagic) =
   else:
     binaryArith(p, e, d, m)
 
-proc skipAddr(n: PNode): PNode =
-  result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
-
 proc genWasMoved(p: BProc; n: PNode) =
   var a: TLoc
   let n1 = n[1].skipAddr
   if p.withinBlockLeaveActions > 0 and notYetAlive(n1):
     discard
   else:
-    initLocExpr(p, n1, a)
+    a = initLocExpr(p, n1, {lfEnforceDeref})
     resetLoc(p, a)
     #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
     #  [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
 
 proc genMove(p: BProc; n: PNode; d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n[1].skipAddr, a)
+  var a: TLoc = initLocExpr(p, n[1].skipAddr)
   if n.len == 4:
     # generated by liftdestructors:
-    var src: TLoc
-    initLocExpr(p, n[2], src)
+    var src: TLoc = initLocExpr(p, n[2])
     linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)])
     genStmts(p, n[3])
     linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)])
   else:
-    if d.k == locNone: getTemp(p, n.typ, d)
-    genAssignment(p, d, a, {})
-    resetLoc(p, a)
+    if d.k == locNone: d = getTemp(p, n.typ)
+    if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+      genAssignment(p, d, a, {})
+      var op = getAttachedOp(p.module.g.graph, n.typ, attachedWasMoved)
+      if op == nil:
+        resetLoc(p, a)
+      else:
+        var b = initLocExpr(p, newSymNode(op))
+        case skipTypes(a.t, abstractVar+{tyStatic}).kind
+        of tyOpenArray, tyVarargs: # todo fixme generated `wasMoved` hooks for
+                                   # openarrays, but it probably shouldn't?
+          var s: string
+          if reifiedOpenArray(a.lode):
+            if a.t.kind in {tyVar, tyLent}:
+              s = "$1->Field0, $1->Field1" % [rdLoc(a)]
+            else:
+              s = "$1.Field0, $1.Field1" % [rdLoc(a)]
+          else:
+            s = "$1, $1Len_0" % [rdLoc(a)]
+          linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), s])
+        else:
+          if p.module.compileToCpp:
+            linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), rdLoc(a)])
+          else:
+            linefmt(p, cpsStmts, "$1($2);$n", [rdLoc(b), byRefLoc(p, a)])
+    else:
+      if n[1].kind == nkSym and isSinkParam(n[1].sym):
+        var tmp = getTemp(p, n[1].typ.skipTypes({tySink}))
+        genAssignment(p, tmp, a, {needToCopySinkParam})
+        genAssignment(p, d, tmp, {})
+        resetLoc(p, tmp)
+      else:
+        genAssignment(p, d, a, {})
+      resetLoc(p, a)
 
 proc genDestroy(p: BProc; n: PNode) =
   if optSeqDestructors in p.config.globalOptions:
@@ -2367,8 +2367,7 @@ proc genDestroy(p: BProc; n: PNode) =
     let t = arg.typ.skipTypes(abstractInst)
     case t.kind
     of tyString:
-      var a: TLoc
-      initLocExpr(p, arg, a)
+      var a: TLoc = initLocExpr(p, arg)
       if optThreads in p.config.globalOptions:
         linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
           " #deallocShared($1.p);$n" &
@@ -2378,12 +2377,11 @@ proc genDestroy(p: BProc; n: PNode) =
           " #dealloc($1.p);$n" &
           "}$n", [rdLoc(a)])
     of tySequence:
-      var a: TLoc
-      initLocExpr(p, arg, a)
+      var a: TLoc = initLocExpr(p, arg)
       linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" &
         " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" &
         "}$n",
-        [rdLoc(a), getTypeDesc(p.module, t.lastSon)])
+        [rdLoc(a), getTypeDesc(p.module, t.elementType)])
     else: discard "nothing to do"
   else:
     let t = n[1].typ.skipTypes(abstractVar)
@@ -2394,10 +2392,9 @@ proc genDestroy(p: BProc; n: PNode) =
 
 proc genDispose(p: BProc; n: PNode) =
   when false:
-    let elemType = n[1].typ.skipTypes(abstractVar).lastSon
+    let elemType = n[1].typ.skipTypes(abstractVar).elementType
 
-    var a: TLoc
-    initLocExpr(p, n[1].skipAddr, a)
+    var a: TLoc = initLocExpr(p, n[1].skipAddr)
 
     if isFinal(elemType):
       if elemType.destructor != nil:
@@ -2410,8 +2407,11 @@ proc genDispose(p: BProc; n: PNode) =
       lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)])
 
 proc genSlice(p: BProc; e: PNode; d: var TLoc) =
-  let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon)
-  if d.k == locNone: getTemp(p, e.typ, d)
+  let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.elementType,
+    prepareForMutation = e[1].kind == nkHiddenDeref and
+                         e[1].typ.skipTypes(abstractInst).kind == tyString and
+                         p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc})
+  if d.k == locNone: d = getTemp(p, e.typ)
   linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y])
   when false:
     localError(p.config, e.info, "invalid context for 'toOpenArray'; " &
@@ -2446,11 +2446,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     if optOverflowCheck notin p.options or underlying.kind in {tyUInt..tyUInt64}:
       binaryStmt(p, e, d, opr[op])
     else:
-      var a, b: TLoc
       assert(e[1].typ != nil)
       assert(e[2].typ != nil)
-      initLocExpr(p, e[1], a)
-      initLocExpr(p, e[2], b)
+      var a = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
 
       let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct})
       let res = binaryArithOverflowRaw(p, ranged, a, b,
@@ -2464,11 +2463,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     if optSeqDestructors in p.config.globalOptions:
       binaryStmtAddr(p, e, d, "nimAddCharV1")
     else:
-      var dest, b, call: TLoc
-      initLoc(call, locCall, e, OnHeap)
-      initLocExpr(p, e[1], dest)
-      initLocExpr(p, e[2], b)
-      call.r = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
+      var call = initLoc(locCall, e, OnHeap)
+      var dest = initLocExpr(p, e[1])
+      var b = initLocExpr(p, e[2])
+      call.snippet = ropecg(p.module, "#addChar($1, $2)", [rdLoc(dest), rdLoc(b)])
       genAssignment(p, dest, call, {})
   of mAppendStrStr: genStrAppend(p, e, d)
   of mAppendSeqElem:
@@ -2481,16 +2479,14 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mLeStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) <= 0)")
   of mLtStr: binaryExpr(p, e, d, "(#cmpStrings($1, $2) < 0)")
   of mIsNil: genIsNil(p, e, d)
-  of mIntToStr: genDollar(p, e, d, "#nimIntToStr($1)")
-  of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)")
   of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)")
   of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)")
-  of mFloatToStr:
-    if e[1].typ.skipTypes(abstractInst).kind == tyFloat32:
-      genDollar(p, e, d, "#nimFloat32ToStr($1)")
+  of mCStrToStr:
+    if p.module.compileToCpp:
+      # fixes for const qualifier; bug #12703; bug #19588
+      genDollar(p, e, d, "#cstrToNimstr((NCSTRING) $1)")
     else:
-      genDollar(p, e, d, "#nimFloatToStr($1)")
-  of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)")
+      genDollar(p, e, d, "#cstrToNimstr($1)")
   of mStrToStr, mUnown: expr(p, e[1], d)
   of generatedMagics: genCall(p, e, d)
   of mEnumToStr:
@@ -2502,8 +2498,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mNew: genNew(p, e)
   of mNewFinalize:
     if optTinyRtti in p.config.globalOptions:
-      var a: TLoc
-      initLocExpr(p, e[1], a)
+      var a: TLoc = initLocExpr(p, e[1])
       rawGenNew(p, a, "", needsInit = true)
       gcUsage(p.config, e)
     else:
@@ -2517,10 +2512,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mNewSeqOfCap: genNewSeqOfCap(p, e, d)
   of mSizeOf:
     let t = e[1].typ.skipTypes({tyTypeDesc})
-    putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, skVar)])
+    putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, dkVar)])
   of mAlignOf:
     let t = e[1].typ.skipTypes({tyTypeDesc})
-    putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)])
+    putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, dkVar)])
   of mOffsetOf:
     var dotExpr: PNode
     if e[1].kind == nkDotExpr:
@@ -2528,20 +2523,25 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     elif e[1].kind == nkCheckedFieldExpr:
       dotExpr = e[1][0]
     else:
+      dotExpr = nil
       internalError(p.config, e.info, "unknown ast")
     let t = dotExpr[0].typ.skipTypes({tyTypeDesc})
-    let tname = getTypeDesc(p.module, t, skVar)
+    let tname = getTypeDesc(p.module, t, dkVar)
     let member =
       if t.kind == tyTuple:
         "Field" & rope(dotExpr[1].sym.position)
-      else: dotExpr[1].sym.loc.r
+      else: dotExpr[1].sym.loc.snippet
     putIntoDest(p,d,e, "((NI)offsetof($1, $2))" % [tname, member])
   of mChr: genSomeCast(p, e, d)
   of mOrd: genOrd(p, e, d)
   of mLengthArray, mHigh, mLengthStr, mLengthSeq, mLengthOpenArray:
     genArrayLen(p, e, d, op)
-  of mGCref: unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
-  of mGCunref: unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
+  of mGCref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCref($1); }$n")
+  of mGCunref:
+    # only a magic for the old GCs
+    unaryStmt(p, e, d, "if ($1) { #nimGCunref($1); }$n")
   of mSetLengthStr: genSetLengthStr(p, e, d)
   of mSetLengthSeq: genSetLengthSeq(p, e, d)
   of mIncl, mExcl, mCard, mLtSet, mLeSet, mEqSet, mMulSet, mPlusSet, mMinusSet,
@@ -2553,8 +2553,8 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
     # - not sure, and it wouldn't work if the symbol behind the magic isn't
     #   somehow forward-declared from some other usage, but it is *possible*
     if lfNoDecl notin opr.loc.flags:
-      let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.r)
-      assert prc != nil, $opr.loc.r
+      let prc = magicsys.getCompilerProc(p.module.g.graph, $opr.loc.snippet)
+      assert prc != nil, $opr.loc.snippet
       # HACK:
       # Explicitly add this proc as declared here so the cgsym call doesn't
       # add a forward declaration - without this we could end up with the same
@@ -2567,14 +2567,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       let wasDeclared = containsOrIncl(p.module.declaredProtos, prc.id)
       # Make the function behind the magic get actually generated - this will
       # not lead to a forward declaration! The genCall will lead to one.
-      cgsym(p.module, $opr.loc.r)
+      cgsym(p.module, $opr.loc.snippet)
       # make sure we have pointer-initialising code for hot code reloading
       if not wasDeclared and p.hcrOn:
         p.module.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
              [mangleDynLibProc(prc), getTypeDesc(p.module, prc.loc.t), getModuleDllPath(p.module, prc)])
     genCall(p, e, d)
-  of mDefault: genDefault(p, e, d)
-  of mReset: genReset(p, e)
+  of mDefault, mZeroDefault: genDefault(p, e, d)
   of mEcho: genEcho(p, e[1].skipConv)
   of mArrToSeq: genArrToSeq(p, e, d)
   of mNLen..mNError, mSlurp..mQuoteAst:
@@ -2592,14 +2591,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
       let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e)
       expr(p, n, d)
   of mDeepCopy:
-    if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
+    if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions:
       localError(p.config, e.info,
-        "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
+        "for --mm:arc|atomicArc|orc 'deepcopy' support has to be enabled with --deepcopy:on")
 
-    var a, b: TLoc
     let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1]
-    initLocExpr(p, x, a)
-    initLocExpr(p, e[2], b)
+    var a = initLocExpr(p, x)
+    var b = initLocExpr(p, e[2])
     genDeepCopy(p, a, b)
   of mDotDot, mEqCString: genCall(p, e, d)
   of mWasMoved: genWasMoved(p, e)
@@ -2609,6 +2607,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
   of mAccessTypeField: genAccessTypeField(p, e, d)
   of mSlice: genSlice(p, e, d)
   of mTrace: discard "no code to generate"
+  of mEnsureMove:
+    expr(p, e[1], d)
+  of mDup:
+    expr(p, e[1], d)
   else:
     when defined(debugMagics):
       echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind
@@ -2620,22 +2622,23 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
   # nimZeroMem(tmp, sizeof(tmp)); inclRange(tmp, a, b); incl(tmp, c);
   # incl(tmp, d); incl(tmp, e); inclRange(tmp, f, g);
   var
-    a, b, idx: TLoc
+    a, b: TLoc
+  var idx: TLoc
   if nfAllConst in e.flags:
     var elem = newRopeAppender()
     genSetNode(p, e, elem)
     putIntoDest(p, d, e, elem)
   else:
-    if d.k == locNone: getTemp(p, e.typ, d)
+    if d.k == locNone: d = getTemp(p, e.typ)
     if getSize(p.config, e.typ) > 8:
       # big set:
       linefmt(p, cpsStmts, "#nimZeroMem($1, sizeof($2));$n",
           [rdLoc(d), getTypeDesc(p.module, e.typ)])
       for it in e.sons:
         if it.kind == nkRange:
-          getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter
-          initLocExpr(p, it[0], a)
-          initLocExpr(p, it[1], b)
+          idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+          a = initLocExpr(p, it[0])
+          b = initLocExpr(p, it[1])
           var aa = newRopeAppender()
           rdSetElemLoc(p.config, a, e.typ, aa)
           var bb = newRopeAppender()
@@ -2644,7 +2647,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
               "$2[(NU)($1)>>3] |=(1U<<((NU)($1)&7U));$n", [rdLoc(idx), rdLoc(d),
               aa, bb])
         else:
-          initLocExpr(p, it, a)
+          a = initLocExpr(p, it)
           var aa = newRopeAppender()
           rdSetElemLoc(p.config, a, e.typ, aa)
           lineF(p, cpsStmts, "$1[(NU)($2)>>3] |=(1U<<((NU)($2)&7U));$n",
@@ -2655,9 +2658,9 @@ 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(p.module.g.graph, unknownLineInfo, tyInt), idx) # our counter
-          initLocExpr(p, it[0], a)
-          initLocExpr(p, it[1], b)
+          idx = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt)) # our counter
+          a = initLocExpr(p, it[0])
+          b = initLocExpr(p, it[1])
           var aa = newRopeAppender()
           rdSetElemLoc(p.config, a, e.typ, aa)
           var bb = newRopeAppender()
@@ -2667,7 +2670,7 @@ proc genSetConstr(p: BProc, e: PNode, d: var TLoc) =
               "$2 |=(($5)(1)<<(($1)%(sizeof($5)*8)));$n", [
               rdLoc(idx), rdLoc(d), aa, bb, rope(ts)])
         else:
-          initLocExpr(p, it, a)
+          a = initLocExpr(p, it)
           var aa = newRopeAppender()
           rdSetElemLoc(p.config, a, e.typ, aa)
           lineF(p, cpsStmts,
@@ -2679,15 +2682,30 @@ proc genTupleConstr(p: BProc, n: PNode, d: var TLoc) =
   if not handleConstExpr(p, n, d):
     let t = n.typ
     discard getTypeDesc(p.module, t) # so that any fields are initialized
-    if d.k == locNone: getTemp(p, t, d)
+
+    var tmp: TLoc = default(TLoc)
+    # bug #16331
+    let doesAlias = lhsDoesAlias(d.lode, n)
+    let dest = if doesAlias: addr(tmp) else: addr(d)
+    if doesAlias:
+      tmp = getTemp(p, n.typ)
+    elif d.k == locNone:
+      d = getTemp(p, n.typ)
+
     for i in 0..<n.len:
       var it = n[i]
       if it.kind == nkExprColonExpr: it = it[1]
-      initLoc(rec, locExpr, it, d.storage)
-      rec.r = "$1.Field$2" % [rdLoc(d), rope(i)]
+      rec = initLoc(locExpr, it, dest[].storage)
+      rec.snippet = "$1.Field$2" % [rdLoc(dest[]), rope(i)]
       rec.flags.incl(lfEnforceDeref)
       expr(p, it, rec)
 
+    if doesAlias:
+      if d.k == locNone:
+        d = tmp
+      else:
+        genAssignment(p, d, tmp, {})
+
 proc isConstClosure(n: PNode): bool {.inline.} =
   result = n[0].kind == nkSym and isRoutine(n[0].sym) and
       n[1].kind == nkNilLit
@@ -2704,9 +2722,9 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
     p.module.s[cfsData].add data
     putIntoDest(p, d, n, tmp, OnStatic)
   else:
-    var tmp, a, b: TLoc
-    initLocExpr(p, n[0], a)
-    initLocExpr(p, n[1], b)
+    var tmp: TLoc
+    var a = initLocExpr(p, n[0])
+    var b = initLocExpr(p, n[1])
     if n[0].skipConv.kind == nkClosure:
       internalError(p.config, n.info, "closure to closure created")
     # tasyncawait.nim breaks with this optimization:
@@ -2715,7 +2733,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
         linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
                 [d.rdLoc, a.rdLoc, b.rdLoc])
     else:
-      getTemp(p, n.typ, tmp)
+      tmp = getTemp(p, n.typ)
       linefmt(p, cpsStmts, "$1.ClP_0 = $2; $1.ClE_0 = $3;$n",
               [tmp.rdLoc, a.rdLoc, b.rdLoc])
       putLocIntoDest(p, d, tmp)
@@ -2723,17 +2741,17 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) =
 proc genArrayConstr(p: BProc, n: PNode, d: var TLoc) =
   var arr: TLoc
   if not handleConstExpr(p, n, d):
-    if d.k == locNone: getTemp(p, n.typ, d)
+    if d.k == locNone: d = getTemp(p, n.typ)
     for i in 0..<n.len:
-      initLoc(arr, locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
+      arr = initLoc(locExpr, lodeTyp elemType(skipTypes(n.typ, abstractInst)), d.storage)
       var lit = newRopeAppender()
       intLiteral(i, lit)
-      arr.r = "$1[$2]" % [rdLoc(d), lit]
+      arr.snippet = "$1[$2]" % [rdLoc(d), lit]
       expr(p, n[i], arr)
 
 proc genComplexConst(p: BProc, sym: PSym, d: var TLoc) =
   requestConstImpl(p, sym)
-  assert((sym.loc.r != "") and (sym.loc.t != nil))
+  assert((sym.loc.snippet != "") and (sym.loc.t != nil))
   putLocIntoDest(p, d, sym.loc)
 
 template genStmtListExprImpl(exprOrStmt) {.dirty.} =
@@ -2769,23 +2787,29 @@ proc genStmtList(p: BProc, n: PNode) =
 from parampatterns import isLValue
 
 proc upConv(p: BProc, n: PNode, d: var TLoc) =
-  var a: TLoc
-  initLocExpr(p, n[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   let dest = skipTypes(n.typ, abstractPtrs)
   if optObjCheck in p.options and not isObjLackingTypeField(dest):
     var nilCheck = ""
     var r = newRopeAppender()
     rdMType(p, a, nilCheck, r)
-    let checkFor = if optTinyRtti in p.config.globalOptions:
-                     genTypeInfo2Name(p.module, dest)
-                   else:
-                     genTypeInfoV1(p.module, dest, n.info)
-    if nilCheck != "":
-      linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
-              [nilCheck, r, checkFor])
-    else:
-      linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
-              [r, checkFor])
+    if optTinyRtti in p.config.globalOptions:
+      let checkFor = $getObjDepth(dest)
+      let token = $genDisplayElem(MD5Digest(hashType(dest, p.config)))
+      if nilCheck != "":
+        linefmt(p, cpsStmts, "if ($1 && !#isObjDisplayCheck($2, $3, $4)){ #raiseObjectConversionError(); ",
+                [nilCheck, r, checkFor, token])
+      else:
+        linefmt(p, cpsStmts, "if (!#isObjDisplayCheck($1, $2, $3)){ #raiseObjectConversionError(); ",
+                [r, checkFor, token])
+    else:
+      let checkFor = genTypeInfoV1(p.module, dest, n.info)
+      if nilCheck != "":
+        linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); ",
+                [nilCheck, r, checkFor])
+      else:
+        linefmt(p, cpsStmts, "if (!#isObj($1, $2)){ #raiseObjectConversionError(); ",
+                [r, checkFor])
     raiseInstr(p, p.s(cpsStmts))
     linefmt p, cpsStmts, "}$n", []
 
@@ -2814,16 +2838,14 @@ proc downConv(p: BProc, n: PNode, d: var TLoc) =
     # (see bug #837). However sometimes using a temporary is not correct:
     # init(TFigure(my)) # where it is passed to a 'var TFigure'. We test
     # this by ensuring the destination is also a pointer:
-    var a: TLoc
-    initLocExpr(p, arg, a)
+    var a: TLoc = initLocExpr(p, arg)
     putIntoDest(p, d, n,
               "(*(($1*) (&($2))))" % [getTypeDesc(p.module, n.typ), rdLoc(a)], a.storage)
   elif p.module.compileToCpp:
     # C++ implicitly downcasts for us
     expr(p, arg, d)
   else:
-    var a: TLoc
-    initLocExpr(p, arg, a)
+    var a: TLoc = initLocExpr(p, arg)
     var r = rdLoc(a) & (if isRef: "->Sup" else: ".Sup")
     for i in 2..abs(inheritanceDiff(dest, src)): r.add(".Sup")
     putIntoDest(p, d, n, if isRef: "&" & r else: r, a.storage)
@@ -2838,7 +2860,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) =
     # expression not found in the cache:
     inc(p.module.labels)
     p.module.s[cfsData].addf("static NIM_CONST $1 $2 = ",
-         [getTypeDesc(p.module, t, skConst), tmp])
+         [getTypeDesc(p.module, t, dkConst), tmp])
     genBracedInit(p, n, isConst = true, t, p.module.s[cfsData])
     p.module.s[cfsData].addf(";$n", [])
 
@@ -2856,47 +2878,47 @@ proc genConstSetup(p: BProc; sym: PSym): bool =
   useHeader(m, sym)
   if sym.loc.k == locNone:
     fillBackendName(p.module, sym)
-    fillLoc(sym.loc, locData, sym.ast, OnStatic)
+    fillLoc(sym.loc, locData, sym.astdef, OnStatic)
   if m.hcrOn: incl(sym.loc.flags, lfIndirect)
   result = lfNoDecl notin sym.loc.flags
 
 proc genConstHeader(m, q: BModule; p: BProc, sym: PSym) =
-  if sym.loc.r == "":
+  if sym.loc.snippet == "":
     if not genConstSetup(p, sym): return
-  assert(sym.loc.r != "", $sym.name.s & $sym.itemId)
+  assert(sym.loc.snippet != "", $sym.name.s & $sym.itemId)
   if m.hcrOn:
-    m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]);
+    m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]);
     m.initProc.procSec(cpsLocals).addf(
-      "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
-      getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)])
+      "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
+      getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(q, sym)])
   else:
     let headerDecl = "extern NIM_CONST $1 $2;$n" %
-        [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]
+        [getTypeDesc(m, sym.loc.t, dkVar), sym.loc.snippet]
     m.s[cfsData].add(headerDecl)
     if sfExportc in sym.flags and p.module.g.generatedHeader != nil:
       p.module.g.generatedHeader.s[cfsData].add(headerDecl)
 
 proc genConstDefinition(q: BModule; p: BProc; sym: PSym) =
   # add a suffix for hcr - will later init the global pointer with this data
-  let actualConstName = if q.hcrOn: sym.loc.r & "_const" else: sym.loc.r
+  let actualConstName = if q.hcrOn: sym.loc.snippet & "_const" else: sym.loc.snippet
   var data = newRopeAppender()
   data.addf("N_LIB_PRIVATE NIM_CONST $1 $2 = ",
            [getTypeDesc(q, sym.typ), actualConstName])
-  genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ, data)
+  genBracedInit(q.initProc, sym.astdef, isConst = true, sym.typ, data)
   data.addf(";$n", [])
   q.s[cfsData].add data
   if q.hcrOn:
     # generate the global pointer with the real name
-    q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, skVar), sym.loc.r])
+    q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(q, sym.loc.t, dkVar), sym.loc.snippet])
     # register it (but ignore the boolean result of hcrRegisterGlobal)
     q.initProc.procSec(cpsLocals).addf(
       "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n",
-      [getModuleDllPath(q, sym), sym.loc.r, rdLoc(sym.loc)])
+      [getModuleDllPath(q, sym), sym.loc.snippet, rdLoc(sym.loc)])
     # always copy over the contents of the actual constant with the _const
     # suffix ==> this means that the constant is reloadable & updatable!
     q.initProc.procSec(cpsLocals).add(ropecg(q,
       "\t#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
-      [sym.loc.r, actualConstName, rdLoc(sym.loc)]))
+      [sym.loc.snippet, actualConstName, rdLoc(sym.loc)]))
 
 proc genConstStmt(p: BProc, n: PNode) =
   # This code is only used in the new DCE implementation.
@@ -2936,17 +2958,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         genProcPrototype(p.module, sym)
       else:
         genProc(p.module, sym)
-      if sym.loc.r == "" or sym.loc.lode == nil:
+      if sym.loc.snippet == "" or sym.loc.lode == nil:
         internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
       putLocIntoDest(p, d, sym.loc)
     of skConst:
       if isSimpleConst(sym.typ):
         var lit = newRopeAppender()
-        genLiteral(p, sym.ast, sym.typ, lit)
+        genLiteral(p, sym.astdef, sym.typ, lit)
         putIntoDest(p, d, n, lit, OnStatic)
       elif useAliveDataFromDce in p.module.flags:
         genConstHeader(p.module, p.module, p, sym)
-        assert((sym.loc.r != "") and (sym.loc.t != nil))
+        assert((sym.loc.snippet != "") and (sym.loc.t != nil))
         putLocIntoDest(p, d, sym.loc)
       else:
         genComplexConst(p, sym, d)
@@ -2961,14 +2983,14 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
         if sfCompileTime in sym.flags:
           genSingleVar(p, sym, n, astdef(sym))
 
-      if sym.loc.r == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         internalError p.config, n.info, "expr: var not init " & sym.name.s & "_" & $sym.id
       if sfThread in sym.flags:
         accessThreadLocalVar(p, sym)
         if emulatedThreadVars(p.config):
-          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.r)
+          putIntoDest(p, d, sym.loc.lode, "NimTV_->" & sym.loc.snippet)
         else:
           putLocIntoDest(p, d, sym.loc)
       else:
@@ -2976,17 +2998,17 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     of skTemp:
       when false:
         # this is more harmful than helpful.
-        if sym.loc.r == "":
+        if sym.loc.snippet == "":
           # we now support undeclared 'skTemp' variables for easier
           # transformations in other parts of the compiler:
           assignLocalVar(p, n)
-      if sym.loc.r == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         #echo "FAILED FOR PRCO ", p.prc.name.s
         #echo renderTree(p.prc.ast, {renderIds})
         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 == "" or sym.loc.t == nil:
+      if sym.loc.snippet == "" or sym.loc.t == nil:
         # echo "FAILED FOR PRCO ", p.prc.name.s
         # debug p.prc.typ.n
         # echo renderTree(p.prc.ast, {renderIds})
@@ -3012,7 +3034,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     let op = n[0]
     if n.typ.isNil:
       # discard the value:
-      var a: TLoc
+      var a: TLoc = default(TLoc)
       if op.kind == nkSym and op.sym.magic != mNone:
         genMagicExpr(p, n, a, op.sym.magic)
       else:
@@ -3047,7 +3069,16 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkObjConstr: genObjConstr(p, n, d)
   of nkCast: genCast(p, n, d)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, d)
-  of nkHiddenAddr, nkAddr: genAddr(p, n, d)
+  of nkHiddenAddr:
+    if n[0].kind == nkDerefExpr:
+      # addr ( deref ( x )) --> x
+      var x = n[0][0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        x.typ = n.typ
+      expr(p, x, d)
+      return
+    genAddr(p, n, d)
+  of nkAddr: genAddr(p, n, d)
   of nkBracketExpr: genBracketExpr(p, n, d)
   of nkDerefExpr, nkHiddenDeref: genDeref(p, n, d)
   of nkDotExpr: genRecordField(p, n, d)
@@ -3067,7 +3098,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
   of nkLambdaKinds:
     var sym = n[namePos].sym
     genProc(p.module, sym)
-    if sym.loc.r == "" or sym.loc.lode == nil:
+    if sym.loc.snippet == "" or sym.loc.lode == nil:
       internalError(p.config, n.info, "expr: proc not init " & sym.name.s)
     putLocIntoDest(p, d, sym.loc)
   of nkClosure: genClosure(p, n, d)
@@ -3087,7 +3118,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     cow(p, n[1])
     if nfPreventCg notin n.flags:
       genAsgn(p, n, fastAsgn=false)
-  of nkFastAsgn:
+  of nkFastAsgn, nkSinkAsgn:
     cow(p, n[1])
     if nfPreventCg notin n.flags:
       # transf is overly aggressive with 'nkFastAsgn', so we work around here.
@@ -3097,9 +3128,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
     let ex = n[0]
     if ex.kind != nkEmpty:
       genLineDir(p, n)
-      var a: TLoc
-      initLocExprSingleUse(p, ex, a)
-      line(p, cpsStmts, "(void)(" & a.r & ");\L")
+      var a: TLoc = initLocExprSingleUse(p, ex)
+      line(p, cpsStmts, "(void)(" & a.snippet & ");\L")
   of nkAsmStmt: genAsmStmt(p, n)
   of nkTryStmt, nkHiddenTryStmt:
     case p.config.exc
@@ -3186,15 +3216,17 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
     result.add "}"
   of tyTuple:
     result.add "{"
-    for i in 0..<t.len:
+    if p.vccAndC and t.isEmptyTupleType:
+      result.add "0"
+    for i, a in t.ikids:
       if i > 0: result.add ", "
-      getDefaultValue(p, t[i], info, result)
+      getDefaultValue(p, a, info, result)
     result.add "}"
   of tyArray:
     result.add "{"
-    for i in 0..<toInt(lengthOrd(p.config, t.sons[0])):
+    for i in 0..<toInt(lengthOrd(p.config, t.indexType)):
       if i > 0: result.add ", "
-      getDefaultValue(p, t.sons[1], info, result)
+      getDefaultValue(p, t.elementType, info, result)
     result.add "}"
     #result = rope"{}"
   of tyOpenArray, tyVarargs:
@@ -3205,20 +3237,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) =
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
 
-proc caseObjDefaultBranch(obj: PNode; branch: Int128): int =
-  for i in 1 ..< obj.len:
-    for j in 0 .. obj[i].len - 2:
-      if obj[i][j].kind == nkRange:
-        let x = getOrdValue(obj[i][j][0])
-        let y = getOrdValue(obj[i][j][1])
-        if branch >= x and branch <= y:
-          return i
-      elif getOrdValue(obj[i][j]) == branch:
-        return i
-    if obj[i].len == 1:
-      # else branch
-      return i
-  assert(false, "unreachable")
+proc isEmptyCaseObjectBranch(n: PNode): bool =
+  for it in n:
+    if it.kind == nkSym and not isEmptyType(it.sym.typ): return false
+  return true
 
 proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
                      result: var Rope; count: var int;
@@ -3229,7 +3251,8 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
       getNullValueAux(p, t, it, constOrNil, result, count, isConst, info)
   of nkRecCase:
     getNullValueAux(p, t, obj[0], constOrNil, result, count, isConst, info)
-    if count > 0: result.add ", "
+    var res = ""
+    if count > 0: res.add ", "
     var branch = Zero
     if constOrNil != nil:
       ## find kind value, default is zero if not specified
@@ -3243,18 +3266,21 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
           break
 
     let selectedBranch = caseObjDefaultBranch(obj, branch)
-    result.add "{"
+    res.add "{"
     var countB = 0
     let b = lastSon(obj[selectedBranch])
     # designated initilization is the only way to init non first element of unions
     # branches are allowed to have no members (b.len == 0), in this case they don't need initializer
-    if b.kind == nkRecList and b.len > 0:
-      result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
-      getNullValueAux(p, t,  b, constOrNil, result, countB, isConst, info)
-      result.add "}"
+    if b.kind == nkRecList and not isEmptyCaseObjectBranch(b):
+      res.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+      res.add "}"
     elif b.kind == nkSym:
-      result.add "." & mangleRecFieldName(p.module, b.sym) & " = "
-      getNullValueAux(p, t,  b, constOrNil, result, countB, isConst, info)
+      res.add "." & mangleRecFieldName(p.module, b.sym) & " = "
+      getNullValueAux(p, t,  b, constOrNil, res, countB, isConst, info)
+    else:
+      return
+    result.add res
     result.add "}"
 
   of nkSym:
@@ -3264,6 +3290,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
     if constOrNil != nil:
       for i in 1..<constOrNil.len:
         if constOrNil[i].kind == nkExprColonExpr:
+          assert constOrNil[i][0].kind == nkSym, "illformed object constr; the field is not a sym"
           if constOrNil[i][0].sym.name.id == field.name.id:
             genBracedInit(p, constOrNil[i][1], isConst, field.typ, result)
             return
@@ -3278,7 +3305,7 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
 proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode,
                       result: var Rope; count: var int;
                       isConst: bool, info: TLineInfo) =
-  var base = t[0]
+  var base = t.baseClass
   let oldRes = result
   let oldcount = count
   if base != nil:
@@ -3309,6 +3336,8 @@ proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) =
 
 proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) =
   result.add "{"
+  if p.vccAndC and n.len == 0 and n.typ.kind == tyArray:
+    getDefaultValue(p, n.typ.elementType, n.info, result)
   for i in 0..<n.len:
     let it = n[i]
     if i > 0: result.add ",\n"
@@ -3318,6 +3347,8 @@ proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) =
 
 proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) =
   result.add "{"
+  if p.vccAndC and n.len == 0:
+    result.add "0"
   for i in 0..<n.len:
     let it = n[i]
     if i > 0: result.add ",\n"
@@ -3351,18 +3382,19 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope)
 
 proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool; result: var Rope) =
   let base = t.skipTypes(abstractInst)[0]
-  var data = rope"{"
-  for i in 0..<n.len:
-    if i > 0: data.addf(",$n", [])
-    genBracedInit(p, n[i], isConst, base, data)
-  data.add("}")
+  var data = rope""
+  if n.len > 0:
+    data.add(", {")
+    for i in 0..<n.len:
+      if i > 0: data.addf(",$n", [])
+      genBracedInit(p, n[i], isConst, base, data)
+    data.add("}")
 
   let payload = getTempName(p.module)
-
   appcg(p.module, cfsStrData,
     "static $5 struct {$n" &
     "  NI cap; $1 data[$2];$n" &
-    "} $3 = {$2 | NIM_STRLIT_FLAG, $4};$n", [
+    "} $3 = {$2 | NIM_STRLIT_FLAG$4};$n", [
     getTypeDesc(p.module, base), n.len, payload, data,
     if isConst: "const" else: ""])
   result.add "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload]
@@ -3405,12 +3437,10 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
         if n[0].kind == nkNilLit:
           result.add "{NIM_NIL,NIM_NIL}"
         else:
-          var d: TLoc
-          initLocExpr(p, n[0], d)
+          var d: TLoc = initLocExpr(p, n[0])
           result.add "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)]
       else:
-        var d: TLoc
-        initLocExpr(p, n, d)
+        var d: TLoc = initLocExpr(p, n)
         result.add rdLoc(d)
     of tyArray, tyVarargs:
       genConstSimpleList(p, n, isConst, result)
@@ -3424,7 +3454,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
       genConstSimpleList(p, n, isConst, data)
 
       let payload = getTempName(p.module)
-      let ctype = getTypeDesc(p.module, typ[0])
+      let ctype = getTypeDesc(p.module, typ.elementType)
       let arrLen = n.len
       appcg(p.module, cfsStrData,
         "static $5 $1 $3[$2] = $4;$n", [
@@ -3438,10 +3468,8 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType; resul
       if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString:
         genStringLiteralV2Const(p.module, n, isConst, result)
       else:
-        var d: TLoc
-        initLocExpr(p, n, d)
+        var d: TLoc = initLocExpr(p, n)
         result.add rdLoc(d)
     else:
-      var d: TLoc
-      initLocExpr(p, n, d)
+      var d: TLoc = initLocExpr(p, n)
       result.add rdLoc(d)
diff --git a/compiler/ccgliterals.nim b/compiler/ccgliterals.nim
index 7130c8462..cbef6771f 100644
--- a/compiler/ccgliterals.nim
+++ b/compiler/ccgliterals.nim
@@ -21,7 +21,7 @@ template detectVersion(field, corename) =
     if core == nil or core.kind != skConst:
       m.g.field = 1
     else:
-      m.g.field = toInt(ast.getInt(core.ast))
+      m.g.field = toInt(ast.getInt(core.astdef))
   result = m.g.field
 
 proc detectStrVersion(m: BModule): int =
diff --git a/compiler/ccgmerge_unused.nim b/compiler/ccgmerge_unused.nim
index c7d19da7a..a1413034f 100644
--- a/compiler/ccgmerge_unused.nim
+++ b/compiler/ccgmerge_unused.nim
@@ -19,13 +19,11 @@ import
 
 const
   CFileSectionNames: array[TCFileSection, string] = [
-    cfsMergeInfo: "",
     cfsHeaders: "NIM_merge_HEADERS",
     cfsFrameDefines: "NIM_merge_FRAME_DEFINES",
     cfsForwardTypes: "NIM_merge_FORWARD_TYPES",
     cfsTypes: "NIM_merge_TYPES",
     cfsSeqTypes: "NIM_merge_SEQ_TYPES",
-    cfsFieldInfo: "NIM_merge_FIELD_INFO",
     cfsTypeInfo: "NIM_merge_TYPE_INFO",
     cfsProcHeaders: "NIM_merge_PROC_HEADERS",
     cfsData: "NIM_merge_DATA",
@@ -34,11 +32,8 @@ const
     cfsInitProc: "NIM_merge_INIT_PROC",
     cfsDatInitProc: "NIM_merge_DATINIT_PROC",
     cfsTypeInit1: "NIM_merge_TYPE_INIT1",
-    cfsTypeInit2: "NIM_merge_TYPE_INIT2",
     cfsTypeInit3: "NIM_merge_TYPE_INIT3",
-    cfsDebugInit: "NIM_merge_DEBUG_INIT",
-    cfsDynLibInit: "NIM_merge_DYNLIB_INIT",
-    cfsDynLibDeinit: "NIM_merge_DYNLIB_DEINIT",
+    cfsDynLibInit: "NIM_merge_DYNLIB_INIT"
   ]
   CProcSectionNames: array[TCProcSection, string] = [
     cpsLocals: "NIM_merge_PROC_LOCALS",
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
index 5e6456704..6caeb8084 100644
--- a/compiler/ccgreset.nim
+++ b/compiler/ccgreset.nim
@@ -24,10 +24,10 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
   of nkRecCase:
     if (n[0].kind != nkSym): internalError(p.config, n.info, "specializeResetN")
     let disc = n[0].sym
-    if disc.loc.r == "": fillObjectFields(p.module, typ)
+    if disc.loc.snippet == "": fillObjectFields(p.module, typ)
     if disc.loc.t == nil:
       internalError(p.config, n.info, "specializeResetN()")
-    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
     for i in 1..<n.len:
       let branch = n[i]
       assert branch.kind in {nkOfBranch, nkElse}
@@ -38,14 +38,14 @@ proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
       specializeResetN(p, accessor, lastSon(branch), typ)
       lineF(p, cpsStmts, "break;$n", [])
     lineF(p, cpsStmts, "} $n", [])
-    specializeResetT(p, "$1.$2" % [accessor, disc.loc.r], disc.loc.t)
+    specializeResetT(p, "$1.$2" % [accessor, disc.loc.snippet], disc.loc.t)
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
-    if field.loc.r == "": fillObjectFields(p.module, typ)
+    if field.loc.snippet == "": fillObjectFields(p.module, typ)
     if field.loc.t == nil:
       internalError(p.config, n.info, "specializeResetN()")
-    specializeResetT(p, "$1.$2" % [accessor, field.loc.r], field.loc.t)
+    specializeResetT(p, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
   else: internalError(p.config, n.info, "specializeResetN()")
 
 proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
@@ -54,25 +54,23 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
   case typ.kind
   of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
      tySink, tyOwned:
-    specializeResetT(p, accessor, lastSon(typ))
+    specializeResetT(p, accessor, skipModifier(typ))
   of tyArray:
-    let arraySize = lengthOrd(p.config, typ[0])
-    var i: TLoc
-    getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i)
+    let arraySize = lengthOrd(p.config, typ.indexType)
+    var i: TLoc = getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt))
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-            [i.r, arraySize])
-    specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.r]), typ[1])
+            [i.snippet, arraySize])
+    specializeResetT(p, ropecg(p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
     lineF(p, cpsStmts, "}$n", [])
   of tyObject:
-    for i in 0..<typ.len:
-      var x = typ[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      specializeResetT(p, accessor.parentObj(p.module), x)
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    specializeResetT(p, accessor.parentObj(p.module), x)
     if typ.n != nil: specializeResetN(p, accessor, typ.n, typ)
   of tyTuple:
     let typ = getUniqueType(typ)
-    for i in 0..<typ.len:
-      specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), typ[i])
+    for i, a in typ.ikids:
+      specializeResetT(p, ropecg(p.module, "$1.Field$2", [accessor, i]), a)
 
   of tyString, tyRef, tySequence:
     lineCg(p, cpsStmts, "#unsureAsgnRef((void**)&$1, NIM_NIL);$n", [accessor])
@@ -83,7 +81,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
       lineCg(p, cpsStmts, "$1.ClP_0 = NIM_NIL;$n", [accessor])
     else:
       lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
-  of tyChar, tyBool, tyEnum, tyInt..tyUInt64:
+  of tyChar, tyBool, tyEnum, tyRange, tyInt..tyUInt64:
     lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
   of tyCstring, tyPointer, tyPtr, tyVar, tyLent:
     lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor])
@@ -95,12 +93,12 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) =
     of ctInt8, ctInt16, ctInt32, ctInt64:
       lineCg(p, cpsStmts, "$1 = 0;$n", [accessor])
     else:
-      doAssert false, "unexpected set type kind"
-  of {tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
-      tyGenericParam, tyOrdinal, tyRange, tyOpenArray, tyForward, tyVarargs,
-      tyUncheckedArray, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
+      raiseAssert "unexpected set type kind"
+  of tyNone, tyEmpty, tyNil, tyUntyped, tyTyped, tyGenericInvocation,
+      tyGenericParam, tyOrdinal, tyOpenArray, tyForward, tyVarargs,
+      tyUncheckedArray, tyError, tyBuiltInTypeClass, tyUserTypeClass,
       tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot,
-      tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable}:
+      tyAnything, tyStatic, tyFromExpr, tyConcept, tyVoid, tyIterable:
     discard
 
 proc specializeReset(p: BProc, a: TLoc) =
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 7b5f4ff72..883108f2c 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -8,7 +8,6 @@
 #
 
 # included from cgen.nim
-
 const
   RangeExpandLimit = 256      # do not generate ranges
                               # over 'RangeExpandLimit' elements
@@ -36,7 +35,9 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
   if n.kind == nkEmpty:
     result = false
   elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc:
-    if isInvalidReturnType(conf, n[0].typ, true):
+    if n[0].kind == nkSym and sfConstructor in n[0].sym.flags:
+      result = true
+    elif isInvalidReturnType(conf, n[0].typ, true):
       # var v = f()
       # is transformed into: var v;  f(addr v)
       # where 'f' **does not** initialize the result!
@@ -49,6 +50,7 @@ proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} =
     result = true
 
 proc inExceptBlockLen(p: BProc): int =
+  result = 0
   for x in p.nestedTryStmts:
     if x.inExcept: result.inc
 
@@ -70,7 +72,6 @@ template startBlock(p: BProc, start: FormatStr = "{$n",
 proc endBlock(p: BProc)
 
 proc genVarTuple(p: BProc, n: PNode) =
-  var tup, field: TLoc
   if n.kind != nkVarTuple: internalError(p.config, n.info, "genVarTuple")
 
   # if we have a something that's been captured, use the lowering instead:
@@ -82,7 +83,7 @@ proc genVarTuple(p: BProc, n: PNode) =
   # check only the first son
   var forHcr = treatGlobalDifferentlyForHCR(p.module, n[0].sym)
   let hcrCond = if forHcr: getTempName(p.module) else: ""
-  var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]]
+  var hcrGlobals: seq[tuple[loc: TLoc, tp: Rope]] = @[]
   # determine if the tuple is constructed at top-level scope or inside of a block (if/while/block)
   let isGlobalInBlock = forHcr and p.blocks.len > 2
   # do not close and reopen blocks if this is a 'global' but inside of a block (if/while/block)
@@ -94,7 +95,7 @@ proc genVarTuple(p: BProc, n: PNode) =
     startBlock(p)
 
   genLineDir(p, n)
-  initLocExpr(p, n[^1], tup)
+  var tup = initLocExpr(p, n[^1])
   var t = tup.t.skipTypes(abstractInst)
   for i in 0..<n.len-2:
     let vn = n[i]
@@ -107,12 +108,12 @@ proc genVarTuple(p: BProc, n: PNode) =
     else:
       assignLocalVar(p, vn)
       initLocalVar(p, v, immediateAsgn=isAssignedImmediately(p.config, n[^1]))
-    initLoc(field, locExpr, vn, tup.storage)
+    var field = initLoc(locExpr, vn, tup.storage)
     if t.kind == tyTuple:
-      field.r = "$1.Field$2" % [rdLoc(tup), rope(i)]
+      field.snippet = "$1.Field$2" % [rdLoc(tup), rope(i)]
     else:
       if t.n[i].kind != nkSym: internalError(p.config, n.info, "genVarTuple")
-      field.r = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
+      field.snippet = "$1.$2" % [rdLoc(tup), mangleRecFieldName(p.module, t.n[i].sym)]
     putLocIntoDest(p, v.loc, field)
     if forHcr or isGlobalInBlock:
       hcrGlobals.add((loc: v.loc, tp: "NULL"))
@@ -127,7 +128,7 @@ proc genVarTuple(p: BProc, n: PNode) =
     lineCg(p, cpsLocals, "NIM_BOOL $1 = NIM_FALSE;$n", [hcrCond])
     for curr in hcrGlobals:
       lineCg(p, cpsLocals, "$1 |= hcrRegisterGlobal($4, \"$2\", sizeof($3), $5, (void**)&$2);$N",
-              [hcrCond, curr.loc.r, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
+              [hcrCond, curr.loc.snippet, rdLoc(curr.loc), getModuleDllPath(p.module, n[0].sym), curr.tp])
 
 
 proc loadInto(p: BProc, le, ri: PNode, a: var TLoc) {.inline.} =
@@ -168,7 +169,7 @@ proc endBlock(p: BProc, blockEnd: Rope) =
 proc endBlock(p: BProc) =
   let topBlock = p.blocks.len - 1
   let frameLen = p.blocks[topBlock].frameLen
-  var blockEnd: Rope
+  var blockEnd: Rope = ""
   if frameLen > 0:
     blockEnd.addf("FR_.len-=$1;$n", [frameLen.rope])
   if p.blocks[topBlock].label.len != 0:
@@ -243,8 +244,7 @@ proc genGotoState(p: BProc, n: PNode) =
   # switch (x.state) {
   #   case 0: goto STATE0;
   # ...
-  var a: TLoc
-  initLocExpr(p, n[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   lineF(p, cpsStmts, "switch ($1) {$n", [rdLoc(a)])
   p.flags.incl beforeRetNeeded
   lineF(p, cpsStmts, "case -1:$n", [])
@@ -263,15 +263,15 @@ proc genGotoState(p: BProc, n: PNode) =
 
 proc genBreakState(p: BProc, n: PNode, d: var TLoc) =
   var a: TLoc
-  initLoc(d, locExpr, n, OnUnknown)
+  d = initLoc(locExpr, n, OnUnknown)
 
   if n[0].kind == nkClosure:
-    initLocExpr(p, n[0][1], a)
-    d.r = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
+    a = initLocExpr(p, n[0][1])
+    d.snippet = "(((NI*) $1)[1] < 0)" % [rdLoc(a)]
   else:
-    initLocExpr(p, n[0], a)
+    a = initLocExpr(p, n[0])
     # the environment is guaranteed to contain the 'state' field at offset 1:
-    d.r = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
+    d.snippet = "((((NI*) $1.ClE_0)[1]) < 0)" % [rdLoc(a)]
 
 proc genGotoVar(p: BProc; value: PNode) =
   if value.kind notin {nkCharLit..nkUInt64Lit}:
@@ -289,11 +289,46 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode; result: var Rope) =
     #echo "New code produced for ", v.name.s, " ", p.config $ value.info
     genBracedInit(p, value, isConst = false, v.typ, result)
 
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string =
+  result = ""
+  var argsCounter = 0
+  let typ = skipTypes(call[0].typ, abstractInst)
+  assert(typ.kind == tyProc)
+  for i in 1..<call.len:
+    #if it's a type we can just generate here another initializer as we are in an initializer context
+    if call[i].kind == nkCall and call[i][0].kind == nkSym and call[i][0].sym.kind == skType:
+      if argsCounter > 0: result.add ","
+      result.add genCppInitializer(p.module, p, call[i][0].sym.typ, didGenTemp)
+    else:
+      #We need to test for temp in globals, see: #23657
+      let param =
+        if typ[i].kind in {tyVar} and call[i].kind == nkHiddenAddr:
+          call[i][0]
+        else:
+          call[i]
+      if param.kind != nkBracketExpr or param.typ.kind in
+        {tyRef, tyPtr, tyUncheckedArray, tyArray, tyOpenArray,
+          tyVarargs, tySequence, tyString, tyCstring, tyTuple}:
+        let tempLoc = initLocExprSingleUse(p, param)
+        didGenTemp = didGenTemp or tempLoc.k == locTemp
+      genOtherArg(p, call, i, typ, result, argsCounter)
+
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope, didGenTemp: var bool) =
+  let params = genCppParamsForCtor(p, call, didGenTemp)
+  if params.len == 0:
+    decl = runtimeFormat("$#;\n", [decl])
+  else:
+    decl = runtimeFormat("$#($#);\n", [decl, params])
+
 proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
   if sfGoto in v.flags:
     # translate 'var state {.goto.} = X' into 'goto LX':
     genGotoVar(p, value)
     return
+  let imm = isAssignedImmediately(p.config, value)
+  let isCppCtorCall = p.module.compileToCpp and imm and
+    value.kind in nkCallKinds and value[0].kind == nkSym and
+    v.typ.kind != tyPtr and sfConstructor in value[0].sym.flags
   var targetProc = p
   var valueAsRope = ""
   potentialValueInit(p, v, value, valueAsRope)
@@ -305,7 +340,18 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     if sfPure in v.flags:
       # v.owner.kind != skModule:
       targetProc = p.module.preInitProc
-    assignGlobalVar(targetProc, vn, valueAsRope)
+    if isCppCtorCall and not containsHiddenPointer(v.typ):
+      var didGenTemp = false
+      callGlobalVarCppCtor(targetProc, v, vn, value, didGenTemp)
+      if didGenTemp:
+        message(p.config, vn.info, warnGlobalVarConstructorTemporary, vn.sym.name.s)
+        #We fail to call the constructor in the global scope so we do the call inside the main proc
+        assignGlobalVar(targetProc, vn, valueAsRope)
+        var loc = initLocExprSingleUse(targetProc, value)
+        genAssignment(targetProc, v.loc, loc, {})
+    else:
+      assignGlobalVar(targetProc, vn, valueAsRope)
+
     # XXX: be careful here.
     # Global variables should not be zeromem-ed within loops
     # (see bug #20).
@@ -314,13 +360,12 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     # global variables will be initialized to zero.
     if valueAsRope.len == 0:
       var loc = v.loc
-
       # When the native TLS is unavailable, a global thread-local variable needs
       # one more layer of indirection in order to access the TLS block.
       # Only do this for complex types that may need a call to `objectInit`
       if sfThread in v.flags and emulatedThreadVars(p.config) and
         isComplexValueType(v.typ):
-        initLocExprSingleUse(p.module.preInitProc, vn, loc)
+        loc = initLocExprSingleUse(p.module.preInitProc, vn)
       genObjectInit(p.module.preInitProc, cpsInit, v.typ, loc, constructObj)
     # Alternative construction using default constructor (which may zeromem):
     # if sfImportc notin v.flags: constructLoc(p.module.preInitProc, v.loc)
@@ -328,31 +373,25 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
       genVarPrototype(p.module.g.generatedHeader, vn)
     registerTraverseProc(p, v)
   else:
-    let imm = isAssignedImmediately(p.config, value)
     if imm and p.module.compileToCpp and p.splitDecls == 0 and
-        not containsHiddenPointer(v.typ):
+        not containsHiddenPointer(v.typ) and
+        nimErrorFlagAccessed notin p.flags:
       # C++ really doesn't like things like 'Foo f; f = x' as that invokes a
       # parameterless constructor followed by an assignment operator. So we
       # generate better code here: 'Foo f = x;'
       genLineDir(p, vn)
-      let decl = localVarDecl(p, vn)
+      var decl = localVarDecl(p, vn)
       var tmp: TLoc
-      if value.kind in nkCallKinds and value[0].kind == nkSym and
-           sfConstructor in value[0].sym.flags:
-        var params = newRopeAppender()
-        var argsCounter = 0
-        let typ = skipTypes(value[0].typ, abstractInst)
-        assert(typ.kind == tyProc)
-        for i in 1..<value.len:
-          assert(typ.len == typ.n.len)
-          genOtherArg(p, value, i, typ, params, argsCounter)
-        if params.len == 0:
-          lineF(p, cpsStmts, "$#;$n", [decl])
-        else:
-          lineF(p, cpsStmts, "$#($#);$n", [decl, params])
+      if isCppCtorCall:
+        var didGenTemp = false
+        genCppVarForCtor(p, value, decl, didGenTemp)
+        line(p, cpsStmts, decl)
       else:
-        initLocExprSingleUse(p, value, tmp)
-        lineF(p, cpsStmts, "$# = $#;$n", [decl, tmp.rdLoc])
+        tmp = initLocExprSingleUse(p, value)
+        if value.kind == nkEmpty:
+          lineF(p, cpsStmts, "$#;\n", [decl])
+        else:
+          lineF(p, cpsStmts, "$# = $#;\n", [decl, tmp.rdLoc])
       return
     assignLocalVar(p, vn)
     initLocalVar(p, v, imm)
@@ -366,7 +405,7 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
     # put it in the locals section - mainly because of loops which
     # use the var in a call to resetLoc() in the statements section
     lineCg(targetProc, cpsLocals, "hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1);$n",
-           [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
     # nothing special left to do later on - let's avoid closing and reopening blocks
     forHcr = false
 
@@ -375,11 +414,12 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) =
   # be able to re-run it but without the top level code - just the init of globals
   if forHcr:
     lineCg(targetProc, cpsStmts, "if (hcrRegisterGlobal($3, \"$1\", sizeof($2), $4, (void**)&$1))$N",
-           [v.loc.r, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
+           [v.loc.snippet, rdLoc(v.loc), getModuleDllPath(p.module, v), traverseProc])
     startBlock(targetProc)
   if value.kind != nkEmpty and valueAsRope.len == 0:
     genLineDir(targetProc, vn)
-    loadInto(targetProc, vn, value, v.loc)
+    if not isCppCtorCall:
+      loadInto(targetProc, vn, value, v.loc)
   if forHcr:
     endBlock(targetProc)
 
@@ -396,8 +436,7 @@ proc genSingleVar(p: BProc, a: PNode) =
 
 proc genClosureVar(p: BProc, a: PNode) =
   var immediateAsgn = a[2].kind != nkEmpty
-  var v: TLoc
-  initLocExpr(p, a[0], v)
+  var v: TLoc = initLocExpr(p, a[0])
   genLineDir(p, a)
   if immediateAsgn:
     loadInto(p, a[0], a[2], v)
@@ -432,7 +471,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
     a: TLoc
     lelse: TLabel
   if not isEmptyType(n.typ) and d.k == locNone:
-    getTemp(p, n.typ, d)
+    d = getTemp(p, n.typ)
   genLineDir(p, n)
   let lend = getLabel(p)
   for it in n.sons:
@@ -440,7 +479,7 @@ proc genIf(p: BProc, n: PNode, d: var TLoc) =
     if d.k == locTemp and isEmptyType(n.typ): d.k = locNone
     if it.len == 2:
       startBlock(p)
-      initLocExprSingleUse(p, it[0], a)
+      a = initLocExprSingleUse(p, it[0])
       lelse = getLabel(p)
       inc(p.labels)
       lineF(p, cpsStmts, "if (!$1) goto $2;$n",
@@ -508,7 +547,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
   # wrapped inside stmt lists by inject destructors won't be recognised
   let n = n.flattenStmts()
   var casePos = -1
-  var arraySize: int
+  var arraySize: int = 0
   for i in 0..<n.len:
     let it = n[i]
     if it.kind == nkCaseStmt:
@@ -542,8 +581,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
     genStmts(p, n[j])
 
   let caseStmt = n[casePos]
-  var a: TLoc
-  initLocExpr(p, caseStmt[0], a)
+  var a: TLoc = initLocExpr(p, caseStmt[0])
   # first goto:
   lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
 
@@ -581,8 +619,7 @@ proc genComputedGoto(p: BProc; n: PNode) =
       else:
         genStmts(p, it)
 
-    var a: TLoc
-    initLocExpr(p, caseStmt[0], a)
+    var a: TLoc = initLocExpr(p, caseStmt[0])
     lineF(p, cpsStmts, "goto *$#[$#];$n", [tmp, a.rdLoc])
     endBlock(p)
 
@@ -610,11 +647,11 @@ proc genWhileStmt(p: BProc, t: PNode) =
     else:
       p.breakIdx = startBlock(p, "while (1) {$n")
       p.blocks[p.breakIdx].isLoop = true
-      initLocExpr(p, t[0], a)
+      a = initLocExpr(p, t[0])
       if (t[0].kind != nkIntLit) or (t[0].intVal == 0):
         lineF(p, cpsStmts, "if (!$1) goto ", [rdLoc(a)])
         assignLabel(p.blocks[p.breakIdx], p.s(cpsStmts))
-        lineF(p, cpsStmts, ";$n", [])
+        appcg(p, cpsStmts, ";$n", [])
       genStmts(p, loopBody)
 
       if optProfiler in p.options:
@@ -629,7 +666,7 @@ proc genBlock(p: BProc, n: PNode, d: var TLoc) =
     # bug #4505: allocate the temp in the outer scope
     # so that it can escape the generated {}:
     if d.k == locNone:
-      getTemp(p, n.typ, d)
+      d = getTemp(p, n.typ)
     d.flags.incl(lfEnforceDeref)
   preserveBreakIdx:
     p.breakIdx = startBlock(p)
@@ -649,14 +686,13 @@ proc genParForStmt(p: BProc, t: PNode) =
 
   preserveBreakIdx:
     let forLoopVar = t[0].sym
-    var rangeA, rangeB: TLoc
     assignLocalVar(p, t[0])
     #initLoc(forLoopVar.loc, locLocalVar, forLoopVar.typ, onStack)
     #discard mangleName(forLoopVar)
     let call = t[1]
-    assert(call.len in {4, 5})
-    initLocExpr(p, call[1], rangeA)
-    initLocExpr(p, call[2], rangeB)
+    assert(call.len == 4 or call.len == 5)
+    var rangeA = initLocExpr(p, call[1])
+    var rangeB = initLocExpr(p, call[2])
 
     # $n at the beginning because of #9710
     if call.len == 4: # procName(a, b, annotation)
@@ -673,8 +709,7 @@ proc genParForStmt(p: BProc, t: PNode) =
                     rangeA.rdLoc, rangeB.rdLoc,
                     call[3].getStr.rope])
     else: # `||`(a, b, step, annotation)
-      var step: TLoc
-      initLocExpr(p, call[3], step)
+      var step: TLoc = initLocExpr(p, call[3])
       lineF(p, cpsStmts, "$n#pragma omp $5$n" &
                     "for ($1 = $2; $1 <= $3; $1 += $4)",
                     [forLoopVar.loc.rdLoc,
@@ -720,6 +755,18 @@ proc raiseExit(p: BProc) =
       lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) goto LA$1_;$n",
         [p.nestedTryStmts[^1].label])
 
+proc raiseExitCleanup(p: BProc, destroy: string) =
+  assert p.config.exc == excGoto
+  if nimErrorFlagDisabled notin p.flags:
+    p.flags.incl nimErrorFlagAccessed
+    if p.nestedTryStmts.len == 0:
+      p.flags.incl beforeRetNeeded
+      # easy case, simply goto 'ret':
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$1; goto BeforeRet_;}$n", [destroy])
+    else:
+      lineCg(p, cpsStmts, "if (NIM_UNLIKELY(*nimErr_)) {$2; goto LA$1_;}$n",
+        [p.nestedTryStmts[^1].label, destroy])
+
 proc finallyActions(p: BProc) =
   if p.config.exc != excGoto and p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept:
     # if the current try stmt have a finally block,
@@ -744,16 +791,19 @@ proc raiseInstr(p: BProc; result: var Rope) =
 
 proc genRaiseStmt(p: BProc, t: PNode) =
   if t[0].kind != nkEmpty:
-    var a: TLoc
-    initLocExprSingleUse(p, t[0], a)
+    var a: TLoc = initLocExprSingleUse(p, t[0])
     finallyActions(p)
     var e = rdLoc(a)
     discard getTypeDesc(p.module, t[0].typ)
     var typ = skipTypes(t[0].typ, abstractPtrs)
-    # XXX For reasons that currently escape me, this is only required by the new
-    # C++ based exception handling:
-    if p.config.exc == excCpp:
+    case p.config.exc
+    of excCpp:
       blockLeaveActions(p, howManyTrys = 0, howManyExcepts = p.inExceptBlockLen)
+    of excGoto:
+      blockLeaveActions(p, howManyTrys = 0,
+        howManyExcepts = (if p.nestedTryStmts.len > 0 and p.nestedTryStmts[^1].inExcept: 1 else: 0))
+    else:
+      discard
     genLineDir(p, t)
     if isImportedException(typ, p.config):
       lineF(p, cpsStmts, "throw $1;$n", [e])
@@ -767,11 +817,7 @@ proc genRaiseStmt(p: BProc, t: PNode) =
   else:
     finallyActions(p)
     genLineDir(p, t)
-    # reraise the last exception:
-    if p.config.exc == excCpp:
-      line(p, cpsStmts, "throw;\n")
-    else:
-      linefmt(p, cpsStmts, "#reraiseException();$n", [])
+    linefmt(p, cpsStmts, "#reraiseException();$n", [])
   raiseInstr(p, p.s(cpsStmts))
 
 template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
@@ -779,12 +825,12 @@ template genCaseGenericBranch(p: BProc, b: PNode, e: TLoc,
   var x, y: TLoc
   for i in 0..<b.len - 1:
     if b[i].kind == nkRange:
-      initLocExpr(p, b[i][0], x)
-      initLocExpr(p, b[i][1], y)
+      x = initLocExpr(p, b[i][0])
+      y = initLocExpr(p, b[i][1])
       lineCg(p, cpsStmts, rangeFormat,
            [rdCharLoc(e), rdCharLoc(x), rdCharLoc(y), labl])
     else:
-      initLocExpr(p, b[i], x)
+      x = initLocExpr(p, b[i])
       lineCg(p, cpsStmts, eqFormat, [rdCharLoc(e), rdCharLoc(x), labl])
 
 proc genCaseSecondPass(p: BProc, t: PNode, d: var TLoc,
@@ -826,8 +872,7 @@ template genIfForCaseUntil(p: BProc, t: PNode, d: var TLoc,
 
 template genCaseGeneric(p: BProc, t: PNode, d: var TLoc,
                     rangeFormat, eqFormat: FormatStr) =
-  var a: TLoc
-  initLocExpr(p, t[0], a)
+  var a: TLoc = initLocExpr(p, t[0])
   var lend = genIfForCaseUntil(p, t, d, rangeFormat, eqFormat, t.len-1, a)
   fixLabel(p, lend)
 
@@ -837,8 +882,8 @@ proc genCaseStringBranch(p: BProc, b: PNode, e: TLoc, labl: TLabel,
   var x: TLoc
   for i in 0..<b.len - 1:
     assert(b[i].kind != nkRange)
-    initLocExpr(p, b[i], x)
-    var j: int
+    x = initLocExpr(p, b[i])
+    var j: int = 0
     case b[i].kind
     of nkStrLit..nkTripleStrLit:
       j = int(hashString(p.config, b[i].strVal) and high(branches))
@@ -861,8 +906,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
     var bitMask = math.nextPowerOfTwo(strings) - 1
     var branches: seq[Rope]
     newSeq(branches, bitMask + 1)
-    var a: TLoc
-    initLocExpr(p, t[0], a) # fist pass: generate ifs+goto:
+    var a: TLoc = initLocExpr(p, t[0]) # first pass: generate ifs+goto:
     var labId = p.labels
     for i in 1..<t.len:
       inc(p.labels)
@@ -898,6 +942,7 @@ proc genStringCase(p: BProc, t: PNode, stringKind: TTypeKind, d: var TLoc) =
       genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
 
 proc branchHasTooBigRange(b: PNode): bool =
+  result = false
   for it in b:
     # last son is block
     if (it.kind == nkRange) and
@@ -905,6 +950,7 @@ proc branchHasTooBigRange(b: PNode): bool =
       return true
 
 proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
+  result = 0
   for i in 1..<n.len:
     var branch = n[i]
     var stmtBlock = lastSon(branch)
@@ -940,8 +986,7 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
   var splitPoint = ifSwitchSplitPoint(p, n)
 
   # generate if part (might be empty):
-  var a: TLoc
-  initLocExpr(p, n[0], a)
+  var a: TLoc = initLocExpr(p, n[0])
   var lend = if splitPoint > 0: genIfForCaseUntil(p, n, d,
                     rangeFormat = "if ($1 >= $2 && $1 <= $3) goto $4;$n",
                     eqFormat = "if ($1 == $2) goto $3;$n",
@@ -963,15 +1008,18 @@ proc genOrdinalCase(p: BProc, n: PNode, d: var TLoc) =
         hasDefault = true
       exprBlock(p, branch.lastSon, d)
       lineF(p, cpsStmts, "break;$n", [])
-    if (hasAssume in CC[p.config.cCompiler].props) and not hasDefault:
-      lineF(p, cpsStmts, "default: __assume(0);$n", [])
+    if not hasDefault:
+      if hasBuiltinUnreachable in CC[p.config.cCompiler].props:
+        lineF(p, cpsStmts, "default: __builtin_unreachable();$n", [])
+      elif hasAssume in CC[p.config.cCompiler].props:
+        lineF(p, cpsStmts, "default: __assume(0);$n", [])
     lineF(p, cpsStmts, "}$n", [])
   if lend != "": fixLabel(p, lend)
 
 proc genCase(p: BProc, t: PNode, d: var TLoc) =
   genLineDir(p, t)
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
   case skipTypes(t[0].typ, abstractVarRange).kind
   of tyString:
     genStringCase(p, t, tyString, d)
@@ -997,7 +1045,7 @@ proc genRestoreFrameAfterException(p: BProc) =
 proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   #[ code to generate:
 
-    std::exception_ptr error = nullptr;
+    std::exception_ptr error;
     try {
       body;
     } catch (Exception e) {
@@ -1022,13 +1070,13 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   p.module.includeHeader("<exception>")
 
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
   genLineDir(p, t)
 
   inc(p.labels, 2)
   let etmp = p.labels
-
-  p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp]))
+  #init on locals, fixes #23306
+  lineCg(p, cpsLocals, "std::exception_ptr T$1_;$n", [etmp])
 
   let fin = if t[^1].kind == nkFinally: t[^1] else: nil
   p.nestedTryStmts.add((fin, false, 0.Natural))
@@ -1062,7 +1110,6 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
       if hasIf: lineF(p, cpsStmts, "else ", [])
       startBlock(p)
       # we handled the error:
-      linefmt(p, cpsStmts, "T$1_ = nullptr;$n", [etmp])
       expr(p, t[i][0], d)
       linefmt(p, cpsStmts, "#popCurrentException();$n", [])
       endBlock(p)
@@ -1079,12 +1126,13 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
           hasImportedCppExceptions = true
         else:
           if orExpr.len != 0: orExpr.add("||")
-          let checkFor = if optTinyRtti in p.config.globalOptions:
-            genTypeInfo2Name(p.module, typeNode.typ)
-          else:
-            genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
           let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
-          appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+          if optTinyRtti in p.config.globalOptions:
+            let checkFor = $getObjDepth(typeNode.typ)
+            appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))])
+          else:
+            let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info)
+            appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
 
       if orExpr.len != 0:
         if hasIf:
@@ -1124,7 +1172,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
 
       if t[i].len == 1:
         # general except section:
-        startBlock(p, "catch (...) {", [])
+        startBlock(p, "catch (...) {$n", [])
         genExceptBranchBody(t[i][0])
         endBlock(p)
         catchAllPresent = true
@@ -1150,7 +1198,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
   # general finally block:
   if t.len > 0 and t[^1].kind == nkFinally:
     if not catchAllPresent:
-      startBlock(p, "catch (...) {", [])
+      startBlock(p, "catch (...) {$n", [])
       genRestoreFrameAfterException(p)
       linefmt(p, cpsStmts, "T$1_ = std::current_exception();$n", [etmp])
       endBlock(p)
@@ -1187,7 +1235,7 @@ proc genTryCppOld(p: BProc, t: PNode, d: var TLoc) =
     expr(p, body, d)
 
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
   genLineDir(p, t)
   cgsym(p.module, "popCurrentExceptionEx")
   let fin = if t[^1].kind == nkFinally: t[^1] else: nil
@@ -1265,7 +1313,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
   p.flags.incl nimErrorFlagAccessed
 
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
 
   expr(p, t[0], d)
 
@@ -1297,12 +1345,14 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) =
       for j in 0..<t[i].len - 1:
         assert(t[i][j].kind == nkType)
         if orExpr.len != 0: orExpr.add("||")
-        let checkFor = if optTinyRtti in p.config.globalOptions:
-          genTypeInfo2Name(p.module, t[i][j].typ)
-        else:
-          genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
         let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
-        appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+        if optTinyRtti in p.config.globalOptions:
+          let checkFor = $getObjDepth(t[i][j].typ)
+          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
+            [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
+        else:
+          let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
+          appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
 
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
@@ -1370,7 +1420,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
   #    propagateCurrentException();
   #
   if not isEmptyType(t.typ) and d.k == locNone:
-    getTemp(p, t.typ, d)
+    d = getTemp(p, t.typ)
   let quirkyExceptions = p.config.exc == excQuirky or
       (t.kind == nkHiddenTryStmt and sfSystemModule in p.module.module.flags)
   if not quirkyExceptions:
@@ -1379,7 +1429,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
     p.flags.incl noSafePoints
   genLineDir(p, t)
   cgsym(p.module, "Exception")
-  var safePoint: Rope
+  var safePoint: Rope = ""
   if not quirkyExceptions:
     safePoint = getTempName(p.module)
     linefmt(p, cpsLocals, "#TSafePoint $1;$n", [safePoint])
@@ -1441,12 +1491,14 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
       for j in 0..<t[i].len - 1:
         assert(t[i][j].kind == nkType)
         if orExpr.len != 0: orExpr.add("||")
-        let checkFor = if optTinyRtti in p.config.globalOptions:
-          genTypeInfo2Name(p.module, t[i][j].typ)
-        else:
-          genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
         let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type"
-        appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
+        if optTinyRtti in p.config.globalOptions:
+          let checkFor = $getObjDepth(t[i][j].typ)
+          appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)",
+              [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))])
+        else:
+          let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info)
+          appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor])
 
       if i > 1: line(p, cpsStmts, "else ")
       startBlock(p, "if ($1) {$n", [orExpr])
@@ -1474,28 +1526,31 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
 
 proc genAsmOrEmitStmt(p: BProc, t: PNode, isAsmStmt=false; result: var Rope) =
   var res = ""
-  for it in t.sons:
+  let offset =
+    if isAsmStmt: 1 # first son is pragmas
+    else: 0
+
+  for i in offset..<t.len:
+    let it = t[i]
     case it.kind
     of nkStrLit..nkTripleStrLit:
       res.add(it.strVal)
     of nkSym:
       var sym = it.sym
       if sym.kind in {skProc, skFunc, skIterator, skMethod}:
-        var a: TLoc
-        initLocExpr(p, it, a)
+        var a: TLoc = initLocExpr(p, it)
         res.add($rdLoc(a))
       elif sym.kind == skType:
         res.add($getTypeDesc(p.module, sym.typ))
       else:
         discard getTypeDesc(p.module, skipTypes(sym.typ, abstractPtrs))
         fillBackendName(p.module, sym)
-        res.add($sym.loc.r)
+        res.add($sym.loc.snippet)
     of nkTypeOfExpr:
       res.add($getTypeDesc(p.module, it.typ))
     else:
       discard getTypeDesc(p.module, skipTypes(it.typ, abstractPtrs))
-      var a: TLoc
-      initLocExpr(p, it, a)
+      var a: TLoc = initLocExpr(p, it)
       res.add($a.rdLoc)
 
   if isAsmStmt and hasGnuAsm in CC[p.config.cCompiler].props:
@@ -1520,6 +1575,21 @@ proc genAsmStmt(p: BProc, t: PNode) =
   assert(t.kind == nkAsmStmt)
   genLineDir(p, t)
   var s = newRopeAppender()
+
+  var asmSyntax = ""
+  if (let p = t[0]; p.kind == nkPragma):
+    for i in p:
+      if whichPragma(i) == wAsmSyntax:
+        asmSyntax = i[1].strVal
+
+  if asmSyntax != "" and
+     not (
+      asmSyntax == "gcc" and hasGnuAsm in CC[p.config.cCompiler].props or
+      asmSyntax == "vcc" and hasGnuAsm notin CC[p.config.cCompiler].props):
+    localError(
+      p.config, t.info,
+      "Your compiler does not support the specified inline assembler")
+
   genAsmOrEmitStmt(p, t, isAsmStmt=true, s)
   # see bug #2362, "top level asm statements" seem to be a mis-feature
   # but even if we don't do this, the example in #2362 cannot possibly
@@ -1535,7 +1605,7 @@ proc determineSection(n: PNode): TCFileSection =
   result = cfsProcHeaders
   if n.len >= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}:
     let sec = n[0].strVal
-    if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes
+    if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND
     elif sec.startsWith("/*VARSECTION*/"): result = cfsVars
     elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders
 
@@ -1552,9 +1622,14 @@ proc genEmit(p: BProc, t: PNode) =
     line(p, cpsStmts, s)
 
 proc genPragma(p: BProc, n: PNode) =
-  for it in n.sons:
+  for i in 0..<n.len:
+    let it = n[i]
     case whichPragma(it)
     of wEmit: genEmit(p, it)
+    of wPush:
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
+    of wPop:
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
 
@@ -1572,6 +1647,8 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType,
         "#FieldDiscriminantCheck((NI)(NU)($1), (NI)(NU)($2), $3, $4);$n",
         [rdLoc(a), rdLoc(tmp), discriminatorTableName(p.module, t, field),
          lit])
+  if p.config.exc == excGoto:
+    raiseExit(p)
 
 when false:
   proc genCaseObjDiscMapping(p: BProc, e: PNode, t: PType, field: PSym; d: var TLoc) =
@@ -1590,13 +1667,12 @@ when false:
     expr(p, call, d)
 
 proc asgnFieldDiscriminant(p: BProc, e: PNode) =
-  var a, tmp: TLoc
   var dotExpr = e[0]
   if dotExpr.kind == nkCheckedFieldExpr: dotExpr = dotExpr[0]
-  initLocExpr(p, e[0], a)
-  getTemp(p, a.t, tmp)
+  var a = initLocExpr(p, e[0])
+  var tmp: TLoc = getTemp(p, a.t)
   expr(p, e[1], tmp)
-  if optTinyRtti notin p.config.globalOptions and p.inUncheckedAssignSection == 0:
+  if p.inUncheckedAssignSection == 0:
     let field = dotExpr[1].sym
     genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field)
     message(p.config, e.info, warnCaseTransition)
@@ -1612,11 +1688,11 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
   else:
     let le = e[0]
     let ri = e[1]
-    var a: TLoc
-    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar)
-    initLoc(a, locNone, le, OnUnknown)
+    var a: TLoc = initLoc(locNone, le, OnUnknown)
+    discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), dkVar)
     a.flags.incl(lfEnforceDeref)
     a.flags.incl(lfPrepareForMutation)
+    genLineDir(p, le) # it can be a nkBracketExpr, which may raise
     expr(p, le, a)
     a.flags.excl(lfPrepareForMutation)
     if fastAsgn: incl(a.flags, lfNoDeepCopy)
@@ -1625,7 +1701,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) =
     loadInto(p, le, ri, a)
 
 proc genStmts(p: BProc, t: PNode) =
-  var a: TLoc
+  var a: TLoc = default(TLoc)
 
   let isPush = p.config.hasHint(hintExtendedContext)
   if isPush: pushInfoContext(p.config, t.info)
diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim
index abf830b57..1f551f022 100644
--- a/compiler/ccgthreadvars.nim
+++ b/compiler/ccgthreadvars.nim
@@ -30,7 +30,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
     # allocator for it :-(
     if not containsOrIncl(m.g.nimtvDeclared, s.id):
       m.g.nimtvDeps.add(s.loc.t)
-      m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.r])
+      m.g.nimtv.addf("$1 $2;$n", [getTypeDesc(m, s.loc.t), s.loc.snippet])
   else:
     if isExtern: m.s[cfsVars].add("extern ")
     elif lfExportLib in s.loc.flags: m.s[cfsVars].add("N_LIB_EXPORT_VAR ")
@@ -41,7 +41,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) =
         m.s[cfsVars].add("NIM_THREAD_LOCAL ")
       else: m.s[cfsVars].add("NIM_THREADVAR ")
     m.s[cfsVars].add(getTypeDesc(m, s.loc.t))
-    m.s[cfsVars].addf(" $1;$n", [s.loc.r])
+    m.s[cfsVars].addf(" $1;$n", [s.loc.snippet])
 
 proc generateThreadLocalStorage(m: BModule) =
   if m.g.nimtv != "" and (usesThreadVars in m.flags or sfMainModule in m.module.flags):
diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim
index 96f5869b0..ed4c79d9a 100644
--- a/compiler/ccgtrav.nim
+++ b/compiler/ccgtrav.nim
@@ -21,7 +21,7 @@ const
 
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType)
 proc genCaseRange(p: BProc, branch: PNode)
-proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false)
+proc getTemp(p: BProc, t: PType, needsInit=false): TLoc
 
 proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
                      typ: PType) =
@@ -34,10 +34,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
     if (n[0].kind != nkSym): internalError(c.p.config, n.info, "genTraverseProc")
     var p = c.p
     let disc = n[0].sym
-    if disc.loc.r == "": fillObjectFields(c.p.module, typ)
+    if disc.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if disc.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.r])
+    lineF(p, cpsStmts, "switch ($1.$2) {$n", [accessor, disc.loc.snippet])
     for i in 1..<n.len:
       let branch = n[i]
       assert branch.kind in {nkOfBranch, nkElse}
@@ -51,10 +51,10 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, n: PNode;
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
-    if field.loc.r == "": fillObjectFields(c.p.module, typ)
+    if field.loc.snippet == "": fillObjectFields(c.p.module, typ)
     if field.loc.t == nil:
       internalError(c.p.config, n.info, "genTraverseProc()")
-    genTraverseProc(c, "$1.$2" % [accessor, field.loc.r], field.loc.t)
+    genTraverseProc(c, "$1.$2" % [accessor, field.loc.snippet], field.loc.t)
   else: internalError(c.p.config, n.info, "genTraverseProc()")
 
 proc parentObj(accessor: Rope; m: BModule): Rope {.inline.} =
@@ -71,38 +71,36 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
   case typ.kind
   of tyGenericInst, tyGenericBody, tyTypeDesc, tyAlias, tyDistinct, tyInferred,
      tySink, tyOwned:
-    genTraverseProc(c, accessor, lastSon(typ))
+    genTraverseProc(c, accessor, skipModifier(typ))
   of tyArray:
-    let arraySize = lengthOrd(c.p.config, typ[0])
-    var i: TLoc
-    getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
+    let arraySize = lengthOrd(c.p.config, typ.indexType)
+    var i: TLoc = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
     var oldCode = p.s(cpsStmts)
     freeze oldCode
     linefmt(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-            [i.r, arraySize])
+            [i.snippet, arraySize])
     let oldLen = p.s(cpsStmts).len
-    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.r]), typ[1])
+    genTraverseProc(c, ropecg(c.p.module, "$1[$2]", [accessor, i.snippet]), typ.elementType)
     if p.s(cpsStmts).len == oldLen:
       # do not emit dummy long loops for faster debug builds:
       p.s(cpsStmts) = oldCode
     else:
       lineF(p, cpsStmts, "}$n", [])
   of tyObject:
-    for i in 0..<typ.len:
-      var x = typ[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      genTraverseProc(c, accessor.parentObj(c.p.module), x)
+    var x = typ.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    genTraverseProc(c, accessor.parentObj(c.p.module), x)
     if typ.n != nil: genTraverseProc(c, accessor, typ.n, typ)
   of tyTuple:
     let typ = getUniqueType(typ)
-    for i in 0..<typ.len:
-      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), typ[i])
+    for i, a in typ.ikids:
+      genTraverseProc(c, ropecg(c.p.module, "$1.Field$2", [accessor, i]), a)
   of tyRef:
     lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
   of tySequence:
     if optSeqDestructors notin c.p.module.config.globalOptions:
       lineCg(p, cpsStmts, visitorFrmt, [accessor, c.visitorFrmt])
-    elif containsGarbageCollectedRef(typ.lastSon):
+    elif containsGarbageCollectedRef(typ.elementType):
       # destructor based seqs are themselves not traced but their data is, if
       # they contain a GC'ed type:
       lineCg(p, cpsStmts, "#nimGCvisitSeq((void*)$1, $2);$n", [accessor, c.visitorFrmt])
@@ -119,17 +117,15 @@ proc genTraverseProc(c: TTraversalClosure, accessor: Rope, typ: PType) =
 proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
   var p = c.p
   assert typ.kind == tySequence
-  var i: TLoc
-  getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt), i)
+  var i = getTemp(p, getSysType(c.p.module.g.graph, unknownLineInfo, tyInt))
   var oldCode = p.s(cpsStmts)
   freeze oldCode
-  var a: TLoc
-  a.r = accessor
+  var a = TLoc(snippet: accessor)
 
   lineF(p, cpsStmts, "for ($1 = 0; $1 < $2; $1++) {$n",
-      [i.r, lenExpr(c.p, a)])
+      [i.snippet, lenExpr(c.p, a)])
   let oldLen = p.s(cpsStmts).len
-  genTraverseProc(c, "$1$3[$2]" % [accessor, i.r, dataField(c.p)], typ[0])
+  genTraverseProc(c, "$1$3[$2]" % [accessor, i.snippet, dataField(c.p)], typ.elementType)
   if p.s(cpsStmts).len == oldLen:
     # do not emit dummy long loops for faster debug builds:
     p.s(cpsStmts) = oldCode
@@ -137,7 +133,6 @@ proc genTraverseProcSeq(c: TTraversalClosure, accessor: Rope, typ: PType) =
     lineF(p, cpsStmts, "}$n", [])
 
 proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
-  var c: TTraversalClosure
   var p = newProc(nil, m)
   result = "Marker_" & getTypeName(m, origTyp, sig)
   let
@@ -150,18 +145,19 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
   lineF(p, cpsLocals, "$1 a;$n", [t])
   lineF(p, cpsInit, "a = ($1)p;$n", [t])
 
-  c.p = p
-  c.visitorFrmt = "op" # "#nimGCvisit((void*)$1, op);$n"
+  var c = TTraversalClosure(p: p,
+    visitorFrmt: "op" # "#nimGCvisit((void*)$1, op);$n"
+    )
 
   assert typ.kind != tyTypeDesc
   if typ.kind == tySequence:
     genTraverseProcSeq(c, "a".rope, typ)
   else:
-    if skipTypes(typ[0], typedescInst+{tyOwned}).kind == tyArray:
+    if skipTypes(typ.elementType, typedescInst+{tyOwned}).kind == tyArray:
       # C's arrays are broken beyond repair:
-      genTraverseProc(c, "a".rope, typ[0])
+      genTraverseProc(c, "a".rope, typ.elementType)
     else:
-      genTraverseProc(c, "(*a)".rope, typ[0])
+      genTraverseProc(c, "(*a)".rope, typ.elementType)
 
   let generatedProc = "$1 {$n$2$3$4}\n" %
         [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)]
@@ -177,7 +173,6 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope =
 proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
   discard genTypeInfoV1(m, s.loc.t, info)
 
-  var c: TTraversalClosure
   var p = newProc(nil, m)
   var sLoc = rdLoc(s.loc)
   result = getTempName(m)
@@ -186,8 +181,10 @@ proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope =
     accessThreadLocalVar(p, s)
     sLoc = "NimTV_->" & sLoc
 
-  c.visitorFrmt = "0" # "#nimGCvisit((void*)$1, 0);$n"
-  c.p = p
+  var c = TTraversalClosure(p: p,
+    visitorFrmt: "0" # "#nimGCvisit((void*)$1, 0);$n"
+  )
+
   let header = "static N_NIMCALL(void, $1)(void)" % [result]
   genTraverseProc(c, sLoc, s.loc.t)
 
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 1aa8292bd..2c2556336 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -11,7 +11,29 @@
 
 # ------------------------- Name Mangling --------------------------------
 
-import sighashes, modulegraphs
+import sighashes, modulegraphs, std/strscans
+import ../dist/checksums/src/checksums/md5
+import std/sequtils
+
+type
+  TypeDescKind = enum
+    dkParam #skParam
+    dkRefParam #param passed by ref when {.byref.} is used. Cpp only. C goes straight to dkParam and is handled as a regular pointer
+    dkRefGenericParam #param passed by ref when {.byref.} is used that is also a generic. Cpp only. C goes straight to dkParam and is handled as a regular pointer
+    dkVar #skVar
+    dkField #skField
+    dkResult #skResult
+    dkConst #skConst
+    dkOther #skType, skTemp, skLet and skForVar so far
+
+proc descKindFromSymKind(kind: TSymKind): TypeDescKind =
+  case kind
+  of skParam: dkParam
+  of skVar: dkVar
+  of skField: dkField
+  of skResult: dkResult
+  of skConst: dkConst
+  else: dkOther
 
 proc isKeyword(w: PIdent): bool =
   # Nim and C++ share some keywords
@@ -33,25 +55,39 @@ proc mangleField(m: BModule; name: PIdent): string =
   if isKeyword(name):
     result.add "_0"
 
+proc mangleProc(m: BModule; s: PSym; makeUnique: bool): string =
+  result = "_Z"  # Common prefix in Itanium ABI
+  result.add encodeSym(m, s, makeUnique)
+  if s.typ.len > 1: #we dont care about the return param
+    for i in 1..<s.typ.len:
+      if s.typ[i].isNil: continue
+      result.add encodeType(m, s.typ[i])
+
+  if result in m.g.mangledPrcs:
+    result = mangleProc(m, s, true)
+  else:
+    m.g.mangledPrcs.incl(result)
+
 proc fillBackendName(m: BModule; s: PSym) =
-  if s.loc.r == "":
-    var result = s.name.s.mangle.rope
-    result.add "__"
-    result.add m.g.graph.ifaces[s.itemId.module].uniqueName
-    result.add "_"
-    result.add rope s.itemId.item
+  if s.loc.snippet == "":
+    var result: Rope
+    if not m.compileToCpp and s.kind in routineKinds and optCDebug in m.g.config.globalOptions and
+      m.g.config.symbolFiles == disabledSf:
+      result = mangleProc(m, s, false).rope
+    else:
+      result = s.name.s.mangle.rope
+      result.add mangleProcNameExt(m.g.graph, s)
     if m.hcrOn:
-      result.add "_"
-      result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts))
-    s.loc.r = result
+      result.add '_'
+      result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config))
+    s.loc.snippet = result
     writeMangledName(m.ndi, s, m.config)
 
 proc fillParamName(m: BModule; s: PSym) =
-  ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc.
-  ## Fortunately C's scoping rules are sane enough so that that doesn't
-  ## cause any trouble.
-  if s.loc.r == "":
+  if s.loc.snippet == "":
     var res = s.name.s.mangle
+    res.add mangleParamExt(s)
+    #res.add idOrSig(s, res, m.sigConflicts, m.config)
     # Take into account if HCR is on because of the following scenario:
     #   if a module gets imported and it has some more importc symbols in it,
     # some param names might receive the "_0" suffix to distinguish from what
@@ -68,15 +104,13 @@ proc fillParamName(m: BModule; s: PSym) =
     # and a function called in main or proxy uses `socket` as a parameter name.
     # That would lead to either needing to reload `proxy` or to overwrite the
     # executable file for the main module, which is running (or both!) -> error.
-    if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res):
-      res.add "_0"
-    s.loc.r = res.rope
+    s.loc.snippet = res.rope
     writeMangledName(m.ndi, s, m.config)
 
 proc fillLocalName(p: BProc; s: PSym) =
   assert s.kind in skLocalVars+{skTemp}
   #assert sfGlobal notin s.flags
-  if s.loc.r == "":
+  if s.loc.snippet == "":
     var key = s.name.s.mangle
     let counter = p.sigConflicts.getOrDefault(key)
     var result = key.rope
@@ -86,7 +120,7 @@ proc fillLocalName(p: BProc; s: PSym) =
     elif counter != 0 or isKeyword(s.name) or p.module.g.config.cppDefines.contains(key):
       result.add "_" & rope(counter+1)
     p.sigConflicts.inc(key)
-    s.loc.r = result
+    s.loc.snippet = result
     if s.kind != skTemp: writeMangledName(p.module.ndi, s, p.config)
 
 proc scopeMangledParam(p: BProc; param: PSym) =
@@ -113,23 +147,23 @@ proc getTypeName(m: BModule; typ: PType; sig: SigHash): Rope =
   var t = typ
   while true:
     if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}:
-      return t.sym.loc.r
+      return t.sym.loc.snippet
 
     if t.kind in irrelevantForBackend:
-      t = t.lastSon
+      t = t.skipModifier
     else:
       break
-  let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.lastSon else: typ
-  if typ.loc.r == "":
-    typ.typeName(typ.loc.r)
-    typ.loc.r.add $sig
+  let typ = if typ.kind in {tyAlias, tySink, tyOwned}: typ.elementType else: typ
+  if typ.loc.snippet == "":
+    typ.typeName(typ.loc.snippet)
+    typ.loc.snippet.add $sig
   else:
     when defined(debugSigHashes):
       # check consistency:
       var tn = newRopeAppender()
       typ.typeName(tn)
-      assert($typ.loc.r == $(tn & $sig))
-  result = typ.loc.r
+      assert($typ.loc.snippet == $(tn & $sig))
+  result = typ.loc.snippet
   if result == "": internalError(m.config, "getTypeName: " & $typ.kind)
 
 proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
@@ -140,7 +174,7 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
   of 8: result = ctInt64
   else: result = ctArray
 
-proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
+proc mapType(conf: ConfigRef; typ: PType; isParam: bool): TCTypeKind =
   ## Maps a Nim type to a C type
   case typ.kind
   of tyNone, tyTyped: result = ctVoid
@@ -149,16 +183,16 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
   of tyNil: result = ctPtr
   of tySet: result = mapSetType(conf, typ)
   of tyOpenArray, tyVarargs:
-    if kind == skParam: result = ctArray
+    if isParam: result = ctArray
     else: result = ctStruct
   of tyArray, tyUncheckedArray: result = ctArray
   of tyObject, tyTuple: result = ctStruct
   of tyUserTypeClasses:
     doAssert typ.isResolvedUserTypeClass
-    return mapType(conf, typ.lastSon, kind)
+    result = mapType(conf, typ.skipModifier, isParam)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
      tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned:
-    result = mapType(conf, lastSon(typ), kind)
+    result = mapType(conf, skipModifier(typ), isParam)
   of tyEnum:
     if firstOrd(conf, typ) < 0:
       result = ctInt32
@@ -169,9 +203,9 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
       of 4: result = ctInt32
       of 8: result = ctInt64
       else: result = ctInt32
-  of tyRange: result = mapType(conf, typ[0], kind)
+  of tyRange: result = mapType(conf, typ.elementType, isParam)
   of tyPtr, tyVar, tyLent, tyRef:
-    var base = skipTypes(typ.lastSon, typedescInst)
+    var base = skipTypes(typ.elementType, typedescInst)
     case base.kind
     of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctPtrToArray
     of tySet:
@@ -186,14 +220,19 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind =
   of tyInt..tyUInt64:
     result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt))
   of tyStatic:
-    if typ.n != nil: result = mapType(conf, lastSon typ, kind)
-    else: doAssert(false, "mapType: " & $typ.kind)
-  else: doAssert(false, "mapType: " & $typ.kind)
+    if typ.n != nil: result = mapType(conf, typ.skipModifier, isParam)
+    else:
+      result = ctVoid
+      doAssert(false, "mapType: " & $typ.kind)
+  else:
+    result = ctVoid
+    doAssert(false, "mapType: " & $typ.kind)
+
 
 proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind =
   #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr
   #else:
-  result = mapType(conf, typ, skResult)
+  result = mapType(conf, typ, false)
 
 proc isImportedType(t: PType): bool =
   result = t.sym != nil and sfImportc in t.sym.flags
@@ -203,11 +242,17 @@ proc isImportedCppType(t: PType): bool =
   result = (t.sym != nil and sfInfixCall in t.sym.flags) or
            (x.sym != nil and sfInfixCall in x.sym.flags)
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope
+proc isOrHasImportedCppType(typ: PType): bool =
+  searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType)
+
+proc hasNoInit(t: PType): bool =
+  result = t.sym != nil and sfNoInit in t.sym.flags
+
+proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope
 
 proc isObjLackingTypeField(typ: PType): bool {.inline.} =
   result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and
-      (typ[0] == nil) or isPureObject(typ))
+      (typ.baseClass == nil) or isPureObject(typ))
 
 proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
   # Arrays and sets cannot be returned by a C procedure, because C is
@@ -223,15 +268,21 @@ proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool =
                     getSize(conf, rettype) > conf.target.floatSize*3):
     result = true
   else:
-    case mapType(conf, rettype, skResult)
+    case mapType(conf, rettype, false)
     of ctArray:
       result = not (skipTypes(rettype, typedescInst).kind in
           {tyVar, tyLent, tyRef, tyPtr})
     of ctStruct:
       let t = skipTypes(rettype, typedescInst)
-      if rettype.isImportedCppType or t.isImportedCppType: return false
-      result = containsGarbageCollectedRef(t) or
-          (t.kind == tyObject and not isObjLackingTypeField(t))
+      if rettype.isImportedCppType or t.isImportedCppType or
+          (typ.callConv == ccCDecl and conf.selectedGC in {gcArc, gcAtomicArc, gcOrc}):
+        # prevents nrvo for cdecl procs; # bug #23401
+        result = false
+      else:
+        result = containsGarbageCollectedRef(t) or
+            (t.kind == tyObject and not isObjLackingTypeField(t)) or
+            (getSize(conf, rettype) == szUnknownSize and (t.sym == nil or sfImportc notin t.sym.flags))
+
     else: result = false
 
 const
@@ -239,7 +290,9 @@ const
     "N_STDCALL", "N_CDECL", "N_SAFECALL",
     "N_SYSCALL", # this is probably not correct for all platforms,
                  # but one can #define it to what one wants
-    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV"]
+    "N_INLINE", "N_NOINLINE", "N_FASTCALL", "N_THISCALL", "N_CLOSURE", "N_NOCONV",
+    "N_NOCONV" #ccMember is N_NOCONV
+    ]
 
 proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
   # returns nil if we need to declare this type
@@ -247,7 +300,7 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope =
   # linear search is not necessary anymore:
   result = tab.getOrDefault(sig)
 
-proc addAbiCheck(m: BModule, t: PType, name: Rope) =
+proc addAbiCheck(m: BModule; t: PType, name: Rope) =
   if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize):
     var msg = "backend & Nim disagree on size for: "
     msg.addTypeHeader(m.config, t)
@@ -268,11 +321,11 @@ proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) =
 proc typeNameOrLiteral(m: BModule; t: PType, literal: string): Rope =
   if t.sym != nil and sfImportc in t.sym.flags and t.sym.magic == mNone:
     useHeader(m, t.sym)
-    result = t.sym.loc.r
+    result = t.sym.loc.snippet
   else:
     result = rope(literal)
 
-proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
+proc getSimpleTypeDesc(m: BModule; typ: PType): Rope =
   const
     NumericalTypeToStr: array[tyInt..tyUInt64, string] = [
       "NI", "NI8", "NI16", "NI32", "NI64",
@@ -296,39 +349,34 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope =
   of tyNil: result = typeNameOrLiteral(m, typ, "void*")
   of tyInt..tyUInt64:
     result = typeNameOrLiteral(m, typ, NumericalTypeToStr[typ.kind])
-  of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ[0])
+  of tyDistinct, tyRange, tyOrdinal: result = getSimpleTypeDesc(m, typ.skipModifier)
   of tyStatic:
-    if typ.n != nil: result = getSimpleTypeDesc(m, lastSon typ)
-    else: internalError(m.config, "tyStatic for getSimpleTypeDesc")
+    if typ.n != nil: result = getSimpleTypeDesc(m, skipModifier typ)
+    else:
+      result = ""
+      internalError(m.config, "tyStatic for getSimpleTypeDesc")
   of tyGenericInst, tyAlias, tySink, tyOwned:
-    result = getSimpleTypeDesc(m, lastSon typ)
+    result = getSimpleTypeDesc(m, skipModifier typ)
   else: result = ""
 
   if result != "" and typ.isImportedType():
-    let sig = hashType typ
+    let sig = hashType(typ, m.config)
     if cacheGetType(m.typeCache, sig) == "":
       m.typeCache[sig] = result
 
-proc pushType(m: BModule, typ: PType) =
+proc pushType(m: BModule; typ: PType) =
   for i in 0..high(m.typeStack):
     # pointer equality is good enough here:
     if m.typeStack[i] == typ: return
   m.typeStack.add(typ)
 
-proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope =
+proc getTypePre(m: BModule; typ: PType; sig: SigHash): Rope =
   if typ == nil: result = rope("void")
   else:
     result = getSimpleTypeDesc(m, typ)
     if result == "": result = cacheGetType(m.typeCache, sig)
 
-proc structOrUnion(t: PType): Rope =
-  let cachedUnion = rope("union")
-  let cachedStruct = rope("struct")
-  let t = t.skipTypes({tyAlias, tySink})
-  if tfUnion in t.flags: cachedUnion
-  else: cachedStruct
-
-proc addForwardStructFormat(m: BModule, structOrUnion: Rope, typename: Rope) =
+proc addForwardStructFormat(m: BModule; structOrUnion: Rope, typename: Rope) =
   if m.compileToCpp:
     m.s[cfsForwardTypes].addf "$1 $2;$n", [structOrUnion, typename]
   else:
@@ -338,7 +386,7 @@ proc seqStar(m: BModule): string =
   if optSeqDestructors in m.config.globalOptions: result = ""
   else: result = "*"
 
-proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
+proc getTypeForward(m: BModule; typ: PType; sig: SigHash): Rope =
   result = cacheGetType(m.forwTypeCache, sig)
   if result != "": return
   result = getTypePre(m, typ, sig)
@@ -355,7 +403,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope =
     doAssert m.forwTypeCache[sig] == result
   else: internalError(m.config, "getTypeForward(" & $typ.kind & ')')
 
-proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): Rope =
+proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TypeDescKind): Rope =
   ## like getTypeDescAux but creates only a *weak* dependency. In other words
   ## we know we only need a pointer to it so we only generate a struct forward
   ## declaration:
@@ -365,10 +413,10 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
     if isImportedCppType(etB) and t.kind == tyGenericInst:
       result = getTypeDescAux(m, t, check, kind)
     else:
-      result = getTypeForward(m, t, hashType(t))
+      result = getTypeForward(m, t, hashType(t, m.config))
       pushType(m, t)
   of tySequence:
-    let sig = hashType(t)
+    let sig = hashType(t, m.config)
     if optSeqDestructors in m.config.globalOptions:
       if skipTypes(etB[0], typedescInst).kind == tyEmpty:
         internalError(m.config, "cannot map the empty seq type to a C type")
@@ -386,34 +434,30 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R
         m.typeCache[sig] = result
         #echo "adding ", sig, " ", typeToString(t), " ", m.module.name.s
         appcg(m, m.s[cfsTypes],
-          "struct $1 {$N" &
-          "  NI len; $1_Content* p;$N" &
-          "};$N", [result])
+          "struct $1 {\n" &
+          "  NI len; $1_Content* p;\n" &
+          "};\n", [result])
+        pushType(m, t)
     else:
       result = getTypeForward(m, t, sig) & seqStar(m)
-    pushType(m, t)
+      pushType(m, t)
   else:
     result = getTypeDescAux(m, t, check, kind)
 
 proc getSeqPayloadType(m: BModule; t: PType): Rope =
   var check = initIntSet()
-  result = getTypeDescWeak(m, t, check, skParam) & "_Content"
+  result = getTypeDescWeak(m, t, check, dkParam) & "_Content"
   #result = getTypeForward(m, t, hashType(t)) & "_Content"
 
 proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) =
-  let sig = hashType(t)
+  let sig = hashType(t, m.config)
   let result = cacheGetType(m.typeCache, sig)
   if result == "":
-    discard getTypeDescAux(m, t, check, skVar)
+    discard getTypeDescAux(m, t, check, dkVar)
   else:
-    # little hack for now to prevent multiple definitions of the same
-    # Seq_Content:
-    appcg(m, m.s[cfsTypes], """$N
-$3ifndef $2_Content_PP
-$3define $2_Content_PP
-struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];};
-$3endif$N
-      """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, skVar), result, rope"#"])
+    appcg(m, m.s[cfsTypes], """
+struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE]; };
+""", [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, dkVar), result])
 
 proc paramStorageLoc(param: PSym): TStorageLoc =
   if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin {
@@ -422,80 +466,234 @@ proc paramStorageLoc(param: PSym): TStorageLoc =
   else:
     result = OnUnknown
 
-proc genProcParams(m: BModule, t: PType, rettype, params: var Rope,
+macro unrollChars(x: static openArray[char], name, body: untyped) =
+  result = newStmtList()
+  for a in x:
+    result.add(newBlockStmt(newStmtList(
+      newConstStmt(name, newLit(a)),
+      copy body
+    )))
+
+proc multiFormat*(frmt: var string, chars: static openArray[char], args: openArray[seq[string]]) =
+  var res: string
+  unrollChars(chars, c):
+    res = ""
+    let arg = args[find(chars, c)]
+    var i = 0
+    var num = 0
+    while i < frmt.len:
+      if frmt[i] == c:
+        inc(i)
+        case frmt[i]
+        of c:
+          res.add(c)
+          inc(i)
+        of '0'..'9':
+          var j = 0
+          while true:
+            j = j * 10 + ord(frmt[i]) - ord('0')
+            inc(i)
+            if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
+          num = j
+          if j > high(arg) + 1:
+            raiseAssert "invalid format string: " & frmt
+          else:
+            res.add(arg[j-1])
+        else:
+          raiseAssert "invalid format string: " & frmt
+      var start = i
+      while i < frmt.len:
+        if frmt[i] != c: inc(i)
+        else: break
+      if i - 1 >= start:
+        res.add(substr(frmt, start, i - 1))
+    frmt = res
+
+template cgDeclFrmt*(s: PSym): string =
+  s.constraint.strVal
+
+proc genMemberProcParams(m: BModule; prc: PSym, superCall, rettype, name, params: var string,
+                   check: var IntSet, declareEnvironment=true;
+                   weakDep=false;) =
+  let t = prc.typ
+  let isCtor = sfConstructor in prc.flags
+  if isCtor or (name[0] == '~' and sfMember in prc.flags):
+    # destructors can't have void
+    rettype = ""
+  elif t.returnType == nil or isInvalidReturnType(m.config, t):
+    rettype = "void"
+  else:
+    if rettype == "":
+      rettype = getTypeDescAux(m, t.returnType, check, dkResult)
+    else:
+      rettype = runtimeFormat(rettype.replace("'0", "$1"), [getTypeDescAux(m, t.returnType, check, dkResult)])
+  var types, names, args: seq[string] = @[]
+  if not isCtor:
+    var this = t.n[1].sym
+    fillParamName(m, this)
+    fillLoc(this.loc, locParam, t.n[1],
+            this.paramStorageLoc)
+    if this.typ.kind == tyPtr:
+      this.loc.snippet = "this"
+    else:
+      this.loc.snippet = "(*this)"
+    names.add this.loc.snippet
+    types.add getTypeDescWeak(m, this.typ, check, dkParam)
+
+  let firstParam = if isCtor: 1 else: 2
+  for i in firstParam..<t.n.len:
+    if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genMemberProcParams")
+    var param = t.n[i].sym
+    var descKind = dkParam
+    if optByRef in param.options:
+      if param.typ.kind == tyGenericInst:
+        descKind = dkRefGenericParam
+      else:
+        descKind = dkRefParam
+    var typ, name: string
+    fillParamName(m, param)
+    fillLoc(param.loc, locParam, t.n[i],
+            param.paramStorageLoc)
+    if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
+      typ = getTypeDescWeak(m, param.typ, check, descKind) & "*"
+      incl(param.loc.flags, lfIndirect)
+      param.loc.storage = OnUnknown
+    elif weakDep:
+      typ = getTypeDescWeak(m, param.typ, check, descKind)
+    else:
+      typ = getTypeDescAux(m, param.typ, check, descKind)
+    if sfNoalias in param.flags:
+      typ.add("NIM_NOALIAS ")
+
+    name = param.loc.snippet
+    types.add typ
+    names.add name
+    if sfCodegenDecl notin param.flags:
+      args.add types[^1] & " " & names[^1]
+    else:
+      args.add runtimeFormat(param.cgDeclFrmt, [types[^1], names[^1]])
+
+  multiFormat(params, @['\'', '#'], [types, names])
+  multiFormat(superCall, @['\'', '#'], [types, names])
+  multiFormat(name, @['\'', '#'], [types, names]) #so we can ~'1 on members
+  if params == "()":
+    if types.len == 0:
+      params = "(void)"
+    else:
+      params = "(" & args.join(", ") & ")"
+  if tfVarargs in t.flags:
+    if params != "(":
+      params[^1] = ','
+    else:
+      params.delete(params.len()-1..params.len()-1)
+    params.add("...)")
+
+proc genProcParams(m: BModule; t: PType, rettype, params: var Rope,
                    check: var IntSet, declareEnvironment=true;
-                   weakDep=false) =
-  params = ""
-  if t[0] == nil or isInvalidReturnType(m.config, t):
+                   weakDep=false;) =
+  params = "("
+  if t.returnType == nil or isInvalidReturnType(m.config, t):
     rettype = "void"
   else:
-    rettype = getTypeDescAux(m, t[0], check, skResult)
+    rettype = getTypeDescAux(m, t.returnType, check, dkResult)
   for i in 1..<t.n.len:
     if t.n[i].kind != nkSym: internalError(m.config, t.n.info, "genProcParams")
     var param = t.n[i].sym
+    var descKind = dkParam
+    if m.config.backend == backendCpp and optByRef in param.options:
+      if param.typ.kind == tyGenericInst:
+        descKind = dkRefGenericParam
+      else:
+        descKind = dkRefParam
     if isCompileTimeOnly(param.typ): continue
-    if params != "": params.add(", ")
+    if params != "(": params.add(", ")
     fillParamName(m, param)
     fillLoc(param.loc, locParam, t.n[i],
             param.paramStorageLoc)
-    if ccgIntroducedPtr(m.config, param, t[0]):
-      params.add(getTypeDescWeak(m, param.typ, check, skParam))
-      params.add("*")
+    var typ: Rope
+    if ccgIntroducedPtr(m.config, param, t.returnType) and descKind == dkParam:
+      typ = (getTypeDescWeak(m, param.typ, check, descKind))
+      typ.add("*")
       incl(param.loc.flags, lfIndirect)
       param.loc.storage = OnUnknown
     elif weakDep:
-      params.add(getTypeDescWeak(m, param.typ, check, skParam))
+      typ = (getTypeDescWeak(m, param.typ, check, descKind))
     else:
-      params.add(getTypeDescAux(m, param.typ, check, skParam))
-    params.add(" ")
+      typ = (getTypeDescAux(m, param.typ, check, descKind))
+    typ.add(" ")
     if sfNoalias in param.flags:
-      params.add("NIM_NOALIAS ")
-    params.add(param.loc.r)
+      typ.add("NIM_NOALIAS ")
+    if sfCodegenDecl notin param.flags:
+      params.add(typ)
+      params.add(param.loc.snippet)
+    else:
+      params.add runtimeFormat(param.cgDeclFrmt, [typ, param.loc.snippet])
     # declare the len field for open arrays:
     var arr = param.typ.skipTypes({tyGenericInst})
-    if arr.kind in {tyVar, tyLent, tySink}: arr = arr.lastSon
+    if arr.kind in {tyVar, tyLent, tySink}: arr = arr.elementType
     var j = 0
     while arr.kind in {tyOpenArray, tyVarargs}:
       # this fixes the 'sort' bug:
       if param.typ.kind in {tyVar, tyLent}: param.loc.storage = OnUnknown
       # need to pass hidden parameter:
-      params.addf(", NI $1Len_$2", [param.loc.r, j.rope])
+      params.addf(", NI $1Len_$2", [param.loc.snippet, j.rope])
       inc(j)
       arr = arr[0].skipTypes({tySink})
-  if t[0] != nil and isInvalidReturnType(m.config, t):
-    var arr = t[0]
-    if params != "": params.add(", ")
-    if mapReturnType(m.config, t[0]) != ctArray:
+  if t.returnType != nil and isInvalidReturnType(m.config, t):
+    var arr = t.returnType
+    if params != "(": params.add(", ")
+    if mapReturnType(m.config, arr) != ctArray:
       if isHeaderFile in m.flags:
         # still generates types for `--header`
-        params.add(getTypeDescAux(m, arr, check, skResult))
+        params.add(getTypeDescAux(m, arr, check, dkResult))
         params.add("*")
       else:
-        params.add(getTypeDescWeak(m, arr, check, skResult))
+        params.add(getTypeDescWeak(m, arr, check, dkResult))
         params.add("*")
     else:
-      params.add(getTypeDescAux(m, arr, check, skResult))
+      params.add(getTypeDescAux(m, arr, check, dkResult))
     params.addf(" Result", [])
   if t.callConv == ccClosure and declareEnvironment:
-    if params != "": params.add(", ")
+    if params != "(": params.add(", ")
     params.add("void* ClE_0")
   if tfVarargs in t.flags:
-    if params != "": params.add(", ")
+    if params != "(": params.add(", ")
     params.add("...")
-  if params == "": params.add("void)")
+  if params == "(": params.add("void)")
   else: params.add(")")
-  params = "(" & params
 
 proc mangleRecFieldName(m: BModule; field: PSym): Rope =
   if {sfImportc, sfExportc} * field.flags != {}:
-    result = field.loc.r
+    result = field.loc.snippet
   else:
     result = rope(mangleField(m, field.name))
   if result == "": internalError(m.config, field.info, "mangleRecFieldName")
 
-proc genRecordFieldsAux(m: BModule, n: PNode,
+proc hasCppCtor(m: BModule; typ: PType): bool =
+  result = false
+  if m.compileToCpp and typ != nil and typ.itemId in m.g.graph.memberProcsPerType:
+    for prc in m.g.graph.memberProcsPerType[typ.itemId]:
+      if sfConstructor in prc.flags:
+        return true
+
+proc genCppParamsForCtor(p: BProc; call: PNode; didGenTemp: var bool): string
+
+proc genCppInitializer(m: BModule, prc: BProc; typ: PType; didGenTemp: var bool): string =
+  #To avoid creating a BProc per test when called inside a struct nil BProc is allowed
+  result = "{}"
+  if typ.itemId in m.g.graph.initializersPerType:
+    let call = m.g.graph.initializersPerType[typ.itemId]
+    if call != nil:
+      var p = prc
+      if p == nil:
+        p = BProc(module: m)
+      result = "{" & genCppParamsForCtor(p, call, didGenTemp) & "}"
+      if prc == nil:
+        assert p.blocks.len == 0, "BProc belongs to a struct doesnt have blocks"
+
+proc genRecordFieldsAux(m: BModule; n: PNode,
                         rectype: PType,
-                        check: var IntSet; result: var Rope; unionPrefix = "") =
+                        check: var IntSet; result: var Builder; unionPrefix = "") =
   case n.kind
   of nkRecList:
     for i in 0..<n.len:
@@ -512,131 +710,100 @@ proc genRecordFieldsAux(m: BModule, n: PNode,
         let k = lastSon(n[i])
         if k.kind != nkSym:
           let structName = "_" & mangleRecFieldName(m, n[0].sym) & "_" & $i
-          var a = newRopeAppender()
+          var a = newBuilder("")
           genRecordFieldsAux(m, k, rectype, check, a, unionPrefix & $structName & ".")
-          if a != "":
-            if tfPacked notin rectype.flags:
-              unionBody.add("struct {")
-            else:
-              if hasAttribute in CC[m.config.cCompiler].props:
-                unionBody.add("struct __attribute__((__packed__)){")
-              else:
-                unionBody.addf("#pragma pack(push, 1)$nstruct{", [])
-            unionBody.add(a)
-            unionBody.addf("} $1;$n", [structName])
-            if tfPacked in rectype.flags and hasAttribute notin CC[m.config.cCompiler].props:
-              unionBody.addf("#pragma pack(pop)$n", [])
+          if a.len != 0:
+            unionBody.addFieldWithStructType(m, rectype, structName):
+              unionBody.add(a)
         else:
           genRecordFieldsAux(m, k, rectype, check, unionBody, unionPrefix)
       else: internalError(m.config, "genRecordFieldsAux(record case branch)")
-    if unionBody != "":
-      result.addf("union{$n$1};$n", [unionBody])
+    if unionBody.len != 0:
+      result.addAnonUnion:
+        result.add(unionBody)
   of nkSym:
     let field = n.sym
     if field.typ.kind == tyVoid: return
     #assert(field.ast == nil)
     let sname = mangleRecFieldName(m, field)
     fillLoc(field.loc, locField, n, unionPrefix & sname, OnUnknown)
-    if field.alignment > 0:
-      result.addf "NIM_ALIGN($1) ", [rope(field.alignment)]
     # for importcpp'ed objects, we only need to set field.loc, but don't
     # have to recurse via 'getTypeDescAux'. And not doing so prevents problems
     # with heavily templatized C++ code:
     if not isImportedCppType(rectype):
-      let noAlias = if sfNoalias in field.flags: " NIM_NOALIAS" else: ""
-
       let fieldType = field.loc.lode.typ.skipTypes(abstractInst)
+      var typ: Rope = ""
+      var isFlexArray = false
+      var initializer = ""
       if fieldType.kind == tyUncheckedArray:
-        result.addf("$1 $2[SEQ_DECL_SIZE];$n",
-            [getTypeDescAux(m, fieldType.elemType, check, skField), sname])
+        typ = getTypeDescAux(m, fieldType.elemType, check, dkField)
+        isFlexArray = true
       elif fieldType.kind == tySequence:
         # we need to use a weak dependency here for trecursive_table.
-        result.addf("$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, skField), sname, noAlias])
-      elif field.bitsize != 0:
-        result.addf("$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, rope($field.bitsize), noAlias])
+        typ = getTypeDescWeak(m, field.loc.t, check, dkField)
       else:
+        typ = getTypeDescAux(m, field.loc.t, check, dkField)
         # don't use fieldType here because we need the
         # tyGenericInst for C++ template support
-        result.addf("$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias])
+        let noInit = sfNoInit in field.flags or (field.typ.sym != nil and sfNoInit in field.typ.sym.flags)
+        if not noInit and (fieldType.isOrHasImportedCppType() or hasCppCtor(m, field.owner.typ)):
+          var didGenTemp = false
+          initializer = genCppInitializer(m, nil, fieldType, didGenTemp)
+      result.addField(field, sname, typ, isFlexArray, initializer)
   else: internalError(m.config, n.info, "genRecordFieldsAux()")
 
-proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope =
-  result = newRopeAppender()
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl:bool = false)
+
+proc addRecordFields(result: var Builder; m: BModule; typ: PType, check: var IntSet) =
   genRecordFieldsAux(m, typ.n, typ, check, result)
+  if typ.itemId in m.g.graph.memberProcsPerType:
+    let procs = m.g.graph.memberProcsPerType[typ.itemId]
+    var isDefaultCtorGen, isCtorGen: bool = false
+    for prc in procs:
+      var header: Rope = ""
+      if sfConstructor in prc.flags:
+        isCtorGen = true
+        if prc.typ.n.len == 1:
+          isDefaultCtorGen = true
+      if lfNoDecl in prc.loc.flags: continue
+      genMemberProcHeader(m, prc, header, false, true)
+      result.addf "$1;$n", [header]
+    if isCtorGen and not isDefaultCtorGen:
+      var ch: IntSet = default(IntSet)
+      result.addf "$1() = default;$n", [getTypeDescAux(m, typ, ch, dkOther)]
 
 proc fillObjectFields*(m: BModule; typ: PType) =
   # sometimes generic objects are not consistently merged. We patch over
   # this fact here.
   var check = initIntSet()
-  discard getRecordFields(m, typ, check)
+  var ignored = newBuilder("")
+  addRecordFields(ignored, m, typ, check)
 
 proc mangleDynLibProc(sym: PSym): Rope
 
-proc getRecordDesc(m: BModule, typ: PType, name: Rope,
+proc getRecordDesc(m: BModule; typ: PType, name: Rope,
                    check: var IntSet): Rope =
   # declare the record:
-  var hasField = false
-
-  if tfPacked in typ.flags:
-    if hasAttribute in CC[m.config.cCompiler].props:
-      result = structOrUnion(typ) & " __attribute__((__packed__))"
-    else:
-      result = "#pragma pack(push, 1)\L" & structOrUnion(typ)
-  else:
-    result = structOrUnion(typ)
-
-  result.add " "
-  result.add name
-
-  if typ.kind == tyObject:
-    if typ[0] == nil:
-      if lacksMTypeField(typ):
-        appcg(m, result, " {$n", [])
-      else:
-        if optTinyRtti in m.config.globalOptions:
-          appcg(m, result, " {$n#TNimTypeV2* m_type;$n", [])
-        else:
-          appcg(m, result, " {$n#TNimType* m_type;$n", [])
-        hasField = true
-    elif m.compileToCpp:
-      appcg(m, result, " : public $1 {$n",
-                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
-      if typ.isException and m.config.exc == excCpp:
-        when false:
-          appcg(m, result, "virtual void raise() { throw *this; }$n", []) # required for polymorphic exceptions
-          if typ.sym.magic == mException:
-            # Add cleanup destructor to Exception base class
-            appcg(m, result, "~$1();$n", [name])
-            # define it out of the class body and into the procs section so we don't have to
-            # artificially forward-declare popCurrentExceptionEx (very VERY troublesome for HCR)
-            appcg(m, cfsProcs, "inline $1::~$1() {if(this->raiseId) #popCurrentExceptionEx(this->raiseId);}$n", [name])
-      hasField = true
-    else:
-      appcg(m, result, " {$n  $1 Sup;$n",
-                      [getTypeDescAux(m, typ[0].skipTypes(skipPtrs), check, skField)])
-      hasField = true
-  else:
-    result.addf(" {$n", [name])
-
-  let desc = getRecordFields(m, typ, check)
-  if desc == "" and not hasField:
-    result.addf("char dummy;$n", [])
+  var baseType: string = ""
+  if typ.baseClass != nil:
+    baseType = getTypeDescAux(m, typ.baseClass.skipTypes(skipPtrs), check, dkField)
+  if typ.sym == nil or sfCodegenDecl notin typ.sym.flags:
+    result = newBuilder("")
+    result.addStruct(m, typ, name, baseType):
+      result.addRecordFields(m, typ, check)
   else:
-    result.add(desc)
-  result.add("};\L")
-  if tfPacked in typ.flags and hasAttribute notin CC[m.config.cCompiler].props:
-    result.add "#pragma pack(pop)\L"
+    var desc = newBuilder("")
+    desc.addRecordFields(m, typ, check)
+    result = runtimeFormat(typ.sym.cgDeclFrmt, [name, desc, baseType])
 
-proc getTupleDesc(m: BModule, typ: PType, name: Rope,
+proc getTupleDesc(m: BModule; typ: PType, name: Rope,
                   check: var IntSet): Rope =
-  result = "$1 $2 {$n" % [structOrUnion(typ), name]
-  var desc: Rope = ""
-  for i in 0..<typ.len:
-    desc.addf("$1 Field$2;$n",
-         [getTypeDescAux(m, typ[i], check, skField), rope(i)])
-  if desc == "": result.add("char dummy;\L")
-  else: result.add(desc)
-  result.add("};\L")
+  result = newBuilder("")
+  result.addStruct(m, typ, name, ""):
+    for i, a in typ.ikids:
+      result.addField(
+        name = "Field" & $i,
+        typ = getTypeDescAux(m, a, check, dkField))
 
 proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
   # A helper proc for handling cppimport patterns, involving numeric
@@ -658,31 +825,30 @@ proc scanCppGenericSlot(pat: string, cursor, outIdx, outStars: var int): bool =
 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:
-    doAssert false, "invalid apostrophe type parameter index"
+  if idx >= typ.kidsLen:
+    raiseAssert "invalid apostrophe type parameter index"
 
   result = typ[idx]
   for i in 1..stars:
-    if result != nil and result.len > 0:
-      result = if result.kind == tyGenericInst: result[1]
+    if result != nil and result.kidsLen > 0:
+      result = if result.kind == tyGenericInst: result[FirstGenericParamAt]
                else: result.elemType
 
-proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope =
-  let sig = hashType(t)
-  if kind == skParam:
-    result = getTypeDescWeak(m, t[0], check, kind) & "*"
+proc getOpenArrayDesc(m: BModule; t: PType, check: var IntSet; kind: TypeDescKind): Rope =
+  let sig = hashType(t, m.config)
+  if kind == dkParam:
+    result = getTypeDescWeak(m, t.elementType, check, kind) & "*"
   else:
     result = cacheGetType(m.typeCache, sig)
     if result == "":
       result = getTypeName(m, t, sig)
       m.typeCache[sig] = result
-      let elemType = getTypeDescWeak(m, t[0], check, kind)
+      let elemType = getTypeDescWeak(m, t.elementType, check, kind)
       m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n",
                          [result, elemType])
 
-proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope =
+proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDescKind): Rope =
   # returns only the type's name
-
   var t = origTyp.skipTypes(irrelevantForBackend-{tyOwned})
   if containsOrIncl(check, t.id):
     if not (isImportedCppType(origTyp) or isImportedCppType(t)):
@@ -692,23 +858,25 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
     # C type generation into an analysis and a code generation phase somehow.
   if t.sym != nil: useHeader(m, t.sym)
   if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym)
-  let sig = hashType(origTyp)
+  let sig = hashType(origTyp, m.config)
 
+  result = getTypePre(m, t, sig)
   defer: # defer is the simplest in this case
     if isImportedType(t) and not m.typeABICache.containsOrIncl(sig):
       addAbiCheck(m, t, result)
 
-  result = getTypePre(m, t, sig)
   if result != "" and t.kind != tyOpenArray:
     excl(check, t.id)
+    if kind == dkRefParam or kind == dkRefGenericParam and origTyp.kind == tyGenericInst:
+      result.add("&")
     return
   case t.kind
   of tyRef, tyPtr, tyVar, tyLent:
     var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and
                     compileToCpp(m): "&" else: "*"
-    var et = origTyp.skipTypes(abstractInst).lastSon
+    var et = origTyp.skipTypes(abstractInst).elementType
     var etB = et.skipTypes(abstractInst)
-    if mapType(m.config, t, kind) == ctPtrToArray and (etB.kind != tyOpenArray or kind == skParam):
+    if mapType(m.config, t, kind == dkParam) == ctPtrToArray and (etB.kind != tyOpenArray or kind == dkParam):
       if etB.kind == tySet:
         et = getSysType(m.g.graph, unknownLineInfo, tyUInt8)
       else:
@@ -721,7 +889,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
         result = getTypeDescAux(m, et, check, kind) & star
       else:
         # no restriction! We have a forward declaration for structs
-        let name = getTypeForward(m, et, hashType et)
+        let name = getTypeForward(m, et, hashType(et, m.config))
         result = name & star
         m.typeCache[sig] = result
     of tySequence:
@@ -730,7 +898,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
         m.typeCache[sig] = result
       else:
         # no restriction! We have a forward declaration for structs
-        let name = getTypeForward(m, et, hashType et)
+        let name = getTypeForward(m, et, hashType(et, m.config))
         result = name & seqStar(m) & star
         m.typeCache[sig] = result
         pushType(m, et)
@@ -772,7 +940,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
   of tyProc:
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
-    var rettype, desc: Rope
+    var rettype, desc: Rope = ""
     genProcParams(m, t, rettype, desc, check, true, true)
     if not isImportedType(t):
       if t.callConv != ccClosure: # procedure vars may need a closure!
@@ -798,19 +966,15 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
       assert(cacheGetType(m.typeCache, sig) == "")
       m.typeCache[sig] = result & seqStar(m)
       if not isImportedType(t):
-        if skipTypes(t[0], typedescInst).kind != tyEmpty:
-          const
-            cppSeq = "struct $2 : #TGenericSeq {$n"
-            cSeq = "struct $2 {$n" &
-                  "  #TGenericSeq Sup;$n"
-          if m.compileToCpp:
-            appcg(m, m.s[cfsSeqTypes],
-                cppSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t[0], check, kind), result])
-          else:
-            appcg(m, m.s[cfsSeqTypes],
-                cSeq & "  $1 data[SEQ_DECL_SIZE];$n" &
-                "};$n", [getTypeDescAux(m, t[0], check, kind), result])
+        if skipTypes(t.elementType, typedescInst).kind != tyEmpty:
+          var struct = newBuilder("")
+          let baseType = cgsymValue(m, "TGenericSeq")
+          struct.addSimpleStruct(m, name = result, baseType = baseType):
+            struct.addField(
+              name = "data",
+              typ = getTypeDescAux(m, t.elementType, check, kind),
+              isFlexArray = true)
+          m.s[cfsSeqTypes].add struct
         else:
           result = rope("TGenericSeq")
       result.add(seqStar(m))
@@ -818,7 +982,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let foo = getTypeDescAux(m, t[0], check, kind)
+      let foo = getTypeDescAux(m, t.elementType, check, kind)
       m.s[cfsTypes].addf("typedef $1 $2[1];$n", [foo, result])
   of tyArray:
     var n: BiggestInt = toInt64(lengthOrd(m.config, t))
@@ -826,11 +990,12 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
     result = getTypeName(m, origTyp, sig)
     m.typeCache[sig] = result
     if not isImportedType(t):
-      let foo = getTypeDescAux(m, t[1], check, kind)
+      let e = getTypeDescAux(m, t.elementType, check, kind)
       m.s[cfsTypes].addf("typedef $1 $2[$3];$n",
-           [foo, result, rope(n)])
+           [e, result, rope(n)])
   of tyObject, tyTuple:
-    if isImportedCppType(t) and origTyp.kind == tyGenericInst:
+    let tt = origTyp.skipTypes({tyDistinct})
+    if isImportedCppType(t) and tt.kind == tyGenericInst:
       let cppNameAsRope = getTypeName(m, t, sig)
       let cppName = $cppNameAsRope
       var i = 0
@@ -848,12 +1013,12 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
       while i < cppName.len:
         if cppName[i] == '\'':
           var chunkEnd = i-1
-          var idx, stars: int
+          var idx, stars: int = 0
           if scanCppGenericSlot(cppName, i, idx, stars):
             result.add cppName.substr(chunkStart, chunkEnd)
             chunkStart = i
 
-            let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars)
+            let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars)
             addResultType(typeInSlot)
         else:
           inc i
@@ -862,9 +1027,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
         result.add cppName.substr(chunkStart)
       else:
         result = cppNameAsRope & "<"
-        for i in 1..<origTyp.len-1:
-          if i > 1: result.add(" COMMA ")
-          addResultType(origTyp[i])
+        for needsComma, a in tt.genericInstParams:
+          if needsComma: result.add(" COMMA ")
+          addResultType(a)
         result.add("> ")
       # always call for sideeffects:
       assert t.kind != tyTuple
@@ -895,8 +1060,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
   of tySet:
     # Don't use the imported name as it may be scoped: 'Foo::SomeKind'
     result = rope("tySet_")
-    t.lastSon.typeName(result)
-    result.add $t.lastSon.hashType
+    t.elementType.typeName(result)
+    result.add $t.elementType.hashType(m.config)
     m.typeCache[sig] = result
     if not isImportedType(t):
       let s = int(getSize(m.config, t))
@@ -906,14 +1071,15 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin
              [result, rope(getSize(m.config, t))])
   of tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyOwned,
      tyUserTypeClass, tyUserTypeClassInst, tyInferred:
-    result = getTypeDescAux(m, lastSon(t), check, kind)
+    result = getTypeDescAux(m, skipModifier(t), check, kind)
   else:
     internalError(m.config, "getTypeDescAux(" & $t.kind & ')')
     result = ""
   # fixes bug #145:
   excl(check, t.id)
 
-proc getTypeDesc(m: BModule, typ: PType; kind = skParam): Rope =
+
+proc getTypeDesc(m: BModule; typ: PType; kind = dkParam): Rope =
   var check = initIntSet()
   result = getTypeDescAux(m, typ, check, kind)
 
@@ -923,11 +1089,11 @@ type
     clHalfWithEnv,    ## fn(args, void* env) type with trailing 'void* env' parameter
     clFull            ## struct {fn(args, void* env), env}
 
-proc getClosureType(m: BModule, t: PType, kind: TClosureTypeKind): Rope =
+proc getClosureType(m: BModule; t: PType, kind: TClosureTypeKind): Rope =
   assert t.kind == tyProc
   var check = initIntSet()
   result = getTempName(m)
-  var rettype, desc: Rope
+  var rettype, desc: Rope = ""
   genProcParams(m, t, rettype, desc, check, declareEnvironment=kind != clHalf)
   if not isImportedType(t):
     if t.callConv != ccClosure or kind != clFull:
@@ -947,35 +1113,103 @@ proc finishTypeDescriptions(m: BModule) =
     if optSeqDestructors in m.config.globalOptions and t.skipTypes(abstractInst).kind == tySequence:
       seqV2ContentType(m, t, check)
     else:
-      discard getTypeDescAux(m, t, check, skParam)
+      discard getTypeDescAux(m, t, check, dkParam)
     inc(i)
   m.typeStack.setLen 0
 
-template cgDeclFrmt*(s: PSym): string =
-  s.constraint.strVal
-
-proc isReloadable(m: BModule, prc: PSym): bool =
+proc isReloadable(m: BModule; prc: PSym): bool =
   return m.hcrOn and sfNonReloadable notin prc.flags
 
-proc isNonReloadable(m: BModule, prc: PSym): bool =
+proc isNonReloadable(m: BModule; prc: PSym): bool =
   return m.hcrOn and sfNonReloadable in prc.flags
 
-proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false) =
+proc parseVFunctionDecl(val: string; name, params, retType, superCall: var string; isFnConst, isOverride, isMemberVirtual, isStatic: var bool; isCtor: bool, isFunctor=false) =
+  var afterParams: string = ""
+  if scanf(val, "$*($*)$s$*", name, params, afterParams):
+    if name.strip() == "operator" and params == "": #isFunctor?
+      parseVFunctionDecl(afterParams, name, params, retType, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor, true)
+      return
+    if name.find("static ") > -1:
+      isStatic = true
+      name = name.replace("static ", "")
+    isFnConst = afterParams.find("const") > -1
+    isOverride = afterParams.find("override") > -1
+    isMemberVirtual = name.find("virtual ") > -1
+    if isMemberVirtual:
+      name = name.replace("virtual ", "")
+    if isFunctor:
+      name = "operator ()"
+    if isCtor:
+      discard scanf(afterParams, ":$s$*", superCall)
+    else:
+      discard scanf(afterParams, "->$s$* ", retType)
+
+  params = "(" & params & ")"
+
+proc genMemberProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false, isFwdDecl: bool = false) =
+  assert sfCppMember * prc.flags != {}
+  let isCtor = sfConstructor in prc.flags
+  var check = initIntSet()
+  fillBackendName(m, prc)
+  fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
+  var memberOp = "#." #only virtual
+  var typ: PType
+  if isCtor:
+    typ = prc.typ.returnType
+  else:
+    typ = prc.typ.firstParamType
+  if typ.kind == tyPtr:
+    typ = typ.elementType
+    memberOp = "#->"
+  var typDesc = getTypeDescWeak(m, typ, check, dkParam)
+  let asPtrStr = rope(if asPtr: "_PTR" else: "")
+  var name, params, rettype, superCall: string = ""
+  var isFnConst, isOverride, isMemberVirtual, isStatic: bool = false
+  parseVFunctionDecl(prc.constraint.strVal, name, params, rettype, superCall, isFnConst, isOverride, isMemberVirtual, isStatic, isCtor)
+  genMemberProcParams(m, prc, superCall, rettype, name, params, check, true, false)
+  let isVirtual = sfVirtual in prc.flags or isMemberVirtual
+  var fnConst, override: string = ""
+  if isCtor:
+    name = typDesc
+  if isFnConst:
+    fnConst = " const"
+  if isFwdDecl:
+    if isStatic:
+      result.add "static "
+    if isVirtual:
+      rettype = "virtual " & rettype
+      if isOverride:
+        override = " override"
+    superCall = ""
+  else:
+    if not isCtor:
+      prc.loc.snippet = "$1$2(@)" % [memberOp, name]
+    elif superCall != "":
+      superCall = " : " & superCall
+
+    name = "$1::$2" % [typDesc, name]
+
+  result.add "N_LIB_PRIVATE "
+  result.addf("$1$2($3, $4)$5$6$7$8",
+        [rope(CallingConvToStr[prc.typ.callConv]), asPtrStr, rettype, name,
+        params, fnConst, override, superCall])
+
+proc genProcHeader(m: BModule; prc: PSym; result: var Rope; asPtr: bool = false) =
   # using static is needed for inline procs
   var check = initIntSet()
   fillBackendName(m, prc)
   fillLoc(prc.loc, locProc, prc.ast[namePos], OnUnknown)
-  var rettype, params: Rope
-  genProcParams(m, prc.typ, rettype, params, check)
+  var rettype, params: Rope = ""
+  genProcParams(m, prc.typ, rettype, params, check, true, false)
   # handle the 2 options for hotcodereloading codegen - function pointer
   # (instead of forward declaration) or header for function body with "_actual" postfix
   let asPtrStr = rope(if asPtr: "_PTR" else: "")
-  var name = prc.loc.r
-  if isReloadable(m, prc) and not asPtr:
+  var name = prc.loc.snippet
+  if not asPtr and isReloadable(m, prc):
     name.add("_actual")
   # careful here! don't access ``prc.ast`` as that could reload large parts of
   # the object graph!
-  if prc.constraint.isNil:
+  if sfCodegenDecl notin prc.flags:
     if lfExportLib in prc.loc.flags:
       if isHeaderFile in m.flags:
         result.add "N_LIB_IMPORT "
@@ -992,14 +1226,15 @@ proc genProcHeader(m: BModule, prc: PSym; result: var Rope; asPtr: bool = false)
     let asPtrStr = if asPtr: (rope("(*") & name & ")") else: name
     result.add runtimeFormat(prc.cgDeclFrmt, [rettype, asPtrStr, params])
 
+
 # ------------------ type info generation -------------------------------------
 
-proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope
+proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope
 proc getNimNode(m: BModule): Rope =
   result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)]
   inc(m.typeNodes)
 
-proc tiNameForHcr(m: BModule, name: Rope): Rope =
+proc tiNameForHcr(m: BModule; name: Rope): Rope =
   return if m.hcrOn: "(*".rope & name & ")" else: name
 
 proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
@@ -1017,7 +1252,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
   if tfIncompleteStruct in typ.flags:
     size = rope"void*"
   else:
-    size = getTypeDesc(m, origType, skVar)
+    size = getTypeDesc(m, origType, dkVar)
   m.s[cfsTypeInit3].addf(
     "$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n",
     [nameHcr, size, rope(nimtypeKind), base]
@@ -1025,7 +1260,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
   # compute type flags for GC optimization
   var flags = 0
   if not containsGarbageCollectedRef(typ): flags = flags or 1
-  if not canFormAcycle(typ): flags = flags or 2
+  if not canFormAcycle(m.g.graph, typ): flags = flags or 2
   #else echo("can contain a cycle: " & typeToString(typ))
   if flags != 0:
     m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)])
@@ -1048,11 +1283,11 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType;
   else:
     m.s[cfsStrData].addf("N_LIB_PRIVATE TNimType $1;$n", [name])
 
-proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
+proc genTypeInfoAux(m: BModule; typ, origType: PType, name: Rope;
                     info: TLineInfo) =
   var base: Rope
-  if typ.len > 0 and typ.lastSon != nil:
-    var x = typ.lastSon
+  if typ.hasElementType and typ.last != nil:
+    var x = typ.last
     if typ.kind == tyObject: x = x.skipTypes(skipPtrs)
     if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x):
       base = rope("0")
@@ -1062,23 +1297,23 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope;
     base = rope("0")
   genTypeInfoAuxBase(m, typ, origType, name, base, info)
 
-proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope =
+proc discriminatorTableName(m: BModule; objtype: PType, d: PSym): Rope =
   # bugfix: we need to search the type that contains the discriminator:
   var objtype = objtype.skipTypes(abstractPtrs)
   while lookupInRecord(objtype.n, d.name) == nil:
     objtype = objtype[0].skipTypes(abstractPtrs)
   if objtype.sym == nil:
     internalError(m.config, d.info, "anonymous obj with discriminator")
-  result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)]
+  result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)]
 
 proc rope(arg: Int128): Rope = rope($arg)
 
-proc discriminatorTableDecl(m: BModule, objtype: PType, d: PSym): Rope =
+proc discriminatorTableDecl(m: BModule; objtype: PType, d: PSym): Rope =
   cgsym(m, "TNimNode")
   var tmp = discriminatorTableName(m, objtype, d)
   result = "TNimNode* $1[$2];$n" % [tmp, rope(lengthOrd(m.config, d.typ)+1)]
 
-proc genTNimNodeArray(m: BModule, name: Rope, size: Rope) =
+proc genTNimNodeArray(m: BModule; name: Rope, size: Rope) =
   if m.hcrOn:
     m.s[cfsData].addf("static TNimNode** $1;$n", [name])
     m.hcrCreateTypeInfosProc.addf("\thcrRegisterGlobal($3, \"$1\", sizeof(TNimNode*) * $2, NULL, (void**)&$1);$n",
@@ -1086,7 +1321,7 @@ proc genTNimNodeArray(m: BModule, name: Rope, size: Rope) =
   else:
     m.s[cfsTypeInit1].addf("static TNimNode* $1[$2];$n", [name, size])
 
-proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
+proc genObjectFields(m: BModule; typ, origType: PType, n: PNode, expr: Rope;
                      info: TLineInfo) =
   case n.kind
   of nkRecList:
@@ -1109,13 +1344,13 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
     var tmp = discriminatorTableName(m, typ, field)
     var L = lengthOrd(m.config, field.typ)
     assert L > 0
-    if field.loc.r == "": fillObjectFields(m, typ)
+    if field.loc.snippet == "": fillObjectFields(m, typ)
     if field.loc.t == nil:
       internalError(m.config, n.info, "genObjectFields")
     m.s[cfsTypeInit3].addf("$1.kind = 3;$n" &
         "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
         "$1.name = $5;$n" & "$1.sons = &$6[0];$n" &
-        "$1.len = $7;$n", [expr, getTypeDesc(m, origType, skVar), field.loc.r,
+        "$1.len = $7;$n", [expr, getTypeDesc(m, origType, dkVar), field.loc.snippet,
                            genTypeInfoV1(m, field.typ, info),
                            makeCString(field.name.s),
                            tmp, rope(L)])
@@ -1147,56 +1382,53 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope;
     # Do not produce code for void types
     if isEmptyType(field.typ): return
     if field.bitsize == 0:
-      if field.loc.r == "": fillObjectFields(m, typ)
+      if field.loc.snippet == "": fillObjectFields(m, typ)
       if field.loc.t == nil:
         internalError(m.config, n.info, "genObjectFields")
       m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
           "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" &
-          "$1.name = $5;$n", [expr, getTypeDesc(m, origType, skVar),
-          field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
+          "$1.name = $5;$n", [expr, getTypeDesc(m, origType, dkVar),
+          field.loc.snippet, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)])
   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(m.config, info, "request for RTTI generation for incomplete object: " &
-                        typeToString(typ))
-    genTypeInfoAux(m, typ, origType, name, info)
-  else:
-    genTypeInfoAuxBase(m, typ, origType, name, rope("0"), info)
+proc genObjectInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
+  assert typ.kind == tyObject
+  if incompleteType(typ):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+                      typeToString(typ))
+  genTypeInfoAux(m, typ, origType, name, info)
   var tmp = getNimNode(m)
-  if not isImportedType(typ):
+  if (not isImportedType(typ)) or tfCompleteStruct in typ.flags:
     genObjectFields(m, typ, origType, typ.n, tmp, info)
   m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), tmp])
-  var t = typ[0]
+  var t = typ.baseClass
   while t != nil:
     t = t.skipTypes(skipPtrs)
     t.flags.incl tfObjHasKids
-    t = t[0]
+    t = t.baseClass
 
-proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) =
+proc genTupleInfo(m: BModule; typ, origType: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAuxBase(m, typ, typ, name, rope("0"), info)
   var expr = getNimNode(m)
-  if typ.len > 0:
-    var tmp = getTempName(m) & "_" & $typ.len
-    genTNimNodeArray(m, tmp, rope(typ.len))
-    for i in 0..<typ.len:
-      var a = typ[i]
+  if not typ.isEmptyTupleType:
+    var tmp = getTempName(m) & "_" & $typ.kidsLen
+    genTNimNodeArray(m, tmp, rope(typ.kidsLen))
+    for i, a in typ.ikids:
       var tmp2 = getNimNode(m)
       m.s[cfsTypeInit3].addf("$1[$2] = &$3;$n", [tmp, rope(i), tmp2])
       m.s[cfsTypeInit3].addf("$1.kind = 1;$n" &
           "$1.offset = offsetof($2, Field$3);$n" &
           "$1.typ = $4;$n" &
           "$1.name = \"Field$3\";$n",
-           [tmp2, getTypeDesc(m, origType, skVar), rope(i), genTypeInfoV1(m, a, info)])
+           [tmp2, getTypeDesc(m, origType, dkVar), rope(i), genTypeInfoV1(m, a, info)])
     m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n",
-         [expr, rope(typ.len), tmp])
+         [expr, rope(typ.kidsLen), tmp])
   else:
     m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2;$n",
-         [expr, rope(typ.len)])
+         [expr, rope(typ.kidsLen)])
   m.s[cfsTypeInit3].addf("$1.node = &$2;$n", [tiNameForHcr(m, name), expr])
 
-proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
+proc genEnumInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
   # Type information for enumerations is quite heavy, so we do some
   # optimizations here: The ``typ`` field is never set, as it is redundant
   # anyway. We generate a cstring array and a loop over it. Exceptional
@@ -1204,7 +1436,7 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
   genTypeInfoAux(m, typ, typ, name, info)
   var nodePtrs = getTempName(m) & "_" & $typ.n.len
   genTNimNodeArray(m, nodePtrs, rope(typ.n.len))
-  var enumNames, specialCases: Rope
+  var enumNames, specialCases: Rope = ""
   var firstNimNode = m.typeNodes
   var hasHoles = false
   for i in 0..<typ.n.len:
@@ -1237,21 +1469,21 @@ proc genEnumInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
     # 1 << 2 is {ntfEnumHole}
     m.s[cfsTypeInit3].addf("$1.flags = 1<<2;$n", [tiNameForHcr(m, name)])
 
-proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
-  assert(typ[0] != nil)
+proc genSetInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
+  assert(typ.elementType != nil)
   genTypeInfoAux(m, typ, typ, name, info)
   var tmp = getNimNode(m)
   m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 0;$n$3.node = &$1;$n",
        [tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)])
 
-proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) =
-  genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ[1], info), info)
+proc genArrayInfo(m: BModule; typ: PType, name: Rope; info: TLineInfo) =
+  genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ.elementType, info), info)
 
 proc fakeClosureType(m: BModule; owner: PSym): PType =
   # we generate the same RTTI as for a tuple[pointer, ref tuple[]]
-  result = newType(tyTuple, nextTypeId m.idgen, owner)
-  result.rawAddSon(newType(tyPointer, nextTypeId m.idgen, owner))
-  var r = newType(tyRef, nextTypeId m.idgen, owner)
+  result = newType(tyTuple, m.idgen, owner)
+  result.rawAddSon(newType(tyPointer, m.idgen, owner))
+  var r = newType(tyRef, m.idgen, owner)
   let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false)
   r.rawAddSon(obj)
   result.rawAddSon(r)
@@ -1261,9 +1493,9 @@ include ccgtrav
 proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
   genProc(m, s)
   m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n",
-     [result, s.loc.r])
+     [result, s.loc.snippet])
 
-proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
+proc declareNimType(m: BModule; name: string; str: Rope, module: int) =
   let nr = rope(name)
   if m.hcrOn:
     m.s[cfsStrData].addf("static $2* $1;$n", [str, nr])
@@ -1273,30 +1505,68 @@ proc declareNimType(m: BModule, name: string; str: Rope, module: int) =
     m.s[cfsStrData].addf("extern $2 $1;$n", [str, nr])
 
 proc genTypeInfo2Name(m: BModule; t: PType): Rope =
-  var res = "|"
   var it = t
-  while it != nil:
-    it = it.skipTypes(skipPtrs)
-    if it.sym != nil and tfFromGeneric notin it.flags:
-      var m = it.sym.owner
-      while m != nil and m.kind != skModule: m = m.owner
-      if m == nil or sfSystemModule in m.flags:
-        # produce short names for system types:
-        res.add it.sym.name.s
-      else:
-        var p = m.owner
-        if p != nil and p.kind == skPackage:
-          res.add p.name.s & "."
-        res.add m.name.s & "."
-        res.add it.sym.name.s
+  it = it.skipTypes(skipPtrs)
+  if it.sym != nil and tfFromGeneric notin it.flags:
+    var m = it.sym.owner
+    while m != nil and m.kind != skModule: m = m.owner
+    if m == nil or sfSystemModule in m.flags:
+      # produce short names for system types:
+      result = it.sym.name.s
     else:
-      res.add $hashType(it)
-    res.add "|"
-    it = it[0]
-  result = makeCString(res)
+      var p = m.owner
+      result = ""
+      if p != nil and p.kind == skPackage:
+        result.add p.name.s & "."
+      result.add m.name.s & "."
+      result.add it.sym.name.s
+  else:
+    result = $hashType(it, m.config)
+  result = makeCString(result)
 
 proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0
 
+proc generateRttiDestructor(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; theProc: PSym): PSym =
+  # the wrapper is roughly like:
+  # proc rttiDestroy(x: pointer) =
+  #   `=destroy`(cast[ptr T](x)[])
+  let procname = getIdent(g.cache, "rttiDestroy")
+  result = newSym(skProc, procname, idgen, owner, info)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
+
+  dest.typ = getSysType(g, info, tyPointer)
+
+  result.typ = newProcType(info, idgen, owner)
+  result.typ.addParam dest
+
+  var n = newNodeI(nkProcDef, info, bodyPos+1)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  let body = newNodeI(nkStmtList, info)
+  let castType = makePtrType(typ, idgen)
+  if theProc.typ.firstParamType.kind != tyVar:
+    body.add newTreeI(nkCall, info, newSymNode(theProc), newDeref(newTreeIT(
+      nkCast, info, castType, newNodeIT(nkType, info, castType),
+      newSymNode(dest)
+    ))
+    )
+  else:
+    let addrOf = newNodeIT(nkHiddenAddr, info, theProc.typ.firstParamType)
+    addrOf.add newDeref(newTreeIT(
+      nkCast, info, castType, newNodeIT(nkType, info, castType),
+      newSymNode(dest)
+    ))
+    body.add newTreeI(nkCall, info, newSymNode(theProc),
+      addrOf
+    )
+  n[bodyPos] = body
+  result.ast = n
+
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
+
 proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result: var Rope) =
   let theProc = getAttachedOp(m.g.graph, t, op)
   if theProc != nil and not isTrivialProc(m.g.graph, theProc):
@@ -1307,11 +1577,17 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
       localError(m.config, info,
         theProc.name.s & " needs to have the 'nimcall' calling convention")
 
-    genProc(m, theProc)
-    result.add theProc.loc.r
+    if op == attachedDestructor:
+      let wrapper = generateRttiDestructor(m.g.graph, t, theProc.owner, attachedDestructor,
+                theProc.info, m.idgen, theProc)
+      genProc(m, wrapper)
+      result.add wrapper.loc.snippet
+    else:
+      genProc(m, theProc)
+      result.add theProc.loc.snippet
 
     when false:
-      if not canFormAcycle(t) and op == attachedTrace:
+      if not canFormAcycle(m.g.graph, t) and op == attachedTrace:
         echo "ayclic but has this =trace ", t, " ", theProc.ast
   else:
     when false:
@@ -1322,21 +1598,49 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp; result:
         internalError(m.config, info, "no attached trace proc found")
     result.add rope("NIM_NIL")
 
-proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) =
-  var typeName: Rope
-  if t.kind in {tyObject, tyDistinct}:
-    if incompleteType(t):
-      localError(m.config, info, "request for RTTI generation for incomplete object: " &
-                 typeToString(t))
-    typeName = genTypeInfo2Name(m, t)
-  else:
-    typeName = rope("NIM_NIL")
-
+proc getObjDepth(t: PType): int16 =
+  var x = t
+  result = -1
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    x = x[0]
+    inc(result)
+
+proc genDisplayElem(d: MD5Digest): uint32 =
+  result = 0
+  for i in 0..3:
+    result += uint32(d[i])
+    result = result shl 8
+
+proc genDisplay(m: BModule; t: PType, depth: int): Rope =
+  result = Rope"{"
+  var x = t
+  var seqs = newSeq[string](depth+1)
+  var i = 0
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    seqs[i] = $genDisplayElem(MD5Digest(hashType(x, m.config)))
+    x = x[0]
+    inc i
+
+  for i in countdown(depth, 1):
+    result.add seqs[i] & ", "
+  result.add seqs[0]
+  result.add "}"
+
+proc genVTable(seqs: seq[PSym]): string =
+  result = "{"
+  for i in 0..<seqs.len:
+    if i > 0: result.add ", "
+    result.add "(void *) " & seqs[i].loc.snippet
+  result.add "}"
+
+proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
   cgsym(m, "TNimTypeV2")
   m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
 
   var flags = 0
-  if not canFormAcycle(t): flags = flags or 1
+  if not canFormAcycle(m.g.graph, t): flags = flags or 1
 
   var typeEntry = newRopeAppender()
   addf(typeEntry, "$1.destructor = (void*)", [name])
@@ -1345,22 +1649,99 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn
   addf(typeEntry, "; $1.traceImpl = (void*)", [name])
   genHook(m, t, info, attachedTrace, typeEntry)
 
-  addf(typeEntry, "; $1.name = $2;$n; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.flags = $4;",
-    [name, typeName, getTypeDesc(m, t), rope(flags)])
+  let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
+
+  if t.kind in {tyObject, tyDistinct} and incompleteType(t):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+              typeToString(t))
+
+  if isDefined(m.config, "nimTypeNames"):
+    var typeName: Rope
+    if t.kind in {tyObject, tyDistinct}:
+      typeName = genTypeInfo2Name(m, t)
+    else:
+      typeName = rope("NIM_NIL")
+    addf(typeEntry, "; $1.name = $2", [name, typeName])
+  addf(typeEntry, "; $1.size = sizeof($2); $1.align = (NI16) NIM_ALIGNOF($2); $1.depth = $3; $1.flags = $4;",
+    [name, getTypeDesc(m, t), rope(objDepth), rope(flags)])
+
+  if objDepth >= 0:
+    let objDisplay = genDisplay(m, t, objDepth)
+    let objDisplayStore = getTempName(m)
+    m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay])
+    addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)])
+
+  let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
+  if dispatchMethods.len > 0:
+    let vTablePointerName = getTempName(m)
+    m.s[cfsVars].addf("static void* $1[$2] = $3;$n", [vTablePointerName, rope(dispatchMethods.len), genVTable(dispatchMethods)])
+    for i in dispatchMethods:
+      genProcPrototype(m, i)
+    addf(typeEntry, "$1.vTable = $2;$n", [name, vTablePointerName])
 
   m.s[cfsTypeInit3].add typeEntry
 
-  if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions:
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
+    discard genTypeInfoV1(m, t, info)
+
+proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineInfo) =
+  cgsym(m, "TNimTypeV2")
+  m.s[cfsStrData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name])
+
+  var flags = 0
+  if not canFormAcycle(m.g.graph, t): flags = flags or 1
+
+  var typeEntry = newRopeAppender()
+  addf(typeEntry, "N_LIB_PRIVATE TNimTypeV2 $1 = {", [name])
+  add(typeEntry, ".destructor = (void*)")
+  genHook(m, t, info, attachedDestructor, typeEntry)
+
+  let objDepth = if t.kind == tyObject: getObjDepth(t) else: -1
+
+  if t.kind in {tyObject, tyDistinct} and incompleteType(t):
+    localError(m.config, info, "request for RTTI generation for incomplete object: " &
+              typeToString(t))
+
+  addf(typeEntry, ", .size = sizeof($1), .align = (NI16) NIM_ALIGNOF($1), .depth = $2",
+    [getTypeDesc(m, t), rope(objDepth)])
+
+  if objDepth >= 0:
+    let objDisplay = genDisplay(m, t, objDepth)
+    let objDisplayStore = getTempName(m)
+    m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), dkVar), objDisplayStore, rope(objDepth+1), objDisplay])
+    addf(typeEntry, ", .display = $1", [rope(objDisplayStore)])
+  if isDefined(m.config, "nimTypeNames"):
+    var typeName: Rope
+    if t.kind in {tyObject, tyDistinct}:
+      typeName = genTypeInfo2Name(m, t)
+    else:
+      typeName = rope("NIM_NIL")
+    addf(typeEntry, ", .name = $1", [typeName])
+  add(typeEntry, ", .traceImpl = (void*)")
+  genHook(m, t, info, attachedTrace, typeEntry)
+
+  let dispatchMethods = toSeq(getMethodsPerType(m.g.graph, t))
+  if dispatchMethods.len > 0:
+    addf(typeEntry, ", .flags = $1", [rope(flags)])
+    for i in dispatchMethods:
+      genProcPrototype(m, i)
+    addf(typeEntry, ", .vTable = $1};$n", [genVTable(dispatchMethods)])
+    m.s[cfsVars].add typeEntry
+  else:
+    addf(typeEntry, ", .flags = $1};$n", [rope(flags)])
+    m.s[cfsVars].add typeEntry
+
+  if t.kind == tyObject and t.baseClass != nil and optEnableDeepCopy in m.config.globalOptions:
     discard genTypeInfoV1(m, t, info)
 
-proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
+proc genTypeInfoV2(m: BModule; t: PType; info: TLineInfo): Rope =
   let origType = t
   # distinct types can have their own destructors
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses - {tyDistinct})
 
   let prefixTI = if m.hcrOn: "(" else: "(&"
 
-  let sig = hashType(origType)
+  let sig = hashType(origType, m.config)
   result = m.typeInfoMarkerV2.getOrDefault(sig)
   if result != "":
     return prefixTI.rope & result & ")".rope
@@ -1386,14 +1767,17 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope =
     return prefixTI.rope & result & ")".rope
 
   m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner)
-  genTypeInfoV2Impl(m, t, origType, result, info)
+  if m.compileToCpp or m.hcrOn:
+    genTypeInfoV2OldImpl(m, t, origType, result, info)
+  else:
+    genTypeInfoV2Impl(m, t, origType, result, info)
   result = prefixTI.rope & result & ")".rope
 
 proc openArrayToTuple(m: BModule; t: PType): PType =
-  result = newType(tyTuple, nextTypeId m.idgen, t.owner)
-  let p = newType(tyPtr, nextTypeId m.idgen, t.owner)
-  let a = newType(tyUncheckedArray, nextTypeId m.idgen, t.owner)
-  a.add t.lastSon
+  result = newType(tyTuple, m.idgen, t.owner)
+  let p = newType(tyPtr, m.idgen, t.owner)
+  let a = newType(tyUncheckedArray, m.idgen, t.owner)
+  a.add t.elementType
   p.add a
   result.add p
   result.add getSysType(m.g.graph, t.owner.info, tyInt)
@@ -1403,8 +1787,7 @@ proc typeToC(t: PType): string =
   ## to be unique.
   let s = typeToString(t)
   result = newStringOfCap(s.len)
-  for i in 0..<s.len:
-    let c = s[i]
+  for c in s:
     case c
     of 'a'..'z':
       result.add c
@@ -1425,13 +1808,13 @@ proc typeToC(t: PType): string =
       # be clashes with our special meanings
       result.addInt ord(c)
 
-proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
+proc genTypeInfoV1(m: BModule; t: PType; info: TLineInfo): Rope =
   let origType = t
   var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
 
   let prefixTI = if m.hcrOn: "(" else: "(&"
 
-  let sig = hashType(origType)
+  let sig = hashType(origType, m.config)
   result = m.typeInfoMarker.getOrDefault(sig)
   if result != "":
     return prefixTI.rope & result & ")".rope
@@ -1475,11 +1858,11 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
   of tyPointer, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64, tyVar, tyLent:
     genTypeInfoAuxBase(m, t, t, result, rope"0", info)
   of tyStatic:
-    if t.n != nil: result = genTypeInfoV1(m, lastSon t, info)
+    if t.n != nil: result = genTypeInfoV1(m, skipModifier t, info)
     else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')')
   of tyUserTypeClasses:
     internalAssert m.config, t.isResolvedUserTypeClass
-    return genTypeInfoV1(m, t.lastSon, info)
+    return genTypeInfoV1(m, t.skipModifier, info)
   of tyProc:
     if t.callConv != ccClosure:
       genTypeInfoAuxBase(m, t, t, result, rope"0", info)
@@ -1526,11 +1909,23 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope =
 
   result = prefixTI.rope & result & ")".rope
 
-proc genTypeSection(m: BModule, n: PNode) =
-  discard
-
-proc genTypeInfo*(config: ConfigRef, m: BModule, t: PType; info: TLineInfo): Rope =
+proc genTypeInfo*(config: ConfigRef, m: BModule; t: PType; info: TLineInfo): Rope =
   if optTinyRtti in config.globalOptions:
     result = genTypeInfoV2(m, t, info)
   else:
     result = genTypeInfoV1(m, t, info)
+
+proc genTypeSection(m: BModule, n: PNode) =
+  var intSet = initIntSet()
+  for i in 0..<n.len:
+    if len(n[i]) == 0: continue
+    if n[i][0].kind != nkPragmaExpr: continue
+    for p in 0..<n[i][0].len:
+      if (n[i][0][p].kind notin {nkSym, nkPostfix}): continue
+      var s = n[i][0][p]
+      if s.kind == nkPostfix:
+        s = n[i][0][p][1]
+      if {sfExportc, sfCompilerProc} * s.sym.flags == {sfExportc}:
+        discard getTypeDescAux(m, s.typ, intSet, descKindFromSymKind(s.sym.kind))
+        if m.g.generatedHeader != nil:
+          discard getTypeDescAux(m.g.generatedHeader, s.typ, intSet, descKindFromSymKind(s.sym.kind))
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index e19fccfa7..c0e574186 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -10,8 +10,10 @@
 # This module declares some helpers for the C code generator.
 
 import
-  ast, types, hashes, strutils, msgs, wordrecg,
-  platform, trees, options, cgendata
+  ast, types, msgs, wordrecg,
+  platform, trees, options, cgendata, mangleutils
+
+import std/[hashes, strutils, formatfloat]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -19,13 +21,16 @@ when defined(nimPreviewSlimSystem):
 proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
   case n.kind
   of nkStmtList:
+    result = nil
     for i in 0..<n.len:
       result = getPragmaStmt(n[i], w)
       if result != nil: break
   of nkPragma:
+    result = nil
     for i in 0..<n.len:
       if whichPragma(n[i]) == w: return n[i]
-  else: discard
+  else:
+    result = nil
 
 proc stmtsContainPragma*(n: PNode, w: TSpecialWord): bool =
   result = getPragmaStmt(n, w) != nil
@@ -53,7 +58,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt =
     a = a + (a shl 3)
     a = a xor (a shr 11)
     a = a + (a shl 15)
-    result = cast[Hash](a)
+    result = cast[Hash](uint(a))
 
 template getUniqueType*(key: PType): PType = key
 
@@ -63,53 +68,6 @@ proc makeSingleLineCString*(s: string): string =
     c.toCChar(result)
   result.add('\"')
 
-proc mangle*(name: string): string =
-  result = newStringOfCap(name.len)
-  var start = 0
-  if name[0] in Digits:
-    result.add("X" & name[0])
-    start = 1
-  var requiresUnderscore = false
-  template special(x) =
-    result.add x
-    requiresUnderscore = true
-  for i in start..<name.len:
-    let c = name[i]
-    case c
-    of 'a'..'z', '0'..'9', 'A'..'Z':
-      result.add(c)
-    of '_':
-      # we generate names like 'foo_9' for scope disambiguations and so
-      # disallow this here:
-      if i > 0 and i < name.len-1 and name[i+1] in Digits:
-        discard
-      else:
-        result.add(c)
-    of '$': special "dollar"
-    of '%': special "percent"
-    of '&': special "amp"
-    of '^': special "roof"
-    of '!': special "emark"
-    of '?': special "qmark"
-    of '*': special "star"
-    of '+': special "plus"
-    of '-': special "minus"
-    of '/': special "slash"
-    of '\\': special "backslash"
-    of '=': special "eq"
-    of '<': special "lt"
-    of '>': special "gt"
-    of '~': special "tilde"
-    of ':': special "colon"
-    of '.': special "dot"
-    of '@': special "at"
-    of '|': special "bar"
-    else:
-      result.add("X" & toHex(ord(c), 2))
-      requiresUnderscore = true
-  if requiresUnderscore:
-    result.add "_"
-
 proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind =
   case int(getSize(conf, typ))
   of 1: result = ctInt8
@@ -122,13 +80,19 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
   var pt = skipTypes(s.typ, typedescInst)
   assert skResult != s.kind
 
-  if tfByRef in pt.flags: return true
+  #note precedence: params override types
+  if optByRef in s.options: return true
+  elif sfByCopy in s.flags: return false
+  elif tfByRef in pt.flags: return true
   elif tfByCopy in pt.flags: return false
   case pt.kind
   of tyObject:
     if s.typ.sym != nil and sfForward in s.typ.sym.flags:
       # forwarded objects are *always* passed by pointers for consistency!
       result = true
+    elif s.typ.kind == tySink and conf.selectedGC notin {gcArc, gcAtomicArc, gcOrc, gcHooks}:
+      # bug #23354:
+      result = false
     elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3):
       result = true           # requested anyway
     elif (tfFinal in pt.flags) and (pt[0] == nil):
@@ -145,3 +109,62 @@ proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool =
     result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or
       pt.kind == tySet and mapSetType(conf, pt) == ctArray)
 
+proc encodeName*(name: string): string =
+  result = mangle(name)
+  result = $result.len & result
+
+proc makeUnique(m: BModule; s: PSym, name: string = ""): string =
+  result = if name == "": s.name.s else: name
+  result.add "__"
+  result.add m.g.graph.ifaces[s.itemId.module].uniqueName
+  result.add "_u"
+  result.add $s.itemId.item
+
+proc encodeSym*(m: BModule; s: PSym; makeUnique: bool = false): string =
+  #Module::Type
+  var name = s.name.s
+  if makeUnique:
+    name = makeUnique(m, s, name)
+  "N" & encodeName(s.skipGenericOwner.name.s) & encodeName(name) & "E"
+
+proc encodeType*(m: BModule; t: PType): string =
+  result = ""
+  var kindName = ($t.kind)[2..^1]
+  kindName[0] = toLower($kindName[0])[0]
+  case t.kind
+  of tyObject, tyEnum, tyDistinct, tyUserTypeClass, tyGenericParam:
+    result = encodeSym(m, t.sym)
+  of tyGenericInst, tyUserTypeClassInst, tyGenericBody:
+    result = encodeName(t[0].sym.name.s)
+    result.add "I"
+    for i in 1..<t.len - 1:
+      result.add encodeType(m, t[i])
+    result.add "E"
+  of tySequence, tyOpenArray, tyArray, tyVarargs, tyTuple, tyProc, tySet, tyTypeDesc,
+    tyPtr, tyRef, tyVar, tyLent, tySink, tyStatic, tyUncheckedArray, tyOr, tyAnd, tyBuiltInTypeClass:
+    result =
+      case t.kind:
+      of tySequence: encodeName("seq")
+      else: encodeName(kindName)
+    result.add "I"
+    for i in 0..<t.len:
+      let s = t[i]
+      if s.isNil: continue
+      result.add encodeType(m, s)
+    result.add "E"
+  of tyRange:
+    var val = "range_"
+    if t.n[0].typ.kind in {tyFloat..tyFloat128}:
+      val.addFloat t.n[0].floatVal
+      val.add "_"
+      val.addFloat t.n[1].floatVal
+    else:
+      val.add $t.n[0].intVal & "_" & $t.n[1].intVal
+    result = encodeName(val)
+  of tyString..tyUInt64, tyPointer, tyBool, tyChar, tyVoid, tyAnything, tyNil, tyEmpty:
+    result = encodeName(kindName)
+  of tyAlias, tyInferred, tyOwned:
+    result = encodeType(m, t.elementType)
+  else:
+    assert false, "encodeType " & $t.kind
+
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 794abc1ad..091f5c842 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -10,12 +10,17 @@
 ## This module implements the C code generator.
 
 import
-  ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
+  ast, astalgo, trees, platform, magicsys, extccomp, options,
   nversion, nimsets, msgs, bitsets, idents, types,
-  ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
+  ccgutils, ropes, wordrecg, treetab, cgmeth,
   rodutils, renderer, cgendata, aliases,
-  lowerings, tables, sets, ndi, lineinfos, pathutils, transf,
-  injectdestructors, astmsgs, modulepaths
+  lowerings, ndi, lineinfos, pathutils, transf,
+  injectdestructors, astmsgs, modulepaths, pushpoppragmas,
+  mangleutils
+
+from expanddefaults import caseObjDefaultBranch
+
+import pipelineutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -23,10 +28,16 @@ when defined(nimPreviewSlimSystem):
 when not defined(leanCompiler):
   import spawn, semparallel
 
-import strutils except `%`, addf # collides with ropes.`%`
+import std/strutils except `%`, addf # collides with ropes.`%`
 
 from ic / ic import ModuleBackendFlag
-import dynlib
+import std/[dynlib, math, tables, sets, os, intsets, hashes]
+
+const
+  # we use some ASCII control characters to insert directives that will be converted to real code in a postprocessing pass
+  postprocessDirStart = '\1'
+  postprocessDirSep = '\31'
+  postprocessDirEnd = '\23'
 
 when not declared(dynlib.libCandidates):
   proc libCandidates(s: string, dest: var seq[string]) =
@@ -59,12 +70,9 @@ proc findPendingModule(m: BModule, s: PSym): BModule =
     var ms = getModule(s)
     result = m.g.modules[ms.position]
 
-proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) =
-  result.k = k
-  result.storage = s
-  result.lode = lode
-  result.r = ""
-  result.flags = {}
+proc initLoc(k: TLocKind, lode: PNode, s: TStorageLoc, flags: TLocFlags = {}): TLoc =
+  result = TLoc(k: k, storage: s, lode: lode,
+                snippet: "", flags: flags)
 
 proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.inline.} =
   # fills the loc if it is not already initialized
@@ -72,7 +80,7 @@ proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, r: Rope, s: TStorageLoc) {.i
     a.k = k
     a.lode = lode
     a.storage = s
-    if a.r == "": a.r = r
+    if a.snippet == "": a.snippet = r
 
 proc fillLoc(a: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) {.inline.} =
   # fills the loc if it is not already initialized
@@ -119,7 +127,7 @@ proc getModuleDllPath(m: BModule, module: int): Rope =
 proc getModuleDllPath(m: BModule, s: PSym): Rope =
   result = getModuleDllPath(m.g.modules[s.itemId.module])
 
-import macros
+import std/macros
 
 proc cgFormatValue(result: var string; value: string) =
   result.add value
@@ -211,19 +219,23 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope =
       let ident = args[j-1]
       flushStrLit()
       result.add newCall(formatValue, resVar, newCall(ident"cgsymValue", m, ident))
-    var start = i
-    while i < frmt.len:
-      if frmt[i] != '$' and frmt[i] != '#': inc(i)
-      else: break
-    if i - 1 >= start:
-      strLit.add(substr(frmt, start, i - 1))
+    elif frmt[i] == '#' and frmt[i+1] == '#':
+      inc(i, 2)
+      strLit.add("#")
+    else:
+      strLit.add(frmt[i])
+      inc(i)
 
   flushStrLit()
   result.add newCall(ident"rope", resVar)
 
 proc addIndent(p: BProc; result: var Rope) =
-  for i in 0..<p.blocks.len:
-    result.add "\t".rope
+  var i = result.len
+  let newLen = i + p.blocks.len
+  result.setLen newLen
+  while i < newLen:
+    result[i] = '\t'
+    inc i
 
 template appcg(m: BModule, c: var Rope, frmt: FormatStr,
            args: untyped) =
@@ -260,14 +272,28 @@ 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; conf: ConfigRef) =
+proc genPostprocessDir(field1, field2, field3: string): string =
+  result = postprocessDirStart & field1 & postprocessDirSep & field2 & postprocessDirSep & field3 & postprocessDirEnd
+
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; conf: ConfigRef) =
   assert line >= 0
   if optLineDir in conf.options and line > 0:
-    r.addf("$N#line $2 $1$N",
-        [rope(makeSingleLineCString(filename)), rope(line)])
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
+    else:
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
+
+proc genCLineDir(r: var Rope, fileIdx: FileIndex, line: int; p: BProc; info: TLineInfo; lastFileIndex: FileIndex) =
+  assert line >= 0
+  if optLineDir in p.config.options and line > 0:
+    if fileIdx == InvalidFileIdx:
+      r.add(rope("\n#line " & $line & " \"generated_not_to_break_here\"\n"))
+    else:
+      r.add(rope("\n#line " & $line & " FX_" & $fileIdx.int32 & "\n"))
 
 proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) =
-  genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf)
+  if optLineDir in conf.options:
+    genCLineDir(r, info.fileIndex, info.safeLineNm, conf)
 
 proc freshLineInfo(p: BProc; info: TLineInfo): bool =
   if p.lastLineInfo.line != info.line or
@@ -275,18 +301,29 @@ proc freshLineInfo(p: BProc; info: TLineInfo): bool =
     p.lastLineInfo.line = info.line
     p.lastLineInfo.fileIndex = info.fileIndex
     result = true
+  else:
+    result = false
+
+proc genCLineDir(r: var Rope, p: BProc, info: TLineInfo; conf: ConfigRef) =
+  if optLineDir in conf.options:
+    let lastFileIndex = p.lastLineInfo.fileIndex
+    if freshLineInfo(p, info):
+      genCLineDir(r, info.fileIndex, info.safeLineNm, p, info, lastFileIndex)
 
 proc genLineDir(p: BProc, t: PNode) =
+  if p == p.module.preInitProc: return
   let line = t.info.safeLineNm
 
   if optEmbedOrigSrc in p.config.globalOptions:
     p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L")
-  genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config)
+  let lastFileIndex = p.lastLineInfo.fileIndex
+  let freshLine = freshLineInfo(p, t.info)
+  if freshLine:
+    genCLineDir(p.s(cpsStmts), t.info.fileIndex, line, p, t.info, lastFileIndex)
   if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and
       (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx:
-    if freshLineInfo(p, t.info):
-      linefmt(p, cpsStmts, "nimln_($1, $2);$n",
-              [line, quotedFilename(p.config, t.info)])
+      if freshLine:
+        line(p, cpsStmts, genPostprocessDir("nimln", $line, $t.info.fileIndex.int32))
 
 proc accessThreadLocalVar(p: BProc, s: PSym)
 proc emulatedThreadVars(conf: ConfigRef): bool {.inline.}
@@ -303,15 +340,15 @@ proc getTempName(m: BModule): Rope =
 proc rdLoc(a: TLoc): Rope =
   # 'read' location (deref if indirect)
   if lfIndirect in a.flags:
-    result = "(*" & a.r & ")"
+    result = "(*" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc addRdLoc(a: TLoc; result: var Rope) =
   if lfIndirect in a.flags:
-    result.add "(*" & a.r & ")"
+    result.add "(*" & a.snippet & ")"
   else:
-    result.add a.r
+    result.add a.snippet
 
 proc lenField(p: BProc): Rope {.inline.} =
   result = rope(if p.module.compileToCpp: "len" else: "Sup.len")
@@ -322,12 +359,21 @@ proc lenExpr(p: BProc; a: TLoc): Rope =
   else:
     result = "($1 ? $1->$2 : 0)" % [rdLoc(a), lenField(p)]
 
+proc dataFieldAccessor(p: BProc, sym: Rope): Rope =
+  if optSeqDestructors in p.config.globalOptions:
+    result = "(" & sym & ").p"
+  else:
+    result = sym
+
 proc dataField(p: BProc): Rope =
   if optSeqDestructors in p.config.globalOptions:
     result = rope".p->data"
   else:
     result = rope"->data"
 
+proc genProcPrototype(m: BModule, sym: PSym)
+
+include cbuilder
 include ccgliterals
 include ccgtypes
 
@@ -339,23 +385,23 @@ template mapTypeChooser(n: PNode): TSymKind =
 template mapTypeChooser(a: TLoc): TSymKind = mapTypeChooser(a.lode)
 
 proc addAddrLoc(conf: ConfigRef; a: TLoc; result: var Rope) =
-  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
-    result.add "(&" & a.r & ")"
+  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
+    result.add "(&" & a.snippet & ")"
   else:
-    result.add a.r
+    result.add a.snippet
 
 proc addrLoc(conf: ConfigRef; a: TLoc): Rope =
-  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a)) != ctArray:
-    result = "(&" & a.r & ")"
+  if lfIndirect notin a.flags and mapType(conf, a.t, mapTypeChooser(a) == skParam) != ctArray:
+    result = "(&" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc byRefLoc(p: BProc; a: TLoc): Rope =
-  if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a)) != ctArray and not
+  if lfIndirect notin a.flags and mapType(p.config, a.t, mapTypeChooser(a) == skParam) != ctArray and not
       p.module.compileToCpp:
-    result = "(&" & a.r & ")"
+    result = "(&" & a.snippet & ")"
   else:
-    result = a.r
+    result = a.snippet
 
 proc rdCharLoc(a: TLoc): Rope =
   # read a location that may need a char-cast:
@@ -366,6 +412,9 @@ proc rdCharLoc(a: TLoc): Rope =
 type
   TAssignmentFlag = enum
     needToCopy
+    needToCopySinkParam
+    needTempForOpenArray
+    needAssignCall
   TAssignmentFlags = set[TAssignmentFlag]
 
 proc genObjConstr(p: BProc, e: PNode, d: var TLoc)
@@ -397,13 +446,13 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc,
       linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)])
   of frEmbedded:
     if optTinyRtti in p.config.globalOptions:
-      var tmp: TLoc
+      var tmp: TLoc = default(TLoc)
       if mode == constructRefObj:
         let objType = t.skipTypes(abstractInst+{tyRef})
         rawConstExpr(p, newNodeIT(nkType, a.lode.info, objType), tmp)
         linefmt(p, cpsStmts,
             "#nimCopyMem((void*)$1, (NIM_CONST void*)&$2, sizeof($3));$n",
-            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, mapTypeChooser(a))])
+            [rdLoc(a), rdLoc(tmp), getTypeDesc(p.module, objType, descKindFromSymKind mapTypeChooser(a))])
       else:
         rawConstExpr(p, newNodeIT(nkType, a.lode.info, t), tmp)
         genAssignment(p, a, tmp, {})
@@ -434,9 +483,12 @@ include ccgreset
 proc resetLoc(p: BProc, loc: var TLoc) =
   let containsGcRef = optSeqDestructors notin p.config.globalOptions and containsGarbageCollectedRef(loc.t)
   let typ = skipTypes(loc.t, abstractVarRange)
-  if isImportedCppType(typ): return
+  if isImportedCppType(typ): 
+    var didGenTemp = false
+    linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(loc), genCppInitializer(p.module, p, typ, didGenTemp)])
+    return
   if optSeqDestructors in p.config.globalOptions and typ.kind in {tyString, tySequence}:
-    assert loc.r != ""
+    assert loc.snippet != ""
 
     let atyp = skipTypes(loc.t, abstractInst)
     if atyp.kind in {tyVar, tyLent}:
@@ -445,9 +497,8 @@ proc resetLoc(p: BProc, loc: var TLoc) =
       linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
   elif not isComplexValueType(typ):
     if containsGcRef:
-      var nilLoc: TLoc
-      initLoc(nilLoc, locTemp, loc.lode, OnStack)
-      nilLoc.r = rope("NIM_NIL")
+      var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
+      nilLoc.snippet = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc)
     else:
       linefmt(p, cpsStmts, "$1 = 0;$n", [rdLoc(loc)])
@@ -463,9 +514,17 @@ proc resetLoc(p: BProc, loc: var TLoc) =
     else:
       # array passed as argument decayed into pointer, bug #7332
       # so we use getTypeDesc here rather than rdLoc(loc)
-      linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
-              [addrLoc(p.config, loc),
-              getTypeDesc(p.module, loc.t, mapTypeChooser(loc))])
+      let tyDesc = getTypeDesc(p.module, loc.t, descKindFromSymKind mapTypeChooser(loc))
+      if p.module.compileToCpp and isOrHasImportedCppType(typ):
+        if lfIndirect in loc.flags:
+          #C++ cant be just zeroed. We need to call the ctors
+          var tmp = getTemp(p, loc.t)
+          linefmt(p, cpsStmts,"#nimCopyMem((void*)$1, (NIM_CONST void*)$2, sizeof($3));$n",
+                  [addrLoc(p.config, loc), addrLoc(p.config, tmp), tyDesc])
+      else:
+        linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
+                [addrLoc(p.config, loc), tyDesc])
+
       # XXX: We can be extra clever here and call memset only
       # on the bytes following the m_type field?
       genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
@@ -476,20 +535,19 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) =
     linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)])
   elif not isComplexValueType(typ):
     if containsGarbageCollectedRef(loc.t):
-      var nilLoc: TLoc
-      initLoc(nilLoc, locTemp, loc.lode, OnStack)
-      nilLoc.r = rope("NIM_NIL")
+      var nilLoc: TLoc = initLoc(locTemp, loc.lode, OnStack)
+      nilLoc.snippet = rope("NIM_NIL")
       genRefAssign(p, loc, nilLoc)
     else:
       linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc),
-        getTypeDesc(p.module, typ, mapTypeChooser(loc))])
+        getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
   else:
-    if not isTemp or containsGarbageCollectedRef(loc.t):
+    if (not isTemp or containsGarbageCollectedRef(loc.t)) and not hasNoInit(loc.t):
       # don't use nimZeroMem for temporary values for performance if we can
       # avoid it:
-      if not isImportedCppType(typ):
+      if not isOrHasImportedCppType(typ):
         linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
-                [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))])
+                [addrLoc(p.config, loc), getTypeDesc(p.module, typ, descKindFromSymKind mapTypeChooser(loc))])
     genObjectInit(p, cpsStmts, loc.t, loc, constructObj)
 
 proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
@@ -504,14 +562,16 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) =
     if not immediateAsgn:
       constructLoc(p, v.loc)
 
-proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
+proc getTemp(p: BProc, t: PType, needsInit=false): TLoc =
   inc(p.labels)
-  result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r])
-  result.k = locTemp
-  result.lode = lodeTyp t
-  result.storage = OnStack
-  result.flags = {}
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+                storage: OnStack, flags: {})
+  if p.module.compileToCpp and isOrHasImportedCppType(t):
+    var didGenTemp = false
+    linefmt(p, cpsLocals, "$1 $2$3;$n", [getTypeDesc(p.module, t, dkVar), result.snippet,
+      genCppInitializer(p.module, p, t, didGenTemp)])
+  else:
+    linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, dkVar), result.snippet])
   constructLoc(p, result, not needsInit)
   when false:
     # XXX Introduce a compiler switch in order to detect these easily.
@@ -522,25 +582,21 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) =
         echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo
       writeStackTrace()
 
-proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) =
+proc getTempCpp(p: BProc, t: PType, value: Rope): TLoc =
   inc(p.labels)
-  result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsStmts, "$1 $2 = $3;$n", [getTypeDesc(p.module, t, skVar), result.r, value])
-  result.k = locTemp
-  result.lode = lodeTyp t
-  result.storage = OnStack
-  result.flags = {}
-
-proc getIntTemp(p: BProc, result: var TLoc) =
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp, lode: lodeTyp t,
+                storage: OnStack, flags: {})
+  linefmt(p, cpsStmts, "auto $1 = $2;$n", [result.snippet, value])
+
+proc getIntTemp(p: BProc): TLoc =
   inc(p.labels)
-  result.r = "T" & rope(p.labels) & "_"
-  linefmt(p, cpsLocals, "NI $1;$n", [result.r])
-  result.k = locTemp
-  result.storage = OnStack
-  result.lode = lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt)
-  result.flags = {}
+  result = TLoc(snippet: "T" & rope(p.labels) & "_", k: locTemp,
+                storage: OnStack, lode: lodeTyp getSysType(p.module.g.graph, unknownLineInfo, tyInt),
+                flags: {})
+  linefmt(p, cpsLocals, "NI $1;$n", [result.snippet])
 
 proc localVarDecl(p: BProc; n: PNode): Rope =
+  result = ""
   let s = n.sym
   if s.loc.k == locNone:
     fillLocalName(p, s)
@@ -548,24 +604,31 @@ proc localVarDecl(p: BProc; n: PNode): Rope =
     if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy)
   if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
     result.addf("NIM_ALIGN($1) ", [rope(s.alignment)])
-  result.add getTypeDesc(p.module, s.typ, skVar)
-  if s.constraint.isNil:
+
+  genCLineDir(result, p, n.info, p.config)
+
+  result.add getTypeDesc(p.module, s.typ, dkVar)
+  if sfCodegenDecl notin s.flags:
     if sfRegister in s.flags: result.add(" register")
     #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds:
     #  decl.add(" GC_GUARD")
     if sfVolatile in s.flags: result.add(" volatile")
     if sfNoalias in s.flags: result.add(" NIM_NOALIAS")
     result.add(" ")
-    result.add(s.loc.r)
+    result.add(s.loc.snippet)
   else:
-    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.r])
+    result = runtimeFormat(s.cgDeclFrmt, [result, s.loc.snippet])
 
 proc assignLocalVar(p: BProc, n: PNode) =
   #assert(s.loc.k == locNone) # not yet assigned
   # this need not be fulfilled for inline procs; they are regenerated
   # for each module that uses them!
-  let nl = if optLineDir in p.config.options: "" else: "\L"
-  let decl = localVarDecl(p, n) & ";" & nl
+  let nl = if optLineDir in p.config.options: "" else: "\n"
+  var decl = localVarDecl(p, n)
+  if p.module.compileToCpp and isOrHasImportedCppType(n.typ):
+    var didGenTemp = false
+    decl.add genCppInitializer(p.module, p, n.typ, didGenTemp)
+  decl.add ";" & nl
   line(p, cpsLocals, decl)
 
 include ccgthreadvars
@@ -578,6 +641,29 @@ proc treatGlobalDifferentlyForHCR(m: BModule, s: PSym): bool =
       # and s.owner.kind == skModule # owner isn't always a module (global pragma on local var)
       # and s.loc.k == locGlobalVar  # loc isn't always initialized when this proc is used
 
+proc genGlobalVarDecl(p: BProc, n: PNode; td, value: Rope; decl: var Rope) =
+  let s = n.sym
+  if sfCodegenDecl notin s.flags:
+    if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
+      decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
+    if p.hcrOn: decl.add("static ")
+    elif sfImportc in s.flags: decl.add("extern ")
+    elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
+    else: decl.add("N_LIB_PRIVATE ")
+    if s.kind == skLet and value != "": decl.add("NIM_CONST ")
+    decl.add(td)
+    if p.hcrOn: decl.add("*")
+    if sfRegister in s.flags: decl.add(" register")
+    if sfVolatile in s.flags: decl.add(" volatile")
+    if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
+  else:
+    if value != "":
+      decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.snippet, value])
+    else:
+      decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.snippet])
+
+proc genCppVarForCtor(p: BProc; call: PNode; decl: var Rope; didGenTemp: var bool)
+
 proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
   let s = n.sym
   if s.loc.k == locNone:
@@ -590,7 +676,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
     if q != nil and not containsOrIncl(q.declaredThings, s.id):
       varInDynamicLib(q, s)
     else:
-      s.loc.r = mangleDynLibProc(s)
+      s.loc.snippet = mangleDynLibProc(s)
     if value != "":
       internalError(p.config, n.info, ".dynlib variables cannot have a value")
     return
@@ -603,36 +689,49 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) =
         internalError(p.config, n.info, ".threadvar variables cannot have a value")
     else:
       var decl: Rope = ""
-      var td = getTypeDesc(p.module, s.loc.t, skVar)
+      let td = getTypeDesc(p.module, s.loc.t, dkVar)
+      genGlobalVarDecl(p, n, td, value, decl)
       if s.constraint.isNil:
-        if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0:
-          decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)]
-        if p.hcrOn: decl.add("static ")
-        elif sfImportc in s.flags: decl.add("extern ")
-        elif lfExportLib in s.loc.flags: decl.add("N_LIB_EXPORT_VAR ")
-        else: decl.add("N_LIB_PRIVATE ")
-        if s.kind == skLet and value != "": decl.add("NIM_CONST ")
-        decl.add(td)
-        if p.hcrOn: decl.add("*")
-        if sfRegister in s.flags: decl.add(" register")
-        if sfVolatile in s.flags: decl.add(" volatile")
-        if sfNoalias in s.flags: decl.add(" NIM_NOALIAS")
-        if value != "":
-          decl.addf(" $1 = $2;$n", [s.loc.r, value])
-        else:
-          decl.addf(" $1;$n", [s.loc.r])
-      else:
         if value != "":
-          decl = runtimeFormat(s.cgDeclFrmt & " = $#;$n", [td, s.loc.r, value])
+          if p.module.compileToCpp and value.startsWith "{{}":
+            # TODO: taking this branch, re"\{\{\}(,\s\{\})*\}" might be emitted, resulting in
+            # either warnings (GCC 12.2+) or errors (Clang 15, MSVC 19.3+) of C++11+ compilers **when
+            # explicit constructors are around** due to overload resolution rules in place [^0][^1][^2]
+            # *Workaround* here: have C++'s static initialization mechanism do the default init work,
+            # for us lacking a deeper knowledge of an imported object's constructors' ex-/implicitness
+            # (so far) *and yet* trying to achieve default initialization.
+            # Still, generating {}s in genConstObjConstr() just to omit them here is faaaar from ideal;
+            # need to figure out a better way, possibly by keeping around more data about the
+            # imported objects' contructors?
+            #
+            # [^0]: https://en.cppreference.com/w/cpp/language/aggregate_initialization
+            # [^1]: https://cplusplus.github.io/CWG/issues/1518.html
+            # [^2]: https://eel.is/c++draft/over.match.ctor
+            decl.addf(" $1;$n", [s.loc.snippet])
+          else:
+            decl.addf(" $1 = $2;$n", [s.loc.snippet, value])
         else:
-          decl = runtimeFormat(s.cgDeclFrmt & ";$n", [td, s.loc.r])
+          decl.addf(" $1;$n", [s.loc.snippet])
+
       p.module.s[cfsVars].add(decl)
   if p.withinLoop > 0 and value == "":
     # fixes tests/run/tzeroarray:
     resetLoc(p, s.loc)
 
+proc callGlobalVarCppCtor(p: BProc; v: PSym; vn, value: PNode; didGenTemp: var bool) =
+  let s = vn.sym
+  fillBackendName(p.module, s)
+  fillLoc(s.loc, locGlobalVar, vn, OnHeap)
+  var decl: Rope = ""
+  let td = getTypeDesc(p.module, vn.sym.typ, dkVar)
+  genGlobalVarDecl(p, vn, td, "", decl)
+  decl.add " " & $s.loc.snippet
+  genCppVarForCtor(p, value, decl, didGenTemp)
+  if didGenTemp:  return # generated in the caller
+  p.module.s[cfsVars].add decl
+
 proc assignParam(p: BProc, s: PSym, retType: PType) =
-  assert(s.loc.r != "")
+  assert(s.loc.snippet != "")
   scopeMangledParam(p, s)
 
 proc fillProcLoc(m: BModule; n: PNode) =
@@ -646,25 +745,26 @@ proc getLabel(p: BProc): TLabel =
   result = "LA" & rope(p.labels) & "_"
 
 proc fixLabel(p: BProc, labl: TLabel) =
-  lineF(p, cpsStmts, "$1: ;$n", [labl])
+  p.s(cpsStmts).add("$1: ;$n" % [labl])
 
 proc genVarPrototype(m: BModule, n: PNode)
 proc requestConstImpl(p: BProc, sym: PSym)
 proc genStmts(p: BProc, t: PNode)
 proc expr(p: BProc, n: PNode, d: var TLoc)
-proc genProcPrototype(m: BModule, sym: PSym)
+
 proc putLocIntoDest(p: BProc, d: var TLoc, s: TLoc)
 proc intLiteral(i: BiggestInt; result: var Rope)
 proc genLiteral(p: BProc, n: PNode; result: var Rope)
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType; result: var Rope; argsCounter: var int)
 proc raiseExit(p: BProc)
+proc raiseExitCleanup(p: BProc, destroy: string)
 
-proc initLocExpr(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e, OnUnknown)
+proc initLocExpr(p: BProc, e: PNode, flags: TLocFlags = {}): TLoc =
+  result = initLoc(locNone, e, OnUnknown, flags)
   expr(p, e, result)
 
-proc initLocExprSingleUse(p: BProc, e: PNode, result: var TLoc) =
-  initLoc(result, locNone, e, OnUnknown)
+proc initLocExprSingleUse(p: BProc, e: PNode): TLoc =
+  result = initLoc(locNone, e, OnUnknown)
   if e.kind in nkCallKinds and (e[0].kind != nkSym or e[0].sym.magic == mNone):
     # We cannot check for tfNoSideEffect here because of mutable parameters.
     discard "bug #8202; enforce evaluation order for nested calls for C++ too"
@@ -680,17 +780,17 @@ include ccgcalls, "ccgstmts.nim"
 
 proc initFrame(p: BProc, procname, filename: Rope): Rope =
   const frameDefines = """
-  $1  define nimfr_(proc, file) \
-      TFrame FR_; \
-      FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
+$1define nimfr_(proc, file) \
+  TFrame FR_; \
+  FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = 0; #nimFrame(&FR_);
 
-  $1  define nimfrs_(proc, file, slots, length) \
-      struct {TFrame* prev;NCSTRING procname;NI line;NCSTRING filename; NI len; VarSlot s[slots];} FR_; \
-      FR_.procname = proc; FR_.filename = file; FR_.line = 0; FR_.len = length; #nimFrame((TFrame*)&FR_);
+$1define nimln_(n) \
+  FR_.line = n;
 
-  $1  define nimln_(n, file) \
-      FR_.line = n; FR_.filename = file;
-  """
+$1define nimlf_(n, file) \
+  FR_.line = n; FR_.filename = file;
+
+"""
   if p.module.s[cfsFrameDefines].len == 0:
     appcg(p.module, p.module.s[cfsFrameDefines], frameDefines, ["#"])
 
@@ -751,11 +851,10 @@ proc loadDynamicLib(m: BModule, lib: PLib) =
       var p = newProc(nil, m)
       p.options.excl optStackTrace
       p.flags.incl nimErrorFlagDisabled
-      var dest: TLoc
-      initLoc(dest, locTemp, lib.path, OnStack)
-      dest.r = getTempName(m)
+      var dest: TLoc = initLoc(locTemp, lib.path, OnStack)
+      dest.snippet = getTempName(m)
       appcg(m, m.s[cfsDynLibInit],"$1 $2;$n",
-           [getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)])
+           [getTypeDesc(m, lib.path.typ, dkVar), rdLoc(dest)])
       expr(p, lib.path, dest)
 
       m.s[cfsVars].add(p.s(cpsLocals))
@@ -771,7 +870,7 @@ proc mangleDynLibProc(sym: PSym): Rope =
   # we have to build this as a single rope in order not to trip the
   # optimization in genInfixCall, see test tests/cpp/t8241.nim
   if sfCompilerProc in sym.flags:
-    # NOTE: sym.loc.r is the external name!
+    # NOTE: sym.loc.snippet is the external name!
     result = rope(sym.name.s)
   else:
     result = rope(strutils.`%`("Dl_$1_", $sym.id))
@@ -779,23 +878,22 @@ proc mangleDynLibProc(sym: PSym): Rope =
 proc symInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
   let isCall = isGetProcAddr(lib)
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   if not isCall: loadDynamicLib(m, lib)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   sym.typ.sym = nil           # generate a new name
   inc(m.labels, 2)
   if isCall:
     let n = lib.path
-    var a: TLoc
-    initLocExpr(m.initProc, n[0], a)
+    var a: TLoc = initLocExpr(m.initProc, n[0])
     var params = rdLoc(a) & "("
     for i in 1..<n.len-1:
-      initLocExpr(m.initProc, n[i], a)
+      a = initLocExpr(m.initProc, n[i])
       params.add(rdLoc(a))
       params.add(", ")
     let load = "\t$1 = ($2) ($3$4));$n" %
-        [tmp, getTypeDesc(m, sym.typ, skVar), params, makeCString($extname)]
+        [tmp, getTypeDesc(m, sym.typ, dkVar), params, makeCString($extname)]
     var last = lastSon(n)
     if last.kind == nkHiddenStdConv: last = last[1]
     internalAssert(m.config, last.kind == nkStrLit)
@@ -809,25 +907,25 @@ proc symInDynamicLib(m: BModule, sym: PSym) =
   else:
     appcg(m, m.s[cfsDynLibInit],
         "\t$1 = ($2) #nimGetProcAddr($3, $4);$n",
-        [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
-  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
+        [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
+  m.s[cfsVars].addf("$2 $1;$n", [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc varInDynamicLib(m: BModule, sym: PSym) =
   var lib = sym.annex
-  var extname = sym.loc.r
+  var extname = sym.loc.snippet
   loadDynamicLib(m, lib)
   incl(sym.loc.flags, lfIndirect)
   var tmp = mangleDynLibProc(sym)
-  sym.loc.r = tmp             # from now on we only need the internal name
+  sym.loc.snippet = tmp             # from now on we only need the internal name
   inc(m.labels, 2)
   appcg(m, m.s[cfsDynLibInit],
       "$1 = ($2*) #nimGetProcAddr($3, $4);$n",
-      [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)])
+      [tmp, getTypeDesc(m, sym.typ, dkVar), lib.name, makeCString($extname)])
   m.s[cfsVars].addf("$2* $1;$n",
-      [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)])
+      [sym.loc.snippet, getTypeDesc(m, sym.loc.t, dkVar)])
 
 proc symInDynamicLibPartial(m: BModule, sym: PSym) =
-  sym.loc.r = mangleDynLibProc(sym)
+  sym.loc.snippet = mangleDynLibProc(sym)
   sym.typ.sym = nil           # generate a new name
 
 proc cgsymImpl(m: BModule; sym: PSym) {.inline.} =
@@ -850,12 +948,14 @@ proc cgsymValue(m: BModule, name: string): Rope =
     cgsymImpl m, sym
   else:
     rawMessage(m.config, errGenerated, "system module needs: " & name)
-  result = sym.loc.r
+  result = sym.loc.snippet
   if m.hcrOn and sym != nil and sym.kind in {skProc..skIterator}:
     result.addActualSuffixForHCR(m.module, sym)
 
 proc generateHeaders(m: BModule) =
-  m.s[cfsHeaders].add("\L#include \"nimbase.h\"\L")
+  var nimbase = m.config.nimbasePattern
+  if nimbase == "": nimbase = "nimbase.h"
+  m.s[cfsHeaders].addf("\L#include \"$1\"\L", [nimbase])
 
   for it in m.headerFiles:
     if it[0] == '#':
@@ -904,11 +1004,19 @@ proc closureSetup(p: BProc, prc: PSym) =
     linefmt(p, cpsStmts, "$1 = ($2) ClE_0;$n",
             [rdLoc(env.loc), getTypeDesc(p.module, env.typ)])
 
+const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
+                  nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
+                  declarativeDefs
+
 proc containsResult(n: PNode): bool =
   result = false
   case n.kind
-  of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams:
+  of succ(nkEmpty)..pred(nkSym), succ(nkSym)..nkNilLit, harmless:
     discard
+  of nkReturnStmt:
+    for i in 0..<n.len:
+      if containsResult(n[i]): return true
+    result = n.len > 0 and n[0].kind == nkEmpty
   of nkSym:
     if n.sym.kind == skResult:
       result = true
@@ -916,17 +1024,14 @@ proc containsResult(n: PNode): bool =
     for i in 0..<n.len:
       if containsResult(n[i]): return true
 
-const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef,
-                  nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} +
-                  declarativeDefs
-
 proc easyResultAsgn(n: PNode): PNode =
+  result = nil
   case n.kind
   of nkStmtList, nkStmtListExpr:
     var i = 0
     while i < n.len and n[i].kind in harmless: inc i
     if i < n.len: result = easyResultAsgn(n[i])
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     if n[0].kind == nkSym and n[0].sym.kind == skResult and not containsResult(n[1]):
       incl n.flags, nfPreventCg
       return n[1]
@@ -939,7 +1044,7 @@ proc easyResultAsgn(n: PNode): PNode =
 type
   InitResultEnum = enum Unknown, InitSkippable, InitRequired
 
-proc allPathsAsgnResult(n: PNode): InitResultEnum =
+proc allPathsAsgnResult(p: BProc; n: PNode): InitResultEnum =
   # Exceptions coming from calls don't have not be considered here:
   #
   # proc bar(): string = raise newException(...)
@@ -954,7 +1059,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
   #   echo "a was not written to"
   #
   template allPathsInBranch(it) =
-    let a = allPathsAsgnResult(it)
+    let a = allPathsAsgnResult(p, it)
     case a
     of InitRequired: return InitRequired
     of InitSkippable: discard
@@ -966,14 +1071,20 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
   case n.kind
   of nkStmtList, nkStmtListExpr:
     for it in n:
-      result = allPathsAsgnResult(it)
+      result = allPathsAsgnResult(p, it)
       if result != Unknown: return result
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     if n[0].kind == nkSym and n[0].sym.kind == skResult:
-      if not containsResult(n[1]): result = InitSkippable
+      if not containsResult(n[1]):
+        if allPathsAsgnResult(p, n[1]) == InitRequired:
+          result = InitRequired
+        else:
+          result = InitSkippable
       else: result = InitRequired
     elif containsResult(n):
       result = InitRequired
+    else:
+      result = allPathsAsgnResult(p, n[1])
   of nkReturnStmt:
     if n.len > 0:
       if n[0].kind == nkEmpty and result != InitSkippable:
@@ -982,7 +1093,7 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
         # initialized. This avoids cases like #9286 where this heuristic lead to
         # wrong code being generated.
         result = InitRequired
-      else: result = allPathsAsgnResult(n[0])
+      else: result = allPathsAsgnResult(p, n[0])
   of nkIfStmt, nkIfExpr:
     var exhaustive = false
     result = InitSkippable
@@ -1008,9 +1119,9 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
   of nkWhileStmt:
     # some dubious code can assign the result in the 'while'
     # condition and that would be fine. Everything else isn't:
-    result = allPathsAsgnResult(n[0])
+    result = allPathsAsgnResult(p, n[0])
     if result == Unknown:
-      result = allPathsAsgnResult(n[1])
+      result = allPathsAsgnResult(p, n[1])
       # we cannot assume that the 'while' loop is really executed at least once:
       if result == InitSkippable: result = Unknown
   of harmless:
@@ -1035,9 +1146,21 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
     allPathsInBranch(n[0])
     for i in 1..<n.len:
       if n[i].kind == nkFinally:
-        result = allPathsAsgnResult(n[i].lastSon)
+        result = allPathsAsgnResult(p, n[i].lastSon)
       else:
         allPathsInBranch(n[i].lastSon)
+  of nkCallKinds:
+    if canRaiseDisp(p, n[0]):
+      result = InitRequired
+    else:
+      for i in 0..<n.safeLen:
+        allPathsInBranch(n[i])
+  of nkRaiseStmt:
+    result = InitRequired
+  of nkChckRangeF, nkChckRange64, nkChckRange:
+    # TODO: more checks might need to be covered like overflow, indexDefect etc.
+    # bug #22852
+    result = InitRequired
   else:
     for i in 0..<n.safeLen:
       allPathsInBranch(n[i])
@@ -1045,14 +1168,14 @@ proc allPathsAsgnResult(n: PNode): InitResultEnum =
 proc getProcTypeCast(m: BModule, prc: PSym): Rope =
   result = getTypeDesc(m, prc.loc.t)
   if prc.typ.callConv == ccClosure:
-    var rettype, params: Rope
+    var rettype, params: Rope = ""
     var check = initIntSet()
     genProcParams(m, prc.typ, rettype, params, check)
     result = "$1(*)$2" % [rettype, params]
 
 proc genProcBody(p: BProc; procBody: PNode) =
   genStmts(p, procBody) # modifies p.locals, p.init, etc.
-  if {nimErrorFlagAccessed, nimErrorFlagDeclared} * p.flags == {nimErrorFlagAccessed}:
+  if {nimErrorFlagAccessed, nimErrorFlagDeclared, nimErrorFlagDisabled} * p.flags == {nimErrorFlagAccessed}:
     p.flags.incl nimErrorFlagDeclared
     p.blocks[0].sections[cpsLocals].add(ropecg(p.module, "NIM_BOOL* nimErr_;$n", []))
     p.blocks[0].sections[cpsInit].add(ropecg(p.module, "nimErr_ = #nimErrorFlag();$n", []))
@@ -1060,38 +1183,54 @@ proc genProcBody(p: BProc; procBody: PNode) =
 proc isNoReturn(m: BModule; s: PSym): bool {.inline.} =
   sfNoReturn in s.flags and m.config.exc != excGoto
 
-proc genProcAux(m: BModule, prc: PSym) =
+proc genProcAux*(m: BModule, prc: PSym) =
   var p = newProc(prc, m)
   var header = newRopeAppender()
-  genProcHeader(m, prc, header)
+  let isCppMember = m.config.backend == backendCpp and sfCppMember * prc.flags != {}
+  if isCppMember:
+    genMemberProcHeader(m, prc, header)
+  else:
+    genProcHeader(m, prc, header)
   var returnStmt: Rope = ""
   assert(prc.ast != nil)
 
-  var procBody = transformBody(m.g.graph, m.idgen, prc, cache = false)
+  var procBody = transformBody(m.g.graph, m.idgen, prc, {})
   if sfInjectDestructors in prc.flags:
     procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)
 
-  if sfPure notin prc.flags and prc.typ[0] != nil:
+  let tmpInfo = prc.info
+  discard freshLineInfo(p, prc.info)
+
+  if sfPure notin prc.flags and prc.typ.returnType != nil:
     if resultPos >= prc.ast.len:
       internalError(m.config, prc.info, "proc has no result symbol")
     let resNode = prc.ast[resultPos]
     let res = resNode.sym # get result symbol
-    if not isInvalidReturnType(m.config, prc.typ):
+    if not isInvalidReturnType(m.config, prc.typ) and sfConstructor notin prc.flags:
       if sfNoInit in prc.flags: incl(res.flags, sfNoInit)
       if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil):
         var decl = localVarDecl(p, resNode)
-        var a: TLoc
-        initLocExprSingleUse(p, val, a)
+        var a: TLoc = initLocExprSingleUse(p, val)
         linefmt(p, cpsStmts, "$1 = $2;$n", [decl, rdLoc(a)])
       else:
         # declare the result symbol:
         assignLocalVar(p, resNode)
-        assert(res.loc.r != "")
-        initLocalVar(p, res, immediateAsgn=false)
+        assert(res.loc.snippet != "")
+        if p.config.selectedGC in {gcArc, gcAtomicArc, gcOrc} and
+            allPathsAsgnResult(p, procBody) == InitSkippable:
+          # In an ideal world the codegen could rely on injectdestructors doing its job properly
+          # and then the analysis step would not be required.
+          discard "result init optimized out"
+        else:
+          initLocalVar(p, res, immediateAsgn=false)
       returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)])
+    elif sfConstructor in prc.flags:
+      resNode.sym.loc.flags.incl lfIndirect
+      fillLoc(resNode.sym.loc, locParam, resNode, "this", OnHeap)
+      prc.loc.snippet = getTypeDesc(m, resNode.sym.loc.t, dkVar)
     else:
       fillResult(p.config, resNode, prc.typ)
-      assignParam(p, res, prc.typ[0])
+      assignParam(p, res, prc.typ.returnType)
       # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)'
       # to 'unsureAsgn(result, x)'
       # Sketch why this is correct: If 'result' points to a stack location
@@ -1099,7 +1238,7 @@ proc genProcAux(m: BModule, prc: PSym) =
       # global is either 'nil' or points to valid memory and so the RC operation
       # succeeds without touching not-initialized memory.
       if sfNoInit in prc.flags: discard
-      elif allPathsAsgnResult(procBody) == InitSkippable: discard
+      elif allPathsAsgnResult(p, procBody) == InitSkippable: discard
       else:
         resetLoc(p, res.loc)
       if skipTypes(res.typ, abstractInst).kind == tyArray:
@@ -1109,17 +1248,19 @@ proc genProcAux(m: BModule, prc: PSym) =
   for i in 1..<prc.typ.n.len:
     let param = prc.typ.n[i].sym
     if param.typ.isCompileTimeOnly: continue
-    assignParam(p, param, prc.typ[0])
+    assignParam(p, param, prc.typ.returnType)
   closureSetup(p, prc)
   genProcBody(p, procBody)
 
-  var generatedProc: Rope
+  prc.info = tmpInfo
+
+  var generatedProc: Rope = ""
   generatedProc.genCLineDir prc.info, m.config
   if isNoReturn(p.module, prc):
-    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
       header = "__declspec(noreturn) " & header
   if sfPure in prc.flags:
-    if hasDeclspec in extccomp.CC[p.config.cCompiler].props:
+    if hasDeclspec in extccomp.CC[p.config.cCompiler].props and not isCppMember:
       header = "__declspec(naked) " & header
     generatedProc.add ropecg(p.module, "$1 {$n$2$3$4}$N$N",
                          [header, p.s(cpsLocals), p.s(cpsInit), p.s(cpsStmts)])
@@ -1151,7 +1292,7 @@ proc genProcAux(m: BModule, prc: PSym) =
   m.s[cfsProcs].add(generatedProc)
   if isReloadable(m, prc):
     m.s[cfsDynLibInit].addf("\t$1 = ($3) hcrRegisterProc($4, \"$1\", (void*)$2);$n",
-         [prc.loc.r, prc.loc.r & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
+         [prc.loc.snippet, prc.loc.snippet & "_actual", getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
 
 proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
   result = (sfCompileToCpp in m.module.flags and
@@ -1164,7 +1305,7 @@ proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} =
 
 proc genProcPrototype(m: BModule, sym: PSym) =
   useHeader(m, sym)
-  if lfNoDecl in sym.loc.flags: return
+  if lfNoDecl in sym.loc.flags or sfCppMember * sym.flags != {}: return
   if lfDynamicLib in sym.loc.flags:
     if sym.itemId.module != m.module.position and
         not containsOrIncl(m.declaredThings, sym.id):
@@ -1200,6 +1341,20 @@ proc genProcNoForward(m: BModule, prc: PSym) =
   if lfNoDecl in prc.loc.flags:
     fillProcLoc(m, prc.ast[namePos])
     genProcPrototype(m, prc)
+  elif lfDynamicLib in prc.loc.flags:
+    var q = findPendingModule(m, prc)
+    fillProcLoc(q, prc.ast[namePos])
+    genProcPrototype(m, prc)
+    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
+      symInDynamicLib(q, prc)
+      # register the procedure even though it is in a different dynamic library and will not be
+      # reloadable (and has no _actual suffix) - other modules will need to be able to get it through
+      # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
+      if isReloadable(q, prc):
+        q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
+            [prc.loc.snippet, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
+    else:
+      symInDynamicLibPartial(m, prc)
   elif prc.typ.callConv == ccInline:
     # We add inline procs to the calling module to enable C based inlining.
     # This also means that a check with ``q.declaredThings`` is wrong, we need
@@ -1214,24 +1369,10 @@ proc genProcNoForward(m: BModule, prc: PSym) =
       #elif {sfExportc, sfImportc} * prc.flags == {}:
       #  # reset name to restore consistency in case of hashing collisions:
       #  echo "resetting ", prc.id, " by ", m.module.name.s
-      #  prc.loc.r = nil
-      #  prc.loc.r = mangleName(m, prc)
+      #  prc.loc.snippet = nil
+      #  prc.loc.snippet = mangleName(m, prc)
       genProcPrototype(m, prc)
       genProcAux(m, prc)
-  elif lfDynamicLib in prc.loc.flags:
-    var q = findPendingModule(m, prc)
-    fillProcLoc(q, prc.ast[namePos])
-    genProcPrototype(m, prc)
-    if q != nil and not containsOrIncl(q.declaredThings, prc.id):
-      symInDynamicLib(q, prc)
-      # register the procedure even though it is in a different dynamic library and will not be
-      # reloadable (and has no _actual suffix) - other modules will need to be able to get it through
-      # the hcr dynlib (also put it in the DynLibInit section - right after it gets loaded)
-      if isReloadable(q, prc):
-        q.s[cfsDynLibInit].addf("\t$1 = ($2) hcrRegisterProc($3, \"$1\", (void*)$1);$n",
-            [prc.loc.r, getTypeDesc(q, prc.loc.t), getModuleDllPath(m, q.module)])
-    else:
-      symInDynamicLibPartial(m, prc)
   elif sfImportc notin prc.flags:
     var q = findPendingModule(m, prc)
     fillProcLoc(q, prc.ast[namePos])
@@ -1241,7 +1382,7 @@ proc genProcNoForward(m: BModule, prc: PSym) =
     if isReloadable(m, prc) and prc.id notin m.declaredProtos and
       q != nil and q.module.id != m.module.id:
       m.s[cfsDynLibInit].addf("\t$1 = ($2) hcrGetProc($3, \"$1\");$n",
-           [prc.loc.r, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
+           [prc.loc.snippet, getProcTypeCast(m, prc), getModuleDllPath(m, prc)])
     genProcPrototype(m, prc)
     if q != nil and not containsOrIncl(q.declaredThings, prc.id):
       # make sure there is a "prototype" in the external module
@@ -1294,24 +1435,24 @@ proc genVarPrototype(m: BModule, n: PNode) =
     return
   if sym.owner.id != m.module.id:
     # else we already have the symbol generated!
-    assert(sym.loc.r != "")
+    assert(sym.loc.snippet != "")
+    incl(m.declaredThings, sym.id)
     if sfThread in sym.flags:
       declareThreadVar(m, sym, true)
     else:
-      incl(m.declaredThings, sym.id)
       if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0:
         m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)]
       m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ")
-      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar))
+      m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, dkVar))
       if m.hcrOn: m.s[cfsVars].add("*")
       if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*")
       if sfRegister in sym.flags: m.s[cfsVars].add(" register")
       if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile")
       if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS")
-      m.s[cfsVars].addf(" $1;$n", [sym.loc.r])
+      m.s[cfsVars].addf(" $1;$n", [sym.loc.snippet])
       if m.hcrOn: m.initProc.procSec(cpsLocals).addf(
-        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r,
-        getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)])
+        "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.snippet,
+        getTypeDesc(m, sym.loc.t, dkVar), getModuleDllPath(m, sym)])
 
 proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} =
   result.addf("#define NIM_INTBITS $1\L", [
@@ -1343,17 +1484,19 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope =
 
 proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope =
   ## Returns a mangled module name.
-  result.add mangleModuleName(conf, filename).mangle
+  result = mangleModuleName(conf, filename).mangle
 
 proc getSomeNameForModule(m: BModule): Rope =
   ## Returns a mangled module name.
   assert m.module.kind == skModule
   assert m.module.owner.kind == skPackage
-  result.add mangleModuleName(m.g.config, m.filename).mangle
+  result = mangleModuleName(m.g.config, m.filename).mangle
 
 proc getSomeInitName(m: BModule, suffix: string): Rope =
   if not m.hcrOn:
     result = getSomeNameForModule(m)
+  else:
+    result = ""
   result.add suffix
 
 proc getInitName(m: BModule): Rope =
@@ -1372,10 +1515,10 @@ proc genMainProc(m: BModule) =
   ## this function is called in cgenWriteModules after all modules are closed,
   ## it means raising dependency on the symbols is too late as it will not propagate
   ## into other modules, only simple rope manipulations are allowed
-
-  var preMainCode: Rope
+  var preMainCode: Rope = ""
   if m.hcrOn:
     proc loadLib(handle: string, name: string): Rope =
+      result = ""
       let prc = magicsys.getCompilerProc(m.g.graph, name)
       assert prc != nil
       let n = newStrNode(nkStrLit, prc.annex.path.strVal)
@@ -1387,44 +1530,44 @@ proc genMainProc(m: BModule) =
                        [handle, strLit])
 
     preMainCode.add(loadLib("hcr_handle", "hcrGetProc"))
-    preMainCode.add("\tvoid* rtl_handle;\L")
-    preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
-    preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
-    preMainCode.add("\tinner = PreMain;\L")
-    preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
-    preMainCode.add("\t(*inner)();\L")
+    if m.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+      preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
+    else:
+      preMainCode.add("\tvoid* rtl_handle;\L")
+      preMainCode.add(loadLib("rtl_handle", "nimGC_setStackBottom"))
+      preMainCode.add(hcrGetProcLoadCode(m, "nimGC_setStackBottom", "nimrtl_", "rtl_handle", "nimGetProcAddr"))
+      preMainCode.add("\tinner = $1PreMain;\L" % [rope m.config.nimMainPrefix])
+      preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L")
+      preMainCode.add("\t(*inner)();\L")
   else:
     preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix])
 
-  var posixCmdLine: Rope
+  var posixCmdLine: Rope = ""
   if optNoMain notin m.config.globalOptions:
-    posixCmdLine.add "\tN_LIB_PRIVATE int cmdCount;\L"
-    posixCmdLine.add "\tN_LIB_PRIVATE char** cmdLine;\L"
-    posixCmdLine.add "\tN_LIB_PRIVATE char** gEnv;\L"
+    posixCmdLine.add "N_LIB_PRIVATE int cmdCount;\L"
+    posixCmdLine.add "N_LIB_PRIVATE char** cmdLine;\L"
+    posixCmdLine.add "N_LIB_PRIVATE char** gEnv;\L"
 
   const
     # The use of a volatile function pointer to call Pre/NimMainInner
     # prevents inlining of the NimMainInner function and dependent
     # functions, which might otherwise merge their stack frames.
 
-    PreMainVolatileBody =
-      "\tvoid (*volatile inner)(void);$N" &
-      "\tinner = $3PreMainInner;$N" &
-      "$1" &
-      "\t(*inner)();$N"
-
-    PreMainNonVolatileBody =
-      "$1" &
-      "\t$3PreMainInner();$N"
-
-    PreMainBodyStart = "$N" &
+    PreMainBody = "$N" &
       "N_LIB_PRIVATE void $3PreMainInner(void) {$N" &
       "$2" &
       "}$N$N" &
       "$4" &
-      "N_LIB_PRIVATE void $3PreMain(void) {$N"
-
-    PreMainBodyEnd =
+      "N_LIB_PRIVATE void $3PreMain(void) {$N" &
+      "##if $5$N" & # 1 for volatile call, 0 for non-volatile
+      "\tvoid (*volatile inner)(void);$N" &
+      "\tinner = $3PreMainInner;$N" &
+      "$1" &
+      "\t(*inner)();$N" &
+      "##else$N" &
+      "$1" &
+      "\t$3PreMainInner();$N" &
+      "##endif$N" &
       "}$N$N"
 
     MainProcs =
@@ -1437,32 +1580,23 @@ proc genMainProc(m: BModule) =
         "$1" &
       "}$N$N"
 
-    NimMainVolatileBody =
+    NimMainProc =
+      "N_CDECL(void, $5NimMain)(void) {$N" &
+      "##if $6$N" & # 1 for volatile call, 0 for non-volatile
       "\tvoid (*volatile inner)(void);$N" &
       "$4" &
       "\tinner = $5NimMainInner;$N" &
       "$2" &
-      "\t(*inner)();$N"
-
-    NimMainNonVolatileBody =
+      "\t(*inner)();$N" &
+      "##else$N" &
       "$4" &
       "$2" &
-      "\t$5NimMainInner();$N"
-
-    NimMainProcStart =
-      "N_CDECL(void, $5NimMain)(void) {$N"
-
-    NimMainProcEnd =
+      "\t$5NimMainInner();$N" &
+      "##endif$N" &
       "}$N$N"
 
-    NimMainProc = NimMainProcStart & NimMainVolatileBody & NimMainProcEnd
-
-    NimSlimMainProc = NimMainProcStart & NimMainNonVolatileBody & NimMainProcEnd
-
     NimMainBody = NimMainInner & NimMainProc
 
-    NimSlimMainBody = NimMainInner & NimSlimMainProc
-
     PosixCMain =
       "int main(int argc, char** args, char** env) {$N" &
         "\tcmdLine = args;$N" &
@@ -1489,7 +1623,7 @@ proc genMainProc(m: BModule) =
     WinCDllMain =
       "BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, $N" &
       "                    LPVOID lpvReserved) {$N" &
-      "\tif(fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "}$N" &
+      "\tif (fwdreason == DLL_PROCESS_ATTACH) {$N" & MainProcs & "\t}$N" &
       "\treturn 1;$N}$N$N"
 
     PosixNimDllMain = WinNimDllMain
@@ -1523,45 +1657,35 @@ proc genMainProc(m: BModule) =
     m.includeHeader("<libc/component.h>")
 
   let initStackBottomCall =
-    if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope
+    if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcAtomicArc, gcOrc}: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])
   inc(m.labels)
-  if m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
-    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine])
-  else:
-    appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine])
+
+  let isVolatile = if m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}: "1" else: "0"
+  appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine, isVolatile])
 
   if m.config.target.targetOS == osWindows and
       m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}:
     if optGenGuiApp in m.config.globalOptions:
       const nimMain = WinNimMain
       appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
     else:
       const nimMain = WinNimDllMain
       appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
   elif m.config.target.targetOS == osGenode:
     const nimMain = GenodeNimMain
     appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
   elif optGenDynLib in m.config.globalOptions:
     const nimMain = PosixNimDllMain
     appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
-  elif m.config.target.targetOS == osStandalone:
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
+  else:
     const nimMain = NimMainBody
     appcg(m, m.s[cfsProcs], nimMain,
-        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
-  else:
-    if m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
-      const nimMain = NimMainBody
-      appcg(m, m.s[cfsProcs], nimMain,
-          [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
-    else:
-      const nimMain = NimSlimMainBody
-      appcg(m, m.s[cfsProcs], nimMain,
-          [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix])
+        [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix, isVolatile])
 
   if optNoMain notin m.config.globalOptions:
     if m.config.cppCustomNamespace.len > 0:
@@ -1636,7 +1760,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
     hcrModuleMeta.addf("\t\"\"};$n", [])
     hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", [])
     hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n",
-                          [($sigHash(m.module)).rope])
+                          [($sigHash(m.module, m.config)).rope])
     if sfMainModule in m.module.flags:
       g.mainModProcs.add(hcrModuleMeta)
       g.mainModProcs.addf("static void* hcr_handle;$N", [])
@@ -1677,7 +1801,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
   if sfSystemModule in m.module.flags:
     if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
       g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
-    if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
+    if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
       g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
 
   if m.s[cfsInitProc].len > 0:
@@ -1702,7 +1826,7 @@ proc genDatInitCode(m: BModule) =
 
   # we don't want to break into such init code - could happen if a line
   # directive from a function written by the user spills after itself
-  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
 
   for i in cfsTypeInit1..cfsDynLibInit:
     if m.s[i].len != 0:
@@ -1724,14 +1848,14 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R
 
   var extname = prefix & sym
   var tmp = mangleDynLibProc(prc)
-  prc.loc.r = tmp
+  prc.loc.snippet = tmp
   prc.typ.sym = nil
 
   if not containsOrIncl(m.declaredThings, prc.id):
-    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)])
+    m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.snippet, getTypeDesc(m, prc.loc.t, dkVar)])
 
   result = "\t$1 = ($2) $3($4, $5);$n" %
-      [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
+      [tmp, getTypeDesc(m, prc.typ, dkVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)]
 
 proc genInitCode(m: BModule) =
   ## this function is called in cgenWriteModules after all modules are closed,
@@ -1743,7 +1867,7 @@ proc genInitCode(m: BModule) =
     [rope(if m.hcrOn: "N_LIB_EXPORT" else: "N_LIB_PRIVATE"), initname]
   # we don't want to break into such init code - could happen if a line
   # directive from a function written by the user spills after itself
-  genCLineDir(prc, "generated_not_to_break_here", 999999, m.config)
+  genCLineDir(prc, InvalidFileIdx, 999999, m.config)
   if m.typeNodes > 0:
     if m.hcrOn:
       appcg(m, m.s[cfsTypeInit1], "\t#TNimNode* $1;$N", [m.typeNodesName])
@@ -1807,7 +1931,7 @@ proc genInitCode(m: BModule) =
     if beforeRetNeeded in m.initProc.flags:
       prc.add("\tBeforeRet_: ;\n")
 
-    if sfMainModule in m.module.flags and m.config.exc == excGoto:
+    if m.config.exc == excGoto:
       if getCompilerProc(m.g.graph, "nimTestErrorFlag") != nil:
         m.appcg(prc, "\t#nimTestErrorFlag();$n", [])
 
@@ -1855,6 +1979,40 @@ proc genInitCode(m: BModule) =
 
   registerModuleToMain(m.g, m)
 
+proc postprocessCode(conf: ConfigRef, r: var Rope) =
+  # find the first directive
+  var f = r.find(postprocessDirStart)
+  if f == -1:
+    return
+
+  var
+    nimlnDirLastF = ""
+
+  var res: Rope = r.substr(0, f - 1)
+  while f != -1:
+    var
+      e = r.find(postprocessDirEnd, f + 1)
+      dir = r.substr(f + 1, e - 1).split(postprocessDirSep)
+    case dir[0]
+    of "nimln":
+      if dir[2] == nimlnDirLastF:
+        res.add("nimln_(" & dir[1] & ");")
+      else:
+        res.add("nimlf_(" & dir[1] & ", " & quotedFilename(conf, dir[2].parseInt.FileIndex) & ");")
+        nimlnDirLastF = dir[2]
+    else:
+      raiseAssert "unexpected postprocess directive"
+
+    # find the next directive
+    f = r.find(postprocessDirStart, e + 1)
+    # copy the code until the next directive
+    if f != -1:
+      res.add(r.substr(e + 1, f - 1))
+    else:
+      res.add(r.substr(e + 1))
+
+  r = res
+
 proc genModule(m: BModule, cfile: Cfile): Rope =
   var moduleIsEmpty = true
 
@@ -1867,8 +2025,6 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
     openNamespaceNim(m.config.cppCustomNamespace, result)
   if m.s[cfsFrameDefines].len > 0:
     result.add(m.s[cfsFrameDefines])
-  else:
-    result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n")
 
   for i in cfsForwardTypes..cfsProcs:
     if m.s[i].len > 0:
@@ -1885,9 +2041,17 @@ proc genModule(m: BModule, cfile: Cfile): Rope =
   if m.config.cppCustomNamespace.len > 0:
     closeNamespaceNim(result)
 
+  if optLineDir in m.config.options:
+    var srcFileDefs = ""
+    for fi in 0..m.config.m.fileInfos.high:
+      srcFileDefs.add("#define FX_" & $fi & " " & makeSingleLineCString(toFullPath(m.config, fi.FileIndex)) & "\n")
+    result = srcFileDefs & result
+
   if moduleIsEmpty:
     result = ""
 
+  postprocessCode(m.config, result)
+
 proc initProcOptions(m: BModule): TOptions =
   let opts = m.config.options
   if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts
@@ -1912,7 +2076,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule
   result.preInitProc = newProc(nil, result)
   result.preInitProc.flags.incl nimErrorFlagDisabled
   result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated
-  initNodeTable(result.dataCache)
+  result.dataCache = initNodeTable()
   result.typeStack = @[]
   result.typeNodesName = getTempName(result)
   result.nimTypesName = getTempName(result)
@@ -1941,10 +2105,7 @@ template injectG() {.dirty.} =
     graph.backend = newModuleList(graph)
   let g = BModuleList(graph.backend)
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
-
-proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
+proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   injectG()
   result = newModule(g, module, graph.config)
   result.idgen = idgen
@@ -2012,7 +2173,7 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) =
 
 proc genTopLevelStmt*(m: BModule; n: PNode) =
   ## Also called from `ic/cbackend.nim`.
-  if passes.skipCodegen(m.config, n): return
+  if pipelineutils.skipCodegen(m.config, n): return
   m.initProc.options = initProcOptions(m)
   #softRnl = if optLineDir in m.config.options: noRnl else: rnl
   # XXX replicate this logic!
@@ -2025,12 +2186,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) =
   else:
     genProcBody(m.initProc, transformedN)
 
-proc myProcess(b: PPassContext, n: PNode): PNode =
-  result = n
-  if b != nil:
-    var m = BModule(b)
-    genTopLevelStmt(m, n)
-
 proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool =
   if optForceFullMake notin m.config.globalOptions:
     if not moduleHasChanged(m.g.graph, m.module):
@@ -2096,6 +2251,22 @@ proc updateCachedModule(m: BModule) =
   cf.flags = {CfileFlag.Cached}
   addFileToCompile(m.config, cf)
 
+proc generateLibraryDestroyGlobals(graph: ModuleGraph; m: BModule; body: PNode; isDynlib: bool): PSym =
+  let procname = getIdent(graph.cache, "NimDestroyGlobals")
+  result = newSym(skProc, procname, m.idgen, m.module.owner, m.module.info)
+  result.typ = newProcType(m.module.info, m.idgen, m.module.owner)
+  result.typ.callConv = ccCDecl
+  incl result.flags, sfExportc
+  result.loc.snippet = "NimDestroyGlobals"
+  if isDynlib:
+    incl(result.loc.flags, lfExportLib)
+
+  let theProc = newNodeI(nkProcDef, m.module.info, bodyPos+1)
+  for i in 0..<theProc.len: theProc[i] = newNodeI(nkEmpty, m.module.info)
+  theProc[namePos] = newSymNode(result)
+  theProc[bodyPos] = body
+  result.ast = theProc
+
 proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
   ## Also called from IC.
   if sfMainModule in m.module.flags:
@@ -2107,7 +2278,14 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
     if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}:
       for i in countdown(high(graph.globalDestructors), 0):
         n.add graph.globalDestructors[i]
-  if passes.skipCodegen(m.config, n): return
+    else:
+      var body = newNodeI(nkStmtList, m.module.info)
+      for i in countdown(high(graph.globalDestructors), 0):
+        body.add graph.globalDestructors[i]
+      body.flags.incl nfTransf # should not be further transformed
+      let dtor = generateLibraryDestroyGlobals(graph, m, body, optGenDynLib in m.config.globalOptions)
+      genProcAux(m, dtor)
+  if pipelineutils.skipCodegen(m.config, n): return
   if moduleHasChanged(graph, m.module):
     # if the module is cached, we don't regenerate the main proc
     # nor the dispatchers? But if the dispatchers changed?
@@ -2118,7 +2296,10 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
 
     if m.hcrOn:
       # make sure this is pulled in (meaning hcrGetGlobal() is called for it during init)
-      cgsym(m, "programResult")
+      let sym = magicsys.getCompilerProc(m.g.graph, "programResult")
+      # ignore when not available, could be a module imported early in `system`
+      if sym != nil:
+        cgsymImpl m, sym
       if m.inHcrInitGuard:
         endBlock(m.initProc)
 
@@ -2135,25 +2316,22 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) =
         cgsym(m, "rawWrite")
 
       # raise dependencies on behalf of genMainProc
-      if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
+      if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcAtomicArc, gcOrc}:
         cgsym(m, "initStackBottomWith")
       if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
         cgsym(m, "initThreadVarsEmulation")
 
       if m.g.forwardedProcs.len == 0:
         incl m.flags, objHasKidsValid
-      let disp = generateMethodDispatchers(graph)
-      for x in disp: genProcAux(m, x.sym)
+      if optMultiMethods in m.g.config.globalOptions or
+          m.g.config.selectedGC notin {gcArc, gcOrc, gcAtomicArc} or
+          vtables notin m.g.config.features:
+        generateIfMethodDispatchers(graph, m.idgen)
+
 
   let mm = m
   m.g.modulesClosed.add mm
 
-
-proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
-  result = n
-  if b == nil: return
-  finalCodegenActions(graph, BModule(b), n)
-
 proc genForwardedProcs(g: BModuleList) =
   # Forward declared proc:s lack bodies when first encountered, so they're given
   # a second pass here
@@ -2180,5 +2358,3 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) =
     m.writeModule(pending=true)
   writeMapping(config, g.mapping)
   if g.generatedHeader != nil: writeHeader(g.generatedHeader)
-
-const cgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index d017fdd1e..5368e9dc7 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -10,13 +10,14 @@
 ## This module contains the data structures for the C code generation phase.
 
 import
-  ast, ropes, options, intsets,
-  tables, ndi, lineinfos, pathutils, modulegraphs, sets
+  ast, ropes, options,
+  ndi, lineinfos, pathutils, modulegraphs
+
+import std/[intsets, tables, sets]
 
 type
   TLabel* = Rope              # for the C generator a label is just a rope
   TCFileSection* = enum       # the sections a generated C file consists of
-    cfsMergeInfo,             # section containing merge information
     cfsHeaders,               # section for C include file headers
     cfsFrameDefines           # section for nim frame macros
     cfsForwardTypes,          # section for C forward typedefs
@@ -24,7 +25,6 @@ type
     cfsSeqTypes,              # section for sequence types only
                               # this is needed for strange type generation
                               # reasons
-    cfsFieldInfo,             # section for field information
     cfsTypeInfo,              # section for type information (ag ABI checks)
     cfsProcHeaders,           # section for C procs prototypes
     cfsStrData,               # section for constant string literals
@@ -34,12 +34,8 @@ type
     cfsInitProc,              # section for the C init proc
     cfsDatInitProc,           # section for the C datInit proc
     cfsTypeInit1,             # section 1 for declarations of type information
-    cfsTypeInit2,             # section 2 for init of type information
     cfsTypeInit3,             # section 3 for init of type information
-    cfsDebugInit,             # section for init of debug information
     cfsDynLibInit,            # section for init of dynamic library binding
-    cfsDynLibDeinit           # section for deinitialization of dynamic
-                              # libraries
   TCTypeKind* = enum          # describes the type kind of a C type
     ctVoid, ctChar, ctBool,
     ctInt, ctInt8, ctInt16, ctInt32, ctInt64,
@@ -92,6 +88,7 @@ type
     options*: TOptions        # options that should be used for code
                               # generation; this is the same as prc.options
                               # unless prc == nil
+    optionsStack*: seq[(TOptions, TNoteKinds)]
     module*: BModule          # used to prevent excessive parameter passing
     withinLoop*: int          # > 0 if we are within a loop
     splitDecls*: int          # > 0 if we are in some context for C++ that
@@ -140,6 +137,7 @@ type
                             # unconditionally...
                             # nimtvDeps is VERY hard to cache because it's
                             # not a list of IDs nor can it be made to be one.
+    mangledPrcs*: HashSet[string]
 
   TCGen = object of PPassContext # represents a C source file
     s*: TCFileSections        # sections of the C file
@@ -153,7 +151,7 @@ type
     typeABICache*: HashSet[SigHash] # cache for ABI checks; reusing typeCache
                               # would be ideal but for some reason enums
                               # don't seem to get cached so it'd generate
-                              # 1 ABI check per occurence in code
+                              # 1 ABI check per occurrence in code
     forwTypeCache*: TypeCache # cache for forward declarations of types
     declaredThings*: IntSet   # things we have declared in this .c file
     declaredProtos*: IntSet   # prototypes we have declared in this .c file
@@ -178,6 +176,7 @@ type
 
 template config*(m: BModule): ConfigRef = m.g.config
 template config*(p: BProc): ConfigRef = p.module.g.config
+template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC
 
 proc includeHeader*(this: BModule; header: string) =
   if not this.headerFiles.contains header:
@@ -197,15 +196,17 @@ proc initBlock*(): TBlock =
     result.sections[i] = newRopeAppender()
 
 proc newProc*(prc: PSym, module: BModule): BProc =
-  new(result)
-  result.prc = prc
-  result.module = module
-  result.options = if prc != nil: prc.options
-                   else: module.config.options
-  result.blocks = @[initBlock()]
-  result.nestedTryStmts = @[]
-  result.finallySafePoints = @[]
-  result.sigConflicts = initCountTable[string]()
+  result = BProc(
+    prc: prc,
+    module: module,
+    optionsStack: if module.initProc != nil: module.initProc.optionsStack
+                  else: @[],
+    options: if prc != nil: prc.options
+             else: module.config.options,
+    blocks: @[initBlock()],
+    sigConflicts: initCountTable[string]())
+  if optQuirky in result.options:
+    result.flags = {nimErrorFlagDisabled}
 
 proc newModuleList*(g: ModuleGraph): BModuleList =
   BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](),
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index 6b4322a13..ca97d0494 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -10,12 +10,15 @@
 ## This module implements code generation for methods.
 
 import
-  intsets, options, ast, msgs, idents, renderer, types, magicsys,
-  sempass2, strutils, modulegraphs, lineinfos
+  options, ast, msgs, idents, renderer, types, magicsys,
+  sempass2, modulegraphs, lineinfos, astalgo
+
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
+import std/[tables]
 
 proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
   var dest = skipTypes(d, abstractPtrs)
@@ -44,12 +47,15 @@ proc getDispatcher*(s: PSym): PSym =
   if dispatcherPos < s.ast.len:
     result = s.ast[dispatcherPos].sym
     doAssert sfDispatcher in result.flags
+  else:
+    result = nil
 
 proc methodCall*(n: PNode; conf: ConfigRef): PNode =
   result = n
   # replace ordinary method by dispatcher method:
   let disp = getDispatcher(result[0].sym)
   if disp != nil:
+    result[0].typ = disp.typ
     result[0].sym = disp
     # change the arguments to up/downcasts to fit the dispatcher's parameters:
     for i in 1..<result.len:
@@ -61,22 +67,25 @@ type
   MethodResult = enum No, Invalid, Yes
 
 proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
+  result = No
   if a.name.id != b.name.id: return
-  if a.typ.len != b.typ.len:
+  if a.typ.signatureLen != b.typ.signatureLen:
     return
 
-  for i in 1..<a.typ.len:
-    var aa = a.typ[i]
-    var bb = b.typ[i]
+  var i = 0
+  for x, y in paramTypePairs(a.typ, b.typ):
+    inc i
+    var aa = x
+    var bb = y
     while true:
       aa = skipTypes(aa, {tyGenericInst, tyAlias})
       bb = skipTypes(bb, {tyGenericInst, tyAlias})
       if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}:
-        aa = aa.lastSon
-        bb = bb.lastSon
+        aa = aa.elementType
+        bb = bb.elementType
       else:
         break
-    if sameType(a.typ[i], b.typ[i]):
+    if sameType(x, y):
       if aa.kind == tyObject and result != Invalid:
         result = Yes
     elif aa.kind == tyObject and bb.kind == tyObject and (i == 1 or multiMethods):
@@ -94,10 +103,11 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult =
       return No
   if result == Yes:
     # check for return type:
-    if not sameTypeOrNil(a.typ[0], b.typ[0]):
-      if b.typ[0] != nil and b.typ[0].kind == tyUntyped:
+    # ignore flags of return types; # bug #22673
+    if not sameTypeOrNil(a.typ.returnType, b.typ.returnType, {IgnoreFlags}):
+      if b.typ.returnType != nil and b.typ.returnType.kind == tyUntyped:
         # infer 'auto' from the base to make it consistent:
-        b.typ[0] = a.typ[0]
+        b.typ.setReturnType a.typ.returnType
       else:
         return No
 
@@ -112,21 +122,21 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) =
     s.ast[dispatcherPos] = dispatcher
 
 proc createDispatcher(s: PSym; g: ModuleGraph; idgen: IdGenerator): PSym =
-  var disp = copySym(s, nextSymId(idgen))
+  var disp = copySym(s, idgen)
   incl(disp.flags, sfDispatcher)
   excl(disp.flags, sfExported)
   let old = disp.typ
-  disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner)
+  disp.typ = copyType(disp.typ, idgen, disp.typ.owner)
   copyTypeProps(g, idgen.module, disp.typ, old)
 
   # we can't inline the dispatcher itself (for now):
   if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall
   disp.ast = copyTree(s.ast)
   disp.ast[bodyPos] = newNodeI(nkEmpty, s.info)
-  disp.loc.r = ""
-  if s.typ[0] != nil:
+  disp.loc.snippet = ""
+  if s.typ.returnType != nil:
     if disp.ast.len > resultPos:
-      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen))
+      disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, idgen)
     else:
       # We've encountered a method prototype without a filled-in
       # resultPos slot. We put a placeholder in there that will
@@ -147,25 +157,16 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) =
      disp.ast[resultPos].kind == nkEmpty:
     disp.ast[resultPos] = copyTree(meth.ast[resultPos])
 
-  # The following code works only with lock levels, so we disable
-  # it when they're not available.
-  when declared(TLockLevel):
-    proc `<`(a, b: TLockLevel): bool {.borrow.}
-    proc `==`(a, b: TLockLevel): bool {.borrow.}
-    if disp.typ.lockLevel == UnspecifiedLockLevel:
-      disp.typ.lockLevel = meth.typ.lockLevel
-    elif meth.typ.lockLevel != UnspecifiedLockLevel and
-         meth.typ.lockLevel != disp.typ.lockLevel:
-      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
-      # checkMethodeffects() in sempass2.nim for now.
-      if disp.typ.lockLevel < meth.typ.lockLevel:
-        disp.typ.lockLevel = meth.typ.lockLevel
-
 proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
-  var witness: PSym
+  var witness: PSym = nil
+  if s.typ.firstParamType.owner.getModule != s.getModule and vtables in g.config.features and not
+      g.config.isDefined("nimInternalNonVtablesTesting"):
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` can be defined only in the same module with its type (" & s.typ.firstParamType.typeToString() & ")")
+  if sfImportc in s.flags:
+    localError(g.config, s.info, errGenerated, "method `" & s.name.s &
+          "` is not allowed to have 'importc' pragmas")
+
   for i in 0..<g.methods.len:
     let disp = g.methods[i].dispatcher
     case sameMethodBucket(disp, s, multimethods = optMultiMethods in g.config.globalOptions)
@@ -184,6 +185,11 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
     of Invalid:
       if witness.isNil: witness = g.methods[i].methods[0]
   # create a new dispatcher:
+  # stores the id and the position
+  if s.typ.firstParamType.skipTypes(skipPtrs).itemId notin g.bucketTable:
+    g.bucketTable[s.typ.firstParamType.skipTypes(skipPtrs).itemId] = 1
+  else:
+    g.bucketTable.inc(s.typ.firstParamType.skipTypes(skipPtrs).itemId)
   g.methods.add((methods: @[s], dispatcher: createDispatcher(s, g, idgen)))
   #echo "adding ", s.info
   if witness != nil:
@@ -192,8 +198,9 @@ proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym) =
   elif sfBase notin s.flags:
     message(g.config, s.info, warnUseBase)
 
-proc relevantCol(methods: seq[PSym], col: int): bool =
+proc relevantCol*(methods: seq[PSym], col: int): bool =
   # returns true iff the position is relevant
+  result = false
   var t = methods[0].typ[col].skipTypes(skipPtrs)
   if t.kind == tyObject:
     for i in 1..high(methods):
@@ -202,7 +209,8 @@ proc relevantCol(methods: seq[PSym], col: int): bool =
         return true
 
 proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
-  for col in 1..<a.typ.len:
+  result = 0
+  for col in FirstParamAt..<a.typ.signatureLen:
     if contains(relevantCols, col):
       var aa = skipTypes(a.typ[col], skipPtrs)
       var bb = skipTypes(b.typ[col], skipPtrs)
@@ -210,7 +218,7 @@ proc cmpSignatures(a, b: PSym, relevantCols: IntSet): int =
       if (d != high(int)) and d != 0:
         return d
 
-proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
+proc sortBucket*(a: var seq[PSym], relevantCols: IntSet) =
   # we use shellsort here; fast and simple
   var n = a.len
   var h = 1
@@ -229,16 +237,16 @@ proc sortBucket(a: var seq[PSym], relevantCols: IntSet) =
       a[j] = v
     if h == 1: break
 
-proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PSym =
+proc genIfDispatcher*(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet; idgen: IdGenerator): PSym =
   var base = methods[0].ast[dispatcherPos].sym
   result = base
-  var paramLen = base.typ.len
+  var paramLen = base.typ.signatureLen
   var nilchecks = newNodeI(nkStmtList, base.info)
   var disp = newNodeI(nkIfStmt, base.info)
   var ands = getSysMagic(g, unknownLineInfo, "and", mAnd)
   var iss = getSysMagic(g, unknownLineInfo, "of", mOf)
   let boolType = getSysType(g, unknownLineInfo, tyBool)
-  for col in 1..<paramLen:
+  for col in FirstParamAt..<paramLen:
     if contains(relevantCols, col):
       let param = base.typ.n[col].sym
       if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
@@ -247,7 +255,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS
   for meth in 0..high(methods):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
-    for col in 1..<paramLen:
+    for col in FirstParamAt..<paramLen:
       if contains(relevantCols, col):
         var isn = newNodeIT(nkCall, base.info, boolType)
         isn.add newSymNode(iss)
@@ -262,7 +270,7 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS
           cond = a
         else:
           cond = isn
-    let retTyp = base.typ[0]
+    let retTyp = base.typ.returnType
     let call = newNodeIT(nkCall, base.info, retTyp)
     call.add newSymNode(curr)
     for col in 1..<paramLen:
@@ -288,14 +296,13 @@ proc genDispatcher(g: ModuleGraph; methods: seq[PSym], relevantCols: IntSet): PS
   nilchecks.flags.incl nfTransf # should not be further transformed
   result.ast[bodyPos] = nilchecks
 
-proc generateMethodDispatchers*(g: ModuleGraph): PNode =
-  result = newNode(nkStmtList)
+proc generateIfMethodDispatchers*(g: ModuleGraph, idgen: IdGenerator) =
   for bucket in 0..<g.methods.len:
     var relevantCols = initIntSet()
-    for col in 1..<g.methods[bucket].methods[0].typ.len:
+    for col in FirstParamAt..<g.methods[bucket].methods[0].typ.signatureLen:
       if relevantCol(g.methods[bucket].methods, col): incl(relevantCols, col)
       if optMultiMethods notin g.config.globalOptions:
         # if multi-methods are not enabled, we are interested only in the first field
         break
     sortBucket(g.methods[bucket].methods, relevantCols)
-    result.add newSymNode(genDispatcher(g, g.methods[bucket].methods, relevantCols))
+    g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, idgen)
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index 613fbe582..8bdd04ca7 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -18,7 +18,8 @@
 #    dec a
 #
 # Should be transformed to:
-#  STATE0:
+#  case :state
+#  of 0:
 #    if a > 0:
 #      echo "hi"
 #      :state = 1 # Next state
@@ -26,19 +27,14 @@
 #    else:
 #      :state = 2 # Next state
 #      break :stateLoop # Proceed to the next state
-#  STATE1:
+#  of 1:
 #    dec a
 #    :state = 0 # Next state
 #    break :stateLoop # Proceed to the next state
-#  STATE2:
+#  of 2:
 #    :state = -1 # End of execution
-
-# The transformation should play well with lambdalifting, however depending
-# on situation, it can be called either before or after lambdalifting
-# transformation. As such we behave slightly differently, when accessing
-# iterator state, or using temp variables. If lambdalifting did not happen,
-# we just create local variables, so that they will be lifted further on.
-# Otherwise, we utilize existing env, created by lambdalifting.
+#  else:
+#    return
 
 # Lambdalifting treats :state variable specially, it should always end up
 # as the first field in env. Currently C codegen depends on this behavior.
@@ -104,12 +100,13 @@
 # Is transformed to (yields are left in place for example simplicity,
 #    in reality the code is subdivided even more, as described above):
 #
-# STATE0: # Try
+# case :state
+# of 0: # Try
 #   yield 0
 #   raise ...
 #   :state = 2 # What would happen should we not raise
 #   break :stateLoop
-# STATE1: # Except
+# of 1: # Except
 #   yield 1
 #   :tmpResult = 3           # Return
 #   :unrollFinally = true # Return
@@ -117,7 +114,7 @@
 #   break :stateLoop
 #   :state = 2 # What would happen should we not return
 #   break :stateLoop
-# STATE2: # Finally
+# of 2: # Finally
 #   yield 2
 #   if :unrollFinally: # This node is created by `newEndFinallyNode`
 #     if :curExc.isNil:
@@ -130,11 +127,15 @@
 #       raise
 #   state = -1 # Goto next state. In this case we just exit
 #   break :stateLoop
+# else:
+#   return
 
 import
   ast, msgs, idents,
   renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos,
-  tables, options
+  options
+
+import std/tables
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -143,12 +144,11 @@ type
   Ctx = object
     g: ModuleGraph
     fn: PSym
-    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
     tmpResultSym: PSym # Used when we return, but finally has to interfere
     unrollFinallySym: PSym # Indicates that we're unrolling finally states (either exception happened or premature return)
     curExcSym: PSym # Current exception
 
-    states: seq[PNode] # The resulting states. Every state is an nkState node.
+    states: seq[tuple[label: int, body: PNode]] # The resulting states.
     blockLevel: int # Temp used to transform break and continue stmts
     stateLoopLabel: PSym # Label to break on, when jumping between states.
     exitStateIdx: int # index of the last state
@@ -160,15 +160,23 @@ type
     nearestFinally: int # Index of the nearest finally block. For try/except it
                     # is their finally. For finally it is parent finally. Otherwise -1
     idgen: IdGenerator
+    varStates: Table[ItemId, int] # Used to detect if local variable belongs to multiple states
+    stateVarSym: PSym # :state variable. nil if env already introduced by lambdalifting
+      # remove if -d:nimOptIters is default, treating it as always nil
+    nimOptItersEnabled: bool # tracks if -d:nimOptIters is enabled
+      # should be default when issues are fixed, see #24094
 
 const
   nkSkip = {nkEmpty..nkNilLit, nkTemplateDef, nkTypeSection, nkStaticStmt,
             nkCommentStmt, nkMixinStmt, nkBindStmt} + procDefs
+  emptyStateLabel = -1
+  localNotSeen = -1
+  localRequiresLifting = -2
 
 proc newStateAccess(ctx: var Ctx): PNode =
   if ctx.stateVarSym.isNil:
     result = rawIndirectAccess(newSymNode(getEnvParam(ctx.fn)),
-        getStateField(ctx.g, ctx.fn), ctx.fn.info)
+      getStateField(ctx.g, ctx.fn), ctx.fn.info)
   else:
     result = newSymNode(ctx.stateVarSym)
 
@@ -183,9 +191,10 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode =
   ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt)))
 
 proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
-  result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info)
+  result = newSym(skVar, getIdent(ctx.g.cache, name), ctx.idgen, ctx.fn, ctx.fn.info)
   result.typ = typ
-  assert(not typ.isNil)
+  result.flags.incl sfNoInit
+  assert(not typ.isNil, "Env var needs a type")
 
   if not ctx.stateVarSym.isNil:
     # We haven't gone through labmda lifting yet, so just create a local var,
@@ -196,7 +205,7 @@ proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym =
   else:
     let envParam = getEnvParam(ctx.fn)
     # let obj = envParam.typ.lastSon
-    result = addUniqueField(envParam.typ.lastSon, result, ctx.g.cache, ctx.idgen)
+    result = addUniqueField(envParam.typ.elementType, result, ctx.g.cache, ctx.idgen)
 
 proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   if ctx.stateVarSym.isNil:
@@ -204,9 +213,12 @@ proc newEnvVarAccess(ctx: Ctx, s: PSym): PNode =
   else:
     result = newSymNode(s)
 
+proc newTempVarAccess(ctx: Ctx, s: PSym): PNode =
+  result = newSymNode(s, ctx.fn.info)
+
 proc newTmpResultAccess(ctx: var Ctx): PNode =
   if ctx.tmpResultSym.isNil:
-    ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ[0])
+    ctx.tmpResultSym = ctx.newEnvVar(":tmpResult", ctx.fn.typ.returnType)
   ctx.newEnvVarAccess(ctx.tmpResultSym)
 
 proc newUnrollFinallyAccess(ctx: var Ctx, info: TLineInfo): PNode =
@@ -226,10 +238,7 @@ proc newState(ctx: var Ctx, n, gotoOut: PNode): int =
 
   result = ctx.states.len
   let resLit = ctx.g.newIntLit(n.info, result)
-  let s = newNodeI(nkState, n.info)
-  s.add(resLit)
-  s.add(n)
-  ctx.states.add(s)
+  ctx.states.add((result, n))
   ctx.exceptionTable.add(ctx.curExcHandlingState)
 
   if not gotoOut.isNil:
@@ -248,9 +257,26 @@ proc addGotoOut(n: PNode, gotoOut: PNode): PNode =
   if result.len == 0 or result[^1].kind != nkGotoState:
     result.add(gotoOut)
 
-proc newTempVar(ctx: var Ctx, typ: PType): PSym =
-  result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
+proc newTempVarDef(ctx: Ctx, s: PSym, initialValue: PNode): PNode =
+  var v = initialValue
+  if v == nil:
+    v = ctx.g.emptyNode
+  newTree(nkVarSection, newTree(nkIdentDefs, newSymNode(s), ctx.g.emptyNode, v))
+
+proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode
+
+proc newTempVar(ctx: var Ctx, typ: PType, parent: PNode, initialValue: PNode = nil): PSym =
+  if ctx.nimOptItersEnabled:
+    result = newSym(skVar, getIdent(ctx.g.cache, ":tmpSlLower" & $ctx.tempVarId), ctx.idgen, ctx.fn, ctx.fn.info)
+  else:
+    result = ctx.newEnvVar(":tmpSlLower" & $ctx.tempVarId, typ)
   inc ctx.tempVarId
+  result.typ = typ
+  assert(not typ.isNil, "Temp var needs a type")
+  if ctx.nimOptItersEnabled:
+    parent.add(ctx.newTempVarDef(result, initialValue))
+  elif initialValue != nil:
+    parent.add(ctx.newEnvVarAsgn(result, initialValue))
 
 proc hasYields(n: PNode): bool =
   # TODO: This is very inefficient. It traverses the node, looking for nkYieldStmt.
@@ -258,10 +284,11 @@ proc hasYields(n: PNode): bool =
   of nkYieldStmt:
     result = true
   of nkSkip:
-    discard
+    result = false
   else:
-    for c in n:
-      if c.hasYields:
+    result = false
+    for i in ord(n.kind == nkCast)..<n.len:
+      if n[i].hasYields:
         result = true
         break
 
@@ -325,7 +352,7 @@ proc collectExceptState(ctx: var Ctx, n: PNode): PNode {.inline.} =
       var ifBranch: PNode
 
       if c.len > 1:
-        var cond: PNode
+        var cond: PNode = nil
         for i in 0..<c.len - 1:
           assert(c[i].kind == nkType)
           let nextCond = newTree(nkCall,
@@ -388,26 +415,29 @@ proc getFinallyNode(ctx: var Ctx, n: PNode): PNode =
 proc hasYieldsInExpressions(n: PNode): bool =
   case n.kind
   of nkSkip:
-    discard
+    result = false
   of nkStmtListExpr:
     if isEmptyType(n.typ):
+      result = false
       for c in n:
         if c.hasYieldsInExpressions:
           return true
     else:
       result = n.hasYields
   of nkCast:
+    result = false
     for i in 1..<n.len:
       if n[i].hasYieldsInExpressions:
         return true
   else:
+    result = false
     for c in n:
       if c.hasYieldsInExpressions:
         return true
 
 proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
   assert(n.kind == nkStmtListExpr)
-  result.s = newNodeI(nkStmtList, n.info)
+  result = (newNodeI(nkStmtList, n.info), nil)
   result.s.sons = @[]
 
   var n = n
@@ -418,8 +448,15 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] =
 
   result.res = n
 
+proc newTempVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
+  if isEmptyType(v.typ):
+    result = v
+  else:
+    result = newTree(nkFastAsgn, ctx.newTempVarAccess(s), v)
+    result.info = v.info
 
 proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
+  # unused with -d:nimOptIters
   if isEmptyType(v.typ):
     result = v
   else:
@@ -427,10 +464,13 @@ proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode =
     result.info = v.info
 
 proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) =
+  var input = input
   if input.kind == nkStmtListExpr:
     let (st, res) = exprToStmtList(input)
     output.add(st)
-    output.add(ctx.newEnvVarAsgn(sym, res))
+    input = res
+  if ctx.nimOptItersEnabled:
+    output.add(ctx.newTempVarAsgn(sym, input))
   else:
     output.add(ctx.newEnvVarAsgn(sym, input))
 
@@ -442,6 +482,16 @@ proc newNotCall(g: ModuleGraph; e: PNode): PNode =
   result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e)
   result.typ = g.getSysType(e.info, tyBool)
 
+proc boolLit(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
+  result = newIntLit(g, info, ord value)
+  result.typ = getSysType(g, info, tyBool)
+
+proc captureVar(c: var Ctx, s: PSym) =
+  if c.varStates.getOrDefault(s.itemId) != localRequiresLifting:
+    c.varStates[s.itemId] = localRequiresLifting # Mark this variable for lifting
+    let e = getEnvParam(c.fn)
+    discard addField(e.typ.elementType, s, c.g.cache, c.idgen)
+
 proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
   result = n
   case n.kind
@@ -495,12 +545,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
 
     if ns:
       needsSplit = true
-      var tmp: PSym
+      var tmp: PSym = nil
       let isExpr = not isEmptyType(n.typ)
       if isExpr:
-        tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        tmp = ctx.newTempVar(n.typ, result)
       else:
         result = newNodeI(nkStmtList, n.info)
 
@@ -551,7 +601,11 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         else:
           internalError(ctx.g.config, "lowerStmtListExpr(nkIf): " & $branch.kind)
 
-      if isExpr: result.add(ctx.newEnvVarAccess(tmp))
+      if isExpr:
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
   of nkTryStmt, nkHiddenTryStmt:
     var ns = false
@@ -565,7 +619,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       if isExpr:
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
-        let tmp = ctx.newTempVar(n.typ)
+        let tmp = ctx.newTempVar(n.typ, result)
 
         n[0] = ctx.convertExprBodyToAsgn(n[0], tmp)
         for i in 1..<n.len:
@@ -581,7 +635,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkTryStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
 
   of nkCaseStmt:
     var ns = false
@@ -594,9 +651,9 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       let isExpr = not isEmptyType(n.typ)
 
       if isExpr:
-        let tmp = ctx.newTempVar(n.typ)
         result = newNodeI(nkStmtListExpr, n.info)
         result.typ = n.typ
+        let tmp = ctx.newTempVar(n.typ, result)
 
         if n[0].kind == nkStmtListExpr:
           let (st, ex) = exprToStmtList(n[0])
@@ -613,7 +670,10 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           else:
             internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind)
         result.add(n)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
       elif n[0].kind == nkStmtListExpr:
         result = newNodeI(nkStmtList, n.info)
         let (st, ex) = exprToStmtList(n[0])
@@ -643,10 +703,14 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           result.add(st)
           cond = ex
 
-        let tmp = ctx.newTempVar(cond.typ)
-        result.add(ctx.newEnvVarAsgn(tmp, cond))
+        let tmp = ctx.newTempVar(cond.typ, result, cond)
+        # result.add(ctx.newTempVarAsgn(tmp, cond))
 
-        var check = ctx.newEnvVarAccess(tmp)
+        var check: PNode
+        if ctx.nimOptItersEnabled:
+          check = ctx.newTempVarAccess(tmp)
+        else:
+          check = ctx.newEnvVarAccess(tmp)
         if n[0].sym.magic == mOr:
           check = ctx.g.newNotCall(check)
 
@@ -656,12 +720,18 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
           let (st, ex) = exprToStmtList(cond)
           ifBody.add(st)
           cond = ex
-        ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
+        if ctx.nimOptItersEnabled:
+          ifBody.add(ctx.newTempVarAsgn(tmp, cond))
+        else:
+          ifBody.add(ctx.newEnvVarAsgn(tmp, cond))
 
         let ifBranch = newTree(nkElifBranch, check, ifBody)
         let ifNode = newTree(nkIfStmt, ifBranch)
         result.add(ifNode)
-        result.add(ctx.newEnvVarAccess(tmp))
+        if ctx.nimOptItersEnabled:
+          result.add(ctx.newTempVarAccess(tmp))
+        else:
+          result.add(ctx.newEnvVarAccess(tmp))
       else:
         for i in 0..<n.len:
           if n[i].kind == nkStmtListExpr:
@@ -670,9 +740,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
             n[i] = ex
 
           if n[i].kind in nkCallKinds: # XXX: This should better be some sort of side effect tracking
-            let tmp = ctx.newTempVar(n[i].typ)
-            result.add(ctx.newEnvVarAsgn(tmp, n[i]))
-            n[i] = ctx.newEnvVarAccess(tmp)
+            let tmp = ctx.newTempVar(n[i].typ, result, n[i])
+            # result.add(ctx.newTempVarAsgn(tmp, n[i]))
+            if ctx.nimOptItersEnabled:
+              n[i] = ctx.newTempVarAccess(tmp)
+            else:
+              n[i] = ctx.newEnvVarAccess(tmp)
 
         result.add(n)
 
@@ -688,6 +761,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         let (st, ex) = exprToStmtList(c[^1])
         result.add(st)
         c[^1] = ex
+      for i in 0 .. c.len - 3:
+        if c[i].kind == nkSym:
+          let s = c[i].sym
+          if sfForceLift in s.flags:
+            ctx.captureVar(s)
+
       result.add(varSect)
 
   of nkDiscardStmt, nkReturnStmt, nkRaiseStmt:
@@ -718,7 +797,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
       n[^1] = ex
       result.add(n)
 
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     var ns = false
     for i in 0..<n.len:
       n[i] = ctx.lowerStmtListExprs(n[i], ns)
@@ -773,7 +852,7 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode =
         let check = newTree(nkIfStmt, branch)
         let newBody = newTree(nkStmtList, st, check, n[1])
 
-        n[0] = newSymNode(ctx.g.getSysSym(n[0].info, "true"))
+        n[0] = ctx.g.boolLit(n[0].info, true)
         n[1] = newBody
 
   of nkDotExpr, nkCheckedFieldExpr:
@@ -825,7 +904,7 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode =
   let retStmt =
     if ctx.nearestFinally == 0:
       # last finally, we can return
-      let retValue = if ctx.fn.typ[0].isNil:
+      let retValue = if ctx.fn.typ.returnType.isNil:
                    ctx.g.emptyNode
                  else:
                    newTree(nkFastAsgn,
@@ -855,7 +934,9 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
   case n.kind
   of nkReturnStmt:
     # We're somewhere in try, transform to finally unrolling
-    assert(ctx.nearestFinally != 0)
+    if ctx.nearestFinally == 0:
+      # return is within the finally
+      return
 
     result = newNodeI(nkStmtList, n.info)
 
@@ -868,7 +949,7 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode =
     if n[0].kind != nkEmpty:
       let asgnTmpResult = newNodeI(nkAsgn, n.info)
       asgnTmpResult.add(ctx.newTmpResultAccess())
-      let x = if n[0].kind in {nkAsgn, nkFastAsgn}: n[0][1] else: n[0]
+      let x = if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}: n[0][1] else: n[0]
       asgnTmpResult.add(x)
       result.add(asgnTmpResult)
 
@@ -1125,10 +1206,10 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
     let label = stateIdx
     if label == ctx.exitStateIdx: break
     var newLabel = label
-    if label == -1:
+    if label == emptyStateLabel:
       newLabel = ctx.exitStateIdx
     else:
-      let fs = skipStmtList(ctx, ctx.states[label][1])
+      let fs = skipStmtList(ctx, ctx.states[label].body)
       if fs.kind == nkGotoState:
         newLabel = fs[0].intVal.int
     if label == newLabel: break
@@ -1137,7 +1218,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int =
     if maxJumps == 0:
       assert(false, "Internal error")
 
-  result = ctx.states[stateIdx][0].intVal.int
+  result = ctx.states[stateIdx].label
 
 proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
   result = n
@@ -1152,10 +1233,10 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode=
       n[i] = ctx.skipThroughEmptyStates(n[i])
 
 proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType =
-  result = newType(tyArray, nextTypeId(idgen), owner)
+  result = newType(tyArray, idgen, owner)
 
-  let rng = newType(tyRange, nextTypeId(idgen), owner)
-  rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n))
+  let rng = newType(tyRange, idgen, owner)
+  rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n - 1))
   rng.rawAddSon(t)
 
   result.rawAddSon(rng)
@@ -1255,11 +1336,10 @@ proc wrapIntoTryExcept(ctx: var Ctx, n: PNode): PNode {.inline.} =
 proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
   # while true:
   #   block :stateLoop:
-  #     gotoState :state
   #     local vars decl (if needed)
   #     body # Might get wrapped in try-except
   let loopBody = newNodeI(nkStmtList, n.info)
-  result = newTree(nkWhileStmt, newSymNode(ctx.g.getSysSym(n.info, "true")), loopBody)
+  result = newTree(nkWhileStmt, ctx.g.boolLit(n.info, true), loopBody)
   result.info = n.info
 
   let localVars = newNodeI(nkStmtList, n.info)
@@ -1274,11 +1354,7 @@ proc wrapIntoStateLoop(ctx: var Ctx, n: PNode): PNode =
   let blockStmt = newNodeI(nkBlockStmt, n.info)
   blockStmt.add(newSymNode(ctx.stateLoopLabel))
 
-  let gs = newNodeI(nkGotoState, n.info)
-  gs.add(ctx.newStateAccess())
-  gs.add(ctx.g.newIntLit(n.info, ctx.states.len - 1))
-
-  var blockBody = newTree(nkStmtList, gs, localVars, n)
+  var blockBody = newTree(nkStmtList, localVars, n)
   if ctx.hasExceptions:
     blockBody = ctx.wrapIntoTryExcept(blockBody)
 
@@ -1291,29 +1367,28 @@ proc deleteEmptyStates(ctx: var Ctx) =
 
   # Apply new state indexes and mark unused states with -1
   var iValid = 0
-  for i, s in ctx.states:
-    let body = skipStmtList(ctx, s[1])
+  for i, s in ctx.states.mpairs:
+    let body = skipStmtList(ctx, s.body)
     if body.kind == nkGotoState and i != ctx.states.len - 1 and i != 0:
       # This is an empty state. Mark with -1.
-      s[0].intVal = -1
+      s.label = emptyStateLabel
     else:
-      s[0].intVal = iValid
+      s.label = iValid
       inc iValid
 
   for i, s in ctx.states:
-    let body = skipStmtList(ctx, s[1])
+    let body = skipStmtList(ctx, s.body)
     if body.kind != nkGotoState or i == 0:
-      discard ctx.skipThroughEmptyStates(s)
+      discard ctx.skipThroughEmptyStates(s.body)
       let excHandlState = ctx.exceptionTable[i]
       if excHandlState < 0:
         ctx.exceptionTable[i] = -ctx.skipEmptyStates(-excHandlState)
       elif excHandlState != 0:
         ctx.exceptionTable[i] = ctx.skipEmptyStates(excHandlState)
 
-  var i = 0
+  var i = 1 # ignore the entry and the exit
   while i < ctx.states.len - 1:
-    let fs = skipStmtList(ctx, ctx.states[i][1])
-    if fs.kind == nkGotoState and i != 0:
+    if ctx.states[i].label == emptyStateLabel:
       ctx.states.delete(i)
       ctx.exceptionTable.delete(i)
     else:
@@ -1348,7 +1423,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
         let idefs = copyNode(it)
         for v in 0..it.len-3:
           if it[v].kind == nkSym:
-            let x = copySym(it[v].sym, nextSymId(c.idgen))
+            let x = copySym(it[v].sym, c.idgen)
             c.tab[it[v].sym.id] = x
             idefs.add newSymNode(x)
           else:
@@ -1359,6 +1434,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode =
       else:
         result.add it
   of nkRaiseStmt:
+    result = nil
     localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'")
   else:
     result = n
@@ -1372,6 +1448,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
   # detect: 'finally: raises X' which is currently not supported. We produce
   # an error for this case for now. All this will be done properly with Yuriy's
   # patch.
+
   result = n
   case n.kind
   of nkTryStmt:
@@ -1388,6 +1465,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
       discard c.finallys.pop()
 
   of nkWhileStmt, nkBlockStmt:
+    if not n.hasYields: return n
     c.blocks.add((n, c.finallys.len))
     for i in 0 ..< n.len:
       result[i] = preprocess(c, n[i])
@@ -1419,19 +1497,78 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode =
     for i in 0 ..< n.len:
       result[i] = preprocess(c, n[i])
 
+proc detectCapturedVars(c: var Ctx, n: PNode, stateIdx: int) =
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if s.kind in {skResult, skVar, skLet, skForVar, skTemp} and sfGlobal notin s.flags and s.owner == c.fn:
+      let vs = c.varStates.getOrDefault(s.itemId, localNotSeen)
+      if vs == localNotSeen: # First seing this variable
+        c.varStates[s.itemId] = stateIdx
+      elif vs == localRequiresLifting:
+        discard # Sym already marked
+      elif vs != stateIdx:
+        c.captureVar(s)
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      detectCapturedVars(c, n[0][1], stateIdx)
+    else:
+      detectCapturedVars(c, n[0], stateIdx)
+  else:
+    for i in 0 ..< n.safeLen:
+      detectCapturedVars(c, n[i], stateIdx)
+
+proc detectCapturedVars(c: var Ctx) =
+  for i, s in c.states:
+    detectCapturedVars(c, s.body, i)
+
+proc liftLocals(c: var Ctx, n: PNode): PNode =
+  result = n
+  case n.kind
+  of nkSym:
+    let s = n.sym
+    if c.varStates.getOrDefault(s.itemId) == localRequiresLifting:
+      # lift
+      let e = getEnvParam(c.fn)
+      let field = getFieldFromObj(e.typ.elementType, s)
+      assert(field != nil)
+      result = rawIndirectAccess(newSymNode(e), field, n.info)
+    # elif c.varStates.getOrDefault(s.itemId, localNotSeen) != localNotSeen:
+    #   echo "Not lifting ", s.name.s
+
+  of nkReturnStmt:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
+      # we have a `result = result` expression produced by the closure
+      # transform, let's not touch the LHS in order to make the lifting pass
+      # correct when `result` is lifted
+      n[0][1] = liftLocals(c, n[0][1])
+    else:
+      n[0] = liftLocals(c, n[0])
+  else:
+    for i in 0 ..< n.safeLen:
+      n[i] = liftLocals(c, n[i])
+
 proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode =
-  var ctx: Ctx
-  ctx.g = g
-  ctx.fn = fn
-  ctx.idgen = idgen
+  var ctx = Ctx(g: g, fn: fn, idgen: idgen,
+    # should be default when issues are fixed, see #24094:
+    nimOptItersEnabled: isDefined(g.config, "nimOptIters"))
 
   if getEnvParam(fn).isNil:
-    # Lambda lifting was not done yet. Use temporary :state sym, which will
-    # be handled specially by lambda lifting. Local temp vars (if needed)
-    # should follow the same logic.
-    ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info)
-    ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
-  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info)
+    if ctx.nimOptItersEnabled:
+      # The transformation should always happen after at least partial lambdalifting
+      # is performed, so that the closure iter environment is always created upfront.
+      doAssert(false, "Env param not created before iter transformation")
+    else:
+      # Lambda lifting was not done yet. Use temporary :state sym, which will
+      # be handled specially by lambda lifting. Local temp vars (if needed)
+      # should follow the same logic.
+      ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), idgen, fn, fn.info)
+      ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen)
+
+  ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), idgen, fn, fn.info)
   var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen)
   var n = preprocess(pc, n.toStmtList)
   #echo "transformed into ", n
@@ -1452,21 +1589,30 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n:
   # Optimize empty states away
   ctx.deleteEmptyStates()
 
-  # Make new body by concatenating the list of states
-  result = newNodeI(nkStmtList, n.info)
+  let caseDispatcher = newTreeI(nkCaseStmt, n.info,
+      ctx.newStateAccess())
+
+  if ctx.nimOptItersEnabled:
+    # Lamdalifting will not touch our locals, it is our responsibility to lift those that
+    # need it.
+    detectCapturedVars(ctx)
+
   for s in ctx.states:
-    assert(s.len == 2)
-    let body = s[1]
-    s.sons.del(1)
-    result.add(s)
-    result.add(body)
+    let body = ctx.transformStateAssignments(s.body)
+    caseDispatcher.add newTreeI(nkOfBranch, body.info, g.newIntLit(body.info, s.label), body)
+
+  caseDispatcher.add newTreeI(nkElse, n.info, newTreeI(nkReturnStmt, n.info, g.emptyNode))
+
+  result = wrapIntoStateLoop(ctx, caseDispatcher)
+  if ctx.nimOptItersEnabled:
+    result = liftLocals(ctx, result)
 
-  result = ctx.transformStateAssignments(result)
-  result = ctx.wrapIntoStateLoop(result)
+  when false:
+    echo "TRANSFORM TO STATES: "
+    echo renderTree(result)
 
-  # echo "TRANSFORM TO STATES: "
-  # echo renderTree(result)
+    echo "exception table:"
+    for i, e in ctx.exceptionTable:
+      echo i, " -> ", e
 
-  # echo "exception table:"
-  # for i, e in ctx.exceptionTable:
-  #   echo i, " -> ", e
+    echo "ENV: ", renderTree(getEnvParam(fn).typ.elementType.n)
diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim
index 02162755c..e51248639 100644
--- a/compiler/cmdlinehelper.nim
+++ b/compiler/cmdlinehelper.nim
@@ -7,11 +7,13 @@
 #    distribution, for details about the copyright.
 #
 
-## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix
+## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest
 
 import
   options, idents, nimconf, extccomp, commands, msgs,
-  lineinfos, modulegraphs, condsyms, os, pathutils, parseopt
+  lineinfos, modulegraphs, condsyms, pathutils
+
+import std/[os, parseopt]
 
 proc prependCurDir*(f: AbsoluteFile): AbsoluteFile =
   when defined(unix):
@@ -55,6 +57,11 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi
   if conf.cmd == cmdNimscript:
     incl(conf.globalOptions, optWasNimscript)
   loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files
+  # restores `conf.notes` after loading config files
+  # because it has overwrites the notes when compiling the system module which
+  # is a foreign module compared to the project
+  if conf.cmd in cmdBackends:
+    conf.notes = conf.mainPackageNotes
 
   if not self.suggestMode:
     let scriptFile = conf.projectFull.changeFileExt("nims")
diff --git a/compiler/commands.nim b/compiler/commands.nim
index c2ed01891..cbf915ca6 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -24,10 +24,12 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep")
 bootSwitch(usedGoGC, defined(gogc), "--gc:go")
 bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
-import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs]
+import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs, enumutils]
 import
   msgs, options, nversion, condsyms, extccomp, platform,
-  wordrecg, nimblecmd, lineinfos, pathutils, pathnorm
+  wordrecg, nimblecmd, lineinfos, pathutils
+
+import std/pathnorm
 
 from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect
 
@@ -184,7 +186,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
                          info: TLineInfo; orig: string; conf: ConfigRef) =
   var id = ""  # arg = key or [key] or key:val or [key]:val;  with val=on|off
   var i = 0
-  var notes: set[TMsgKind]
+  var notes: set[TMsgKind] = {}
   var isBracket = false
   if i < arg.len and arg[i] == '[':
     isBracket = true
@@ -205,7 +207,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass,
     # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit
     let x = findStr(noteMin, noteMax, id, errUnknown)
     if x != errUnknown: notes = {TNoteKind(x)}
-    else: localError(conf, info, "unknown $#: $#" % [name, id])
+    else:
+      if isSomeHint:
+        message(conf, info, hintUnknownHint, id)
+      else:
+        localError(conf, info, "unknown $#: $#" % [name, id])
   case id.normalize
   of "all": # other note groups would be easy to support via additional cases
     notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax}
@@ -238,10 +244,11 @@ proc processCompile(conf: ConfigRef; filename: string) =
   extccomp.addExternalFileToCompile(conf, found)
 
 const
-  errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
+  errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'atomicArc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found"
   errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found"
-  errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found"
+  errGuiConsoleOrLibExpectedButXFound = "'gui', 'console', 'lib' or 'staticlib' expected, but '$1' found"
   errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found"
+  errInvalidFeatureButXFound = Feature.toSeq.map(proc(val:Feature): string = "'$1'" % $val).join(", ") & " expected, but '$1' found"
 
 template warningOptionNoop(switch: string) =
   warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch)
@@ -262,13 +269,18 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
     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 "atomicarc": result = conf.selectedGC == gcAtomicArc
+    else:
+      result = false
+      localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
   of "opt":
     case arg.normalize
     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)
+    else:
+      result = false
+      localError(conf, info, errNoneSpeedOrSizeExpectedButXFound % arg)
   of "verbosity": result = $conf.verbosity == arg
   of "app":
     case arg.normalize
@@ -278,7 +290,9 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
                       not contains(conf.globalOptions, optGenGuiApp)
     of "staticlib": result = contains(conf.globalOptions, optGenStaticLib) and
                       not contains(conf.globalOptions, optGenGuiApp)
-    else: localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
+    else:
+      result = false
+      localError(conf, info, errGuiConsoleOrLibExpectedButXFound % arg)
   of "dynliboverride":
     result = isDynlibOverride(conf, arg)
   of "exceptions":
@@ -287,8 +301,18 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo
     of "setjmp": result = conf.exc == excSetjmp
     of "quirky": result = conf.exc == excQuirky
     of "goto": result = conf.exc == excGoto
-    else: localError(conf, info, errInvalidExceptionSystem % arg)
-  else: invalidCmdLineOption(conf, passCmd1, switch, info)
+    else:
+      result = false
+      localError(conf, info, errInvalidExceptionSystem % arg)
+  of "experimental":
+    try:
+      result = conf.features.contains parseEnum[Feature](arg)
+    except ValueError:
+      result = false
+      localError(conf, info, errInvalidFeatureButXFound % arg)
+  else:
+    result = false
+    invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool =
   case switch.normalize
@@ -326,6 +350,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
   of "run", "r": result = contains(conf.globalOptions, optRun)
   of "symbolfiles": result = conf.symbolFiles != disabledSf
   of "genscript": result = contains(conf.globalOptions, optGenScript)
+  of "gencdeps": result = contains(conf.globalOptions, optGenCDeps)
   of "threads": result = contains(conf.globalOptions, optThreads)
   of "tlsemulation": result = contains(conf.globalOptions, optTlsEmulation)
   of "implicitstatic": result = contains(conf.options, optImplicitStatic)
@@ -333,9 +358,14 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
     if switch.normalize == "patterns": deprecatedAlias(switch, "trmacros")
     result = contains(conf.options, optTrMacros)
   of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace)
-  of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch)
+  of "nilseqs", "nilchecks", "taintmode":
+    warningOptionNoop(switch)
+    result = false
   of "panics": result = contains(conf.globalOptions, optPanics)
-  else: invalidCmdLineOption(conf, passCmd1, switch, info)
+  of "jsbigint64": result = contains(conf.globalOptions, optJsBigInt64)
+  else:
+    result = false
+    invalidCmdLineOption(conf, passCmd1, switch, info)
 
 proc processPath(conf: ConfigRef; path: string, info: TLineInfo,
                  notRelativeToProj = false): AbsoluteDir =
@@ -377,7 +407,8 @@ proc makeAbsolute(s: string): AbsoluteFile =
 proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string,
                      info: TLineInfo) =
   ## set tracking info, common code for track, trackDirty, & ideTrack
-  var ln, col: int
+  var ln: int = 0
+  var col: int = 0
   if parseUtils.parseInt(line, ln) <= 0:
     localError(conf, info, errInvalidNumber % line)
   if parseUtils.parseInt(column, col) <= 0:
@@ -442,6 +473,7 @@ proc parseCommand*(command: string): Command =
   of "objc", "compiletooc": cmdCompileToOC
   of "js", "compiletojs": cmdCompileToJS
   of "r": cmdCrun
+  of "m": cmdM
   of "run": cmdTcc
   of "check": cmdCheck
   of "e": cmdNimscript
@@ -514,14 +546,7 @@ proc initOrcDefines*(conf: ConfigRef) =
   if conf.exc == excNone and conf.backend != backendCpp:
     conf.exc = excGoto
 
-proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
-  if isOrc:
-    conf.selectedGC = gcOrc
-    defineSymbol(conf.symbols, "gcorc")
-  else:
-    conf.selectedGC = gcArc
-    defineSymbol(conf.symbols, "gcarc")
-
+proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef) =
   defineSymbol(conf.symbols, "gcdestructors")
   incl conf.globalOptions, optSeqDestructors
   incl conf.globalOptions, optTinyRtti
@@ -531,10 +556,11 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) =
   if conf.exc == excNone and conf.backend != backendCpp:
     conf.exc = excGoto
 
-proc unregisterArcOrc(conf: ConfigRef) =
+proc unregisterArcOrc*(conf: ConfigRef) =
   undefSymbol(conf.symbols, "gcdestructors")
   undefSymbol(conf.symbols, "gcarc")
   undefSymbol(conf.symbols, "gcorc")
+  undefSymbol(conf.symbols, "gcatomicarc")
   undefSymbol(conf.symbols, "nimSeqsV2")
   undefSymbol(conf.symbols, "nimV2")
   excl conf.globalOptions, optSeqDestructors
@@ -560,9 +586,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
       conf.selectedGC = gcMarkAndSweep
       defineSymbol(conf.symbols, "gcmarkandsweep")
     of "destructors", "arc":
-      registerArcOrc(pass, conf, false)
+      conf.selectedGC = gcArc
+      defineSymbol(conf.symbols, "gcarc")
+      registerArcOrc(pass, conf)
     of "orc":
-      registerArcOrc(pass, conf, true)
+      conf.selectedGC = gcOrc
+      defineSymbol(conf.symbols, "gcorc")
+      registerArcOrc(pass, conf)
+    of "atomicarc":
+      conf.selectedGC = gcAtomicArc
+      defineSymbol(conf.symbols, "gcatomicarc")
+      registerArcOrc(pass, conf)
     of "hooks":
       conf.selectedGC = gcHooks
       defineSymbol(conf.symbols, "gchooks")
@@ -584,10 +618,17 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass,
       defineSymbol(conf.symbols, "gcregions")
     else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg)
 
+proc pathRelativeToConfig(arg: string, pass: TCmdLinePass, conf: ConfigRef): string =
+  if pass == passPP and not isAbsolute(arg):
+    assert isAbsolute(conf.currentConfigDir), "something is wrong with currentConfigDir"
+    result = conf.currentConfigDir / arg
+  else:
+    result = arg
+
 proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
                     conf: ConfigRef) =
-  var
-    key, val: string
+  var key = ""
+  var val = ""
   case switch.normalize
   of "eval":
     expectArg(conf, switch, arg, pass, info)
@@ -602,17 +643,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     for path in nimbleSubs(conf, arg):
       addPath(conf, if pass == passPP: processCfgPath(conf, path, info)
                     else: processPath(conf, path, info), info)
-  of "nimblepath", "babelpath":
-    if switch.normalize == "babelpath": deprecatedAlias(switch, "nimblepath")
+  of "nimblepath":
     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 = AbsoluteDir getEnv("NIMBLE_DIR")
       if not nimbleDir.isEmpty and pass == passPP:
+        path = nimbleDir / RelativeDir"pkgs2"
+        nimblePath(conf, path, info)
         path = nimbleDir / RelativeDir"pkgs"
       nimblePath(conf, path, info)
-  of "nonimblepath", "nobabelpath":
-    if switch.normalize == "nobabelpath": deprecatedAlias(switch, "nonimblepath")
+  of "nonimblepath":
     expectNoArg(conf, switch, arg, pass, info)
     disableNimblePath(conf)
   of "clearnimblepath":
@@ -629,7 +670,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set
     # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")`
     if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/')
-    conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true)
+    conf.nimcacheDir = processPath(conf, pathRelativeToConfig(arg, pass, conf), info, notRelativeToProj=true)
   of "out", "o":
     expectArg(conf, switch, arg, pass, info)
     let f = splitFile(processPath(conf, arg, info, notRelativeToProj=true).string)
@@ -648,6 +689,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "backend", "b":
     let backend = parseEnum(arg.normalize, TBackend.default)
     if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
+    if backend == backendJs: # bug #21209
+      conf.globalOptions.excl {optThreadAnalysis, optThreads}
+      if optRun in conf.globalOptions:
+        # for now, -r uses nodejs, so define nodejs
+        defineSymbol(conf.symbols, "nodejs")
     conf.backend = backend
   of "doccmd": conf.docCmd = arg
   of "define", "d":
@@ -758,7 +804,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if conf.backend == backendJs or conf.cmd == cmdNimscript: discard
     else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info)
     #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe)
-  of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
+  of "tlsemulation":
+    processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info)
+    if optTlsEmulation in conf.globalOptions:
+      conf.legacyFeatures.incl emitGenerics
   of "implicitstatic":
     processOnOffSwitch(conf, {optImplicitStatic}, arg, pass, info)
   of "patterns", "trmacros":
@@ -795,6 +844,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       defineSymbol(conf.symbols, "dll")
     of "staticlib":
       incl(conf.globalOptions, optGenStaticLib)
+      incl(conf.globalOptions, optNoMain)
       excl(conf.globalOptions, optGenGuiApp)
       defineSymbol(conf.symbols, "library")
       defineSymbol(conf.symbols, "staticlib")
@@ -818,8 +868,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "header":
     if conf != nil: conf.headerFile = arg
     incl(conf.globalOptions, optGenIndex)
+  of "nimbasepattern":
+    if conf != nil: conf.nimbasePattern = arg
   of "index":
-    processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info)
+    case arg.normalize
+    of "", "on": conf.globalOptions.incl {optGenIndex}
+    of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex}
+    of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly}
+    else: localError(conf, info, errOnOrOffExpectedButXFound % arg)
+  of "noimportdoc":
+    processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info)
   of "import":
     expectArg(conf, switch, arg, pass, info)
     if pass in {passCmd2, passPP}:
@@ -852,21 +910,28 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       setTarget(conf.target, conf.target.targetOS, cpu)
   of "run", "r":
     processOnOffSwitchG(conf, {optRun}, arg, pass, info)
+    if conf.backend == backendJs:
+      # for now, -r uses nodejs, so define nodejs
+      defineSymbol(conf.symbols, "nodejs")
   of "maxloopiterationsvm":
     expectArg(conf, switch, arg, pass, info)
-    conf.maxLoopIterationsVM = parseInt(arg)
+    var value: int = 10_000_000
+    discard parseSaturatedNatural(arg, value)
+    if not value > 0: localError(conf, info, "maxLoopIterationsVM must be a positive integer greater than zero")
+    conf.maxLoopIterationsVM = value
   of "errormax":
     expectArg(conf, switch, arg, pass, info)
     # Note: `nim check` (etc) can overwrite this.
     # `0` is meaningless, give it a useful meaning as in clang's -ferror-limit
     # If user doesn't set this flag and the code doesn't either, it'd
     # have the same effect as errorMax = 1
-    let ret = parseInt(arg)
-    conf.errorMax = if ret == 0: high(int) else: ret
+    var value: int = 0
+    discard parseSaturatedNatural(arg, value)
+    conf.errorMax = if value == 0: high(int) else: value
   of "verbosity":
     expectArg(conf, switch, arg, pass, info)
     let verbosity = parseInt(arg)
-    if verbosity notin {0..3}:
+    if verbosity notin 0..3:
       localError(conf, info, "invalid verbosity level: '$1'" % arg)
     conf.verbosity = verbosity
     var verb = NotesVerbosity[conf.verbosity]
@@ -876,7 +941,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     conf.mainPackageNotes = conf.notes
   of "parallelbuild":
     expectArg(conf, switch, arg, pass, info)
-    conf.numberOfProcessors = parseInt(arg)
+    var value: int = 0
+    discard parseSaturatedNatural(arg, value)
+    conf.numberOfProcessors = value
   of "version", "v":
     expectNoArg(conf, switch, arg, pass, info)
     writeVersionInfo(conf, pass)
@@ -914,6 +981,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     if switch.normalize == "gendeps": deprecatedAlias(switch, "genscript")
     processOnOffSwitchG(conf, {optGenScript}, arg, pass, info)
     processOnOffSwitchG(conf, {optCompileOnly}, arg, pass, info)
+  of "gencdeps":
+    processOnOffSwitchG(conf, {optGenCDeps}, arg, pass, info)
   of "colors": processOnOffSwitchG(conf, {optUseColors}, arg, pass, info)
   of "lib":
     expectArg(conf, switch, arg, pass, info)
@@ -997,6 +1066,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     expectNoArg(conf, switch, arg, pass, info)
     conf.exc = low(ExceptionSystem)
     defineSymbol(conf.symbols, "noCppExceptions")
+  of "shownonexports":
+    expectNoArg(conf, switch, arg, pass, info)
+    showNonExportedFields(conf)
   of "exceptions":
     case arg.normalize
     of "cpp": conf.exc = excCpp
@@ -1051,29 +1123,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
   of "expandarc":
     expectArg(conf, switch, arg, pass, info)
     conf.arcToExpand[arg] = "T"
-  of "useversion":
-    expectArg(conf, switch, arg, pass, info)
-    case arg
-    of "1.0":
-      defineSymbol(conf.symbols, "NimMajor", "1")
-      defineSymbol(conf.symbols, "NimMinor", "0")
-      # old behaviors go here:
-      defineSymbol(conf.symbols, "nimOldRelativePathBehavior")
-      undefSymbol(conf.symbols, "nimDoesntTrackDefects")
-      ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect}
-      conf.globalOptions.incl optNimV1Emulation
-    of "1.2":
-      defineSymbol(conf.symbols, "NimMajor", "1")
-      defineSymbol(conf.symbols, "NimMinor", "2")
-      conf.globalOptions.incl optNimV12Emulation
-    of "1.6":
-      defineSymbol(conf.symbols, "NimMajor", "1")
-      defineSymbol(conf.symbols, "NimMinor", "6")
-      conf.globalOptions.incl optNimV16Emulation
-    else:
-      localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`")
-    # always be compatible with 1.x.100:
-    defineSymbol(conf.symbols, "NimPatch", "100")
   of "benchmarkvm":
     processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info)
   of "profilevm":
@@ -1087,6 +1136,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     processOnOffSwitchG(conf, {optPanics}, arg, pass, info)
     if optPanics in conf.globalOptions:
       defineSymbol(conf.symbols, "nimPanics")
+  of "jsbigint64":
+    processOnOffSwitchG(conf, {optJsBigInt64}, arg, pass, info)
   of "sourcemap": # xxx document in --fullhelp
     conf.globalOptions.incl optSourcemap
     conf.options.incl optLineDir
@@ -1101,7 +1152,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
     else: invalidCmdLineOption(conf, pass, switch, info)
 
 proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) =
-  var cmd, arg: string
+  var cmd = ""
+  var arg = ""
   splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo)
   processSwitch(cmd, arg, pass, gCmdLineInfo, config)
 
@@ -1128,7 +1180,10 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
       config.projectName = unixToNativePath(p.key)
       config.arguments = cmdLineRest(p)
       result = true
-    elif pass != passCmd2: setCommandEarly(config, p.key)
+    elif pass != passCmd2:
+      setCommandEarly(config, p.key)
+      result = false
+    else: result = false
   else:
     if pass == passCmd1: config.commandArgs.add p.key
     if argsCount == 1:
@@ -1139,4 +1194,6 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser;
         config.projectName = unixToNativePath(p.key)
       config.arguments = cmdLineRest(p)
       result = true
+    else:
+      result = false
   inc argsCount
diff --git a/compiler/compiler.nimble b/compiler/compiler.nimble
new file mode 100644
index 000000000..ef3f343e1
--- /dev/null
+++ b/compiler/compiler.nimble
@@ -0,0 +1,28 @@
+include "../lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
+author = "Andreas Rumpf"
+description = "Compiler package providing the compiler sources as a library."
+license = "MIT"
+skipDirs = @["."]
+installDirs = @["compiler"]
+
+import os
+
+var compilerDir = ""
+
+before install:
+  rmDir("compiler")
+
+  let
+    files = listFiles(".")
+    dirs = listDirs(".")
+
+  mkDir("compiler")
+
+  for f in files:
+    cpFile(f, "compiler" / f)
+
+  for d in dirs:
+    cpDir(d, "compiler" / d)
+
+requires "nim"
diff --git a/compiler/concepts.nim b/compiler/concepts.nim
index 89a2bdc10..d48bacdc5 100644
--- a/compiler/concepts.nim
+++ b/compiler/concepts.nim
@@ -11,9 +11,9 @@
 ## for details. Note this is a first implementation and only the "Concept matching"
 ## section has been implemented.
 
-import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets
+import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types
 
-from magicsys import addSonSkipIntLit
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -27,24 +27,12 @@ const
 proc declareSelf(c: PContext; info: TLineInfo) =
   ## Adds the magical 'Self' symbols to the current scope.
   let ow = getCurrOwner(c)
-  let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info)
-  s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow)
+  let s = newSym(skType, getIdent(c.cache, "Self"), c.idgen, ow, info)
+  s.typ = newType(tyTypeDesc, c.idgen, ow)
   s.typ.flags.incl {tfUnresolved, tfPacked}
-  s.typ.add newType(tyEmpty, nextTypeId(c.idgen), ow)
+  s.typ.add newType(tyEmpty, c.idgen, ow)
   addDecl(c, s, info)
 
-proc isSelf*(t: PType): bool {.inline.} =
-  ## Is this the magical 'Self' type?
-  t.kind == tyTypeDesc and tfPacked in t.flags
-
-proc makeTypeDesc*(c: PContext, typ: PType): PType =
-  if typ.kind == tyTypeDesc and not isSelf(typ):
-    result = typ
-  else:
-    result = newTypeS(tyTypeDesc, c)
-    incl result.flags, tfCheckedForDestructor
-    result.addSonSkipIntLit(typ, c.idgen)
-
 proc semConceptDecl(c: PContext; n: PNode): PNode =
   ## Recursive helper for semantic checking for the concept declaration.
   ## Currently we only support (possibly empty) lists of statements
@@ -61,6 +49,8 @@ proc semConceptDecl(c: PContext; n: PNode): PNode =
     for i in 0..<n.len-1:
       result[i] = n[i]
     result[^1] = semConceptDecl(c, n[^1])
+  of nkCommentStmt:
+    result = n
   else:
     localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n))
     result = n
@@ -106,7 +96,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
     ignorableForArgType = {tyVar, tySink, tyLent, tyOwned, tyGenericInst, tyAlias, tyInferred}
   case f.kind
   of tyAlias:
-    result = matchType(c, f.lastSon, a, m)
+    result = matchType(c, f.skipModifier, a, m)
   of tyTypeDesc:
     if isSelf(f):
       #let oldLen = m.inferred.len
@@ -115,15 +105,19 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       #m.inferred.setLen oldLen
       #echo "A for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
     else:
-      if a.kind == tyTypeDesc and f.len == a.len:
-        for i in 0..<a.len:
-          if not matchType(c, f[i], a[i], m): return false
-        return true
+      if a.kind == tyTypeDesc and f.hasElementType == a.hasElementType:
+        if f.hasElementType:
+          result = matchType(c, f.elementType, a.elementType, m)
+        else:
+          result = true # both lack it
+      else:
+        result = false
 
   of tyGenericInvocation:
-    if a.kind == tyGenericInst and a[0].kind == tyGenericBody:
-      if sameType(f[0], a[0]) and f.len == a.len-1:
-        for i in 1 ..< f.len:
+    result = false
+    if a.kind == tyGenericInst and a.genericHead.kind == tyGenericBody:
+      if sameType(f.genericHead, a.genericHead) and f.kidsLen == a.kidsLen-1:
+        for i in FirstGenericParamAt ..< f.kidsLen:
           if not matchType(c, f[i], a[i], m): return false
         return true
   of tyGenericParam:
@@ -133,17 +127,17 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
     else:
       let old = existingBinding(m, f)
       if old == nil:
-        if f.len > 0 and f[0].kind != tyNone:
+        if f.hasElementType and f.elementType.kind != tyNone:
           # also check the generic's constraints:
           let oldLen = m.inferred.len
-          result = matchType(c, f[0], a, m)
+          result = matchType(c, f.elementType, a, m)
           m.inferred.setLen oldLen
           if result:
             when logBindings: echo "A adding ", f, " ", ak
             m.inferred.add((f, ak))
         elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}:
           when logBindings: echo "B adding ", f, " ", lastSon ak
-          m.inferred.add((f, lastSon ak))
+          m.inferred.add((f, last ak))
           result = true
         else:
           when logBindings: echo "C adding ", f, " ", ak
@@ -154,15 +148,17 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
         result = matchType(c, old, ak, m)
         if m.magic == mArrPut and ak.kind == tyGenericParam:
           result = true
+      else:
+        result = false
     #echo "B for ", result, " to ", typeToString(a), " to ", typeToString(m.potentialImplementation)
 
   of tyVar, tySink, tyLent, tyOwned:
     # modifiers in the concept must be there in the actual implementation
     # too but not vice versa.
     if a.kind == f.kind:
-      result = matchType(c, f.sons[0], a.sons[0], m)
+      result = matchType(c, f.elementType, a.elementType, m)
     elif m.magic == mArrPut:
-      result = matchType(c, f.sons[0], a, m)
+      result = matchType(c, f.elementType, a, m)
     else:
       result = false
   of tyEnum, tyObject, tyDistinct:
@@ -172,7 +168,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
   of tyBool, tyChar, tyInt..tyUInt64:
     let ak = a.skipTypes(ignorableForArgType)
     result = ak.kind == f.kind or ak.kind == tyOrdinal or
-       (ak.kind == tyGenericParam and ak.len > 0 and ak[0].kind == tyOrdinal)
+       (ak.kind == tyGenericParam and ak.hasElementType and ak.elementType.kind == tyOrdinal)
   of tyConcept:
     let oldLen = m.inferred.len
     let oldPotentialImplementation = m.potentialImplementation
@@ -183,9 +179,11 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       m.inferred.setLen oldLen
   of tyArray, tyTuple, tyVarargs, tyOpenArray, tyRange, tySequence, tyRef, tyPtr,
      tyGenericInst:
+    # ^ XXX Rewrite this logic, it's more complex than it needs to be.
+    result = false
     let ak = a.skipTypes(ignorableForArgType - {f.kind})
-    if ak.kind == f.kind and f.len == ak.len:
-      for i in 0..<ak.len:
+    if ak.kind == f.kind and f.kidsLen == ak.kidsLen:
+      for i in 0..<ak.kidsLen:
         if not matchType(c, f[i], ak[i], m): return false
       return true
   of tyOr:
@@ -194,29 +192,30 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool =
       # say the concept requires 'int|float|string' if the potentialImplementation
       # says 'int|string' that is good enough.
       var covered = 0
-      for i in 0..<f.len:
-        for j in 0..<a.len:
+      for ff in f.kids:
+        for aa in a.kids:
           let oldLenB = m.inferred.len
-          let r = matchType(c, f[i], a[j], m)
+          let r = matchType(c, ff, aa, m)
           if r:
             inc covered
             break
           m.inferred.setLen oldLenB
 
-      result = covered >= a.len
+      result = covered >= a.kidsLen
       if not result:
         m.inferred.setLen oldLen
     else:
-      for i in 0..<f.len:
-        result = matchType(c, f[i], a, m)
+      result = false
+      for ff in f.kids:
+        result = matchType(c, ff, a, m)
         if result: break # and remember the binding!
         m.inferred.setLen oldLen
   of tyNot:
     if a.kind == tyNot:
-      result = matchType(c, f[0], a[0], m)
+      result = matchType(c, f.elementType, a.elementType, m)
     else:
       let oldLen = m.inferred.len
-      result = not matchType(c, f[0], a, m)
+      result = not matchType(c, f.elementType, a, m)
       m.inferred.setLen oldLen
   of tyAnything:
     result = true
@@ -255,7 +254,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
       m.inferred.setLen oldLen
       return false
 
-  if not matchReturnType(c, n[0].sym.typ.sons[0], candidate.typ.sons[0], m):
+  if not matchReturnType(c, n[0].sym.typ.returnType, candidate.typ.returnType, m):
     m.inferred.setLen oldLen
     return false
 
@@ -272,7 +271,7 @@ proc matchSym(c: PContext; candidate: PSym, n: PNode; m: var MatchCon): bool =
 proc matchSyms(c: PContext, n: PNode; kinds: set[TSymKind]; m: var MatchCon): bool =
   ## Walk the current scope, extract candidates which the same name as 'n[namePos]',
   ## 'n' is the nkProcDef or similar from the concept that we try to match.
-  let candidates = searchInScopesFilterBy(c, n[namePos].sym.name, kinds)
+  let candidates = searchInScopesAllCandidatesFilterBy(c, n[namePos].sym.name, kinds)
   for candidate in candidates:
     #echo "considering ", typeToString(candidate.typ), " ", candidate.magic
     m.magic = candidate.magic
@@ -304,17 +303,19 @@ proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool =
     result = matchSyms(c, n, {skMethod}, m)
   of nkIteratorDef:
     result = matchSyms(c, n, {skIterator}, m)
+  of nkCommentStmt:
+    result = true
   else:
     # error was reported earlier.
     result = false
 
-proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invocation: PType): bool =
+proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TypeMapping; invocation: PType): bool =
   ## Entry point from sigmatch. 'concpt' is the concept we try to match (here still a PType but
-  ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fullfill the
+  ## we extract its AST via 'concpt.n.lastSon'). 'arg' is the type that might fulfill the
   ## concept's requirements. If so, we return true and fill the 'bindings' with pairs of
   ## (typeVar, instance) pairs. ('typeVar' is usually simply written as a generic 'T'.)
   ## 'invocation' can be nil for atomic concepts. For non-atomic concepts, it contains the
-  ## 'C[S, T]' parent type that we look for. We need this because we need to store bindings
+  ## `C[S, T]` parent type that we look for. We need this because we need to store bindings
   ## for 'S' and 'T' inside 'bindings' on a successful match. It is very important that
   ## we do not add any bindings at all on an unsuccessful match!
   var m = MatchCon(inferred: @[], potentialImplementation: arg)
@@ -335,8 +336,8 @@ proc conceptMatch*(c: PContext; concpt, arg: PType; bindings: var TIdTable; invo
     # we have a match, so bind 'arg' itself to 'concpt':
     bindings.idTablePut(concpt, arg)
     # invocation != nil means we have a non-atomic concept:
-    if invocation != nil and arg.kind == tyGenericInst and invocation.len == arg.len-1:
+    if invocation != nil and arg.kind == tyGenericInst and invocation.kidsLen == arg.kidsLen-1:
       # bind even more generic parameters
       assert invocation.kind == tyGenericInvocation
-      for i in 1 ..< invocation.len:
+      for i in FirstGenericParamAt ..< invocation.kidsLen:
         bindings.idTablePut(invocation[i], arg[i])
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 332a802ab..5043fc5d4 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -10,7 +10,7 @@
 # This module handles the conditional symbols.
 
 import
-  strtabs
+  std/strtabs
 
 from options import Feature
 from lineinfos import hintMin, hintMax, warnMin, warnMax
@@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) =
 #  result = if isDefined(symbol): gSymbols[symbol] else: nil
 
 iterator definedSymbolNames*(symbols: StringTableRef): string =
-  for key, val in pairs(symbols):
+  for key in keys(symbols):
     yield key
 
 proc countDefinedSymbols*(symbols: StringTableRef): int =
@@ -45,10 +45,8 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimNewTypedesc") # deadcode
   defineSymbol("nimrequiresnimframe") # deadcode
   defineSymbol("nimparsebiggestfloatmagic") # deadcode
-  defineSymbol("nimalias") # deadcode
   defineSymbol("nimlocks") # deadcode
-  defineSymbol("nimnode") # deadcode pending `nimnode` reference in opengl package
-    # refs https://github.com/nim-lang/opengl/pull/79
+  defineSymbol("nimnode") # deadcode
   defineSymbol("nimvarargstyped") # deadcode
   defineSymbol("nimtypedescfixed") # deadcode
   defineSymbol("nimKnowsNimvm") # deadcode
@@ -84,10 +82,24 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasSignatureHashInMacro") # deadcode
   defineSymbol("nimHasDefault") # deadcode
   defineSymbol("nimMacrosSizealignof") # deadcode
+  defineSymbol("nimNoZeroExtendMagic") # deadcode
+  defineSymbol("nimMacrosGetNodeId") # deadcode
+  defineSymbol("nimFixedForwardGeneric") # deadcode
+  defineSymbol("nimToOpenArrayCString") # deadcode
+  defineSymbol("nimHasUsed") # deadcode
+  defineSymbol("nimnomagic64") # deadcode
+  defineSymbol("nimNewShiftOps") # deadcode
+  defineSymbol("nimHasCursor") # deadcode
+  defineSymbol("nimAlignPragma") # deadcode
+  defineSymbol("nimHasExceptionsQuery") # deadcode
+  defineSymbol("nimHasIsNamedTuple") # deadcode
+  defineSymbol("nimHashOrdinalFixed") # deadcode
+  defineSymbol("nimHasSinkInference") # deadcode
+  defineSymbol("nimNewIntegerOps") # deadcode
+  defineSymbol("nimHasInvariant") # deadcode
+
+
 
-  # > 0.20.0
-  defineSymbol("nimNoZeroExtendMagic")
-  defineSymbol("nimMacrosGetNodeId")
   for f in Feature:
     defineSymbol("nimHas" & $f)
 
@@ -98,31 +110,18 @@ proc initDefines*(symbols: StringTableRef) =
 
   defineSymbol("nimFixedOwned")
   defineSymbol("nimHasStyleChecks")
-  defineSymbol("nimToOpenArrayCString")
-  defineSymbol("nimHasUsed")
-  defineSymbol("nimFixedForwardGeneric")
-  defineSymbol("nimnomagic64")
-  defineSymbol("nimNewShiftOps")
-  defineSymbol("nimHasCursor")
-  defineSymbol("nimAlignPragma")
-  defineSymbol("nimHasExceptionsQuery")
-  defineSymbol("nimHasIsNamedTuple")
-  defineSymbol("nimHashOrdinalFixed")
 
   when defined(nimHasLibFFI):
     # Renaming as we can't conflate input vs output define flags; e.g. this
     # will report the right thing regardless of whether user adds
     # `-d:nimHasLibFFI` in his user config.
-    defineSymbol("nimHasLibFFIEnabled")
+    defineSymbol("nimHasLibFFIEnabled") # deadcode
 
-  defineSymbol("nimHasSinkInference")
-  defineSymbol("nimNewIntegerOps")
-  defineSymbol("nimHasInvariant")
-  defineSymbol("nimHasStacktraceMsgs")
+  defineSymbol("nimHasStacktraceMsgs") # deadcode
   defineSymbol("nimDoesntTrackDefects")
-  defineSymbol("nimHasLentIterators")
-  defineSymbol("nimHasDeclaredMagic")
-  defineSymbol("nimHasStacktracesModule")
+  defineSymbol("nimHasLentIterators") # deadcode
+  defineSymbol("nimHasDeclaredMagic") # deadcode
+  defineSymbol("nimHasStacktracesModule") # deadcode
   defineSymbol("nimHasEffectTraitsModule")
   defineSymbol("nimHasCastPragmaBlocks")
   defineSymbol("nimHasDeclaredLocs")
@@ -133,8 +132,8 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasCustomLiterals")
   defineSymbol("nimHasUnifiedTuple")
   defineSymbol("nimHasIterable")
-  defineSymbol("nimHasTypeofVoid")
-  defineSymbol("nimHasDragonBox")
+  defineSymbol("nimHasTypeofVoid") # deadcode
+  defineSymbol("nimHasDragonBox") # deadcode
   defineSymbol("nimHasHintAll")
   defineSymbol("nimHasTrace")
   defineSymbol("nimHasEffectsOf")
@@ -143,3 +142,30 @@ proc initDefines*(symbols: StringTableRef) =
   defineSymbol("nimHasTopDownInference")
   defineSymbol("nimHasTemplateRedefinitionPragma")
   defineSymbol("nimHasCstringCase")
+  defineSymbol("nimHasCallsitePragma")
+
+  defineSymbol("nimHasWarnCastSizes") # deadcode
+  defineSymbol("nimHasOutParams")
+  defineSymbol("nimHasSystemRaisesDefect")
+  defineSymbol("nimHasWarnUnnamedBreak")
+  defineSymbol("nimHasGenericDefine")
+  defineSymbol("nimHasDefineAliases")
+  defineSymbol("nimHasWarnBareExcept")
+  defineSymbol("nimHasDup")
+  defineSymbol("nimHasChecksums")
+  defineSymbol("nimHasSendable")
+  defineSymbol("nimAllowNonVarDestructor")
+  defineSymbol("nimHasQuirky")
+  defineSymbol("nimHasEnsureMove")
+  defineSymbol("nimHasNoReturnError")
+
+  defineSymbol("nimUseStrictDefs")
+  defineSymbol("nimHasNolineTooLong")
+
+  defineSymbol("nimHasCastExtendedVm")
+  defineSymbol("nimHasWarnStdPrefix")
+
+  defineSymbol("nimHasVtables")
+  defineSymbol("nimHasGenericsOpenSym2")
+  defineSymbol("nimHasGenericsOpenSym3")
+  defineSymbol("nimHasJsNoLambdaLifting")
diff --git a/compiler/depends.nim b/compiler/depends.nim
index 93f34dc7c..638f1eb51 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -9,12 +9,12 @@
 
 # This module implements a dependency file generator.
 
-import options, ast, ropes, passes, pathutils, msgs, lineinfos
+import options, ast, ropes, pathutils, msgs, lineinfos
 
 import modulegraphs
 
 import std/[os, parseutils]
-import strutils except addf
+import std/strutils except addf
 import std/private/globs
 
 when defined(nimPreviewSlimSystem):
@@ -42,7 +42,7 @@ proc toNimblePath(s: string, isStdlib: bool): string =
     let sub = "lib/"
     var start = s.find(sub)
     if start < 0:
-      doAssert false
+      raiseAssert "unreachable"
     else:
       start += sub.len
       let base = s[start..^1]
@@ -63,12 +63,16 @@ proc toNimblePath(s: string, isStdlib: bool): string =
       sub.add "/pkgs/"
     var start = s.find(sub)
     if start < 0:
-      result = s
-    else:
-      start += sub.len
-      start += skipUntil(s, '/', start)
-      start += 1
-      result = pkgPrefix & s[start..^1]
+      sub[^1] = '2'
+      sub.add '/'
+      start = s.find(sub) # /pkgs2
+      if start < 0:
+        return s
+
+    start += sub.len
+    start += skipUntil(s, '/', start)
+    start += 1
+    result = pkgPrefix & s[start..^1]
 
 proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
   doAssert n.kind == nkSym, $n.kind
@@ -79,7 +83,7 @@ proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) =
   let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym))
   addDependencyAux(b, parent, child)
 
-proc addDotDependency(c: PPassContext, n: PNode): PNode =
+proc addDotDependency*(c: PPassContext, n: PNode): PNode =
   result = n
   let g = PGen(c)
   let b = Backend(g.graph.backend)
@@ -100,18 +104,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) =
       rope(project.splitFile.name), b.dotGraph],
             changeFileExt(project, "dot"))
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
-
-proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
-  var g: PGen
-  new(g)
-  g.module = module
-  g.config = graph.config
-  g.graph = graph
+proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  result = PGen(module: module, config: graph.config, graph: graph)
   if graph.backend == nil:
     graph.backend = Backend(dotGraph: "")
-  result = g
-
-const gendependPass* = makePass(open = myOpen, process = addDotDependency)
-
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index 669207151..5534d07e7 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -9,40 +9,34 @@
 
 ## Data flow analysis for Nim.
 ## We transform the AST into a linear list of instructions first to
-## make this easier to handle: There are only 2 different branching
+## make this easier to handle: There are only 3 different branching
 ## instructions: 'goto X' is an unconditional goto, 'fork X'
 ## is a conditional goto (either the next instruction or 'X' can be
-## taken). Exhaustive case statements are translated
+## taken), 'loop X' is the only jump that jumps back.
+##
+## Exhaustive case statements are translated
 ## so that the last branch is transformed into an 'else' branch.
 ## ``return`` and ``break`` are all covered by 'goto'.
 ##
-## Control flow through exception handling:
-## Contrary to popular belief, exception handling doesn't cause
-## many problems for this DFA representation, ``raise`` is a statement
-## that ``goes to`` the outer ``finally`` or ``except`` if there is one,
-## otherwise it is the same as ``return``. Every call is treated as
-## a call that can potentially ``raise``. However, without a surrounding
-## ``try`` we don't emit these ``fork ReturnLabel`` instructions in order
-## to speed up the dataflow analysis passes.
-##
 ## The data structures and algorithms used here are inspired by
 ## "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, intsets, lineinfos, renderer
+import ast, lineinfos, renderer, aliasanalysis
 import std/private/asciitables
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 type
   InstrKind* = enum
-    goto, fork, def, use
+    goto, loop, fork, def, use
   Instr* = object
-    n*: PNode # contains the def/use location.
     case kind*: InstrKind
-    of goto, fork: dest*: int
-    else: discard
+    of goto, fork, loop: dest*: int
+    of def, use:
+      n*: PNode # contains the def/use location.
 
   ControlFlowGraph* = seq[Instr]
 
@@ -52,24 +46,26 @@ type
     case isTryBlock: bool
     of false:
       label: PSym
-      breakFixups: seq[(TPosition, seq[PNode])] #Contains the gotos for the breaks along with their pending finales
+      breakFixups: seq[(TPosition, seq[PNode])] # Contains the gotos for the breaks along with their pending finales
     of true:
       finale: PNode
-      raiseFixups: seq[TPosition] #Contains the gotos for the raises
+      raiseFixups: seq[TPosition] # Contains the gotos for the raises
 
   Con = object
     code: ControlFlowGraph
-    inTryStmt: int
+    inTryStmt, interestingInstructions: int
     blocks: seq[TBlock]
     owner: PSym
+    root: PSym
 
 proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
   # for debugging purposes
   # first iteration: compute all necessary labels:
+  result = ""
   var jumpTargets = initIntSet()
   let last = if last < 0: c.len-1 else: min(last, c.len-1)
   for i in start..last:
-    if c[i].kind in {goto, fork}:
+    if c[i].kind in {goto, fork, loop}:
       jumpTargets.incl(i+c[i].dest)
   var i = start
   while i <= last:
@@ -80,12 +76,12 @@ proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string =
     case c[i].kind
     of def, use:
       result.add renderTree(c[i].n)
-    of goto, fork:
+      result.add("\t#")
+      result.add($c[i].n.info.line)
+      result.add("\n")
+    of goto, fork, loop:
       result.add "L"
       result.addInt c[i].dest+i
-    result.add("\t#")
-    result.add($c[i].n.info.line)
-    result.add("\n")
     inc i
   if i in jumpTargets: result.add("L" & $i & ": End\n")
 
@@ -93,181 +89,13 @@ proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} =
   ## echos the ControlFlowGraph for debugging purposes.
   echo codeListing(c, start, last).alignTable
 
-proc forkI(c: var Con; n: PNode): TPosition =
+proc forkI(c: var Con): TPosition =
   result = TPosition(c.code.len)
-  c.code.add Instr(n: n, kind: fork, dest: 0)
+  c.code.add Instr(kind: fork, dest: 0)
 
-proc gotoI(c: var Con; n: PNode): TPosition =
+proc gotoI(c: var Con): TPosition =
   result = TPosition(c.code.len)
-  c.code.add Instr(n: n, kind: goto, dest: 0)
-
-#[
-
-Join is no more
-===============
-Instead of generating join instructions we adapt our traversal of the CFG.
-
-When encountering a fork we split into two paths, we follow the path
-starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest".
-If we encounter gotos that would jump further than the current joinpoint,
-as can happen with gotos generated by unstructured controlflow such as break, raise or return,
-we simply suspend following the current path, and follow the other path until the new joinpoint
-which is simply the instruction pointer returned to us by the now suspended path.
-If the path we are following now, also encounters a goto that exceeds the joinpoint
-we repeat the process; suspending the current path and evaluating the other one with a new joinpoint.
-If we eventually reach a common joinpoint we join the two paths.
-This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such
-cutting down on the CFG size but is also mandatory for correctly handling complicated cases
-of unstructured controlflow.
-
-
-Design of join
-==============
-
-block:
-  if cond: break
-  def(x)
-
-use(x)
-
-Generates:
-
-L0: fork lab1
-  join L0  # patched.
-  goto Louter
-lab1:
-  def x
-  join L0
-Louter:
-  use x
-
-
-block outer:
-  while a:
-    while b:
-      if foo:
-        if bar:
-          break outer # --> we need to 'join' every pushed 'fork' here
-
-
-This works and then our abstract interpretation needs to deal with 'fork'
-differently. It really causes a split in execution. Two threads are
-"spawned" and both need to reach the 'join L' instruction. Afterwards
-the abstract interpretations are joined and execution resumes single
-threaded.
-
-
-Abstract Interpretation
------------------------
-
-proc interpret(pc, state, comesFrom): state =
-  result = state
-  # we need an explicit 'create' instruction (an explicit heap), in order
-  # to deal with 'var x = create(); var y = x; var z = y; destroy(z)'
-  while true:
-    case pc
-    of fork:
-      let a = interpret(pc+1, result, pc)
-      let b = interpret(forkTarget, result, pc)
-      result = a ++ b # ++ is a union operation
-      inc pc
-    of join:
-      if joinTarget == comesFrom: return result
-      else: inc pc
-    of use X:
-      if not result.contains(x):
-        error "variable not initialized " & x
-      inc pc
-    of def X:
-      if not result.contains(x):
-        result.incl X
-      else:
-        error "overwrite of variable causes memory leak " & x
-      inc pc
-    of destroy X:
-      result.excl X
-
-This is correct but still can lead to false positives:
-
-proc p(cond: bool) =
-  if cond:
-    new(x)
-  otherThings()
-  if cond:
-    destroy x
-
-Is not a leak. We should find a way to model *data* flow, not just
-control flow. One solution is to rewrite the 'if' without a fork
-instruction. The unstructured aspect can now be easily dealt with
-the 'goto' and 'join' instructions.
-
-proc p(cond: bool) =
-  L0: fork Lend
-    new(x)
-    # do not 'join' here!
-
-  Lend:
-    otherThings()
-    join L0  # SKIP THIS FOR new(x) SOMEHOW
-  destroy x
-  join L0 # but here.
-
-
-
-But if we follow 'goto Louter' we will never come to the join point.
-We restore the bindings after popping pc from the stack then there
-"no" problem?!
-
-
-while cond:
-  prelude()
-  if not condB: break
-  postlude()
-
---->
-var setFlag = true
-while cond and not setFlag:
-  prelude()
-  if not condB:
-    setFlag = true   # BUT: Dependency
-  if not setFlag:    # HERE
-    postlude()
-
---->
-var setFlag = true
-while cond and not setFlag:
-  prelude()
-  if not condB:
-    postlude()
-    setFlag = true
-
-
--------------------------------------------------
-
-while cond:
-  prelude()
-  if more:
-    if not condB: break
-    stuffHere()
-  postlude()
-
--->
-var setFlag = true
-while cond and not setFlag:
-  prelude()
-  if more:
-    if not condB:
-      setFlag = false
-    else:
-      stuffHere()
-      postlude()
-  else:
-    postlude()
-
-This is getting complicated. Instead we keep the whole 'join' idea but
-duplicate the 'join' instructions on breaks and return exits!
-
-]#
+  c.code.add Instr(kind: goto, dest: 0)
 
 proc genLabel(c: Con): TPosition = TPosition(c.code.len)
 
@@ -275,8 +103,8 @@ template checkedDistance(dist): int =
   doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2
   dist
 
-proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) =
-  c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len))
+proc jmpBack(c: var Con, p = TPosition(0)) =
+  c.code.add Instr(kind: loop, dest: checkedDistance(p.int - c.code.len))
 
 proc patch(c: var Con, p: TPosition) =
   # patch with current index
@@ -285,13 +113,13 @@ proc patch(c: var Con, p: TPosition) =
 proc gen(c: var Con; n: PNode)
 
 proc popBlock(c: var Con; oldLen: int) =
-  var exits: seq[TPosition]
-  exits.add c.gotoI(newNode(nkEmpty))
+  var exits: seq[TPosition] = @[]
+  exits.add c.gotoI()
   for f in c.blocks[oldLen].breakFixups:
     c.patch(f[0])
     for finale in f[1]:
       c.gen(finale)
-    exits.add c.gotoI(newNode(nkEmpty))
+    exits.add c.gotoI()
   for e in exits:
     c.patch e
   c.blocks.setLen(oldLen)
@@ -302,94 +130,28 @@ template withBlock(labl: PSym; body: untyped) =
   body
   popBlock(c, oldLen)
 
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
-when true:
-  proc genWhile(c: var Con; n: PNode) =
-    # We unroll every loop 3 times. We emulate 0, 1, 2 iterations
-    # through the loop. We need to prove this is correct for our
-    # purposes. But Herb Sutter claims it is. (Proof by authority.)
-    #
-    # EDIT: Actually, we only need to unroll 2 times
-    # because Nim doesn't have a way of breaking/goto-ing into
-    # a loop iteration. Unrolling 2 times is much better for compile
-    # times of nested loops than 3 times, so we do that here.
-    #[
-    while cond:
-      body
-
-    Becomes:
-
-    block:
-      if cond:
-        body
-        if cond:
-          body
-          if cond:
-            body
-
-    We still need to ensure 'break' resolves properly, so an AST to AST
-    translation is impossible.
-
-    So the code to generate is:
-
-      cond
-      fork L4  # F1
-      body
-      cond
-      fork L5  # F2
-      body
-      cond
-      fork L6  # F3
-      body
-    L6:
-      join F3
-    L5:
-      join F2
-    L4:
-      join F1
-    ]#
+template forkT(body) =
+  let lab1 = c.forkI()
+  body
+  c.patch(lab1)
+
+proc genWhile(c: var Con; n: PNode) =
+  # lab1:
+  #   cond, tmp
+  #   fork tmp, lab2
+  #   body
+  #   jmp lab1
+  # lab2:
+  let lab1 = c.genLabel
+  withBlock(nil):
     if isTrue(n[0]):
-      # 'while true' is an idiom in Nim and so we produce
-      # better code for it:
-      withBlock(nil):
-        for i in 0..1:
-          c.gen(n[1])
+      c.gen(n[1])
+      c.jmpBack(lab1)
     else:
-      withBlock(nil):
-        var endings: array[2, TPosition]
-        for i in 0..1:
-          c.gen(n[0])
-          endings[i] = c.forkI(n)
-          c.gen(n[1])
-        for i in countdown(endings.high, 0):
-          c.patch(endings[i])
-
-else:
-  proc genWhile(c: var Con; n: PNode) =
-    # lab1:
-    #   cond, tmp
-    #   fork tmp, lab2
-    #   body
-    #   jmp lab1
-    # lab2:
-    let lab1 = c.genLabel
-    withBlock(nil):
-      if isTrue(n[0]):
+      c.gen(n[0])
+      forkT:
         c.gen(n[1])
-        c.jmpBack(n, lab1)
-      else:
-        c.gen(n[0])
-        forkT(n):
-          c.gen(n[1])
-          c.jmpBack(n, lab1)
-
-template forkT(n, body) =
-  let lab1 = c.forkI(n)
-  body
-  c.patch(lab1)
+        c.jmpBack(lab1)
 
 proc genIf(c: var Con, n: PNode) =
   #[
@@ -419,34 +181,32 @@ proc genIf(c: var Con, n: PNode) =
     goto Lend3
   L3:
     D
-    goto Lend3 # not eliminated to simplify the join generation
-  Lend3:
-    join F3
-  Lend2:
-    join F2
-  Lend:
-    join F1
-
   ]#
   var endings: seq[TPosition] = @[]
+  let oldInteresting = c.interestingInstructions
+  let oldLen = c.code.len
+
   for i in 0..<n.len:
     let it = n[i]
     c.gen(it[0])
     if it.len == 2:
-      forkT(it[1]):
-        c.gen(it[1])
-        endings.add c.gotoI(it[1])
-  for i in countdown(endings.high, 0):
-    c.patch(endings[i])
+      forkT:
+        c.gen(it.lastSon)
+        endings.add c.gotoI()
+
+  if oldInteresting == c.interestingInstructions:
+    setLen c.code, oldLen
+  else:
+    for i in countdown(endings.high, 0):
+      c.patch(endings[i])
 
 proc genAndOr(c: var Con; n: PNode) =
   #   asgn dest, a
   #   fork lab1
   #   asgn dest, b
   # lab1:
-  #   join F1
   c.gen(n[1])
-  forkT(n):
+  forkT:
     c.gen(n[2])
 
 proc genCase(c: var Con; n: PNode) =
@@ -463,37 +223,38 @@ proc genCase(c: var Con; n: PNode) =
   let isExhaustive = skipTypes(n[0].typ,
     abstractVarRange-{tyTypeDesc}).kind notin {tyFloat..tyFloat128, tyString, tyCstring}
 
-  # we generate endings as a set of chained gotos, this is a bit awkward but it
-  # ensures when recursively traversing the CFG for various analysis, we don't
-  # artificially extended the life of each branch (for the purposes of DFA)
-  # beyond the minimum amount.
   var endings: seq[TPosition] = @[]
   c.gen(n[0])
+  let oldInteresting = c.interestingInstructions
+  let oldLen = c.code.len
   for i in 1..<n.len:
     let it = n[i]
     if it.len == 1 or (i == n.len-1 and isExhaustive):
       # treat the last branch as 'else' if this is an exhaustive case statement.
       c.gen(it.lastSon)
-      if endings.len != 0:
-        c.patch(endings[^1])
     else:
-      forkT(it.lastSon):
+      forkT:
         c.gen(it.lastSon)
-        if endings.len != 0:
-          c.patch(endings[^1])
-        endings.add c.gotoI(it.lastSon)
+        endings.add c.gotoI()
+
+  if oldInteresting == c.interestingInstructions:
+    setLen c.code, oldLen
+  else:
+    for i in countdown(endings.high, 0):
+      c.patch(endings[i])
 
 proc genBlock(c: var Con; n: PNode) =
   withBlock(n[0].sym):
     c.gen(n[1])
 
 proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
-  let lab1 = c.gotoI(n)
+  let lab1 = c.gotoI()
   if c.blocks[i].isTryBlock:
     c.blocks[i].raiseFixups.add lab1
   else:
-    var trailingFinales: seq[PNode]
-    if c.inTryStmt > 0: #Ok, we are in a try, lets see which (if any) try's we break out from:
+    var trailingFinales: seq[PNode] = @[]
+    if c.inTryStmt > 0:
+      # Ok, we are in a try, lets see which (if any) try's we break out from:
       for b in countdown(c.blocks.high, i):
         if c.blocks[b].isTryBlock:
           trailingFinales.add c.blocks[b].finale
@@ -501,6 +262,7 @@ proc genBreakOrRaiseAux(c: var Con, i: int, n: PNode) =
     c.blocks[i].breakFixups.add (lab1, trailingFinales)
 
 proc genBreak(c: var Con; n: PNode) =
+  inc c.interestingInstructions
   if n[0].kind == nkSym:
     for i in countdown(c.blocks.high, 0):
       if not c.blocks[i].isTryBlock and c.blocks[i].label == n[0].sym:
@@ -531,9 +293,9 @@ proc genTry(c: var Con; n: PNode) =
   for i in 1..<n.len:
     let it = n[i]
     if it.kind != nkFinally:
-      forkT(it):
+      forkT:
         c.gen(it.lastSon)
-        endings.add c.gotoI(it)
+        endings.add c.gotoI()
   for i in countdown(endings.high, 0):
     c.patch(endings[i])
 
@@ -541,26 +303,28 @@ proc genTry(c: var Con; n: PNode) =
   if fin.kind == nkFinally:
     c.gen(fin[0])
 
-template genNoReturn(c: var Con; n: PNode) =
+template genNoReturn(c: var Con) =
   # leave the graph
-  c.code.add Instr(n: n, kind: goto, dest: high(int) - c.code.len)
+  c.code.add Instr(kind: goto, dest: high(int) - c.code.len)
 
 proc genRaise(c: var Con; n: PNode) =
+  inc c.interestingInstructions
   gen(c, n[0])
   if c.inTryStmt > 0:
     for i in countdown(c.blocks.high, 0):
       if c.blocks[i].isTryBlock:
         genBreakOrRaiseAux(c, i, n)
         return
-    assert false #Unreachable
+    assert false # Unreachable
   else:
-    genNoReturn(c, n)
+    genNoReturn(c)
 
 proc genImplicitReturn(c: var Con) =
   if c.owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter} and resultPos < c.owner.ast.len:
     gen(c, c.owner.ast[resultPos])
 
 proc genReturn(c: var Con; n: PNode) =
+  inc c.interestingInstructions
   if n[0].kind != nkEmpty:
     gen(c, n[0])
   else:
@@ -569,122 +333,6 @@ proc genReturn(c: var Con; n: PNode) =
 
 const
   InterestingSyms = {skVar, skResult, skLet, skParam, skForVar, skTemp}
-  PathKinds0 = {nkDotExpr, nkCheckedFieldExpr,
-                nkBracketExpr, nkDerefExpr, nkHiddenDeref,
-                nkAddr, nkHiddenAddr,
-                nkObjDownConv, nkObjUpConv}
-  PathKinds1 = {nkHiddenStdConv, nkHiddenSubConv}
-
-proc skipConvDfa*(n: PNode): PNode =
-  result = n
-  while true:
-    case result.kind
-    of nkObjDownConv, nkObjUpConv:
-      result = result[0]
-    of PathKinds1:
-      result = result[1]
-    else: break
-
-type AliasKind* = enum
-  yes, no, maybe
-
-proc aliases*(obj, field: PNode): AliasKind =
-  # obj -> field:
-  # x -> x: true
-  # x -> x.f: true
-  # x.f -> x: false
-  # x.f -> x.f: true
-  # x.f -> x.v: false
-  # x -> x[0]: true
-  # x[0] -> x: false
-  # x[0] -> x[0]: true
-  # x[0] -> x[1]: false
-  # x -> x[i]: true
-  # x[i] -> x: false
-  # x[i] -> x[i]: maybe; Further analysis could make this return true when i is a runtime-constant
-  # x[i] -> x[j]: maybe; also returns maybe if only one of i or j is a compiletime-constant
-  template collectImportantNodes(result, n) =
-    var result: seq[PNode]
-    var n = n
-    while true:
-      case n.kind
-      of PathKinds0 - {nkDotExpr, nkBracketExpr}:
-        n = n[0]
-      of PathKinds1:
-        n = n[1]
-      of nkDotExpr, nkBracketExpr:
-        result.add n
-        n = n[0]
-      of nkSym:
-        result.add n; break
-      else: return no
-
-  collectImportantNodes(objImportantNodes, obj)
-  collectImportantNodes(fieldImportantNodes, field)
-
-  # If field is less nested than obj, then it cannot be part of/aliased by obj
-  if fieldImportantNodes.len < objImportantNodes.len: return no
-
-  result = yes
-  for i in 1..objImportantNodes.len:
-    # We compare the nodes leading to the location of obj and field
-    # with each other.
-    # We continue until they diverge, in which case we return no, or
-    # until we reach the location of obj, in which case we do not need
-    # to look further, since field must be part of/aliased by obj now.
-    # If we encounter an element access using an index which is a runtime value,
-    # we simply return maybe instead of yes; should further nodes not diverge.
-    let currFieldPath = fieldImportantNodes[^i]
-    let currObjPath = objImportantNodes[^i]
-
-    if currFieldPath.kind != currObjPath.kind:
-      return no
-
-    case currFieldPath.kind
-    of nkSym:
-      if currFieldPath.sym != currObjPath.sym: return no
-    of nkDotExpr:
-      if currFieldPath[1].sym != currObjPath[1].sym: return no
-    of nkBracketExpr:
-      if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals:
-        if currFieldPath[1].intVal != currObjPath[1].intVal:
-          return no
-      else:
-        result = maybe
-    else: assert false # unreachable
-
-proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool =
-  var n = orig
-  while true:
-    case n.kind
-    of PathKinds0 - {nkHiddenDeref, nkDerefExpr}:
-      n = n[0]
-    of PathKinds1:
-      n = n[1]
-    of nkHiddenDeref, nkDerefExpr:
-      # We "own" sinkparam[].loc but not ourVar[].location as it is a nasty
-      # pointer indirection.
-      # bug #14159, we cannot reason about sinkParam[].location as it can
-      # still be shared for tyRef.
-      n = n[0]
-      return n.kind == nkSym and n.sym.owner == owner and
-         (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned})
-    else: break
-  # XXX Allow closure deref operations here if we know
-  # the owner controlled the closure allocation?
-  result = n.kind == nkSym and n.sym.owner == owner and
-    {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and
-    (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar)
-  # Note: There is a different move analyzer possible that checks for
-  # consume(param.key); param.key = newValue  for all paths. Then code like
-  #
-  #   let splited = split(move self.root, x)
-  #   self.root = merge(splited.lower, splited.greater)
-  #
-  # could be written without the ``move self.root``. However, this would be
-  # wrong! Then the write barrier for the ``self.root`` assignment would
-  # free the old data and all is lost! Lesson: Don't be too smart, trust the
-  # lower level C++ optimizer to specialize this code.
 
 proc skipTrivials(c: var Con, n: PNode): PNode =
   result = n
@@ -703,8 +351,9 @@ proc genUse(c: var Con; orig: PNode) =
   let n = c.skipTrivials(orig)
 
   if n.kind == nkSym:
-    if n.sym.kind in InterestingSyms:
-      c.code.add Instr(n: orig, kind: use)
+    if n.sym.kind in InterestingSyms and n.sym == c.root:
+      c.code.add Instr(kind: use, n: orig)
+      inc c.interestingInstructions
   else:
     gen(c, n)
 
@@ -712,7 +361,9 @@ proc genDef(c: var Con; orig: PNode) =
   let n = c.skipTrivials(orig)
 
   if n.kind == nkSym and n.sym.kind in InterestingSyms:
-    c.code.add Instr(n: orig, kind: def)
+    if n.sym == c.root:
+      c.code.add Instr(kind: def, n: orig)
+      inc c.interestingInstructions
 
 proc genCall(c: var Con; n: PNode) =
   gen(c, n[0])
@@ -720,18 +371,17 @@ proc genCall(c: var Con; n: PNode) =
   if t != nil: t = t.skipTypes(abstractInst)
   for i in 1..<n.len:
     gen(c, n[i])
-    when false:
-      if t != nil and i < t.len and t[i].kind == tyOut:
-        # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
-        genDef(c, n[i])
+    if t != nil and i < t.signatureLen and isOutParam(t[i]):
+      # Pass by 'out' is a 'must def'. Good enough for a move optimizer.
+      genDef(c, n[i])
   # every call can potentially raise:
   if c.inTryStmt > 0 and canRaiseConservative(n[0]):
+    inc c.interestingInstructions
     # we generate the instruction sequence:
     # fork lab1
     # goto exceptionHandler (except or finally)
     # lab1:
-    # join F1
-    forkT(n):
+    forkT:
       for i in countdown(c.blocks.high, 0):
         if c.blocks[i].isTryBlock:
           genBreakOrRaiseAux(c, i, n)
@@ -769,11 +419,11 @@ proc gen(c: var Con; n: PNode) =
       else:
         genCall(c, n)
       if sfNoReturn in n[0].sym.flags:
-        genNoReturn(c, n)
+        genNoReturn(c)
     else:
       genCall(c, n)
   of nkCharLit..nkNilLit: discard
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     gen(c, n[1])
 
     if n[0].kind in PathKinds0:
@@ -807,16 +457,35 @@ proc gen(c: var Con; n: PNode) =
   of nkConv, nkExprColonExpr, nkExprEqExpr, nkCast, PathKinds1:
     gen(c, n[1])
   of nkVarSection, nkLetSection: genVarSection(c, n)
-  of nkDefer: doAssert false, "dfa construction pass requires the elimination of 'defer'"
+  of nkDefer: raiseAssert "dfa construction pass requires the elimination of 'defer'"
   else: discard
 
-proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph =
+when false:
+  proc optimizeJumps(c: var ControlFlowGraph) =
+    for i in 0..<c.len:
+      case c[i].kind
+      of goto, fork:
+        var pc = i + c[i].dest
+        if pc < c.len and c[pc].kind == goto:
+          while pc < c.len and c[pc].kind == goto:
+            let newPc = pc + c[pc].dest
+            if newPc > pc:
+              pc = newPc
+            else:
+              break
+          c[i].dest = pc - i
+      of loop, def, use: discard
+
+proc constructCfg*(s: PSym; body: PNode; root: PSym): ControlFlowGraph =
   ## constructs a control flow graph for ``body``.
-  var c = Con(code: @[], blocks: @[], owner: s)
+  var c = Con(code: @[], blocks: @[], owner: s, root: root)
   withBlock(s):
     gen(c, body)
-    genImplicitReturn(c)
-  when defined(gcArc) or defined(gcOrc):
+    if root.kind == skResult:
+      genImplicitReturn(c)
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     result = c.code # will move
   else:
     shallowCopy(result, c.code)
+  when false:
+    optimizeJumps result
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index bd973b211..8e5f5e4e7 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -7,20 +7,24 @@
 #    distribution, for details about the copyright.
 #
 
-# This is the documentation generator. Cross-references are generated
-# by knowing how the anchors are going to be named.
+## This is the Nim documentation generator. Cross-references are generated
+## by knowing how the anchors are going to be named.
+##
+## .. importdoc:: ../docgen.md
+##
+## For corresponding users' documentation see [Nim DocGen Tools Guide].
 
 import
-  ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents,
+  ast, options, msgs, idents,
   wordrecg, syntaxes, renderer, lexer,
-  packages/docutils/[rst, rstgen, dochelpers],
-  json, xmltree, trees, types,
-  typesrenderer, astalgo, lineinfos, intsets,
-  pathutils, tables, nimpaths, renderverbatim, osproc, packages
+  packages/docutils/[rst, rstidx, rstgen, dochelpers],
+  trees, types,
+  typesrenderer, astalgo, lineinfos,
+  pathutils, nimpaths, renderverbatim, packages
 import packages/docutils/rstast except FileIndex, TLineInfo
 
-from uri import encodeUrl
-from std/private/globs import nativeToUnixPath
+import std/[os, strutils, strtabs, algorithm, json, osproc, tables, intsets, xmltree, sequtils]
+from std/uri import encodeUrl
 from nodejs import findNodeJs
 
 when defined(nimPreviewSlimSystem):
@@ -92,7 +96,7 @@ type
     jEntriesFinal: JsonNode    # final JSON after RST pass 2 and rendering
     types: TStrTable
     sharedState: PRstSharedState
-    standaloneDoc: bool
+    standaloneDoc: bool        # is markup (.rst/.md) document?
     conf*: ConfigRef
     cache*: IdentCache
     exampleCounter: int
@@ -109,13 +113,16 @@ type
 proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst)
 proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str)
 
-proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
+proc addRstFileIndex(d: PDoc, fileIndex: lineinfos.FileIndex): rstast.FileIndex =
   let invalid = rstast.FileIndex(-1)
-  result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid)
+  result = d.nimToRstFid.getOrDefault(fileIndex, default = invalid)
   if result == invalid:
-    let fname = toFullPath(d.conf, info)
+    let fname = toFullPath(d.conf, fileIndex)
     result = addFilename(d.sharedState, fname)
-    d.nimToRstFid[info.fileIndex] = result
+    d.nimToRstFid[fileIndex] = result
+
+proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex =
+  addRstFileIndex(d, info.fileIndex)
 
 proc cmpDecimalsIgnoreCase(a, b: string): int =
   ## For sorting with correct handling of cases like 'uint8' and 'uint16'.
@@ -164,6 +171,7 @@ proc cmpDecimalsIgnoreCase(a, b: string): int =
 
 proc prettyString(a: object): string =
   # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906
+  result = ""
   for k, v in fieldPairs(a):
     result.add k & ": " & $v & "\n"
 
@@ -209,12 +217,16 @@ proc whichType(d: PDoc; n: PNode): PSym =
   if n.kind == nkSym:
     if d.types.strTableContains(n.sym):
       result = n.sym
+    else:
+      result = nil
   else:
+    result = nil
     for i in 0..<n.safeLen:
       let x = whichType(d, n[i])
       if x != nil: return x
 
 proc attachToType(d: PDoc; p: PSym): PSym =
+  result = nil
   let params = p.ast[paramsPos]
   template check(i) =
     result = whichType(d, params[i])
@@ -226,7 +238,7 @@ proc attachToType(d: PDoc; p: PSym): PSym =
   if params.len > 0: check(0)
   for i in 2..<params.len: check(i)
 
-template declareClosures =
+template declareClosures(currentFilename: AbsoluteFile, destFile: string) =
   proc compilerMsgHandler(filename: string, line, col: int,
                           msgKind: rst.MsgKind, arg: string) {.gcsafe.} =
     # translate msg kind:
@@ -250,9 +262,19 @@ template declareClosures =
     of mwBrokenLink: k = warnRstBrokenLink
     of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported
     of mwUnsupportedField: k = warnRstFieldXNotSupported
+    of mwUnusedImportdoc: k = warnRstUnusedImportdoc
     of mwRstStyle: k = warnRstStyle
     {.gcsafe.}:
-      globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
+      let errorsAsWarnings = (roPreferMarkdown in d.sharedState.options) and
+          not d.standaloneDoc  # not tolerate errors in .rst/.md files
+      if whichMsgClass(msgKind) == mcError and errorsAsWarnings:
+        liMessage(conf, newLineInfo(conf, AbsoluteFile filename, line, col),
+                  k, arg, doNothing, instLoc(), ignoreError=true)
+        # when our Markdown parser fails, we currently can only terminate the
+        # parsing (and then we will return monospaced text instead of markup):
+        raiseRecoverableError("")
+      else:
+        globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg)
 
   proc docgenFindFile(s: string): string {.gcsafe.} =
     result = options.findFile(conf, s).string
@@ -260,10 +282,29 @@ template declareClosures =
       result = getCurrentDir() / s
       if not fileExists(result): result = ""
 
+  proc docgenFindRefFile(targetRelPath: string):
+         tuple[targetPath: string, linkRelPath: string] {.gcsafe.} =
+    let fromDir = splitFile(destFile).dir  # dir where we reference from
+    let basedir = os.splitFile(currentFilename.string).dir
+    let outDirPath: RelativeFile =
+        presentationPath(conf, AbsoluteFile(basedir / targetRelPath))
+          # use presentationPath because `..` path can be be mangled to `_._`
+    result = (string(conf.outDir / outDirPath), "")
+    if not fileExists(result.targetPath):
+      # this can happen if targetRelPath goes to parent directory `OUTDIR/..`.
+      # Trying it, this may cause ambiguities, but allows us to insert
+      # "packages" into each other, which is actually used in Nim repo itself.
+      let destPath = fromDir / targetRelPath
+      if destPath != result.targetPath and fileExists(destPath):
+        result.targetPath = destPath
+
+    result.linkRelPath = relativePath(result.targetPath.splitFile.dir,
+                                      fromDir).replace('\\', '/')
+
+
 proc parseRst(text: string,
               line, column: int,
               conf: ConfigRef, sharedState: PRstSharedState): PRstNode =
-  declareClosures()
   result = rstParsePass1(text, line, column, sharedState)
 
 proc getOutFile2(conf: ConfigRef; filename: RelativeFile,
@@ -284,8 +325,10 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
                     outExt: string = HtmlExt, module: PSym = nil,
                     standaloneDoc = false, preferMarkdown = true,
                     hasToc = true): PDoc =
-  declareClosures()
+  let destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
   new(result)
+  let d = result  # pass `d` to `declareClosures`:
+  declareClosures(currentFilename = filename, destFile = destFile)
   result.module = module
   result.conf = conf
   result.cache = cache
@@ -299,11 +342,15 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
   result.hasToc = hasToc
   result.sharedState = newRstSharedState(
       options, filename.string,
-      docgenFindFile, compilerMsgHandler, hasToc)
+      docgenFindFile, docgenFindRefFile, compilerMsgHandler, hasToc)
   initRstGenerator(result[], (if conf.isLatexCmd: outLatex else: outHtml),
                    conf.configVars, filename.string,
                    docgenFindFile, compilerMsgHandler)
 
+  if conf.configVars.hasKey("doc.googleAnalytics") and
+      conf.configVars.hasKey("doc.plausibleAnalytics"):
+    raiseAssert "Either use googleAnalytics or plausibleAnalytics"
+
   if conf.configVars.hasKey("doc.googleAnalytics"):
     result.analytics = """
 <script>
@@ -317,15 +364,19 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
 
 </script>
     """ % [conf.configVars.getOrDefault"doc.googleAnalytics"]
+  elif conf.configVars.hasKey("doc.plausibleAnalytics"):
+    result.analytics = """
+    <script defer data-domain="$1" src="https://plausible.io/js/plausible.js"></script>
+    """ % [conf.configVars.getOrDefault"doc.plausibleAnalytics"]
   else:
     result.analytics = ""
 
   result.seenSymbols = newStringTable(modeCaseInsensitive)
   result.id = 100
   result.jEntriesFinal = newJArray()
-  initStrTable result.types
+  result.types = initStrTable()
   result.onTestSnippet =
-    proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) =
+    proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) {.gcsafe.} =
       if conf.docCmd == docCmdSkip: return
       inc(gen.id)
       var d = (ptr TDocumentor)(addr gen)
@@ -366,7 +417,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef,
       if gotten != status:
         rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output])
   result.emitted = initIntSet()
-  result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string
+  result.destFile = destFile
   result.thisDir = result.destFile.AbsoluteFile.splitFile.dir
 
 template dispA(conf: ConfigRef; dest: var string, xml, tex: string,
@@ -383,17 +434,22 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
 proc genComment(d: PDoc, n: PNode): PRstNode =
   if n.comment.len > 0:
     d.sharedState.currFileIdx = addRstFileIndex(d, n.info)
-    result = parseRst(n.comment,
-                      toLinenumber(n.info),
-                      toColumn(n.info) + DocColOffset,
-                      d.conf, d.sharedState)
+    try:
+      result = parseRst(n.comment,
+                        toLinenumber(n.info),
+                        toColumn(n.info) + DocColOffset,
+                        d.conf, d.sharedState)
+    except ERecoverableError:
+      result = newRstNode(rnLiteralBlock, @[newRstLeaf(n.comment)])
+  else:
+    result = nil
 
 proc genRecCommentAux(d: PDoc, n: PNode): PRstNode =
   if n == nil: return nil
   result = genComment(d, n)
   if result == nil:
-    if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef,
-                  nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkHiddenStdConv}:
+    if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkTypeClassTy,
+                  nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkSinkAsgn, nkHiddenStdConv}:
       # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}:
       for i in 0..<n.len:
         result = genRecCommentAux(d, n[i])
@@ -421,6 +477,7 @@ proc getPlainDocstring(n: PNode): string =
   elif startsWith(n.comment, "##"):
     result = n.comment
   else:
+    result = ""
     for i in 0..<n.safeLen:
       result = getPlainDocstring(n[i])
       if result.len > 0: return
@@ -436,9 +493,8 @@ proc externalDep(d: PDoc; module: PSym): string =
 proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string;
                            renderFlags: TRenderFlags = {};
                            procLink: string) =
-  var r: TSrcGen
+  var r: TSrcGen = initTokRender(n, renderFlags)
   var literal = ""
-  initTokRender(r, n, renderFlags)
   var kind = tkEof
   var tokenPos = 0
   var procTokenPos = 0
@@ -530,10 +586,13 @@ proc runAllExamples(d: PDoc) =
     # most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
     # via command line
     # D20210224T221756:here
-    let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
+    var pathArgs = "--path:$path" % [ "path", quoteShell(d.conf.projectPath) ]
+    for p in d.conf.searchPaths:
+      pathArgs = "$args --path:$path" % [ "args", pathArgs, "path", quoteShell(p) ]
+    let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off $pathArgs --nimcache:$nimcache $rdoccmd $docCmd $file" % [
       "nim", quoteShell(os.getAppFilename()),
       "backend", $d.conf.backend,
-      "path", quoteShell(d.conf.projectPath),
+      "pathArgs", pathArgs,
       "libpath", quoteShell(d.conf.libpath),
       "nimcache", quoteShell(outputDir),
       "file", quoteShell(outp),
@@ -549,9 +608,11 @@ proc runAllExamples(d: PDoc) =
       rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
       # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
 
-proc quoted(a: string): string = result.addQuoted(a)
+proc quoted(a: string): string =
+  result = ""
+  result.addQuoted(a)
 
-proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): auto =
+proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): (string, int, int) =
   # xxx expose in compiler/lineinfos.nim
   (conf.toMsgFilename(info), info.line.int, info.col.int + ColOffset)
 
@@ -568,7 +629,6 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
   let useRenderModule = false
   let loc = d.conf.toFileLineCol(n.info)
   let code = extractRunnableExamplesSource(d.conf, n)
-  let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
 
   if d.conf.errorCounter > 0:
     return (rdoccmd, code)
@@ -584,6 +644,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
     docComment.comment = comment
     var runnableExamples = newTree(nkStmtList,
         docComment,
+        newTree(nkImportStmt, newStrNode(nkStrLit, "std/assertions")),
         newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
     runnableExamples.info = n.info
     for a in n.lastSon: runnableExamples.add a
@@ -597,6 +658,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c
   else:
     var code2 = code
     if code.len > 0 and "codeReordering" notin code:
+      let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2)
       # hacky but simplest solution, until we devise a way to make `{.line.}`
       # work without introducing a scope
       code2 = """
@@ -607,6 +669,7 @@ $#
 #[
 $#
 ]#
+import std/assertions
 import $#
 $#
 """ % [comment, d.filename.quoted, code2]
@@ -673,7 +736,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var ItemPre,
         let (rdoccmd, code) = prepareExample(d, n, topLevel)
         var msg = "Example:"
         if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
-        var s: string
+        var s: string = ""
         dispA(d.conf, s, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
             "\n\n\\textbf{$1}\n", [msg])
         dest.add s
@@ -758,21 +821,24 @@ proc isVisible(d: PDoc; n: PNode): bool =
   elif n.kind == nkPragmaExpr:
     result = isVisible(d, n[0])
 
-proc getName(d: PDoc, n: PNode, splitAfter = -1): string =
+proc getName(n: PNode): string =
   case n.kind
-  of nkPostfix: result = getName(d, n[1], splitAfter)
-  of nkPragmaExpr: result = getName(d, n[0], splitAfter)
-  of nkSym: result = esc(d.target, n.sym.renderDefinitionName, splitAfter)
-  of nkIdent: result = esc(d.target, n.ident.s, splitAfter)
+  of nkPostfix: result = getName(n[1])
+  of nkPragmaExpr: result = getName(n[0])
+  of nkSym: result = n.sym.renderDefinitionName
+  of nkIdent: result = n.ident.s
   of nkAccQuoted:
-    result = esc(d.target, "`")
-    for i in 0..<n.len: result.add(getName(d, n[i], splitAfter))
-    result.add esc(d.target, "`")
-  of nkOpenSymChoice, nkClosedSymChoice:
-    result = getName(d, n[0], splitAfter)
+    result = "`"
+    for i in 0..<n.len: result.add(getName(n[i]))
+    result = "`"
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
+    result = getName(n[0])
   else:
     result = ""
 
+proc getNameEsc(d: PDoc, n: PNode): string =
+  esc(d.target, getName(n))
+
 proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
   case n.kind
   of nkPostfix: result = getNameIdent(cache, n[1])
@@ -783,7 +849,7 @@ proc getNameIdent(cache: IdentCache; n: PNode): PIdent =
     var r = ""
     for i in 0..<n.len: r.add(getNameIdent(cache, n[i]).s)
     result = getIdent(cache, r)
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getNameIdent(cache, n[0])
   else:
     result = nil
@@ -797,7 +863,7 @@ proc getRstName(n: PNode): PRstNode =
   of nkAccQuoted:
     result = getRstName(n[0])
     for i in 1..<n.len: result.text.add(getRstName(n[i]).text)
-  of nkOpenSymChoice, nkClosedSymChoice:
+  of nkOpenSymChoice, nkClosedSymChoice, nkOpenSym:
     result = getRstName(n[0])
   else:
     result = nil
@@ -886,14 +952,17 @@ proc genDeprecationMsg(d: PDoc, n: PNode): string =
     if n[1].kind in {nkStrLit..nkTripleStrLit}:
       result = getConfigVar(d.conf, "doc.deprecationmsg") % [
           "label", "Deprecated:", "message", xmltree.escape(n[1].strVal)]
+    else:
+      result = ""
   else:
-    doAssert false
+    raiseAssert "unreachable"
 
 type DocFlags = enum
   kDefault
   kForceExport
 
 proc genSeeSrc(d: PDoc, path: string, line: int): string =
+  result = ""
   let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc")
   if docItemSeeSrc.len > 0:
     let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/')
@@ -921,14 +990,22 @@ proc symbolPriority(k: TSymKind): int =
     else: 0  # including skProc which have higher priority
     # documentation itself has even higher priority 1
 
+proc getTypeKind(n: PNode): string =
+  case n[2].kind
+  of nkEnumTy: "enum"
+  of nkObjectTy: "object"
+  of nkTupleTy: "tuple"
+  else: ""
+
 proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
   ## Converts symbol info (names/types/parameters) in `n` into format
   ## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
-  result.name = baseName.nimIdentNormalize
-  result.symKind = k.toHumanStr
+  result = LangSymbol(name: baseName.nimIdentNormalize,
+      symKind: k.toHumanStr
+  )
   if k in routineKinds:
     var
-      paramTypes: seq[string]
+      paramTypes: seq[string] = @[]
     renderParamTypes(paramTypes, n[paramsPos], toNormalize=true)
     let paramNames = renderParamNames(n[paramsPos], toNormalize=true)
     # In some rare cases (system.typeof) parameter type is not set for default:
@@ -953,9 +1030,8 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
         genNode = n[miscPos][1]   # FIXME: what is index 1?
     if genNode != nil:
       var literal = ""
-      var r: TSrcGen
-      initTokRender(r, genNode, {renderNoBody, renderNoComments,
-        renderNoPragmas, renderNoProcDefs})
+      var r: TSrcGen = initTokRender(genNode, {renderNoBody, renderNoComments,
+        renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
       var kind = tkEof
       while true:
         getNextTok(r, kind, literal)
@@ -964,31 +1040,26 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol =
         if kind != tkSpaces:
           result.generics.add(literal.nimIdentNormalize)
 
-  if k == skType:
-    case n[2].kind
-    of nkEnumTy: result.symTypeKind = "enum"
-    of nkObjectTy: result.symTypeKind = "object"
-    of nkTupleTy: result.symTypeKind = "tuple"
-    else: discard
+  if k == skType: result.symTypeKind = getTypeKind(n)
 
-proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
+proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags, nonExports: bool = false) =
   if (docFlags != kForceExport) and not isVisible(d, nameNode): return
   let
-    name = getName(d, nameNode)
+    name = getName(nameNode)
+    nameEsc = esc(d.target, name)
   var plainDocstring = getPlainDocstring(n) # call here before genRecComment!
   var result = ""
   var literal, plainName = ""
   var kind = tkEof
-  var comm: ItemPre
+  var comm: ItemPre = default(ItemPre)
   if n.kind in routineDefs:
     getAllRunnableExamples(d, n, comm)
   else:
     comm.add genRecComment(d, n)
 
-  var r: TSrcGen
   # Obtain the plain rendered string for hyperlink titles.
-  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments,
-    renderNoPragmas, renderNoProcDefs})
+  var r: TSrcGen = initTokRender(n, {renderNoBody, renderNoComments, renderDocComments,
+    renderNoPragmas, renderNoProcDefs, renderExpandUsing, renderNoPostfix})
   while true:
     getNextTok(r, kind, literal)
     if kind == tkEof:
@@ -1001,9 +1072,12 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
   inc(d.id)
   let
     plainNameEsc = esc(d.target, plainName.strip)
-    detailedName = k.toHumanStr & " " & (
+    typeDescr =
+      if k == skType and getTypeKind(n) != "": getTypeKind(n)
+      else: k.toHumanStr
+    detailedName = typeDescr & " " & (
         if k in routineKinds: plainName else: name)
-    uniqueName = if k in routineKinds: plainNameEsc else: name
+    uniqueName = if k in routineKinds: plainNameEsc else: nameEsc
     sortName = if k in routineKinds: plainName.strip else: name
     cleanPlainSymbol = renderPlainSymbolName(nameNode)
     complexSymbol = complexName(k, n, cleanPlainSymbol)
@@ -1012,16 +1086,24 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
     symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false)
     deprecationMsg = genDeprecationMsg(d, pragmaNode)
     rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol)
+    symNameNode =
+      if nameNode.kind == nkPostfix: nameNode[1]
+      else: nameNode
 
   # we generate anchors automatically for subsequent use in doc comments
   let lineinfo = rstast.TLineInfo(
       line: nameNode.info.line, col: nameNode.info.col,
       fileIndex: addRstFileIndex(d, nameNode.info))
-  addAnchorNim(d.sharedState, refn = symbolOrId, tooltip = detailedName,
-               rstLangSymbol, priority = symbolPriority(k), info = lineinfo)
+  addAnchorNim(d.sharedState, external = false, refn = symbolOrId,
+               tooltip = detailedName, langSym = rstLangSymbol,
+               priority = symbolPriority(k), info = lineinfo,
+               module = addRstFileIndex(d, FileIndex d.module.position))
 
-  nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments,
-    renderDocComments, renderSyms}, symbolOrIdEnc)
+  var renderFlags = {renderNoBody, renderNoComments, renderDocComments,
+    renderSyms, renderExpandUsing, renderNoPostfix}
+  if nonExports:
+    renderFlags.incl renderNonExportedFields
+  nodeToHighlightedHtml(d, n, result, renderFlags, symbolOrIdEnc)
 
   let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int)
 
@@ -1043,18 +1125,21 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
   let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string
 
   var attype = ""
-  if k in routineKinds and nameNode.kind == nkSym:
+  if k in routineKinds and symNameNode.kind == nkSym:
     let att = attachToType(d, nameNode.sym)
     if att != nil:
       attype = esc(d.target, att.name.s)
-  elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}:
-    let etyp = nameNode.sym.typ
+  elif k == skType and symNameNode.kind == nkSym and
+      symNameNode.sym.typ.kind in {tyEnum, tyBool}:
+    let etyp = symNameNode.sym.typ
     for e in etyp.n:
       if e.sym.kind != skEnumField: continue
       let plain = renderPlainSymbolName(e)
       let symbolOrId = d.newUniquePlainSymbol(plain)
-      setIndexTerm(d[], external, symbolOrId, plain, nameNode.sym.name.s & '.' & plain,
-        xmltree.escape(getPlainDocstring(e).docstringSummary))
+      setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId,
+                   term = plain, linkTitle = symNameNode.sym.name.s & '.' & plain,
+                   linkDesc = xmltree.escape(getPlainDocstring(e).docstringSummary),
+                   line = n.info.line.int)
 
   d.tocSimple[k].add TocItem(
     sortName: sortName,
@@ -1069,27 +1154,27 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
       "itemSymOrID", symbolOrId.replace(",", ",<wbr>"),
       "itemSymOrIDEnc", symbolOrIdEnc])
 
-  # Ironically for types the complexSymbol is *cleaner* than the plainName
-  # because it doesn't include object fields or documentation comments. So we
-  # use the plain one for callable elements, and the complex for the rest.
-  var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": "
-  if n.kind in routineDefs: linkTitle.add(xmltree.escape(plainName.strip))
-  else: linkTitle.add(xmltree.escape(complexSymbol.strip))
-
-  setIndexTerm(d[], external, symbolOrId, name, linkTitle,
-    xmltree.escape(plainDocstring.docstringSummary))
-  if k == skType and nameNode.kind == nkSym:
-    d.types.strTableAdd nameNode.sym
+  setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name,
+               linkTitle = detailedName,
+               linkDesc = xmltree.escape(plainDocstring.docstringSummary),
+               line = n.info.line.int)
+  if k == skType and symNameNode.kind == nkSym:
+    d.types.strTableAdd symNameNode.sym
 
-proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem =
+proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind, nonExports = false): JsonItem =
   if not isVisible(d, nameNode): return
   var
-    name = getName(d, nameNode)
+    name = getNameEsc(d, nameNode)
     comm = genRecComment(d, n)
     r: TSrcGen
-  initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments})
-  result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int,
+    renderFlags = {renderNoBody, renderNoComments, renderDocComments,
+      renderExpandUsing, renderNoPostfix}
+  if nonExports:
+    renderFlags.incl renderNonExportedFields
+  r = initTokRender(n, renderFlags)
+  result = JsonItem(json: %{ "name": %name, "type": %($k), "line": %n.info.line.int,
                    "col": %n.info.col}
+  )
   if comm != nil:
     result.rst = comm
     result.rstField = "description"
@@ -1119,10 +1204,9 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem =
       result.json["signature"]["genericParams"] = newJArray()
       for genericParam in n[genericParamsPos]:
         var param = %{"name": %($genericParam)}
-        if genericParam.sym.typ.sons.len > 0:
+        if genericParam.sym.typ.len > 0:
           param["types"] = newJArray()
-        for kind in genericParam.sym.typ.sons:
-          param["types"].add %($kind)
+          param["types"] = %($genericParam.sym.typ.elementType)
         result.json["signature"]["genericParams"].add param
   if optGenIndex in d.conf.globalOptions:
     genItem(d, n, nameNode, k, kForceExport)
@@ -1216,6 +1300,8 @@ proc documentNewEffect(cache: IdentCache; n: PNode): PNode =
   let s = n[namePos].sym
   if tfReturnsNew in s.typ.flags:
     result = newIdentNode(getIdent(cache, "new"), n.info)
+  else:
+    result = nil
 
 proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, idx: int): PNode =
   let spec = effectSpec(x, effectType)
@@ -1238,6 +1324,8 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id
 
     result = newTreeI(nkExprColonExpr, n.info,
       newIdentNode(getIdent(cache, $effectType), n.info), effects)
+  else:
+    result = nil
 
 proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode =
   let s = n[namePos].sym
@@ -1251,6 +1339,8 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName
   if effects.len > 0:
     result = newTreeI(nkExprColonExpr, n.info,
       newIdentNode(getIdent(cache, pragmaName), n.info), effects)
+  else:
+    result = nil
 
 proc documentRaises*(cache: IdentCache; n: PNode) =
   if n[namePos].kind != nkSym: return
@@ -1272,12 +1362,13 @@ proc documentRaises*(cache: IdentCache; n: PNode) =
     if p5 != nil: n[pragmasPos].add p5
     if p6 != nil: n[pragmasPos].add p6
 
-proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) =
+proc generateDoc*(d: PDoc, n, orig: PNode, config: ConfigRef, docFlags: DocFlags = kDefault) =
   ## Goes through nim nodes recursively and collects doc comments.
   ## Main function for `doc`:option: command,
   ## which is implemented in ``docgen2.nim``.
   template genItemAux(skind) =
     genItem(d, n, n[namePos], skind, docFlags)
+  let showNonExports = optShowNonExportedFields in config.globalOptions
   case n.kind
   of nkPragma:
     let pragmaNode = findPragma(n, wDeprecated)
@@ -1304,35 +1395,58 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) =
       if n[i].kind != nkCommentStmt:
         # order is always 'type var let const':
         genItem(d, n[i], n[i][0],
-                succ(skType, ord(n.kind)-ord(nkTypeSection)), docFlags)
+                succ(skType, ord(n.kind)-ord(nkTypeSection)), docFlags, showNonExports)
   of nkStmtList:
-    for i in 0..<n.len: generateDoc(d, n[i], orig)
+    for i in 0..<n.len: generateDoc(d, n[i], orig, config)
   of nkWhenStmt:
     # generate documentation for the first branch only:
     if not checkForFalse(n[0][0]):
-      generateDoc(d, lastSon(n[0]), orig)
+      generateDoc(d, lastSon(n[0]), orig, config)
   of nkImportStmt:
     for it in n: traceDeps(d, it)
   of nkExportStmt:
     for it in n:
-      if it.kind == nkSym:
+      # bug #23051; don't generate documentation for exported symbols again
+      if it.kind == nkSym and sfExported notin it.sym.flags:
         if d.module != nil and d.module == it.sym.owner:
-          generateDoc(d, it.sym.ast, orig, kForceExport)
+          generateDoc(d, it.sym.ast, orig, config, kForceExport)
         elif it.sym.ast != nil:
           exportSym(d, it.sym)
   of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept"
   of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0])
   of nkCallKinds:
-    var comm: ItemPre
+    var comm: ItemPre = default(ItemPre)
     getAllRunnableExamples(d, n, comm)
     if comm.len != 0: d.modDescPre.add(comm)
   else: discard
 
 proc overloadGroupName(s: string, k: TSymKind): string =
   ## Turns a name like `f` into anchor `f-procs-all`
-  #s & " " & k.toHumanStr & "s all"
   s & "-" & k.toHumanStr & "s-all"
 
+proc setIndexTitle(d: PDoc, useMetaTitle: bool) =
+  let titleKind = if d.standaloneDoc: ieMarkupTitle else: ieNimTitle
+  let external = AbsoluteFile(d.destFile)
+    .relativeTo(d.conf.outDir, '/')
+    .changeFileExt(HtmlExt)
+    .string
+  var term, linkTitle: string
+  if useMetaTitle and d.meta[metaTitle].len != 0:
+    term = d.meta[metaTitleRaw]
+    linkTitle = d.meta[metaTitleRaw]
+  else:
+    let filename = extractFilename(d.filename)
+    term =
+      if d.standaloneDoc: filename  # keep .rst/.md extension
+      else: changeFileExt(filename, "")  # rm .nim extension
+    linkTitle =
+      if d.standaloneDoc: term  # keep .rst/.md extension
+      else: canonicalImport(d.conf, AbsoluteFile d.filename)
+  if not d.standaloneDoc:
+    linkTitle = "module " & linkTitle
+  setIndexTerm(d[], titleKind, htmlFile = external, id = "",
+               term = term, linkTitle = linkTitle)
+
 proc finishGenerateDoc*(d: var PDoc) =
   ## Perform 2nd RST pass for resolution of links/footnotes/headings...
   # copy file map `filenames` to ``rstgen.nim`` for its warnings
@@ -1345,7 +1459,24 @@ proc finishGenerateDoc*(d: var PDoc) =
       firstRst = fragment.rst
       break
   d.hasToc = d.hasToc or d.sharedState.hasToc
-  preparePass2(d.sharedState, firstRst)
+  # in --index:only mode we do NOT want to load other .idx, only write ours:
+  let importdoc = optGenIndexOnly notin d.conf.globalOptions and
+                  optNoImportdoc notin d.conf.globalOptions
+  preparePass2(d.sharedState, firstRst, importdoc)
+
+  if optGenIndexOnly in d.conf.globalOptions:
+    # Top-level doc.comments may contain titles and :idx: statements:
+    for fragment in d.modDescPre:
+      if fragment.isRst:
+        traverseForIndex(d[], fragment.rst)
+    setIndexTitle(d, useMetaTitle = d.standaloneDoc)
+    # Symbol-associated doc.comments may contain :idx: statements:
+    for k in TSymKind:
+      for _, overloadChoices in d.section[k].secItems:
+        for item in overloadChoices:
+          for fragment in item.descRst:
+            if fragment.isRst:
+              traverseForIndex(d[], fragment.rst)
 
   # add anchors to overload groups before RST resolution
   for k in TSymKind:
@@ -1355,13 +1486,25 @@ proc finishGenerateDoc*(d: var PDoc) =
           let refn = overloadGroupName(plainName, k)
           let tooltip = "$1 ($2 overloads)" % [
                       k.toHumanStr & " " & plainName, $overloadChoices.len]
-          addAnchorNim(d.sharedState, refn, tooltip,
+          let name = nimIdentBackticksNormalize(plainName)
+          # save overload group to ``.idx``
+          let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').
+                         changeFileExt(HtmlExt).string
+          setIndexTerm(d[], ieNimGroup, htmlFile = external, id = refn,
+                       term = name, linkTitle = k.toHumanStr,
+                       linkDesc = "", line = overloadChoices[0].info.line.int)
+          if optGenIndexOnly in d.conf.globalOptions: continue
+          addAnchorNim(d.sharedState, external=false, refn, tooltip,
                        LangSymbol(symKind: k.toHumanStr,
-                                  name: nimIdentBackticksNormalize(plainName),
+                                  name: name,
                                   isGroup: true),
                        priority = symbolPriority(k),
                        # select index `0` just to have any meaningful warning:
-                       info = overloadChoices[0].info)
+                       info = overloadChoices[0].info,
+                       module = addRstFileIndex(d, FileIndex d.module.position))
+
+  if optGenIndexOnly in d.conf.globalOptions:
+    return
 
   # Finalize fragments of ``.nim`` or ``.rst`` file
   proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) =
@@ -1381,7 +1524,7 @@ proc finishGenerateDoc*(d: var PDoc) =
       overloadChoices.sort(cmp)
       var nameContent = ""
       for item in overloadChoices:
-        var itemDesc: string
+        var itemDesc: string = ""
         renderItemPre(d, item.descRst, itemDesc)
         nameContent.add(
           getConfigVar(d.conf, "doc.item") % (
@@ -1407,17 +1550,20 @@ proc finishGenerateDoc*(d: var PDoc) =
   for i, entry in d.jEntriesPre:
     if entry.rst != nil:
       let resolved = resolveSubs(d.sharedState, entry.rst)
-      var str: string
+      var str: string = ""
       renderRstToOut(d[], resolved, str)
       entry.json[entry.rstField] = %str
       d.jEntriesPre[i].rst = nil
 
     d.jEntriesFinal.add entry.json # generates docs
 
+  setIndexTitle(d, useMetaTitle = d.standaloneDoc)
+  completePass2(d.sharedState)
+
 proc add(d: PDoc; j: JsonItem) =
   if j.json != nil or j.rst != nil: d.jEntriesPre.add j
 
-proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
+proc generateJson*(d: PDoc, n: PNode, config: ConfigRef, includeComments: bool = true) =
   case n.kind
   of nkPragma:
     let doctypeNode = findPragma(n, wDoctype)
@@ -1449,18 +1595,18 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) =
       if n[i].kind != nkCommentStmt:
         # order is always 'type var let const':
         d.add genJsonItem(d, n[i], n[i][0],
-                succ(skType, ord(n.kind)-ord(nkTypeSection)))
+                succ(skType, ord(n.kind)-ord(nkTypeSection)), optShowNonExportedFields in config.globalOptions)
   of nkStmtList:
     for i in 0..<n.len:
-      generateJson(d, n[i], includeComments)
+      generateJson(d, n[i], config, includeComments)
   of nkWhenStmt:
     # generate documentation for the first branch only:
     if not checkForFalse(n[0][0]):
-      generateJson(d, lastSon(n[0]), includeComments)
+      generateJson(d, lastSon(n[0]), config, includeComments)
   else: discard
 
 proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string =
-  result = getName(d, nameNode) & "\n"
+  result = getNameEsc(d, nameNode) & "\n"
 
 proc generateTags*(d: PDoc, n: PNode, r: var string) =
   case n.kind
@@ -1519,7 +1665,7 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
     for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase):
       var overloadChoices = d.tocTable[kind][plainName]
       overloadChoices.sort(cmp)
-      var content: string
+      var content: string = ""
       for item in overloadChoices:
         content.add item.content
       d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [
@@ -1544,13 +1690,13 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) =
   else:
     # Just have the link
     d.toc[kind] =  getConfigVar(d.conf, "doc.section.toc_item") % sectionValues
-    
+
 proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string =
   $relativeTo(outDir / linkto, destFile.splitFile().dir, '/')
 
 proc genOutFile(d: PDoc, groupedToc = false): string =
   var
-    code, content: string
+    code, content: string = ""
     title = ""
   var j = 0
   var toc = ""
@@ -1567,13 +1713,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
   # Extract the title. Non API modules generate an entry in the index table.
   if d.meta[metaTitle].len != 0:
     title = d.meta[metaTitle]
-    let external = AbsoluteFile(d.destFile)
-      .relativeTo(d.conf.outDir, '/')
-      .changeFileExt(HtmlExt)
-      .string
-    setIndexTerm(d[], external, "", title)
   else:
-    # Modules get an automatic title for the HTML, but no entry in the index.
     title = canonicalImport(d.conf, AbsoluteFile d.filename)
   title = esc(d.target, title)
   var subtitle = ""
@@ -1607,16 +1747,22 @@ proc genOutFile(d: PDoc, groupedToc = false): string =
         "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(),
         "content", content, "author", d.meta[metaAuthor],
         "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics,
-        "deprecationMsg", d.modDeprecationMsg]
+        "deprecationMsg", d.modDeprecationMsg, "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
   else:
     code = content
   result = code
 
+proc indexFile(d: PDoc): AbsoluteFile =
+  let dir = d.conf.outDir
+  result = dir / changeFileExt(presentationPath(d.conf,
+                                                AbsoluteFile d.filename),
+                               IndexExt)
+  let (finalDir, _, _) = result.string.splitFile
+  createDir(finalDir)
+
 proc generateIndex*(d: PDoc) =
   if optGenIndex in d.conf.globalOptions:
-    let dir = d.conf.outDir
-    createDir(dir)
-    let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt)
+    let dest = indexFile(d)
     writeIndexFile(d[], dest.string)
 
 proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
@@ -1627,6 +1773,9 @@ proc updateOutfile(d: PDoc, outfile: AbsoluteFile) =
         d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile
 
 proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
+  if optGenIndexOnly in d.conf.globalOptions:
+    d.conf.outFile = indexFile(d).relativeTo(d.conf.outDir)  # just for display
+    return
   runAllExamples(d)
   var content = genOutFile(d, groupedToc)
   if optStdout in d.conf.globalOptions:
@@ -1656,7 +1805,7 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) =
 
 proc writeOutputJson*(d: PDoc, useWarning = false) =
   runAllExamples(d)
-  var modDesc: string
+  var modDesc: string = ""
   for desc in d.modDescFinal:
     modDesc &= desc
   let content = %*{"orig": d.filename,
@@ -1664,11 +1813,11 @@ proc writeOutputJson*(d: PDoc, useWarning = false) =
     "moduleDescription": modDesc,
     "entries": d.jEntriesFinal}
   if optStdout in d.conf.globalOptions:
-    write(stdout, $content)
+    writeLine(stdout, $content)
   else:
     let dir = d.destFile.splitFile.dir
     createDir(dir)
-    var f: File
+    var f: File = default(File)
     if open(f, d.destFile, fmWrite):
       write(f, $content)
       close(f)
@@ -1691,7 +1840,7 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
   var ast = parseFile(conf.projectMainIdx, cache, conf)
   if ast == nil: return
   var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
-  generateDoc(d, ast, ast)
+  generateDoc(d, ast, ast, conf)
   finishGenerateDoc(d)
   writeOutput(d)
   generateIndex(d)
@@ -1702,13 +1851,16 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
   var filen = addFileExt(filename, "txt")
   var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true,
                         preferMarkdown = preferMarkdown, hasToc = false)
-  let rst = parseRst(readFile(filen.string),
-                     line=LineRstInit, column=ColRstInit,
-                     conf, d.sharedState)
-  d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
-  finishGenerateDoc(d)
-  writeOutput(d)
-  generateIndex(d)
+  try:
+    let rst = parseRst(readFile(filen.string),
+                      line=LineRstInit, column=ColRstInit,
+                      conf, d.sharedState)
+    d.modDescPre = @[ItemFragment(isRst: true, rst: rst)]
+    finishGenerateDoc(d)
+    writeOutput(d)
+    generateIndex(d)
+  except ERecoverableError:
+    discard "already reported the error"
 
 proc commandRst2Html*(cache: IdentCache, conf: ConfigRef,
                       preferMarkdown=false) =
@@ -1724,10 +1876,10 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
   if ast == nil: return
   var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
   d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
-                          status: int; content: string) =
+                          status: int; content: string) {.gcsafe.} =
     localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
                warnUser, "the ':test:' attribute is not supported by this backend")
-  generateJson(d, ast)
+  generateJson(d, ast, conf)
   finishGenerateDoc(d)
   let json = d.jEntriesFinal
   let content = pretty(json)
@@ -1739,7 +1891,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
     let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt)
     try:
       writeFile(filename, content)
-    except:
+    except IOError:
       rawMessage(conf, errCannotOpenFile, filename.string)
 
 proc commandTags*(cache: IdentCache, conf: ConfigRef) =
@@ -1747,7 +1899,7 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
   if ast == nil: return
   var d = newDocumentor(conf.projectFull, cache, conf, hasToc = true)
   d.onTestSnippet = proc (d: var RstGenerator; filename, cmd: string;
-                          status: int; content: string) =
+                          status: int; content: string) {.gcsafe.} =
     localError(conf, newLineInfo(conf, AbsoluteFile d.filename, -1, -1),
                warnUser, "the ':test:' attribute is not supported by this backend")
   var
@@ -1761,10 +1913,12 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
     let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt)
     try:
       writeFile(filename, content)
-    except:
+    except IOError:
       rawMessage(conf, errCannotOpenFile, filename.string)
 
 proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
+  if optGenIndexOnly in conf.globalOptions:
+    return
   var content = mergeIndexes(dir)
 
   var outFile = outFile
@@ -1777,12 +1931,12 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"")
       "title", "Index",
       "subtitle", "", "tableofcontents", "", "moduledesc", "",
       "date", getDateStr(), "time", getClockStr(),
-      "content", content, "author", "", "version", "", "analytics", ""]
+      "content", content, "author", "", "version", "", "analytics", "", "nimVersion", $NimMajor & "." & $NimMinor & "." & $NimPatch]
   # no analytics because context is not available
 
   try:
     writeFile(filename, code)
-  except:
+  except IOError:
     rawMessage(conf, errCannotOpenFile, filename.string)
 
 proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
@@ -1796,5 +1950,5 @@ proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile
 
   try:
     writeFile(filename, $body)
-  except:
+  except IOError:
     rawMessage(conf, errCannotOpenFile, filename.string)
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index ea3253a2c..7fb11a3bd 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,7 +11,7 @@
 # semantic checking.
 
 import
-  options, ast, msgs, passes, docgen, lineinfos, pathutils, packages
+  options, ast, msgs, docgen, lineinfos, pathutils, packages
 
 from modulegraphs import ModuleGraph, PPassContext
 
@@ -38,25 +38,27 @@ template closeImpl(body: untyped) {.dirty.} =
     except IOError:
       discard
 
-proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+  result = nil
   closeImpl:
     writeOutput(g.doc, useWarning, groupedToc)
 
-proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode =
+  result = nil
   closeImpl:
     writeOutputJson(g.doc, useWarning)
 
-proc processNode(c: PPassContext, n: PNode): PNode =
+proc processNode*(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
   if shouldProcess(g):
-    generateDoc(g.doc, n, n)
+    generateDoc(g.doc, n, n, g.config)
 
-proc processNodeJson(c: PPassContext, n: PNode): PNode =
+proc processNodeJson*(c: PPassContext, n: PNode): PNode =
   result = n
   var g = PGen(c)
   if shouldProcess(g):
-    generateJson(g.doc, n, false)
+    generateJson(g.doc, n, g.config, false)
 
 template myOpenImpl(ext: untyped) {.dirty.} =
   var g: PGen
@@ -68,20 +70,11 @@ template myOpenImpl(ext: untyped) {.dirty.} =
   g.doc = d
   result = g
 
-proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   myOpenImpl(HtmlExt)
 
-proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   myOpenImpl(TexExt)
 
-proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   myOpenImpl(JsonExt)
-
-const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close)
-const docgen2TexPass* = makePass(open = myOpenTex, process = processNode,
-                                 close = close)
-const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson,
-                                  close = closeJson)
-
-proc finishDoc2Pass*(project: string) =
-  discard
diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim
index 4a28d066c..dc516d2e5 100644
--- a/compiler/enumtostr.nim
+++ b/compiler/enumtostr.nim
@@ -6,15 +6,15 @@ when defined(nimPreviewSlimSystem):
 
 
 proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
-  result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info)
+  result = newSym(skProc, getIdent(g.cache, "$"), idgen, t.owner, info)
 
-  let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
+  let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
   dest.typ = t
 
-  let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
   res.typ = getSysType(g, info, tyString)
 
-  result.typ = newType(tyProc, nextTypeId idgen, t.owner)
+  result.typ = newType(tyProc, idgen, t.owner)
   result.typ.n = newNodeI(nkFormalParams, info)
   rawAddSon(result.typ, res.typ)
   result.typ.n.add newNodeI(nkEffectList, info)
@@ -30,7 +30,7 @@ proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGener
     assert(t.n[i].kind == nkSym)
     var field = t.n[i].sym
     let val = if field.ast == nil: field.name.s else: field.ast.strVal
-    caseStmt.add newTree(nkOfBranch, newSymNode(field),
+    caseStmt.add newTree(nkOfBranch, newIntTypeNode(field.position, t),
       newTree(nkStmtList, newTree(nkFastAsgn, newSymNode(res), newStrNode(val, info))))
     #newIntTypeNode(nkIntLit, field.position, t)
 
@@ -56,26 +56,27 @@ proc searchObjCaseImpl(obj: PNode; field: PSym): PNode =
     if obj.kind == nkRecCase and obj[0].kind == nkSym and obj[0].sym == field:
       result = obj
     else:
+      result = nil
       for x in obj:
         result = searchObjCaseImpl(x, field)
         if result != nil: break
 
 proc searchObjCase(t: PType; field: PSym): PNode =
   result = searchObjCaseImpl(t.n, field)
-  if result == nil and t.len > 0:
-    result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
+  if result == nil and t.baseClass != nil:
+    result = searchObjCase(t.baseClass.skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field)
   doAssert result != nil
 
 proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym =
-  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info)
+  result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), idgen, t.owner, info)
 
-  let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info)
+  let dest = newSym(skParam, getIdent(g.cache, "e"), idgen, result, info)
   dest.typ = field.typ
 
-  let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
   res.typ = getSysType(g, info, tyUInt8)
 
-  result.typ = newType(tyProc, nextTypeId idgen, t.owner)
+  result.typ = newType(tyProc, idgen, t.owner)
   result.typ.n = newNodeI(nkFormalParams, info)
   rawAddSon(result.typ, res.typ)
   result.typ.n.add newNodeI(nkEffectList, info)
diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim
index a8361105e..2cde9e3fb 100644
--- a/compiler/errorhandling.nim
+++ b/compiler/errorhandling.nim
@@ -10,7 +10,8 @@
 ## This module contains support code for new-styled error
 ## handling via an `nkError` node kind.
 
-import ast, renderer, options, strutils, types
+import ast, renderer, options, types
+import std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -40,7 +41,8 @@ proc newError*(wrongNode: PNode; k: ErrorKind; args: varargs[PNode]): PNode =
   let innerError = errorSubNode(wrongNode)
   if innerError != nil:
     return innerError
-  result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
   result.add wrongNode
   result.add newIntNode(nkIntLit, ord(k))
   for a in args: result.add a
@@ -50,7 +52,8 @@ proc newError*(wrongNode: PNode; msg: string): PNode =
   let innerError = errorSubNode(wrongNode)
   if innerError != nil:
     return innerError
-  result = newNodeIT(nkError, wrongNode.info, newType(tyError, ItemId(module: -1, item: -1), nil))
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newNodeIT(nkError, wrongNode.info, newType(tyError, idgen, nil))
   result.add wrongNode
   result.add newIntNode(nkIntLit, ord(CustomError))
   result.add newStrNode(msg, wrongNode.info)
diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim
index 37edef86c..9871c81af 100644
--- a/compiler/evalffi.nim
+++ b/compiler/evalffi.nim
@@ -9,9 +9,11 @@
 
 ## This file implements the FFI part of the evaluator for Nim code.
 
-import ast, types, options, tables, dynlib, msgs, lineinfos
-from os import getAppFilename
-import pkg/libffi
+import ast, types, options, msgs, lineinfos
+from std/os import getAppFilename
+import libffi/libffi
+
+import std/[tables, dynlib]
 
 when defined(windows):
   const libcDll = "msvcrt.dll"
@@ -37,9 +39,10 @@ else:
   var gExeHandle = loadLib()
 
 proc getDll(conf: ConfigRef, cache: var TDllCache; dll: string; info: TLineInfo): pointer =
+  result = nil
   if dll in cache:
     return cache[dll]
-  var libs: seq[string]
+  var libs: seq[string] = @[]
   libCandidates(dll, libs)
   for c in libs:
     result = loadLib(c)
@@ -61,7 +64,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
     let lib = sym.annex
     if lib != nil and lib.path.kind notin {nkStrLit..nkTripleStrLit}:
       globalError(conf, sym.info, "dynlib needs to be a string lit")
-    var theAddr: pointer
+    var theAddr: pointer = nil
     if (lib.isNil or lib.kind == libHeader) and not gExeHandle.isNil:
       libPathMsg = "current exe: " & getAppFilename() & " nor libc: " & libcDll
       # first try this exe itself:
@@ -77,7 +80,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode =
       theAddr = dllhandle.symAddr(name.cstring)
     if theAddr.isNil: globalError(conf, sym.info,
       "cannot import symbol: " & name & " from " & libPathMsg)
-    result.intVal = cast[ByteAddress](theAddr)
+    result.intVal = cast[int](theAddr)
 
 proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
   if t == nil: return addr libffi.type_void
@@ -96,7 +99,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type =
      tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil:
     result = addr libffi.type_pointer
   of tyDistinct, tyAlias, tySink:
-    result = mapType(conf, t[0])
+    result = mapType(conf, t.skipModifier)
   else:
     result = nil
   # too risky:
@@ -108,12 +111,13 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI
   of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI
   of ccCDecl: result = DEFAULT_ABI
   else:
+    result = default(TABI)
     globalError(conf, info, "cannot map calling convention to FFI")
 
 template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[]
 template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v
 template `+!`(x, y: untyped): untyped =
-  cast[pointer](cast[ByteAddress](x) + y)
+  cast[pointer](cast[int](x) + y)
 
 proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
   ## computes the size of the blob
@@ -122,16 +126,18 @@ proc packSize(conf: ConfigRef, v: PNode, typ: PType): int =
     if v.kind in {nkNilLit, nkPtrLit}:
       result = sizeof(pointer)
     else:
-      result = sizeof(pointer) + packSize(conf, v[0], typ.lastSon)
+      result = sizeof(pointer) + packSize(conf, v[0], typ.elementType)
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    result = packSize(conf, v, typ[0])
+    result = packSize(conf, v, typ.skipModifier)
   of tyArray:
     # consider: ptr array[0..1000_000, int] which is common for interfacing;
     # we use the real length here instead
     if v.kind in {nkNilLit, nkPtrLit}:
       result = sizeof(pointer)
     elif v.len != 0:
-      result = v.len * packSize(conf, v[0], typ[1])
+      result = v.len * packSize(conf, v[0], typ.elementType)
+    else:
+      result = 0
   else:
     result = getSize(conf, typ).int
 
@@ -140,6 +146,7 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer)
 proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
   case n.kind
   of nkRecList:
+    result = nil
     for i in 0..<n.len:
       result = getField(conf, n[i], position)
       if result != nil: return
@@ -154,7 +161,8 @@ proc getField(conf: ConfigRef, n: PNode; position: int): PSym =
       else: internalError(conf, n.info, "getField(record case branch)")
   of nkSym:
     if n.sym.position == position: result = n.sym
-  else: discard
+    else: result = nil
+  else: result = nil
 
 proc packObject(conf: ConfigRef, x: PNode, typ: PType, res: pointer) =
   internalAssert conf, x.kind in {nkObjConstr, nkPar, nkTupleConstr}
@@ -226,19 +234,19 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) =
         packRecCheck = 0
         globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
       inc packRecCheck
-      pack(conf, v[0], typ.lastSon, res +! sizeof(pointer))
+      pack(conf, v[0], typ.elementType, res +! sizeof(pointer))
       dec packRecCheck
       awr(pointer, res +! sizeof(pointer))
   of tyArray:
-    let baseSize = getSize(conf, typ[1])
+    let baseSize = getSize(conf, typ.elementType)
     for i in 0..<v.len:
-      pack(conf, v[i], typ[1], res +! i * baseSize)
+      pack(conf, v[i], typ.elementType, res +! i * baseSize)
   of tyObject, tyTuple:
     packObject(conf, v, typ, res)
   of tyNil:
     discard
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    pack(conf, v, typ[0], res)
+    pack(conf, v, typ.skipModifier, res)
   else:
     globalError(conf, v.info, "cannot map value to FFI " & typeToString(v.typ))
 
@@ -296,9 +304,9 @@ proc unpackArray(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
     result = n
     if result.kind != nkBracket:
       globalError(conf, n.info, "cannot map value from FFI")
-  let baseSize = getSize(conf, typ[1])
+  let baseSize = getSize(conf, typ.elementType)
   for i in 0..<result.len:
-    result[i] = unpack(conf, x +! i * baseSize, typ[1], result[i])
+    result[i] = unpack(conf, x +! i * baseSize, typ.elementType, result[i])
 
 proc canonNodeKind(k: TNodeKind): TNodeKind =
   case k
@@ -356,6 +364,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
     of 4: awi(nkIntLit, rd(int32, x).BiggestInt)
     of 8: awi(nkIntLit, rd(int64, x).BiggestInt)
     else:
+      result = nil
       globalError(conf, n.info, "cannot map value from FFI (tyEnum, tySet)")
   of tyFloat: awf(nkFloatLit, rd(float, x))
   of tyFloat32: awf(nkFloat32Lit, rd(float32, x))
@@ -369,18 +378,19 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
       # in their unboxed representation so nothing it to be unpacked:
       result = n
     else:
-      awi(nkPtrLit, cast[ByteAddress](p))
+      awi(nkPtrLit, cast[int](p))
   of tyPtr, tyRef, tyVar, tyLent:
     let p = rd(pointer, x)
     if p.isNil:
       setNil()
     elif n == nil or n.kind == nkPtrLit:
-      awi(nkPtrLit, cast[ByteAddress](p))
+      awi(nkPtrLit, cast[int](p))
     elif n != nil and n.len == 1:
       internalAssert(conf, n.kind == nkRefTy)
-      n[0] = unpack(conf, p, typ.lastSon, n[0])
+      n[0] = unpack(conf, p, typ.elementType, n[0])
       result = n
     else:
+      result = nil
       globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
   of tyObject, tyTuple:
     result = unpackObject(conf, x, typ, n)
@@ -395,9 +405,10 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode =
   of tyNil:
     setNil()
   of tyDistinct, tyGenericInst, tyAlias, tySink:
-    result = unpack(conf, x, typ.lastSon, n)
+    result = unpack(conf, x, typ.skipModifier, n)
   else:
     # XXX what to do with 'array' here?
+    result = nil
     globalError(conf, n.info, "cannot map value from FFI " & typeToString(typ))
 
 proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
@@ -423,8 +434,8 @@ proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode =
 proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
   internalAssert conf, call[0].kind == nkPtrLit
 
-  var cif: TCif
-  var sig: ParamList
+  var cif: TCif = default(TCif)
+  var sig: ParamList = default(ParamList)
   # use the arguments' types for varargs support:
   for i in 1..<call.len:
     sig[i-1] = mapType(conf, call[i].typ)
@@ -433,24 +444,24 @@ proc callForeignFunction*(conf: ConfigRef, call: PNode): PNode =
 
   let typ = call[0].typ
   if prep_cif(cif, mapCallConv(conf, typ.callConv, call.info), cuint(call.len-1),
-              mapType(conf, typ[0]), sig) != OK:
+              mapType(conf, typ.returnType), sig) != OK:
     globalError(conf, call.info, "error in FFI call")
 
-  var args: ArgList
+  var args: ArgList = default(ArgList)
   let fn = cast[pointer](call[0].intVal)
   for i in 1..<call.len:
     var t = call[i].typ
     args[i-1] = alloc0(packSize(conf, call[i], t))
     pack(conf, call[i], t, args[i-1])
-  let retVal = if isEmptyType(typ[0]): pointer(nil)
-               else: alloc(getSize(conf, typ[0]).int)
+  let retVal = if isEmptyType(typ.returnType): pointer(nil)
+               else: alloc(getSize(conf, typ.returnType).int)
 
   libffi.call(cif, fn, retVal, args)
 
   if retVal.isNil:
     result = newNode(nkEmpty)
   else:
-    result = unpack(conf, retVal, typ[0], nil)
+    result = unpack(conf, retVal, typ.returnType, nil)
     result.info = call.info
 
   if retVal != nil: dealloc retVal
@@ -463,8 +474,8 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
                           info: TLineInfo): PNode =
   internalAssert conf, fn.kind == nkPtrLit
 
-  var cif: TCif
-  var sig: ParamList
+  var cif: TCif = default(TCif)
+  var sig: ParamList = default(ParamList)
   for i in 0..len-1:
     var aTyp = args[i+start].typ
     if aTyp.isNil:
@@ -478,7 +489,7 @@ proc callForeignFunction*(conf: ConfigRef, fn: PNode, fntyp: PType,
               mapType(conf, fntyp[0]), sig) != OK:
     globalError(conf, info, "error in FFI call")
 
-  var cargs: ArgList
+  var cargs: ArgList = default(ArgList)
   let fn = cast[pointer](fn.intVal)
   for i in 0..len-1:
     let t = args[i+start].typ
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 8a6bf9287..77c136d63 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -9,16 +9,16 @@
 
 ## Template evaluation engine. Now hygienic.
 
-import
-  strutils, options, ast, astalgo, msgs, renderer, lineinfos, idents
+import options, ast, astalgo, msgs, renderer, lineinfos, idents, trees
+import std/strutils
 
 type
   TemplCtx = object
     owner, genSymOwner: PSym
     instLines: bool   # use the instantiation lines numbers
     isDeclarative: bool
-    mapping: TIdTable # every gensym'ed symbol needs to be mapped to some
-                      # new symbol
+    mapping: SymMapping # every gensym'ed symbol needs to be mapped to some
+                        # new symbol
     config: ConfigRef
     ic: IdentCache
     instID: int
@@ -26,13 +26,26 @@ type
 
 proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   result = copyNode(a)
-  if ctx.instLines: result.info = b.info
+  if ctx.instLines: setInfoRecursive(result, b.info)
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
   template handleParam(param) =
     let x = param
     if x.kind == nkArgList:
       for y in items(x): result.add(y)
+    elif nfDefaultRefsParam in x.flags:
+      # value of default param needs to be evaluated like template body
+      # if it contains other template params
+      var res: PNode
+      if isAtom(x):
+        res = newNodeI(nkPar, x.info)
+        evalTemplateAux(x, actual, c, res)
+        if res.len == 1: res = res[0]
+      else:
+        res = copyNode(x)
+        for i in 0..<x.safeLen:
+          evalTemplateAux(x[i], actual, c, res)
+      result.add res
     else:
       result.add copyTree(x)
 
@@ -44,18 +57,19 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
         handleParam actual[s.position]
       elif (s.owner != nil) and (s.kind == skGenericParam or
            s.kind == skType and s.typ != nil and s.typ.kind == tyGenericParam):
-        handleParam actual[s.owner.typ.len + s.position - 1]
+        handleParam actual[s.owner.typ.signatureLen + s.position - 1]
       else:
         internalAssert c.config, sfGenSym in s.flags or s.kind == skType
-        var x = PSym(idTableGet(c.mapping, s))
+        var x = idTableGet(c.mapping, s)
         if x == nil:
-          x = copySym(s, nextSymId(c.idgen))
+          x = copySym(s, c.idgen)
           # sem'check needs to set the owner properly later, see bug #9476
           x.owner = nil # c.genSymOwner
           #if x.kind == skParam and x.owner.kind == skModule:
           #  internalAssert c.config, false
           idTablePut(c.mapping, s, x)
         if sfGenSym in s.flags:
+          # TODO: getIdent(c.ic, "`" & x.name.s & "`gensym" & $c.instID)
           result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID),
             if c.instLines: actual.info else: templ.info)
         else:
@@ -116,7 +130,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; conf: ConfigRef; fromHlo: bool): PNode
     # now that we have working untyped parameters.
     genericParams = if fromHlo: 0
                     else: s.ast[genericParamsPos].len
-    expectedRegularParams = s.typ.len-1
+    expectedRegularParams = s.typ.paramsLen
     givenRegularParams = totalParams - genericParams
   if givenRegularParams < 0: givenRegularParams = 0
 
@@ -182,14 +196,14 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
 
   # replace each param by the corresponding node:
   var args = evalTemplateArgs(n, tmpl, conf, fromHlo)
-  var ctx: TemplCtx
-  ctx.owner = tmpl
-  ctx.genSymOwner = genSymOwner
-  ctx.config = conf
-  ctx.ic = ic
-  initIdTable(ctx.mapping)
-  ctx.instID = instID[]
-  ctx.idgen = idgen
+  var ctx = TemplCtx(owner: tmpl,
+    genSymOwner: genSymOwner,
+    config: conf,
+    ic: ic,
+    mapping: initSymMapping(),
+    instID: instID[],
+    idgen: idgen
+  )
 
   let body = tmpl.ast[bodyPos]
   #echo "instantion of ", renderTree(body, {renderIds})
@@ -204,7 +218,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym;
     result = copyNode(body)
     ctx.instLines = sfCallsite in tmpl.flags
     if ctx.instLines:
-      result.info = n.info
+      setInfoRecursive(result, n.info)
     for i in 0..<body.safeLen:
       evalTemplateAux(body[i], args, ctx, result)
   result.flags.incl nfFromTemplate
diff --git a/compiler/expanddefaults.nim b/compiler/expanddefaults.nim
new file mode 100644
index 000000000..c520d8849
--- /dev/null
+++ b/compiler/expanddefaults.nim
@@ -0,0 +1,131 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2023 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+import lineinfos, ast, types
+
+proc caseObjDefaultBranch*(obj: PNode; branch: Int128): int =
+  result = 0
+  for i in 1 ..< obj.len:
+    for j in 0 .. obj[i].len - 2:
+      if obj[i][j].kind == nkRange:
+        let x = getOrdValue(obj[i][j][0])
+        let y = getOrdValue(obj[i][j][1])
+        if branch >= x and branch <= y:
+          return i
+      elif getOrdValue(obj[i][j]) == branch:
+        return i
+    if obj[i].len == 1:
+      # else branch
+      return i
+  return 1
+
+template newZero(t: PType; info: TLineInfo; k = nkIntLit): PNode = newNodeIT(k, info, t)
+
+proc expandDefault*(t: PType; info: TLineInfo): PNode
+
+proc expandField(s: PSym; info: TLineInfo): PNode =
+  result = newNodeIT(nkExprColonExpr, info, s.typ)
+  result.add newSymNode(s)
+  result.add expandDefault(s.typ, info)
+
+proc expandDefaultN(n: PNode; info: TLineInfo; res: PNode) =
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      expandDefaultN(n[i], info, res)
+  of nkRecCase:
+    res.add expandField(n[0].sym, info)
+    var branch = Zero
+    let constOrNil = n[0].sym.astdef
+    if constOrNil != nil:
+      branch = getOrdValue(constOrNil)
+
+    let selectedBranch = caseObjDefaultBranch(n, branch)
+    let b = lastSon(n[selectedBranch])
+    expandDefaultN b, info, res
+  of nkSym:
+    res.add expandField(n.sym, info)
+  else:
+    discard
+
+proc expandDefaultObj(t: PType; info: TLineInfo; res: PNode) =
+  if t.baseClass != nil:
+    expandDefaultObj(t.baseClass, info, res)
+  expandDefaultN(t.n, info, res)
+
+proc expandDefault(t: PType; info: TLineInfo): PNode =
+  case t.kind
+  of tyInt:     result = newZero(t, info, nkIntLit)
+  of tyInt8:    result = newZero(t, info, nkInt8Lit)
+  of tyInt16:   result = newZero(t, info, nkInt16Lit)
+  of tyInt32:   result = newZero(t, info, nkInt32Lit)
+  of tyInt64:   result = newZero(t, info, nkInt64Lit)
+  of tyUInt:    result = newZero(t, info, nkUIntLit)
+  of tyUInt8:   result = newZero(t, info, nkUInt8Lit)
+  of tyUInt16:  result = newZero(t, info, nkUInt16Lit)
+  of tyUInt32:  result = newZero(t, info, nkUInt32Lit)
+  of tyUInt64:  result = newZero(t, info, nkUInt64Lit)
+  of tyFloat:   result = newZero(t, info, nkFloatLit)
+  of tyFloat32: result = newZero(t, info, nkFloat32Lit)
+  of tyFloat64: result = newZero(t, info, nkFloat64Lit)
+  of tyFloat128: result = newZero(t, info, nkFloat64Lit)
+  of tyChar:    result = newZero(t, info, nkCharLit)
+  of tyBool:    result = newZero(t, info, nkIntLit)
+  of tyEnum:
+    # Could use low(T) here to finally fix old language quirks
+    result = newZero(t, info, nkIntLit)
+  of tyRange:
+    # Could use low(T) here to finally fix old language quirks
+    result = expandDefault(skipModifier t, info)
+  of tyVoid: result = newZero(t, info, nkEmpty)
+  of tySink, tyGenericInst, tyDistinct, tyAlias, tyOwned:
+    result = expandDefault(t.skipModifier, info)
+  of tyOrdinal, tyGenericBody, tyGenericParam, tyInferred, tyStatic:
+    if t.hasElementType:
+      result = expandDefault(t.skipModifier, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyFromExpr:
+    if t.n != nil and t.n.typ != nil:
+      result = expandDefault(t.n.typ, info)
+    else:
+      result = newZero(t, info, nkEmpty)
+  of tyArray:
+    result = newZero(t, info, nkBracket)
+    let n = toInt64(lengthOrd(nil, t))
+    for i in 0..<n:
+      result.add expandDefault(t.elementType, info)
+  of tyPtr, tyRef, tyProc, tyPointer, tyCstring:
+    result = newZero(t, info, nkNilLit)
+  of tyVar, tyLent:
+    let e = t.elementType
+    if e.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
+      # skip the modifier, `var openArray` is a (ptr, len) pair too:
+      result = expandDefault(e, info)
+    else:
+      result = newZero(e, info, nkNilLit)
+  of tySet:
+    result = newZero(t, info, nkCurly)
+  of tyObject:
+    result = newNodeIT(nkObjConstr, info, t)
+    result.add newNodeIT(nkType, info, t)
+    expandDefaultObj(t, info, result)
+  of tyTuple:
+    result = newZero(t, info, nkTupleConstr)
+    for it in t.kids:
+      result.add expandDefault(it, info)
+  of tyVarargs, tyOpenArray, tySequence, tyUncheckedArray:
+    result = newZero(t, info, nkBracket)
+  of tyString:
+    result = newZero(t, info, nkStrLit)
+  of tyNone, tyEmpty, tyUntyped, tyTyped, tyTypeDesc,
+     tyNil, tyGenericInvocation, tyError, tyBuiltInTypeClass,
+     tyUserTypeClass, tyUserTypeClassInst, tyCompositeTypeClass,
+     tyAnd, tyOr, tyNot, tyAnything, tyConcept, tyIterable, tyForward:
+    result = newZero(t, info, nkEmpty) # bug indicator
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 0128ff3cb..ce25da773 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -14,12 +14,14 @@
 
 import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths
 
-import std/[os, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
+import std/[os, osproc, streams, sequtils, times, strtabs, json, jsonutils, sugar, parseutils]
 
 import std / strutils except addf
 
 when defined(nimPreviewSlimSystem):
-  import std/syncio
+  import std/[syncio, assertions]
+
+import ../dist/checksums/src/checksums/sha1
 
 type
   TInfoCCProp* = enum         # properties of the C compiler:
@@ -31,6 +33,7 @@ type
     hasGnuAsm,                # CC's asm uses the absurd GNU assembler syntax
     hasDeclspec,              # CC has __declspec(X)
     hasAttribute,             # CC has __attribute__((X))
+    hasBuiltinUnreachable     # CC has __builtin_unreachable
   TInfoCCProps* = set[TInfoCCProp]
   TInfoCC* = tuple[
     name: string,        # the short name of the compiler
@@ -93,7 +96,7 @@ compiler gcc:
     produceAsm: gnuAsmListing,
     cppXsupport: "-std=gnu++17 -funsigned-char",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
-            hasAttribute})
+            hasAttribute, hasBuiltinUnreachable})
 
 # GNU C and C++ Compiler
 compiler nintendoSwitchGCC:
@@ -120,7 +123,7 @@ compiler nintendoSwitchGCC:
     produceAsm: gnuAsmListing,
     cppXsupport: "-std=gnu++17 -funsigned-char",
     props: {hasSwitchRange, hasComputedGoto, hasCpp, hasGcGuard, hasGnuAsm,
-            hasAttribute})
+            hasAttribute, hasBuiltinUnreachable})
 
 # LLVM Frontend for GCC/G++
 compiler llvmGcc:
@@ -155,7 +158,7 @@ compiler vcc:
     compileTmpl: "/c$vccplatform $options $include /nologo /Fo$objfile $file",
     buildGui: " /SUBSYSTEM:WINDOWS user32.lib ",
     buildDll: " /LD",
-    buildLib: "lib /OUT:$libfile $objfiles",
+    buildLib: "vccexe --command:lib$vccplatform /nologo /OUT:$libfile $objfiles",
     linkerExe: "cl",
     linkTmpl: "$builddll$vccplatform /Fe$exefile $objfiles $buildgui /nologo $options",
     includeCmd: " /I",
@@ -169,6 +172,22 @@ compiler vcc:
     cppXsupport: "",
     props: {hasCpp, hasAssume, hasDeclspec})
 
+# Nvidia CUDA NVCC Compiler
+compiler nvcc:
+  result = gcc()
+  result.name = "nvcc"
+  result.compilerExe = "nvcc"
+  result.cppCompiler = "nvcc"
+  result.compileTmpl = "-c -x cu -Xcompiler=\"$options\" $include -o $objfile $file"
+  result.linkTmpl = "$buildgui $builddll -o $exefile $objfiles -Xcompiler=\"$options\""
+
+# AMD HIPCC Compiler (rocm/cuda)
+compiler hipcc:
+  result = clang()
+  result.name = "hipcc"
+  result.compilerExe = "hipcc"
+  result.cppCompiler = "hipcc"
+
 compiler clangcl:
   result = vcc()
   result.name = "clang_cl"
@@ -282,7 +301,9 @@ const
     envcc(),
     icl(),
     icc(),
-    clangcl()]
+    clangcl(),
+    hipcc(),
+    nvcc()]
 
   hExt* = ".h"
 
@@ -328,7 +349,9 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string =
                      platform.OS[conf.target.targetOS].name & '.' &
                      CC[c].name & fullSuffix
     result = getConfigVar(conf, fullCCname)
-    if result.len == 0:
+    if existsConfigVar(conf, fullCCname):
+      result = getConfigVar(conf, fullCCname)
+    else:
       # not overridden for this cross compilation setting?
       result = getConfigVar(conf, CC[c].name & fullSuffix)
   else:
@@ -482,6 +505,10 @@ proc vccplatform(conf: ConfigRef): string =
         of cpuArm: " --platform:arm"
         of cpuAmd64: " --platform:amd64"
         else: ""
+    else:
+      result = ""
+  else:
+    result = ""
 
 proc getLinkOptions(conf: ConfigRef): string =
   result = conf.linkOptions & " " & conf.linkOptionsCmd & " "
@@ -528,9 +555,9 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool =
     var exe = getConfigVar(conf, conf.cCompiler, ".exe")
     if exe.len == 0: exe = CC[conf.cCompiler].compilerExe
     # NOTE: should we need the full version, use -dumpfullversion
-    let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except: ("", 1)
+    let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1)
     if exitCode == 0:
-      var major: int
+      var major: int = 0
       discard parseInt(s, major)
       result = major >= 5
   else:
@@ -576,7 +603,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile,
 
     compilePattern = joinPath(conf.cCompilerPath, exe)
   else:
-    compilePattern = getCompilerExe(conf, c, isCpp)
+    compilePattern = exe
 
   includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)]))
 
@@ -640,7 +667,7 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool =
 
   let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1")
   let currentHash = footprint(conf, cfile)
-  var f: File
+  var f: File = default(File)
   if open(f, hashFile.string, fmRead):
     let oldHash = parseSecureHash(f.readLine())
     close(f)
@@ -676,7 +703,8 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile,
     if removeStaticFile:
       removeFile output # fixes: bug #16947
     result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output),
-                                            "objfiles", objfiles]
+                                            "objfiles", objfiles,
+                                            "vccplatform", vccplatform(conf)]
   else:
     var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe")
     if linkerExe.len == 0: linkerExe = getLinkerExe(conf, conf.cCompiler)
@@ -774,6 +802,7 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body
     raise
 
 proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] =
+  result = @[]
   when defined(macosx):
     if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions:
       # if needed, add an option to skip or override location
@@ -829,10 +858,22 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) =
   else:
     writeFile(linkerArgs, args)
   try:
-    execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
+    when defined(macosx):
+      execLinkCmd(conf, "xargs " & cmd.substr(0, last) & " < " & linkerArgs)
+    else:
+      execLinkCmd(conf, cmd.substr(0, last) & " @" & linkerArgs)
   finally:
     removeFile(linkerArgs)
 
+proc linkViaShellScript(conf: ConfigRef; cmd: string) =
+  let linkerScript = conf.projectName & "_" & "linkerScript.sh"
+  writeFile(linkerScript, cmd)
+  let shell = getEnv("SHELL")
+  try:
+    execLinkCmd(conf, shell & " " & linkerScript)
+  finally:
+    removeFile(linkerScript)
+
 proc getObjFilePath(conf: ConfigRef, f: Cfile): string =
   if noAbsolutePaths(conf): f.obj.extractFilename
   else: f.obj.string
@@ -844,23 +885,38 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu
   result = conf.getNimcacheDir / RelativeFile(targetName)
 
 proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string =
+  result = ""
   if conf.hasHint(hintCC):
     if optListCmd in conf.globalOptions or conf.verbosity > 1:
       result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd)
     else:
       result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name)
 
+proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) =
+  # Prevent linkcmd from exceeding the maximum command line length.
+  # Windows's command line limit is about 8K (8191 characters) so C compilers on
+  # Windows support a feature where the command line can be passed via ``@linkcmd``
+  # to them.
+  const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000
+  if linkCmd.len > MaxCmdLen:
+    when defined(macosx):
+      linkViaShellScript(conf, linkCmd)
+    else:
+      linkViaResponseFile(conf, linkCmd)
+  else:
+    execLinkCmd(conf, linkCmd)
+
 proc callCCompiler*(conf: ConfigRef) =
   var
-    linkCmd: string
+    linkCmd: string = ""
     extraCmds: seq[string]
   if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}:
     return # speed up that call if only compiling and no script shall be
            # generated
   #var c = cCompiler
   var script: Rope = ""
-  var cmds: TStringSeq
-  var prettyCmds: TStringSeq
+  var cmds: TStringSeq = default(TStringSeq)
+  var prettyCmds: TStringSeq = default(TStringSeq)
   let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
 
   for idx, it in conf.toCompile:
@@ -926,14 +982,7 @@ proc callCCompiler*(conf: ConfigRef) =
       linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true)
       extraCmds = getExtraCmds(conf, mainOutput)
       if optCompileOnly notin conf.globalOptions:
-        const MaxCmdLen = when defined(windows): 8_000 else: 32_000
-        if linkCmd.len > MaxCmdLen:
-          # Windows's command line limit is about 8K (don't laugh...) so C compilers on
-          # Windows support a feature where the command line can be passed via ``@linkcmd``
-          # to them.
-          linkViaResponseFile(conf, linkCmd)
-        else:
-          execLinkCmd(conf, linkCmd)
+        preventLinkCmdMaxCmdLen(conf, linkCmd)
         for cmd in extraCmds:
           execExternalProgram(conf, cmd, hintExecuting)
   else:
@@ -951,10 +1000,11 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile =
   # works out of the box with `hashMainCompilationParams`.
   result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json")
 
-const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes
+const cacheVersion = "D20240927T193831" # update when `BuildCache` spec changes
 type BuildCache = object
   cacheVersion: string
   outputFile: string
+  outputLastModificationTime: string
   compile: seq[(string, string)]
   link: seq[string]
   linkcmd: string
@@ -968,7 +1018,7 @@ type BuildCache = object
   depfiles: seq[(string, string)]
   nimexe: string
 
-proc writeJsonBuildInstructions*(conf: ConfigRef) =
+proc writeJsonBuildInstructions*(conf: ConfigRef; deps: StringTableRef) =
   var linkFiles = collect(for it in conf.externalToLink:
     var it = it
     if conf.noAbsolutePaths: it = it.extractFilename
@@ -989,17 +1039,24 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
     currentDir: getCurrentDir())
   if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"):
     bcache.cmdline = conf.commandLine
-    bcache.depfiles = collect(for it in conf.m.fileInfos:
+    for it in conf.m.fileInfos:
       let path = it.fullPath.string
       if isAbsolute(path): # TODO: else?
-        (path, $secureHashFile(path)))
+        if path in deps:
+          bcache.depfiles.add (path, deps[path])
+        else: # backup for configs etc.
+          bcache.depfiles.add (path, $secureHashFile(path))
+
     bcache.nimexe = hashNimExe()
+    if fileExists(bcache.outputFile):
+      bcache.outputLastModificationTime = $getLastModificationTime(bcache.outputFile)
   conf.jsonBuildFile = conf.jsonBuildInstructionsFile
   conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty)
 
 proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool =
+  result = false
   if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true
-  var bcache: BuildCache
+  var bcache: BuildCache = default(BuildCache)
   try: bcache.fromJson(jsonFile.string.parseFile)
   except IOError, OSError, ValueError:
     stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string
@@ -1013,11 +1070,13 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute
     # xxx optimize by returning false if stdin input was the same
   for (file, hash) in bcache.depfiles:
     if $secureHashFile(file) != hash: return true
+  if bcache.outputLastModificationTime != $getLastModificationTime(bcache.outputFile):
+    return true
 
 proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
-  var bcache: BuildCache
+  var bcache: BuildCache = default(BuildCache)
   try: bcache.fromJson(jsonFile.string.parseFile)
-  except:
+  except ValueError, KeyError, JsonKindError:
     let e = getCurrentException()
     conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" %
       [e.msg, e.getStackTrace(), jsonFile.string]
@@ -1028,16 +1087,18 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) =
     globalError(conf, gCmdLineInfo,
       "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " %
       [outputCurrent, output, jsonFile.string])
-  var cmds, prettyCmds: TStringSeq
+  var cmds: TStringSeq = default(TStringSeq)
+  var prettyCmds: TStringSeq = default(TStringSeq)
   let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx])
   for (name, cmd) in bcache.compile:
     cmds.add cmd
     prettyCmds.add displayProgressCC(conf, name, cmd)
   execCmdsInParallel(conf, cmds, prettyCb)
-  execLinkCmd(conf, bcache.linkcmd)
+  preventLinkCmdMaxCmdLen(conf, bcache.linkcmd)
   for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting)
 
 proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope =
+  result = ""
   for it in list:
     result.addf("--file:r\"$1\"$N", [rope(it.cname.string)])
 
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index 84ed99164..921a94b31 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -10,9 +10,11 @@
 # This module implements Nim's standard template filter.
 
 import
-  llstream, strutils, ast, msgs, options,
+  llstream, ast, msgs, options,
   filters, lineinfos, pathutils
 
+import std/strutils
+
 type
   TParseState = enum
     psDirective, psTempl
@@ -201,17 +203,15 @@ proc parseLine(p: var TTmplParser) =
 
 proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
                  call: PNode): PLLStream =
-  var p: TTmplParser
-  p.config = conf
-  p.info = newLineInfo(conf, filename, 0, 0)
-  p.outp = llStreamOpen("")
-  p.inp = stdin
-  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)
+  var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
+                outp: llStreamOpen(""), inp: stdin,
+                subsChar: charArg(conf, call, "subschar", 1, '$'),
+                nimDirective: charArg(conf, call, "metachar", 2, '#'),
+                emit: strArg(conf, call, "emit", 3, "result.add"),
+                conc: strArg(conf, call, "conc", 4, " & "),
+                toStr: strArg(conf, call, "tostring", 5, "$"),
+                x: newStringOfCap(120)
+                )
   # do not process the first line which contains the directive:
   if llStreamReadLine(p.inp, p.x):
     inc p.info.line
diff --git a/compiler/filters.nim b/compiler/filters.nim
index 8151c0b93..3cd56e3be 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -10,9 +10,11 @@
 # This module implements Nim's simple filters and helpers for filters.
 
 import
-  llstream, idents, strutils, ast, msgs, options,
+  llstream, idents, ast, msgs, options,
   renderer, pathutils
 
+import std/strutils
+
 proc invalidPragma(conf: ConfigRef; n: PNode) =
   localError(conf, n.info,
       "'$1' not allowed here" % renderTree(n, {renderNoComments}))
@@ -29,23 +31,30 @@ proc getArg(conf: ConfigRef; n: PNode, name: string, pos: int): PNode =
       return n[i]
 
 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(conf, n)
+  else:
+    result = default(char)
+    invalidPragma(conf, n)
 
 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(conf, n)
+  else:
+    result = ""
+    invalidPragma(conf, n)
 
 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(conf, n)
+  else:
+    result = false
+    invalidPragma(conf, n)
 
 proc filterStrip*(conf: ConfigRef; stdin: PLLStream, filename: AbsoluteFile, call: PNode): PLLStream =
   var pattern = strArg(conf, call, "startswith", 1, "")
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index f58ac5de9..da911c84c 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -9,12 +9,15 @@
 
 ## Module that implements ``gorge`` for the compiler.
 
-import msgs, std / sha1, os, osproc, streams, options,
-  lineinfos, pathutils
+import msgs, options, lineinfos, pathutils
+
+import std/[os, osproc, streams]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
 
+import ../dist/checksums/src/checksums/sha1
+
 proc readOutput(p: Process): (string, int) =
   result[0] = ""
   var output = p.outputStream
@@ -27,10 +30,11 @@ proc readOutput(p: Process): (string, int) =
 
 proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (string, int) =
   let workingDir = parentDir(toFullPath(conf, info))
+  result = ("", 0)
   if cache.len > 0:
     let h = secureHash(cmd & "\t" & input & "\t" & cache)
     let filename = toGeneratedFile(conf, AbsoluteFile("gorge_" & $h), "txt").string
-    var f: File
+    var f: File = default(File)
     if optForceFullMake notin conf.globalOptions and open(f, filename):
       result = (f.readAll, 0)
       f.close
@@ -49,7 +53,11 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
       if result[1] == 0:
         writeFile(filename, result[0])
     except IOError, OSError:
-      if not readSuccessful: result = ("", -1)
+      if not readSuccessful:
+        when defined(nimLegacyGorgeErrors):
+          result = ("", -1)
+        else:
+          result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
   else:
     try:
       var p = startProcess(cmd, workingDir,
@@ -60,4 +68,7 @@ proc opGorge*(cmd, input, cache: string, info: TLineInfo; conf: ConfigRef): (str
       result = p.readOutput
       p.close()
     except IOError, OSError:
-      result = ("", -1)
+      when defined(nimLegacyGorgeErrors):
+        result = ("", -1)
+      else:
+        result = ("Error running startProcess: " & getCurrentExceptionMsg(), -1)
diff --git a/compiler/guards.nim b/compiler/guards.nim
index a50593aca..bbb239867 100644
--- a/compiler/guards.nim
+++ b/compiler/guards.nim
@@ -51,6 +51,10 @@ proc isLet(n: PNode): bool =
     elif n.sym.kind == skParam and skipTypes(n.sym.typ,
                                              abstractInst).kind notin {tyVar}:
       result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc isVar(n: PNode): bool =
   n.kind == nkSym and n.sym.kind in {skResult, skVar} and
@@ -68,9 +72,12 @@ proc isLetLocation(m: PNode, isApprox: bool): bool =
     case n.kind
     of nkDotExpr, nkCheckedFieldExpr, nkObjUpConv, nkObjDownConv:
       n = n[0]
-    of nkDerefExpr, nkHiddenDeref:
+    of nkDerefExpr:
       n = n[0]
       inc derefs
+    of nkHiddenDeref:
+      n = n[0]
+      if not isApprox: inc derefs
     of nkBracketExpr:
       if isConstExpr(n[1]) or isLet(n[1]) or isConstExpr(n[1].skipConv):
         n = n[0]
@@ -133,6 +140,8 @@ proc neg(n: PNode; o: Operators): PNode =
       result = a
     elif b != nil:
       result = b
+    else:
+      result = nil
   else:
     # leave  not (a == 4)  as it is
     result = newNodeI(nkCall, n.info, 2)
@@ -200,7 +209,7 @@ proc highBound*(conf: ConfigRef; x: PNode; o: Operators): PNode =
              nkIntLit.newIntNode(lastOrd(conf, typ))
            elif typ.kind == tySequence and x.kind == nkSym and
                x.sym.kind == skConst:
-             nkIntLit.newIntNode(x.sym.ast.len-1)
+             nkIntLit.newIntNode(x.sym.astdef.len-1)
            else:
              o.opAdd.buildCall(o.opLen.buildCall(x), minusOne())
   result.info = x.info
@@ -327,6 +336,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
         result = n
       elif n[1].getMagic in someLen or n[2].getMagic in someLen:
         result = n
+      else:
+        result = nil
   of someLe+someLt:
     if isLetLocation(n[1], true) or isLetLocation(n[2], true):
       # XXX algebraic simplifications!  'i-1 < a.len' --> 'i < a.len+1'
@@ -334,12 +345,18 @@ proc usefulFact(n: PNode; o: Operators): PNode =
     elif n[1].getMagic in someLen or n[2].getMagic in someLen:
       # XXX Rethink this whole idea of 'usefulFact' for semparallel
       result = n
+    else:
+      result = nil
   of mIsNil:
     if isLetLocation(n[1], false) or isVar(n[1]):
       result = n
+    else:
+      result = nil
   of someIn:
     if isLetLocation(n[1], true):
       result = n
+    else:
+      result = nil
   of mAnd:
     let
       a = usefulFact(n[1], o)
@@ -353,10 +370,14 @@ proc usefulFact(n: PNode; o: Operators): PNode =
       result = a
     elif b != nil:
       result = b
+    else:
+      result = nil
   of mNot:
     let a = usefulFact(n[1], o)
     if a != nil:
       result = a.neg(o)
+    else:
+      result = nil
   of mOr:
     # 'or' sucks! (p.isNil or q.isNil) --> hard to do anything
     # with that knowledge...
@@ -373,6 +394,8 @@ proc usefulFact(n: PNode; o: Operators): PNode =
       result[1] = a
       result[2] = b
       result = result.neg(o)
+    else:
+      result = nil
   elif n.kind == nkSym and n.sym.kind == skLet:
     # consider:
     #   let a = 2 < x
@@ -381,8 +404,12 @@ proc usefulFact(n: PNode; o: Operators): PNode =
     # We make can easily replace 'a' by '2 < x' here:
     if n.sym.astdef != nil:
       result = usefulFact(n.sym.astdef, o)
+    else:
+      result = nil
   elif n.kind == nkStmtListExpr:
     result = usefulFact(n.lastSon, o)
+  else:
+    result = nil
 
 type
   TModel* = object
@@ -448,8 +475,9 @@ proc hasSubTree(n, x: PNode): bool =
     of nkEmpty..nkNilLit:
       result = n.sameTree(x)
     of nkFormalParams:
-      discard
+      result = false
     else:
+      result = false
       for i in 0..<n.len:
         if hasSubTree(n[i], x): return true
 
@@ -480,6 +508,8 @@ proc invalidateFacts*(m: var TModel, n: PNode) =
 proc valuesUnequal(a, b: PNode): bool =
   if a.isValue and b.isValue:
     result = not sameValue(a, b)
+  else:
+    result = false
 
 proc impliesEq(fact, eq: PNode): TImplication =
   let (loc, val) = if isLocation(eq[1]): (1, 2) else: (2, 1)
@@ -490,16 +520,26 @@ proc impliesEq(fact, eq: PNode): TImplication =
       # this is not correct; consider:  a == b;  a == 1 --> unknown!
       if sameTree(fact[2], eq[val]): result = impYes
       elif valuesUnequal(fact[2], eq[val]): result = impNo
+      else:
+        result = impUnknown
     elif sameTree(fact[2], eq[loc]):
       if sameTree(fact[1], eq[val]): result = impYes
       elif valuesUnequal(fact[1], eq[val]): result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of mInSet:
     # remember: mInSet is 'contains' so the set comes first!
     if sameTree(fact[2], eq[loc]) and isValue(eq[val]):
       if inSet(fact[1], eq[val]): result = impYes
       else: result = impNo
-  of mNot, mOr, mAnd: assert(false, "impliesEq")
-  else: discard
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesEq")
+  else: result = impUnknown
 
 proc leImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -509,13 +549,19 @@ proc leImpliesIn(x, c, aSet: PNode): TImplication =
     var value = newIntNode(c.kind, firstOrd(nil, x.typ))
     # don't iterate too often:
     if c.intVal - value.intVal < 1000:
-      var i, pos, neg: int
+      var i, pos, neg: int = 0
       while value.intVal <= c.intVal:
         if inSet(aSet, value): inc pos
         else: inc neg
         inc i; inc value.intVal
       if pos == i: result = impYes
       elif neg == i: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
+  else:
+    result = impUnknown
 
 proc geImpliesIn(x, c, aSet: PNode): TImplication =
   if c.kind in {nkCharLit..nkUInt64Lit}:
@@ -526,17 +572,23 @@ proc geImpliesIn(x, c, aSet: PNode): TImplication =
     let max = lastOrd(nil, x.typ)
     # don't iterate too often:
     if max - getInt(value) < toInt128(1000):
-      var i, pos, neg: int
+      var i, pos, neg: int = 0
       while value.intVal <= max:
         if inSet(aSet, value): inc pos
         else: inc neg
         inc i; inc value.intVal
       if pos == i: result = impYes
       elif neg == i: result = impNo
+      else: result = impUnknown
+    else:
+      result = impUnknown
+  else:
+    result = impUnknown
 
 proc compareSets(a, b: PNode): TImplication =
   if equalSets(nil, a, b): result = impYes
   elif intersectSets(nil, a, b).len == 0: result = impNo
+  else: result = impUnknown
 
 proc impliesIn(fact, loc, aSet: PNode): TImplication =
   case fact[0].sym.magic
@@ -547,22 +599,32 @@ proc impliesIn(fact, loc, aSet: PNode): TImplication =
     elif sameTree(fact[2], loc):
       if inSet(aSet, fact[1]): result = impYes
       else: result = impNo
+    else:
+      result = impUnknown
   of mInSet:
     if sameTree(fact[2], loc):
       result = compareSets(fact[1], aSet)
+    else:
+      result = impUnknown
   of someLe:
     if sameTree(fact[1], loc):
       result = leImpliesIn(fact[1], fact[2], aSet)
     elif sameTree(fact[2], loc):
       result = geImpliesIn(fact[2], fact[1], aSet)
+    else:
+      result = impUnknown
   of someLt:
     if sameTree(fact[1], loc):
       result = leImpliesIn(fact[1], fact[2].pred, aSet)
     elif sameTree(fact[2], loc):
       # 4 < x  -->  3 <= x
       result = geImpliesIn(fact[2], fact[1].pred, aSet)
-  of mNot, mOr, mAnd: assert(false, "impliesIn")
-  else: discard
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesIn")
+  else: result = impUnknown
 
 proc valueIsNil(n: PNode): TImplication =
   if n.kind == nkNilLit: impYes
@@ -574,13 +636,19 @@ proc impliesIsNil(fact, eq: PNode): TImplication =
   of mIsNil:
     if sameTree(fact[1], eq[1]):
       result = impYes
+    else:
+      result = impUnknown
   of someEq:
     if sameTree(fact[1], eq[1]):
       result = valueIsNil(fact[2].skipConv)
     elif sameTree(fact[2], eq[1]):
       result = valueIsNil(fact[1].skipConv)
-  of mNot, mOr, mAnd: assert(false, "impliesIsNil")
-  else: discard
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesIsNil")
+  else: result = impUnknown
 
 proc impliesGe(fact, x, c: PNode): TImplication =
   assert isLocation(x)
@@ -591,32 +659,57 @@ proc impliesGe(fact, x, c: PNode): TImplication =
         # fact:  x = 4;  question x >= 56? --> true iff 4 >= 56
         if leValue(c, fact[2]): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
     elif sameTree(fact[2], x):
       if isValue(fact[1]) and isValue(c):
         if leValue(c, fact[1]): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLt:
     if sameTree(fact[1], x):
       if isValue(fact[2]) and isValue(c):
         # fact:  x < 4;  question N <= x? --> false iff N <= 4
         if leValue(fact[2], c): result = impNo
+        else: result = impUnknown
         # fact:  x < 4;  question 2 <= x? --> we don't know
+      else:
+        result = impUnknown
     elif sameTree(fact[2], x):
       # fact: 3 < x; question: N-1 < x ?  --> true iff N-1 <= 3
       if isValue(fact[1]) and isValue(c):
         if leValue(c.pred, fact[1]): result = impYes
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLe:
     if sameTree(fact[1], x):
       if isValue(fact[2]) and isValue(c):
         # fact:  x <= 4;  question x >= 56? --> false iff 4 <= 56
         if leValue(fact[2], c): result = impNo
         # fact:  x <= 4;  question x >= 2? --> we don't know
+        else:
+          result = impUnknown
+      else:
+        result = impUnknown
     elif sameTree(fact[2], x):
       # fact: 3 <= x; question: x >= 2 ?  --> true iff 2 <= 3
       if isValue(fact[1]) and isValue(c):
         if leValue(c, fact[1]): result = impYes
-  of mNot, mOr, mAnd: assert(false, "impliesGe")
-  else: discard
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesGe")
+  else: result = impUnknown
 
 proc impliesLe(fact, x, c: PNode): TImplication =
   if not isLocation(x):
@@ -631,35 +724,59 @@ proc impliesLe(fact, x, c: PNode): TImplication =
         # fact:  x = 4;  question x <= 56? --> true iff 4 <= 56
         if leValue(fact[2], c): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
     elif sameTree(fact[2], x):
       if isValue(fact[1]) and isValue(c):
         if leValue(fact[1], c): result = impYes
         else: result = impNo
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLt:
     if sameTree(fact[1], x):
       if isValue(fact[2]) and isValue(c):
         # fact:  x < 4;  question x <= N? --> true iff N-1 <= 4
         if leValue(fact[2], c.pred): result = impYes
+        else:
+          result = impUnknown
         # fact:  x < 4;  question x <= 2? --> we don't know
+      else:
+        result = impUnknown
     elif sameTree(fact[2], x):
       # fact: 3 < x; question: x <= 1 ?  --> false iff 1 <= 3
       if isValue(fact[1]) and isValue(c):
         if leValue(c, fact[1]): result = impNo
-
+        else: result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
   of someLe:
     if sameTree(fact[1], x):
       if isValue(fact[2]) and isValue(c):
         # fact:  x <= 4;  question x <= 56? --> true iff 4 <= 56
         if leValue(fact[2], c): result = impYes
+        else: result = impUnknown
         # fact:  x <= 4;  question x <= 2? --> we don't know
+      else:
+        result = impUnknown
 
     elif sameTree(fact[2], x):
       # fact: 3 <= x; question: x <= 2 ?  --> false iff 2 < 3
       if isValue(fact[1]) and isValue(c):
         if leValue(c, fact[1].pred): result = impNo
+        else:result = impUnknown
+      else:
+        result = impUnknown
+    else:
+      result = impUnknown
 
-  of mNot, mOr, mAnd: assert(false, "impliesLe")
-  else: discard
+  of mNot, mOr, mAnd:
+    result = impUnknown
+    assert(false, "impliesLe")
+  else: result = impUnknown
 
 proc impliesLt(fact, x, c: PNode): TImplication =
   # x < 3  same as x <= 2:
@@ -671,6 +788,8 @@ proc impliesLt(fact, x, c: PNode): TImplication =
     let q = x.pred
     if q != x:
       result = impliesLe(fact, q, c)
+    else:
+      result = impUnknown
 
 proc `~`(x: TImplication): TImplication =
   case x
@@ -722,6 +841,7 @@ proc factImplies(fact, prop: PNode): TImplication =
 
 proc doesImply*(facts: TModel, prop: PNode): TImplication =
   assert prop.kind in nkCallKinds
+  result = impUnknown
   for f in facts.s:
     # facts can be invalidated, in which case they are 'nil':
     if not f.isNil:
@@ -750,7 +870,7 @@ template isSub(x): untyped = x.getMagic in someSub
 template isVal(x): untyped = x.kind in {nkCharLit..nkUInt64Lit}
 template isIntVal(x, y): untyped = x.intVal == y
 
-import macros
+import std/macros
 
 macro `=~`(x: PNode, pat: untyped): bool =
   proc m(x, pat, conds: NimNode) =
@@ -897,6 +1017,7 @@ 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
+  result = impUnknown
   for i in 0..m.s.high:
     let fact = m.s[i]
     if fact != nil and fact.getMagic in someLe:
@@ -942,7 +1063,7 @@ proc pleViaModel(model: TModel; aa, bb: PNode): TImplication =
       let b = fact[2]
       if a.kind == nkSym: replacements.add((a,b))
       else: replacements.add((b,a))
-  var m: TModel
+  var m = TModel()
   var a = aa
   var b = bb
   if replacements.len > 0:
@@ -977,8 +1098,8 @@ proc addFactLt*(m: var TModel; a, b: PNode) =
   addFactLe(m, a, bb)
 
 proc settype(n: PNode): PType =
-  result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner)
-  var idgen: IdGenerator
+  var idgen = idGeneratorForPackage(-1'i32)
+  result = newType(tySet, idgen, n.typ.owner)
   addSonSkipIntLit(result, n.typ, idgen)
 
 proc buildOf(it, loc: PNode; o: Operators): PNode =
@@ -1054,8 +1175,12 @@ proc buildProperFieldCheck(access, check: PNode; o: Operators): PNode =
     assert check.getMagic == mNot
     result = buildProperFieldCheck(access, check[1], o).neg(o)
 
-proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef) =
+proc checkFieldAccess*(m: TModel, n: PNode; conf: ConfigRef; produceError: bool) =
   for i in 1..<n.len:
     let check = buildProperFieldCheck(n[0], n[i], m.g.operators)
     if check != nil and m.doesImply(check) != impYes:
-      message(conf, n.info, warnProveField, renderTree(n[0])); break
+      if produceError:
+        localError(conf, n.info, "field access outside of valid case branch: " & renderTree(n[0]))
+      else:
+        message(conf, n.info, warnProveField, renderTree(n[0]))
+      break
diff --git a/compiler/hlo.nim b/compiler/hlo.nim
index e3670312e..9fdec38c0 100644
--- a/compiler/hlo.nim
+++ b/compiler/hlo.nim
@@ -10,9 +10,6 @@
 # This include implements the high level optimization pass.
 # included from sem.nim
 
-when defined(nimPreviewSlimSystem):
-  import std/assertions
-
 proc hlo(c: PContext, n: PNode): PNode
 
 proc evalPattern(c: PContext, n, orig: PNode): PNode =
@@ -20,9 +17,11 @@ proc evalPattern(c: PContext, n, orig: PNode): PNode =
   # we need to ensure that the resulting AST is semchecked. However, it's
   # awful to semcheck before macro invocation, so we don't and treat
   # templates and macros as immediate in this context.
-  var rule: string
-  if c.config.hasHint(hintPattern):
-    rule = renderTree(n, {renderNoComments})
+  var rule: string =
+    if c.config.hasHint(hintPattern):
+      renderTree(n, {renderNoComments})
+    else:
+      ""
   let s = n[0].sym
   case s.kind
   of skMacro:
@@ -71,9 +70,9 @@ proc hlo(c: PContext, n: PNode): PNode =
     # already processed (special cases in semstmts.nim)
     result = n
   else:
-    if n.kind in {nkFastAsgn, nkAsgn, nkIdentDefs, nkVarTuple} and
+    if n.kind in {nkFastAsgn, nkAsgn, nkSinkAsgn, nkIdentDefs, nkVarTuple} and
         n[0].kind == nkSym and
-        {sfGlobal, sfPure} * n[0].sym.flags == {sfGlobal, sfPure}:
+        {sfGlobal, sfPure} <= n[0].sym.flags:
       # do not optimize 'var g {.global} = re(...)' again!
       return n
     result = applyPatterns(c, n)
diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim
index 8adab8388..0c9994c83 100644
--- a/compiler/ic/bitabs.nim
+++ b/compiler/ic/bitabs.nim
@@ -1,7 +1,8 @@
 ## A BiTable is a table that can be seen as an optimized pair
-## of (Table[LitId, Val], Table[Val, LitId]).
+## of `(Table[LitId, Val], Table[Val, LitId])`.
 
-import hashes, rodfiles
+import std/hashes
+import rodfiles
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -13,6 +14,8 @@ type
     vals: seq[T] # indexed by LitId
     keys: seq[LitId]  # indexed by hash(val)
 
+proc initBiTable*[T](): BiTable[T] = BiTable[T](vals: @[], keys: @[])
+
 proc nextTry(h, maxHash: Hash): Hash {.inline.} =
   result = (h + 1) and maxHash
 
@@ -33,9 +36,7 @@ proc mustRehash(length, counter: int): bool {.inline.} =
   result = (length * 2 < counter * 3) or (length - counter < 4)
 
 const
-  idStart = 256 ##
-  ## Ids do not start with 0 but with this value. The IR needs it.
-  ## TODO: explain why
+  idStart = 1
 
 template idToIdx(x: LitId): int = x.int - idStart
 
@@ -118,6 +119,12 @@ proc load*[T](f: var RodFile; t: var BiTable[T]) =
   loadSeq(f, t.vals)
   loadSeq(f, t.keys)
 
+proc sizeOnDisc*(t: BiTable[string]): int =
+  result = 4
+  for x in t.vals:
+    result += x.len + 4
+  result += t.keys.len * sizeof(LitId)
+
 when isMainModule:
 
   var t: BiTable[string]
diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim
index 815078a36..83f1b4cc7 100644
--- a/compiler/ic/cbackend.nim
+++ b/compiler/ic/cbackend.nim
@@ -18,7 +18,7 @@
 ## also doing cross-module dependency tracking and DCE that we don't need
 ## anymore. DCE is now done as prepass over the entire packed module graph.
 
-import std/packedsets, algorithm, tables
+import std/[packedsets, algorithm, tables]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -51,6 +51,8 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive
     cgen.genTopLevelStmt(bmod, n)
 
   finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info))
+  for disp in getDispatchers(g):
+    genProcAux(bmod, disp)
   m.fromDisk.backendFlags = cgen.whichInitProcs(bmod)
 
 proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) =
@@ -98,7 +100,7 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool
   var f2 = rodfiles.open(asymFile.string)
   f2.loadHeader()
   f2.loadSection aliveSymsSection
-  var oldData: seq[int32]
+  var oldData: seq[int32] = @[]
   f2.loadSeq(oldData)
   f2.close
   if f2.err == ok and oldData == s:
@@ -144,12 +146,12 @@ proc generateCode*(g: ModuleGraph) =
   var alive = computeAliveSyms(g.packed, g.config)
 
   when false:
-    for i in 0..high(g.packed):
+    for i in 0..<len(g.packed):
       echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i))
 
   # First pass: Setup all the backend modules for all the modules that have
   # changed:
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     # case statement here to enforce exhaustive checks.
     case g.packed[i].status
     of undefined:
@@ -171,7 +173,7 @@ proc generateCode*(g: ModuleGraph) =
   let mainModuleIdx = g.config.projectMainIdx2.int
   # We need to generate the main module last, because only then
   # all init procs have been registered:
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     if i != mainModuleIdx:
       genPackedModule(g, i, alive)
   if mainModuleIdx >= 0:
diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim
index bc61a38de..6eb36431e 100644
--- a/compiler/ic/dce.nim
+++ b/compiler/ic/dce.nim
@@ -40,10 +40,14 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo
     if ({sfExportc, sfCompilerProc} * flags != {}) or
         (symPtr.kind == skMethod):
       result = true
+    else:
+      result = false
       # XXX: This used to be a condition to:
       #  (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or
     if sfCompilerProc in flags:
       c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId)
+  else:
+    result = false
 
 template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty
 
@@ -105,13 +109,13 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
     discard "ignore non-sym atoms"
   of nkSym:
     # This symbol is alive and everything its body references.
-    followLater(c, g, c.thisModule, n.operand)
+    followLater(c, g, c.thisModule, tree[n].soperand)
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
     let m = n1.litId
-    let item = n2.operand
+    let item = tree[n2].soperand
     let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int
     followLater(c, g, otherModule, item)
   of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr,
@@ -127,7 +131,7 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N
     rangeCheckAnalysis(c, g, tree, n)
   of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef:
     if n.firstSon.kind == nkSym and isNotGeneric(n):
-      let item = n.firstSon.operand
+      let item = tree[n.firstSon].soperand
       if isExportedToC(c, g, item):
         # This symbol is alive and everything its body references.
         followLater(c, g, c.thisModule, item)
@@ -149,7 +153,7 @@ proc computeAliveSyms*(g: PackedModuleGraph; conf: ConfigRef): AliveSyms =
   var c = AliveContext(stack: @[], decoder: PackedDecoder(config: conf),
                        thisModule: -1, alive: newSeq[IntSet](g.len),
                        options: conf.options)
-  for i in countdown(high(g), 0):
+  for i in countdown(len(g)-1, 0):
     if g[i].status != undefined:
       c.thisModule = i
       for p in allNodes(g[i].fromDisk.topLevel):
diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim
index 866237f08..8e81633ef 100644
--- a/compiler/ic/ic.nim
+++ b/compiler/ic/ic.nim
@@ -7,12 +7,16 @@
 #    distribution, for details about the copyright.
 #
 
-import hashes, tables, intsets, std/sha1
+import std/[hashes, tables, intsets, monotimes]
 import packed_ast, bitabs, rodfiles
 import ".." / [ast, idents, lineinfos, msgs, ropes, options,
   pathutils, condsyms, packages, modulepaths]
 #import ".." / [renderer, astalgo]
-from os import removeFile, isAbsolute
+from std/os import removeFile, isAbsolute
+
+import ../../dist/checksums/src/checksums/sha1
+
+import iclineinfos
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions, formatfloat]
@@ -39,26 +43,28 @@ type
     bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position.
     #producedGenerics*: Table[GenericKey, SymId]
     exports*: seq[(LitId, int32)]
-    hidden*: seq[(LitId, int32)]
-    reexports*: seq[(LitId, PackedItemId)]
+    hidden: seq[(LitId, int32)]
+    reexports: seq[(LitId, PackedItemId)]
     compilerProcs*: seq[(LitId, int32)]
     converters*, methods*, trmacros*, pureEnums*: seq[int32]
-    macroUsages*: seq[(PackedItemId, PackedLineInfo)]
 
     typeInstCache*: seq[(PackedItemId, PackedItemId)]
     procInstCache*: seq[PackedInstantiation]
-    attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
-    methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
+    attachedOps*: seq[(PackedItemId, TTypeAttachedOp, PackedItemId)]
+    methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
     enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
+    methodsPerType*: seq[(PackedItemId, PackedItemId)]
+    dispatchers*: seq[PackedItemId]
 
     emittedTypeInfo*: seq[string]
     backendFlags*: set[ModuleBackendFlag]
 
-    syms*: seq[PackedSym]
-    types*: seq[PackedType]
+    syms*: OrderedTable[int32, PackedSym]
+    types*: OrderedTable[int32, PackedType]
     strings*: BiTable[string] # we could share these between modules.
     numbers*: BiTable[BiggestInt] # we also store floats in here so
                                   # that we can assure that every bit is kept
+    man*: LineInfoManager
 
     cfg: PackedConfig
 
@@ -74,37 +80,36 @@ type
     symMarker*: IntSet #Table[ItemId, SymId]    # ItemId.item -> SymId
     config*: ConfigRef
 
-proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int;
+proc toString*(tree: PackedTree; pos: NodePos; m: PackedModule; nesting: int;
                result: var string) =
-  let pos = n.int
   if result.len > 0 and result[^1] notin {' ', '\n'}:
     result.add ' '
 
   result.add $tree[pos].kind
-  case tree.nodes[pos].kind
-  of nkNone, nkEmpty, nkNilLit, nkType: discard
+  case tree[pos].kind
+  of nkEmpty, nkNilLit, nkType: discard
   of nkIdent, nkStrLit..nkTripleStrLit:
     result.add " "
-    result.add m.strings[LitId tree.nodes[pos].operand]
+    result.add m.strings[LitId tree[pos].uoperand]
   of nkSym:
     result.add " "
-    result.add m.strings[m.syms[tree.nodes[pos].operand].name]
+    result.add m.strings[m.syms[tree[pos].soperand].name]
   of directIntLit:
     result.add " "
-    result.addInt tree.nodes[pos].operand
+    result.addInt tree[pos].soperand
   of externSIntLit:
     result.add " "
-    result.addInt m.numbers[LitId tree.nodes[pos].operand]
+    result.addInt m.numbers[LitId tree[pos].uoperand]
   of externUIntLit:
     result.add " "
-    result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand])
+    result.addInt cast[uint64](m.numbers[LitId tree[pos].uoperand])
   of nkFloatLit..nkFloat128Lit:
     result.add " "
-    result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand])
+    result.addFloat cast[BiggestFloat](m.numbers[LitId tree[pos].uoperand])
   else:
     result.add "(\n"
     for i in 1..(nesting+1)*2: result.add ' '
-    for child in sonsReadonly(tree, n):
+    for child in sonsReadonly(tree, pos):
       toString(tree, child, m, nesting + 1, result)
     result.add "\n"
     for i in 1..nesting*2: result.add ' '
@@ -230,10 +235,14 @@ proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex)
   m.imports.add toLitId(f, c, m)
 
 proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
   let nameId = getOrIncl(m.strings, s.name.s)
   m.hidden.add((nameId, s.itemId.item))
+  assert s.itemId.module == c.thisModule
 
 proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
+  assert s.itemId.module == c.thisModule
   let nameId = getOrIncl(m.strings, s.name.s)
   m.exports.add((nameId, s.itemId.item))
 
@@ -252,6 +261,8 @@ proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
   m.methods.add s.itemId.item
 
 proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) =
+  assert s.kind != skUnknown
+  if s.kind == skModule: return
   let nameId = getOrIncl(m.strings, s.name.s)
   m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m),
                                         item: s.itemId.item)))
@@ -283,7 +294,8 @@ proc toLitId(x: BiggestInt; m: var PackedModule): LitId =
   result = getOrIncl(m.numbers, x)
 
 proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo =
-  PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
+  pack(m.man, toLitId(x.fileIndex, c, m), x.line.int32, x.col.int32)
+  #PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m))
 
 proc safeItemId(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId {.inline.} =
   ## given a symbol, produce an ItemId with the correct properties
@@ -335,7 +347,7 @@ proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): Packed
 proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
   if s.isNil: return nilItemId
   assert s.itemId.module >= 0
-  assert s.itemId.module >= 0
+  assert s.itemId.item >= 0
   result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
   if s.itemId.module == c.thisModule:
     # the sym belongs to this module, so serialize it here, eventually.
@@ -350,15 +362,15 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
   result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item)
 
   if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item):
-    if t.uniqueId.item >= m.types.len:
-      setLen m.types, t.uniqueId.item+1
+    #if t.uniqueId.item >= m.types.len:
+    #  setLen m.types, t.uniqueId.item+1
 
-    var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv,
+    var p = PackedType(id: t.uniqueId.item, kind: t.kind, flags: t.flags, callConv: t.callConv,
       size: t.size, align: t.align, nonUniqueId: t.itemId.item,
-      paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel)
+      paddingAtEnd: t.paddingAtEnd)
     storeNode(p, t, n)
     p.typeInst = t.typeInst.storeType(c, m)
-    for kid in items t.sons:
+    for kid in kids t:
       p.types.add kid.storeType(c, m)
     c.addMissing t.sym
     p.sym = t.sym.safeItemId(c, m)
@@ -371,10 +383,9 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI
 proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib =
   ## the plib hangs off the psym via the .annex field
   if l.isNil: return
-  result.kind = l.kind
-  result.generated = l.generated
-  result.isOverriden = l.isOverriden
-  result.name = toLitId($l.name, m)
+  result = PackedLib(kind: l.kind, generated: l.generated,
+    isOverridden: l.isOverridden, name: toLitId($l.name, m)
+  )
   storeNode(result, l, path)
 
 proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId =
@@ -385,13 +396,13 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
   result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item)
 
   if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item):
-    if s.itemId.item >= m.syms.len:
-      setLen m.syms, s.itemId.item+1
+    #if s.itemId.item >= m.syms.len:
+    #  setLen m.syms, s.itemId.item+1
 
     assert sfForward notin s.flags
 
-    var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
-      position: s.position, offset: s.offset, options: s.options,
+    var p = PackedSym(id: s.itemId.item, kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c, m), magic: s.magic,
+      position: s.position, offset: s.offset, disamb: s.disamb, options: s.options,
       name: s.name.s.toLitId(m))
 
     storeNode(p, s, ast)
@@ -403,7 +414,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
       p.bitsize = s.bitsize
       p.alignment = s.alignment
 
-    p.externalName = toLitId(s.loc.r, m)
+    p.externalName = toLitId(s.loc.snippet, m)
     p.locFlags = s.loc.flags
     c.addMissing s.typ
     p.typ = s.typ.storeType(c, m)
@@ -412,6 +423,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
     p.annex = toPackedLib(s.annex, c, m)
     when hasFFI:
       p.cname = toLitId(s.cname, m)
+    p.instantiatedFrom = s.instantiatedFrom.safeItemId(c, m)
 
     # fill the reserved slot, nothing else:
     m.syms[s.itemId.item] = p
@@ -419,53 +431,59 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId
 proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
   ## add a remote symbol reference to the tree
   let info = n.info.toPackedInfo(c, m)
-  ir.nodes.add PackedNode(kind: nkModuleRef, operand: 3.int32, # spans 3 nodes in total
-                          typeId: storeTypeLater(n.typ, c, m), info: info)
-  ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
-                          operand: toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
-  ir.nodes.add PackedNode(kind: nkInt32Lit, info: info,
-                          operand: n.sym.itemId.item)
+  if n.typ != n.sym.typ:
+    ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
+               info = info, flags = n.flags,
+               typeId = storeTypeLater(n.typ, c, m))
+  else:
+    ir.addNode(kind = nkModuleRef, operand = 3.int32, # spans 3 nodes in total
+              info = info, flags = n.flags)
+  ir.addNode(kind = nkNone, info = info,
+             operand = toLitId(n.sym.itemId.module.FileIndex, c, m).int32)
+  ir.addNode(kind = nkNone, info = info,
+             operand = n.sym.itemId.item)
 
 proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) =
   ## serialize a node into the tree
   if n == nil:
-    ir.nodes.add PackedNode(kind: nkNilRodNode, flags: {}, operand: 1)
+    ir.addNode(kind = nkNilRodNode, operand = 1, info = NoLineInfo)
     return
   let info = toPackedInfo(n.info, c, m)
   case n.kind
   of nkNone, nkEmpty, nkNilLit, nkType:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0,
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags, operand = 0,
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkIdent:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.strings, n.ident.s),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+                operand = int32 getOrIncl(m.strings, n.ident.s),
+                typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkSym:
     if n.sym.itemId.module == c.thisModule:
       # it is a symbol that belongs to the module we're currently
       # packing:
       let id = n.sym.storeSymLater(c, m).item
-      ir.nodes.add PackedNode(kind: nkSym, flags: n.flags, operand: id,
-                              typeId: storeTypeLater(n.typ, c, m), info: info)
+      if n.typ != n.sym.typ:
+        ir.addNode(kind = nkSym, flags = n.flags, operand = id,
+                   info = info,
+                   typeId = storeTypeLater(n.typ, c, m))
+      else:
+        ir.addNode(kind = nkSym, flags = n.flags, operand = id,
+                   info = info)
     else:
       # store it as an external module reference:
       addModuleRef(n, ir, c, m)
-  of directIntLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32(n.intVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
   of externIntLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.numbers, n.intVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, n.intVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkStrLit..nkTripleStrLit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.strings, n.strVal),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.strings, n.strVal),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   of nkFloatLit..nkFloat128Lit:
-    ir.nodes.add PackedNode(kind: n.kind, flags: n.flags,
-                            operand: int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
-                            typeId: storeTypeLater(n.typ, c, m), info: info)
+    ir.addNode(kind = n.kind, flags = n.flags,
+               operand = int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)),
+               typeId = storeTypeLater(n.typ, c, m), info = info)
   else:
     let patchPos = ir.prepare(n.kind, n.flags,
                               storeTypeLater(n.typ, c, m), info)
@@ -490,8 +508,8 @@ proc toPackedProcDef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var
       # do not serialize the body of the proc, it's unnecessary since
       # n[0].sym.ast has the sem'checked variant of it which is what
       # everybody should use instead.
-      ir.nodes.add PackedNode(kind: nkEmpty, flags: {}, operand: 0,
-                              typeId: nilItemId, info: info)
+      ir.addNode(kind = nkEmpty, flags = {}, operand = 0,
+                 typeId = nilItemId, info = info)
   ir.patch patchPos
 
 proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var PackedModule) =
@@ -529,6 +547,15 @@ proc toPackedGeneratedProcDef*(s: PSym, encoder: var PackedEncoder; m: var Packe
   toPackedProcDef(s.ast, m.topLevel, encoder, m)
   #flush encoder, m
 
+proc storeAttachedProcDef*(t: PType; op: TTypeAttachedOp; s: PSym,
+                           encoder: var PackedEncoder; m: var PackedModule) =
+  assert s.kind in routineKinds
+  assert isActive(encoder)
+  let tid = storeTypeLater(t, encoder, m)
+  let sid = storeSymLater(s, encoder, m)
+  m.attachedOps.add (tid, op, sid)
+  toPackedGeneratedProcDef(s, encoder, m)
+
 proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: PInstantiation) =
   var t = newSeq[PackedItemId](i.concreteTypes.len)
   for j in 0..high(i.concreteTypes):
@@ -555,6 +582,20 @@ proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile =
   result = changeFileExt(completeGeneratedFilePath(conf,
     mangleModuleName(conf, f).AbsoluteFile), ext)
 
+const
+  BenchIC* = false
+
+when BenchIC:
+  var gloadBodies: MonoTime
+
+  template bench(x, body) =
+    let start = getMonoTime()
+    body
+    x = x + (getMonoTime() - start)
+
+else:
+  template bench(x, body) = body
+
 proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef;
                   ignoreConfig = false): RodFileError =
   var f = rodfiles.open(filename.string)
@@ -572,6 +613,10 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
     f.loadSection section
     f.loadSeq data
 
+  template loadTableSection(section, data) {.dirty.} =
+    f.loadSection section
+    f.loadOrderedTable data
+
   template loadTabSection(section, data) {.dirty.} =
     f.loadSection section
     f.load data
@@ -579,41 +624,48 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef
   loadTabSection stringsSection, m.strings
 
   loadSeqSection checkSumsSection, m.includes
-  if not includesIdentical(m, config):
+  if config.cmd != cmdM and not includesIdentical(m, config):
     f.err = includeFileChanged
 
   loadSeqSection depsSection, m.imports
 
-  loadTabSection numbersSection, m.numbers
+  bench gloadBodies:
+
+    loadTabSection numbersSection, m.numbers
+
+    loadSeqSection exportsSection, m.exports
+    loadSeqSection hiddenSection, m.hidden
+    loadSeqSection reexportsSection, m.reexports
+
+    loadSeqSection compilerProcsSection, m.compilerProcs
 
-  loadSeqSection exportsSection, m.exports
-  loadSeqSection hiddenSection, m.hidden
-  loadSeqSection reexportsSection, m.reexports
+    loadSeqSection trmacrosSection, m.trmacros
 
-  loadSeqSection compilerProcsSection, m.compilerProcs
+    loadSeqSection convertersSection, m.converters
+    loadSeqSection methodsSection, m.methods
+    loadSeqSection pureEnumsSection, m.pureEnums
 
-  loadSeqSection trmacrosSection, m.trmacros
+    loadTabSection toReplaySection, m.toReplay
+    loadTabSection topLevelSection, m.topLevel
 
-  loadSeqSection convertersSection, m.converters
-  loadSeqSection methodsSection, m.methods
-  loadSeqSection pureEnumsSection, m.pureEnums
-  loadSeqSection macroUsagesSection, m.macroUsages
+    loadTabSection bodiesSection, m.bodies
+    loadTableSection symsSection, m.syms
+    loadTableSection typesSection, m.types
 
-  loadSeqSection toReplaySection, m.toReplay.nodes
-  loadSeqSection topLevelSection, m.topLevel.nodes
-  loadSeqSection bodiesSection, m.bodies.nodes
-  loadSeqSection symsSection, m.syms
-  loadSeqSection typesSection, m.types
+    loadSeqSection typeInstCacheSection, m.typeInstCache
+    loadSeqSection procInstCacheSection, m.procInstCache
+    loadSeqSection attachedOpsSection, m.attachedOps
+    loadSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
+    loadSeqSection enumToStringProcsSection, m.enumToStringProcs
+    loadSeqSection methodsPerTypeSection, m.methodsPerType
+    loadSeqSection dispatchersSection, m.dispatchers
+    loadSeqSection typeInfoSection, m.emittedTypeInfo
 
-  loadSeqSection typeInstCacheSection, m.typeInstCache
-  loadSeqSection procInstCacheSection, m.procInstCache
-  loadSeqSection attachedOpsSection, m.attachedOps
-  loadSeqSection methodsPerTypeSection, m.methodsPerType
-  loadSeqSection enumToStringProcsSection, m.enumToStringProcs
-  loadSeqSection typeInfoSection, m.emittedTypeInfo
+    f.loadSection backendFlagsSection
+    f.loadPrim m.backendFlags
 
-  f.loadSection backendFlagsSection
-  f.loadPrim m.backendFlags
+    f.loadSection sideChannelSection
+  f.load m.man
 
   close(f)
   result = f.err
@@ -643,6 +695,10 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
     f.storeSection section
     f.store data
 
+  template storeTableSection(section, data) {.dirty.} =
+    f.storeSection section
+    f.storeOrderedTable data
+
   storeTabSection stringsSection, m.strings
 
   storeSeqSection checkSumsSection, m.includes
@@ -661,26 +717,30 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac
   storeSeqSection convertersSection, m.converters
   storeSeqSection methodsSection, m.methods
   storeSeqSection pureEnumsSection, m.pureEnums
-  storeSeqSection macroUsagesSection, m.macroUsages
 
-  storeSeqSection toReplaySection, m.toReplay.nodes
-  storeSeqSection topLevelSection, m.topLevel.nodes
+  storeTabSection toReplaySection, m.toReplay
+  storeTabSection topLevelSection, m.topLevel
 
-  storeSeqSection bodiesSection, m.bodies.nodes
-  storeSeqSection symsSection, m.syms
+  storeTabSection bodiesSection, m.bodies
+  storeTableSection symsSection, m.syms
 
-  storeSeqSection typesSection, m.types
+  storeTableSection typesSection, m.types
 
   storeSeqSection typeInstCacheSection, m.typeInstCache
   storeSeqSection procInstCacheSection, m.procInstCache
   storeSeqSection attachedOpsSection, m.attachedOps
-  storeSeqSection methodsPerTypeSection, m.methodsPerType
+  storeSeqSection methodsPerGenericTypeSection, m.methodsPerGenericType
   storeSeqSection enumToStringProcsSection, m.enumToStringProcs
+  storeSeqSection methodsPerTypeSection, m.methodsPerType
+  storeSeqSection dispatchersSection, m.dispatchers
   storeSeqSection typeInfoSection, m.emittedTypeInfo
 
   f.storeSection backendFlagsSection
   f.storePrim m.backendFlags
 
+  f.storeSection sideChannelSection
+  f.store m.man
+
   close(f)
   encoder.disable()
   if f.err != ok:
@@ -715,20 +775,36 @@ type
     status*: ModuleStatus
     symsInit, typesInit, loadedButAliveSetChanged*: bool
     fromDisk*: PackedModule
-    syms: seq[PSym] # indexed by itemId
-    types: seq[PType]
+    syms: OrderedTable[int32, PSym] # indexed by itemId
+    types: OrderedTable[int32, PType]
     module*: PSym # the one true module symbol.
     iface, ifaceHidden: Table[PIdent, seq[PackedItemId]]
       # PackedItemId so that it works with reexported symbols too
       # ifaceHidden includes private symbols
 
-  PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex
+type
+  PackedModuleGraph* = object
+    pm*: seq[LoadedModule] # indexed by FileIndex
+    when BenchIC:
+      depAnalysis: MonoTime
+      loadBody: MonoTime
+      loadSym, loadType, loadBodies: MonoTime
+
+when BenchIC:
+  proc echoTimes*(m: PackedModuleGraph) =
+    echo "analysis: ", m.depAnalysis, " loadBody: ", m.loadBody, " loadSym: ",
+      m.loadSym, " loadType: ", m.loadType, " all bodies: ", gloadBodies
+
+template `[]`*(m: PackedModuleGraph; i: int): LoadedModule = m.pm[i]
+template len*(m: PackedModuleGraph): int = m.pm.len
 
 proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t: PackedItemId): PType
 proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym
 
 proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: int; f: LitId): FileIndex =
-  if c.lastLit == f and c.lastModule == thisModule:
+  if f == LitId(0):
+    result = InvalidFileIdx
+  elif c.lastLit == f and c.lastModule == thisModule:
     result = c.lastFile
   else:
     result = toFileIndex(f, g[thisModule].fromDisk, c.config)
@@ -739,8 +815,9 @@ proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule:
 proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
                        x: PackedLineInfo): TLineInfo =
   assert g[thisModule].status in {loaded, storing, stored}
-  result = TLineInfo(line: x.line, col: x.col,
-            fileIndex: toFileIndexCached(c, g, thisModule, x.file))
+  let (fileId, line, col) = unpack(g[thisModule].fromDisk.man, x)
+  result = TLineInfo(line: line.uint16, col: col.int16,
+            fileIndex: toFileIndexCached(c, g, thisModule, fileId))
 
 proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
                 tree: PackedTree; n: NodePos): PNode =
@@ -754,14 +831,14 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
   result.flags = n.flags
 
   case k
-  of nkEmpty, nkNilLit, nkType:
+  of nkNone, nkEmpty, nkNilLit, nkType:
     discard
   of nkIdent:
     result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId])
   of nkSym:
-    result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand))
-  of directIntLit:
-    result.intVal = tree.nodes[n.int].operand
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree[n].soperand))
+    if result.typ == nil:
+      result.typ = result.sym.typ
   of externIntLit:
     result.intVal = g[thisModule].fromDisk.numbers[n.litId]
   of nkStrLit..nkTripleStrLit:
@@ -770,10 +847,12 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
     result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.numbers[n.litId])
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
     transitionNoneToSym(result)
-    result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
+    result.sym = loadSym(c, g, thisModule, PackedItemId(module: n1.litId, item: tree[n2].soperand))
+    if result.typ == nil:
+      result.typ = result.sym.typ
   else:
     for n0 in sonsReadonly(tree, n):
       result.addAllowNil loadNodes(c, g, thisModule, tree, n0)
@@ -805,6 +884,7 @@ proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule:
 
 proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int;
                   tree: PackedTree; n: NodePos): PNode =
+  result = nil
   var i = 0
   for n0 in sonsReadonly(tree, n):
     if i == bodyPos:
@@ -824,6 +904,7 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
     options: s.options,
     position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position,
     offset: if s.kind in routineKinds: defaultOffset else: s.offset,
+    disamb: s.disamb,
     name: getIdent(c.cache, g[si].fromDisk.strings[s.name])
   )
 
@@ -841,7 +922,7 @@ proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph;
   if l.name.int == 0:
     result = nil
   else:
-    result = PLib(generated: l.generated, isOverriden: l.isOverriden,
+    result = PLib(generated: l.generated, isOverridden: l.isOverridden,
                   kind: l.kind, name: rope g[si].fromDisk.strings[l.name])
     loadAstBody(l, path)
 
@@ -864,20 +945,35 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
   result.owner = loadSym(c, g, si, s.owner)
   let externalName = g[si].fromDisk.strings[s.externalName]
   if externalName != "":
-    result.loc.r = rope externalName
+    result.loc.snippet = externalName
   result.loc.flags = s.locFlags
+  result.instantiatedFrom = loadSym(c, g, si, s.instantiatedFrom)
+
+proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                    fileIdx: FileIndex; cachedModules: var seq[FileIndex]): bool
+proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
+                       fileIdx: FileIndex; m: var LoadedModule)
 
 proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: PackedItemId): PSym =
   if s == nilItemId:
     result = nil
   else:
     let si = moduleIndex(c, g, thisModule, s)
+    if si >= g.len:
+      g.pm.setLen(si+1)
+
+    if g[si].status == undefined and c.config.cmd == cmdM:
+      var cachedModules: seq[FileIndex] = @[]
+      discard needsRecompile(g, c.config, c.cache, FileIndex(si), cachedModules)
+      for m in cachedModules:
+        loadToReplayNodes(g, c.config, c.cache, m, g[int m])
+
     assert g[si].status in {loaded, storing, stored}
-    if not g[si].symsInit:
-      g[si].symsInit = true
-      setLen g[si].syms, g[si].fromDisk.syms.len
+    #if not g[si].symsInit:
+    #  g[si].symsInit = true
+    #  setLen g[si].syms, g[si].fromDisk.syms.len
 
-    if g[si].syms[s.item] == nil:
+    if g[si].syms.getOrDefault(s.item) == nil:
       if g[si].fromDisk.syms[s.item].kind != skModule:
         result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item)
         # store it here early on, so that recursions work properly:
@@ -886,6 +982,7 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s:
       else:
         result = g[si].module
         assert result != nil
+        g[si].syms[s.item] = result
 
     else:
       result = g[si].syms[s.item]
@@ -894,7 +991,7 @@ proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
                           t: PackedType; si, item: int32): PType =
   result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind,
                 flags: t.flags, size: t.size, align: t.align,
-                paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel,
+                paddingAtEnd: t.paddingAtEnd,
                 uniqueId: ItemId(module: si, item: item),
                 callConv: t.callConv)
 
@@ -906,8 +1003,10 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph;
     for op, item in pairs t.attachedOps:
       result.attachedOps[op] = loadSym(c, g, si, item)
   result.typeInst = loadType(c, g, si, t.typeInst)
+  var sons = newSeq[PType]()
   for son in items t.types:
-    result.sons.add loadType(c, g, si, son)
+    sons.add loadType(c, g, si, son)
+  result.setSons(sons)
   loadAstBody(t, n)
   when false:
     for gen, id in items t.methods:
@@ -921,18 +1020,20 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t
     assert g[si].status in {loaded, storing, stored}
     assert t.item > 0
 
-    if not g[si].typesInit:
-      g[si].typesInit = true
-      setLen g[si].types, g[si].fromDisk.types.len
+    #if not g[si].typesInit:
+    #  g[si].typesInit = true
+    #  setLen g[si].types, g[si].fromDisk.types.len
 
-    if g[si].types[t.item] == nil:
+    if g[si].types.getOrDefault(t.item) == nil:
       result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item)
       # store it here early on, so that recursions work properly:
       g[si].types[t.item] = result
       typeBodyFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item, result)
+      #assert result.itemId.item == t.item, $(result.itemId.item, t.item)
+      assert result.itemId.item > 0, $(result.itemId.item, t.item)
     else:
       result = g[si].types[t.item]
-    assert result.itemId.item > 0
+      assert result.itemId.item > 0, "2"
 
 proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
                        fileIdx: FileIndex; m: var LoadedModule) =
@@ -982,30 +1083,37 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
   # Does the file belong to the fileIdx need to be recompiled?
   let m = int(fileIdx)
   if m >= g.len:
-    g.setLen(m+1)
+    g.pm.setLen(m+1)
 
   case g[m].status
   of undefined:
     g[m].status = loading
     let fullpath = msgs.toFullPath(conf, fileIdx)
     let rod = toRodFile(conf, AbsoluteFile fullpath)
-    let err = loadRodFile(rod, g[m].fromDisk, conf)
+    let err = loadRodFile(rod, g[m].fromDisk, conf, ignoreConfig = conf.cmd == cmdM)
     if err == ok:
-      result = optForceFullMake in conf.globalOptions
-      # check its dependencies:
-      for dep in g[m].fromDisk.imports:
-        let fid = toFileIndex(dep, g[m].fromDisk, conf)
-        # Warning: we need to traverse the full graph, so
-        # do **not use break here**!
-        if needsRecompile(g, conf, cache, fid, cachedModules):
-          result = true
-
-      if not result:
+      if conf.cmd == cmdM:
         setupLookupTables(g, conf, cache, fileIdx, g[m])
         cachedModules.add fileIdx
         g[m].status = loaded
+        result = false
       else:
-        g[m] = LoadedModule(status: outdated, module: g[m].module)
+        result = optForceFullMake in conf.globalOptions
+        # check its dependencies:
+        let imp = g[m].fromDisk.imports
+        for dep in imp:
+          let fid = toFileIndex(dep, g[m].fromDisk, conf)
+          # Warning: we need to traverse the full graph, so
+          # do **not use break here**!
+          if needsRecompile(g, conf, cache, fid, cachedModules):
+            result = true
+
+        if not result:
+          setupLookupTables(g, conf, cache, fileIdx, g[m])
+          cachedModules.add fileIdx
+          g[m].status = loaded
+        else:
+          g.pm[m] = LoadedModule(status: outdated, module: g[m].module)
     else:
       loadError(err, rod, conf)
       g[m].status = outdated
@@ -1020,12 +1128,13 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache
 proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
                         fileIdx: FileIndex; cachedModules: var seq[FileIndex]): PSym =
   ## Returns 'nil' if the module needs to be recompiled.
-  if needsRecompile(g, conf, cache, fileIdx, cachedModules):
-    result = nil
-  else:
-    result = g[int fileIdx].module
-    assert result != nil
-    assert result.position == int(fileIdx)
+  bench g.depAnalysis:
+    if needsRecompile(g, conf, cache, fileIdx, cachedModules):
+      result = nil
+    else:
+      result = g[int fileIdx].module
+      assert result != nil
+      assert result.position == int(fileIdx)
   for m in cachedModules:
     loadToReplayNodes(g, conf, cache, m, g[int m])
 
@@ -1039,46 +1148,43 @@ template setupDecoder() {.dirty.} =
 
 proc loadProcBody*(config: ConfigRef, cache: IdentCache;
                    g: var PackedModuleGraph; s: PSym): PNode =
-  let mId = s.itemId.module
-  var decoder = PackedDecoder(
-    lastModule: int32(-1),
-    lastLit: LitId(0),
-    lastFile: FileIndex(-1),
-    config: config,
-    cache: cache)
-  let pos = g[mId].fromDisk.syms[s.itemId.item].ast
-  assert pos != emptyNodeId
-  result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos)
-
-proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
-                     g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
-  if id.item < g[module].types.len:
-    result = g[module].types[id.item]
-  else:
-    result = nil
-  if result == nil:
+  bench g.loadBody:
+    let mId = s.itemId.module
     var decoder = PackedDecoder(
       lastModule: int32(-1),
       lastLit: LitId(0),
       lastFile: FileIndex(-1),
       config: config,
       cache: cache)
-    result = loadType(decoder, g, module, id)
+    let pos = g[mId].fromDisk.syms[s.itemId.item].ast
+    assert pos != emptyNodeId
+    result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos)
+
+proc loadTypeFromId*(config: ConfigRef, cache: IdentCache;
+                     g: var PackedModuleGraph; module: int; id: PackedItemId): PType =
+  bench g.loadType:
+    result = g[module].types.getOrDefault(id.item)
+    if result == nil:
+      var decoder = PackedDecoder(
+        lastModule: int32(-1),
+        lastLit: LitId(0),
+        lastFile: FileIndex(-1),
+        config: config,
+        cache: cache)
+      result = loadType(decoder, g, module, id)
 
 proc loadSymFromId*(config: ConfigRef, cache: IdentCache;
                     g: var PackedModuleGraph; module: int; id: PackedItemId): PSym =
-  if id.item < g[module].syms.len:
-    result = g[module].syms[id.item]
-  else:
-    result = nil
-  if result == nil:
-    var decoder = PackedDecoder(
-      lastModule: int32(-1),
-      lastLit: LitId(0),
-      lastFile: FileIndex(-1),
-      config: config,
-      cache: cache)
-    result = loadSym(decoder, g, module, id)
+  bench g.loadSym:
+    result = g[module].syms.getOrDefault(id.item)
+    if result == nil:
+      var decoder = PackedDecoder(
+        lastModule: int32(-1),
+        lastLit: LitId(0),
+        lastFile: FileIndex(-1),
+        config: config,
+        cache: cache)
+      result = loadSym(decoder, g, module, id)
 
 proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; config: ConfigRef): ItemId =
   if id.module == LitId(0):
@@ -1086,19 +1192,6 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi
   else:
     ItemId(module: toFileIndex(id.module, g[thisModule].fromDisk, config).int32, item: id.item)
 
-proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) =
-  var bugs = 0
-  for i in 1 .. high(m.syms):
-    if m.syms[i].kind == skUnknown:
-      echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId))
-      inc bugs
-  assert bugs == 0
-  when false:
-    var nones = 0
-    for i in 1 .. high(m.types):
-      inc nones, m.types[i].kind == tyNone
-    assert nones < 1
-
 proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache;
                            moduleSym: PSym; m: PackedModule) =
   # For now only used for heavy debugging. In the future we could use this to reduce the
@@ -1138,9 +1231,11 @@ proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache;
   if it.i < it.values.len:
     result = loadSym(it.decoder, g, int(module), it.values[it.i])
     inc it.i
+  else:
+    result = nil
 
 proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
-                         g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym =
+                         g: var PackedModuleGraph; module: FileIndex; importHidden: bool): PSym =
   it.decoder = PackedDecoder(
     lastModule: int32(-1),
     lastLit: LitId(0),
@@ -1155,11 +1250,15 @@ proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache;
   if it.i < it.values.len:
     result = loadSym(it.decoder, g, int(module), it.values[it.i])
     inc it.i
+  else:
+    result = nil
 
 proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym =
   if it.i < it.values.len:
     result = loadSym(it.decoder, g, it.module, it.values[it.i])
     inc it.i
+  else:
+    result = nil
 
 iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache;
                            g: var PackedModuleGraph; module: FileIndex;
@@ -1192,12 +1291,12 @@ proc searchForCompilerproc*(m: LoadedModule; name: string): int32 =
 # ------------------------- .rod file viewer ---------------------------------
 
 proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
-  var m: PackedModule
+  var m: PackedModule = PackedModule()
   let err = loadRodFile(rodfile, m, config, ignoreConfig=true)
   if err != ok:
     config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err
 
-  when true:
+  when false:
     echo "exports:"
     for ex in m.exports:
       echo "  ", m.strings[ex[0]], " local ID: ", ex[1]
@@ -1213,13 +1312,32 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) =
     for ex in m.hidden:
       echo "  ", m.strings[ex[0]], " local ID: ", ex[1]
 
-  echo "all symbols"
-  for i in 0..high(m.syms):
-    if m.syms[i].name != LitId(0):
-      echo "  ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind
-    else:
-      echo "  <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
+  when false:
+    echo "all symbols"
+    for i in 0..high(m.syms):
+      if m.syms[i].name != LitId(0):
+        echo "  ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind
+      else:
+        echo "  <anon symbol?> local ID: ", i, " kind ", m.syms[i].kind
 
   echo "symbols: ", m.syms.len, " types: ", m.types.len,
-    " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len,
+    " top level nodes: ", m.topLevel.len, " other nodes: ", m.bodies.len,
     " strings: ", m.strings.len, " numbers: ", m.numbers.len
+
+  echo "SIZES:"
+  echo "symbols: ", m.syms.len * sizeof(PackedSym), " types: ", m.types.len * sizeof(PackedType),
+    " top level nodes: ", m.topLevel.len * sizeof(PackedNode),
+    " other nodes: ", m.bodies.len * sizeof(PackedNode),
+    " strings: ", sizeOnDisc(m.strings)
+  when false:
+    var tt = 0
+    var fc = 0
+    for x in m.topLevel:
+      if x.kind == nkSym or x.typeId == nilItemId: inc tt
+      if x.flags == {}: inc fc
+    for x in m.bodies:
+      if x.kind == nkSym or x.typeId == nilItemId: inc tt
+      if x.flags == {}: inc fc
+    let total = float(m.topLevel.len + m.bodies.len)
+    echo "nodes with nil type: ", tt, " in % ", tt.float / total
+    echo "nodes with empty flags: ", fc.float / total
diff --git a/compiler/ic/iclineinfos.nim b/compiler/ic/iclineinfos.nim
new file mode 100644
index 000000000..74a7d971b
--- /dev/null
+++ b/compiler/ic/iclineinfos.nim
@@ -0,0 +1,84 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2024 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# For the line information we use 32 bits. They are used as follows:
+# Bit 0 (AsideBit): If we have inline line information or not. If not, the
+# remaining 31 bits are used as an index into a seq[(LitId, int, int)].
+#
+# We use 10 bits for the "file ID", this means a program can consist of as much
+# as 1024 different files. (If it uses more files than that, the overflow bit
+# would be set.)
+# This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
+# so 128 is the limit and 14 bits for the line number.
+# The packed representation supports files with up to 16384 lines.
+# Keep in mind that whenever any limit is reached the AsideBit is set and the real line
+# information is kept in a side channel.
+
+import std / assertions
+
+const
+  AsideBit = 1
+  FileBits = 10
+  LineBits = 14
+  ColBits = 7
+  FileMax = (1 shl FileBits) - 1
+  LineMax = (1 shl LineBits) - 1
+  ColMax = (1 shl ColBits) - 1
+
+static:
+  assert AsideBit + FileBits + LineBits + ColBits == 32
+
+import .. / ic / [bitabs, rodfiles] # for LitId
+
+type
+  PackedLineInfo* = distinct uint32
+
+  LineInfoManager* = object
+    aside: seq[(LitId, int32, int32)]
+
+const
+  NoLineInfo* = PackedLineInfo(0'u32)
+
+proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
+  if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
+    let col = if col < 0'i32: 0'u32 else: col.uint32
+    let line = if line < 0'i32: 0'u32 else: line.uint32
+    # use inline representation:
+    result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
+      (col shl uint32(AsideBit + FileBits + LineBits)))
+  else:
+    result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
+    m.aside.add (file, line, col)
+
+proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
+  let i = i.uint32
+  if (i and 1'u32) == 0'u32:
+    # inline representation:
+    result = (LitId((i shr 1'u32) and FileMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
+      int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
+  else:
+    result = m.aside[int(i shr 1'u32)]
+
+proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
+  result = unpack(m, i)[0]
+
+proc store*(r: var RodFile; m: LineInfoManager) = storeSeq(r, m.aside)
+proc load*(r: var RodFile; m: var LineInfoManager) = loadSeq(r, m.aside)
+
+when isMainModule:
+  var m = LineInfoManager(aside: @[])
+  for i in 0'i32..<16388'i32:
+    for col in 0'i32..<100'i32:
+      let packed = pack(m, LitId(1023), i, col)
+      let u = unpack(m, packed)
+      assert u[0] == LitId(1023)
+      assert u[1] == i
+      assert u[2] == col
+  echo m.aside.len
diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim
index d341fd653..3e8ea2503 100644
--- a/compiler/ic/integrity.nim
+++ b/compiler/ic/integrity.nim
@@ -10,7 +10,7 @@
 ## Integrity checking for a set of .rod files.
 ## The set must cover a complete Nim project.
 
-import sets
+import std/[sets, tables]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -72,15 +72,16 @@ proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) =
     c.thisModule = oldThisModule
 
 proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
-  if tree[n.int].typeId != nilItemId:
-    checkType(c, tree[n.int].typeId)
+  let t = findType(tree, n)
+  if t != nilItemId:
+    checkType(c, t)
   case n.kind
   of nkEmpty, nkNilLit, nkType, nkNilRodNode:
     discard
   of nkIdent:
     assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
   of nkSym:
-    checkLocalSym(c, tree.nodes[n.int].operand)
+    checkLocalSym(c, tree[n].soperand)
   of directIntLit:
     discard
   of externIntLit, nkFloatLit..nkFloat128Lit:
@@ -89,9 +90,9 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) =
     assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId
   of nkModuleRef:
     let (n1, n2) = sons2(tree, n)
-    assert n1.kind == nkInt32Lit
-    assert n2.kind == nkInt32Lit
-    checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand))
+    assert n1.kind == nkNone
+    assert n2.kind == nkNone
+    checkForeignSym(c, PackedItemId(module: n1.litId, item: tree[n2].soperand))
   else:
     for n0 in sonsReadonly(tree, n):
       checkNode(c, tree, n0)
@@ -107,18 +108,18 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
   # We check that:
   # - Every symbol references existing types and symbols.
   # - Every tree node references existing types and symbols.
-  for i in 0..high(m.syms):
-    checkLocalSym c, int32(i)
+  for _, v in pairs(m.syms):
+    checkLocalSym c, v.id
 
   checkTree c, m.toReplay
   checkTree c, m.topLevel
 
   for e in m.exports:
-    assert e[1] >= 0 and e[1] < m.syms.len
+    #assert e[1] >= 0 and e[1] < m.syms.len
     assert e[0] == m.syms[e[1]].name
 
   for e in m.compilerProcs:
-    assert e[1] >= 0 and e[1] < m.syms.len
+    #assert e[1] >= 0 and e[1] < m.syms.len
     assert e[0] == m.syms[e[1]].name
 
   checkLocalSymIds c, m, m.converters
@@ -134,13 +135,15 @@ proc checkModule(c: var CheckedContext; m: PackedModule) =
     typeInstCache*: seq[(PackedItemId, PackedItemId)]
     procInstCache*: seq[PackedInstantiation]
     attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)]
-    methodsPerType*: seq[(PackedItemId, int, PackedItemId)]
+    methodsPerGenericType*: seq[(PackedItemId, int, PackedItemId)]
     enumToStringProcs*: seq[(PackedItemId, PackedItemId)]
+    methodsPerType*: seq[(PackedItemId, PackedItemId)]
+    dispatchers*: seq[PackedItemId]
   ]#
 
 proc checkIntegrity*(g: ModuleGraph) =
   var c = CheckedContext(g: g)
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     # case statement here to enforce exhaustive checks.
     case g.packed[i].status
     of undefined:
@@ -150,4 +153,3 @@ proc checkIntegrity*(g: ModuleGraph) =
     of stored, storing, outdated, loaded:
       c.thisModule = int32 i
       checkModule(c, g.packed[i].fromDisk)
-
diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim
index cbba591c5..39037b94f 100644
--- a/compiler/ic/navigator.nim
+++ b/compiler/ic/navigator.nim
@@ -11,59 +11,70 @@
 ## IDE-like features. It uses the set of .rod files to accomplish
 ## its task. The set must cover a complete Nim project.
 
-import sets
+import std/[sets, tables]
 
-from os import nil
+from std/os import nil
 from std/private/miscdollars import toLocation
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import ".." / [ast, modulegraphs, msgs, options]
+import iclineinfos
 import packed_ast, bitabs, ic
 
 type
+  UnpackedLineInfo = object
+    file: LitId
+    line, col: int
   NavContext = object
     g: ModuleGraph
     thisModule: int32
-    trackPos: PackedLineInfo
+    trackPos: UnpackedLineInfo
     alreadyEmitted: HashSet[string]
     outputSep: char # for easier testing, use short filenames and spaces instead of tabs.
 
-proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool =
-  if current.file == trackPos.file and current.line == trackPos.line:
+proc isTracked(man: LineInfoManager; current: PackedLineInfo, trackPos: UnpackedLineInfo, tokenLen: int): bool =
+  let (currentFile, currentLine, currentCol) = man.unpack(current)
+  if currentFile == trackPos.file and currentLine == trackPos.line:
     let col = trackPos.col
-    if col >= current.col and col < current.col+tokenLen:
-      return true
+    if col >= currentCol and col < currentCol+tokenLen:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool =
   result = s.name != LitId(0) and
-    isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len)
 
 proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool =
   let name = c.g.packed[s.module].fromDisk.syms[s.item].name
   result = name != LitId(0) and
-    isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
+    isTracked(c.g.packed[c.thisModule].fromDisk.man, info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len)
 
 const
   EmptyItemId = ItemId(module: -1'i32, item: -1'i32)
 
 proc search(c: var NavContext; tree: PackedTree): ItemId =
   # We use the linear representation here directly:
-  for i in 0..high(tree.nodes):
-    case tree.nodes[i].kind
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
     of nkSym:
-      let item = tree.nodes[i].operand
-      if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info):
+      let item = tree[i].soperand
+      if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree[i].info):
         return ItemId(module: c.thisModule, item: item)
     of nkModuleRef:
-      if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file:
-        let (n1, n2) = sons2(tree, NodePos i)
+      let (currentFile, currentLine, currentCol) = c.g.packed[c.thisModule].fromDisk.man.unpack(tree[i].info)
+      if currentLine == c.trackPos.line and currentFile == c.trackPos.file:
+        let (n1, n2) = sons2(tree, i)
         assert n1.kind == nkInt32Lit
         assert n2.kind == nkInt32Lit
-        let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
+        let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
         let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
-        if searchForeignSym(c, itemId, tree.nodes[i].info):
+        if searchForeignSym(c, itemId, tree[i].info):
           return itemId
     else: discard
   return EmptyItemId
@@ -73,36 +84,38 @@ proc isDecl(tree: PackedTree; n: NodePos): bool =
   const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef,
     nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection,
     nkIdentDefs, nkEnumTy, nkVarTuple}
-  result = n.int >= 0 and tree[n.int].kind in declarativeNodes
+  result = n.int >= 0 and tree[n].kind in declarativeNodes
 
 proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) =
+  let (fileId, line, col) = unpack(c.g.packed[c.thisModule].fromDisk.man, info)
   var m = ""
-  var file = c.g.packed[c.thisModule].fromDisk.strings[info.file]
+  var file = c.g.packed[c.thisModule].fromDisk.strings[fileId]
   if c.outputSep == ' ':
     file = os.extractFilename file
-  toLocation(m, file, info.line.int, info.col.int + ColOffset)
+  toLocation(m, file, line, col + ColOffset)
   if not c.alreadyEmitted.containsOrIncl(m):
     msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m
 
 proc list(c: var NavContext; tree: PackedTree; sym: ItemId) =
-  for i in 0..high(tree.nodes):
-    case tree.nodes[i].kind
+  for i in 0..<len(tree):
+    let i = NodePos(i)
+    case tree[i].kind
     of nkSym:
-      let item = tree.nodes[i].operand
+      let item = tree[i].soperand
       if sym.item == item and sym.module == c.thisModule:
-        usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
     of nkModuleRef:
-      let (n1, n2) = sons2(tree, NodePos i)
-      assert n1.kind == nkInt32Lit
-      assert n2.kind == nkInt32Lit
-      let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)
+      let (n1, n2) = sons2(tree, i)
+      assert n1.kind == nkNone
+      assert n2.kind == nkNone
+      let pId = PackedItemId(module: n1.litId, item: tree[n2].soperand)
       let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config)
       if itemId.item == sym.item and sym.module == itemId.module:
-        usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i)))
+        usage(c, tree[i].info, isDecl(tree, parent(i)))
     else: discard
 
 proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int =
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     for k in 1..high(g.packed[i].fromDisk.includes):
       # we start from 1 because the first "include" file is
       # the module's filename.
@@ -134,7 +147,7 @@ proc nav(g: ModuleGraph) =
   var c = NavContext(
     g: g,
     thisModule: int32 mid,
-    trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId),
+    trackPos: UnpackedLineInfo(line: unpacked.line.int, col: unpacked.col.int, file: fileId),
     outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t'
   )
   var symId = search(c, g.packed[mid].fromDisk.topLevel)
@@ -145,7 +158,7 @@ proc nav(g: ModuleGraph) =
     localError(g.config, unpacked, "no symbol at this position")
     return
 
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     # case statement here to enforce exhaustive checks.
     case g.packed[i].status
     of undefined:
@@ -162,7 +175,7 @@ proc navUsages*(g: ModuleGraph) = nav(g)
 proc navDefusages*(g: ModuleGraph) = nav(g)
 
 proc writeRodFiles*(g: ModuleGraph) =
-  for i in 0..high(g.packed):
+  for i in 0..<len(g.packed):
     case g.packed[i].status
     of undefined, loading, stored, loaded:
       discard "nothing to do"
diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim
index c78fe56f5..a39bb7adf 100644
--- a/compiler/ic/packed_ast.nim
+++ b/compiler/ic/packed_ast.nim
@@ -12,10 +12,12 @@
 ## use this representation directly in all the transformations,
 ## it is superior.
 
-import hashes, tables, strtabs
-import bitabs
+import std/[hashes, tables, strtabs]
+import bitabs, rodfiles
 import ".." / [ast, options]
 
+import iclineinfos
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -31,25 +33,21 @@ type
     item*: int32         # same as the in-memory representation
 
 const
-  nilItemId* = PackedItemId(module: LitId(0), item: -1.int32)
+  nilItemId* = PackedItemId(module: LitId(0), item: 0.int32)
 
 const
   emptyNodeId* = NodeId(-1)
 
 type
-  PackedLineInfo* = object
-    line*: uint16
-    col*: int16
-    file*: LitId
-
   PackedLib* = object
     kind*: TLibKind
     generated*: bool
-    isOverriden*: bool
+    isOverridden*: bool
     name*: LitId
     path*: NodeId
 
   PackedSym* = object
+    id*: int32
     kind*: TSymKind
     name*: LitId
     typ*: PackedItemId
@@ -63,15 +61,18 @@ type
     alignment*: int # for alignment
     options*: TOptions
     position*: int
-    offset*: int
+    offset*: int32
+    disamb*: int32
     externalName*: LitId # instead of TLoc
     locFlags*: TLocFlags
     annex*: PackedLib
     when hasFFI:
       cname*: LitId
     constraint*: NodeId
+    instantiatedFrom*: PackedItemId
 
   PackedType* = object
+    id*: int32
     kind*: TTypeKind
     callConv*: TCallingConvention
     #nodekind*: TNodeKind
@@ -84,28 +85,39 @@ type
     size*: BiggestInt
     align*: int16
     paddingAtEnd*: int16
-    lockLevel*: TLockLevel # lock level as required for deadlock checking
     # not serialized: loc*: TLoc because it is backend-specific
     typeInst*: PackedItemId
     nonUniqueId*: int32
 
-  PackedNode* = object     # 28 bytes
-    kind*: TNodeKind
-    flags*: TNodeFlags
-    operand*: int32  # for kind in {nkSym, nkSymDef}: SymId
-                     # for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId
-                     # for kind in nkInt32Lit: direct value
-                     # for non-atom kinds: the number of nodes (for easy skipping)
-    typeId*: PackedItemId
+  PackedNode* = object     # 8 bytes
+    x: uint32
     info*: PackedLineInfo
 
   PackedTree* = object ## usually represents a full Nim module
-    nodes*: seq[PackedNode]
+    nodes: seq[PackedNode]
+    withFlags: seq[(int32, TNodeFlags)]
+    withTypes: seq[(int32, PackedItemId)]
 
   PackedInstantiation* = object
     key*, sym*: PackedItemId
     concreteTypes*: seq[PackedItemId]
 
+const
+  NodeKindBits = 8'u32
+  NodeKindMask = (1'u32 shl NodeKindBits) - 1'u32
+
+template kind*(n: PackedNode): TNodeKind = TNodeKind(n.x and NodeKindMask)
+template uoperand*(n: PackedNode): uint32 = (n.x shr NodeKindBits)
+template soperand*(n: PackedNode): int32 = int32(uoperand(n))
+
+template toX(k: TNodeKind; operand: uint32): uint32 =
+  uint32(k) or (operand shl NodeKindBits)
+
+template toX(k: TNodeKind; operand: LitId): uint32 =
+  uint32(k) or (operand.uint32 shl NodeKindBits)
+
+template typeId*(n: PackedNode): PackedItemId = n.typ
+
 proc `==`*(a, b: SymId): bool {.borrow.}
 proc hash*(a: SymId): Hash {.borrow.}
 
@@ -114,71 +126,35 @@ proc `==`*(a, b: NodePos): bool {.borrow.}
 proc `==`*(a, b: NodeId): bool {.borrow.}
 
 proc newTreeFrom*(old: PackedTree): PackedTree =
-  result.nodes = @[]
+  result = PackedTree(nodes: @[])
   when false: result.sh = old.sh
 
-when false:
-  proc declareSym*(tree: var PackedTree; kind: TSymKind;
-                  name: LitId; info: PackedLineInfo): SymId =
-    result = SymId(tree.sh.syms.len)
-    tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info)
-
-  proc litIdFromName*(tree: PackedTree; name: string): LitId =
-    result = tree.sh.strings.getOrIncl(name)
-
-  proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) =
-    tree.nodes.add PackedNode(kind: kind, info: info,
-                              operand: int32 getOrIncl(tree.sh.strings, token))
-
-  proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) =
-    tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
-
-proc throwAwayLastNode*(tree: var PackedTree) =
-  tree.nodes.setLen(tree.nodes.len-1)
-
 proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) =
-  tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info)
+  tree.nodes.add PackedNode(x: toX(nkIdent, uint32(s)), info: info)
 
 proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) =
-  tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info)
-
-proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) =
-  tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info)
+  tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
 
 proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) =
-  tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info)
+  tree.nodes.add PackedNode(x: toX(nkSym, cast[uint32](s)), info: info)
 
 proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit
 
-proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) =
-  # and this is why the IR is superior. We can copy subtrees
-  # via a linear scan.
-  let pos = n.int
-  let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
-  let d = dest.nodes.len
-  dest.nodes.setLen(d + L)
-  for i in 0..<L:
-    dest.nodes[d+i] = tree.nodes[pos+i]
-
-when false:
-  proc copySym*(dest: var PackedTree; tree: PackedTree; s: SymId): SymId =
-    result = SymId(dest.sh.syms.len)
-    assert int(s) < tree.sh.syms.len
-    let oldSym = tree.sh.syms[s.int]
-    dest.sh.syms.add oldSym
-
 type
   PatchPos = distinct int
 
-when false:
-  proc prepare*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo): PatchPos =
-    result = PatchPos tree.nodes.len
-    tree.nodes.add PackedNode(kind: kind, operand: 0, info: info)
+proc addNode*(t: var PackedTree; kind: TNodeKind; operand: int32;
+              typeId: PackedItemId = nilItemId; info: PackedLineInfo;
+              flags: TNodeFlags = {}) =
+  t.nodes.add PackedNode(x: toX(kind, cast[uint32](operand)), info: info)
+  if flags != {}:
+    t.withFlags.add (t.nodes.len.int32 - 1, flags)
+  if typeId != nilItemId:
+    t.withTypes.add (t.nodes.len.int32 - 1, typeId)
 
 proc prepare*(tree: var PackedTree; kind: TNodeKind; flags: TNodeFlags; typeId: PackedItemId; info: PackedLineInfo): PatchPos =
   result = PatchPos tree.nodes.len
-  tree.nodes.add PackedNode(kind: kind, flags: flags, operand: 0, info: info,
-                            typeId: typeId)
+  tree.addNode(kind = kind, flags = flags, operand = 0, info = info, typeId = typeId)
 
 proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): PatchPos =
   result = PatchPos dest.nodes.len
@@ -186,26 +162,30 @@ proc prepare*(dest: var PackedTree; source: PackedTree; sourcePos: NodePos): Pat
 
 proc patch*(tree: var PackedTree; pos: PatchPos) =
   let pos = pos.int
-  assert tree.nodes[pos].kind > nkNilLit
+  let k = tree.nodes[pos].kind
+  assert k > nkNilLit
   let distance = int32(tree.nodes.len - pos)
-  tree.nodes[pos].operand = distance
+  assert distance > 0
+  tree.nodes[pos].x = toX(k, cast[uint32](distance))
 
 proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len
 
-proc `[]`*(tree: PackedTree; i: int): lent PackedNode {.inline.} =
-  tree.nodes[i]
+proc `[]`*(tree: PackedTree; i: NodePos): lent PackedNode {.inline.} =
+  tree.nodes[i.int]
+
+template rawSpan(n: PackedNode): int = int(uoperand(n))
 
 proc nextChild(tree: PackedTree; pos: var int) {.inline.} =
   if tree.nodes[pos].kind > nkNilLit:
-    assert tree.nodes[pos].operand > 0
-    inc pos, tree.nodes[pos].operand
+    assert tree.nodes[pos].uoperand > 0
+    inc pos, tree.nodes[pos].rawSpan
   else:
     inc pos
 
 iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos =
   var pos = n.int
   assert tree.nodes[pos].kind > nkNilLit
-  let last = pos + tree.nodes[pos].operand
+  let last = pos + tree.nodes[pos].rawSpan
   inc pos
   while pos < last:
     yield NodePos pos
@@ -226,7 +206,7 @@ iterator isons*(dest: var PackedTree; tree: PackedTree;
 iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos =
   var pos = n.int
   assert tree.nodes[pos].kind > nkNilLit
-  let last = pos + tree.nodes[pos].operand
+  let last = pos + tree.nodes[pos].rawSpan
   inc pos
   if pos < last:
     nextChild tree, pos
@@ -240,7 +220,7 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
     inc count
   var pos = n.int
   assert tree.nodes[pos].kind > nkNilLit
-  let last = pos + tree.nodes[pos].operand
+  let last = pos + tree.nodes[pos].rawSpan
   inc pos
   while pos < last and count > 2:
     yield NodePos pos
@@ -250,7 +230,7 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos =
 proc parentImpl(tree: PackedTree; n: NodePos): NodePos =
   # finding the parent of a node is rather easy:
   var pos = n.int - 1
-  while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)):
+  while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].rawSpan - 1 < n.int)):
     dec pos
   #assert pos >= 0, "node has no parent"
   result = NodePos(pos)
@@ -276,20 +256,32 @@ proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} =
 proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} =
   tree.nodes[n.int].kind
 proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} =
-  LitId tree.nodes[n.int].operand
+  LitId tree.nodes[n.int].uoperand
 proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} =
   tree.nodes[n.int].info
 
+proc findType*(tree: PackedTree; n: NodePos): PackedItemId =
+  for x in tree.withTypes:
+    if x[0] == int32(n): return x[1]
+    if x[0] > int32(n): return nilItemId
+  return nilItemId
+
+proc findFlags*(tree: PackedTree; n: NodePos): TNodeFlags =
+  for x in tree.withFlags:
+    if x[0] == int32(n): return x[1]
+    if x[0] > int32(n): return {}
+  return {}
+
 template typ*(n: NodePos): PackedItemId =
-  tree.nodes[n.int].typeId
+  tree.findType(n)
 template flags*(n: NodePos): TNodeFlags =
-  tree.nodes[n.int].flags
+  tree.findFlags(n)
 
-template operand*(n: NodePos): int32 =
-  tree.nodes[n.int].operand
+template uoperand*(n: NodePos): uint32 =
+  tree.nodes[n.int].uoperand
 
 proc span*(tree: PackedTree; pos: int): int {.inline.} =
-  if isAtom(tree, pos): 1 else: tree.nodes[pos].operand
+  if isAtom(tree, pos): 1 else: tree.nodes[pos].rawSpan
 
 proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) =
   assert(not isAtom(tree, n.int))
@@ -305,6 +297,7 @@ proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) =
   result = (NodePos a, NodePos b, NodePos c)
 
 proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos =
+  result = default(NodePos)
   if tree.nodes[n.int].kind > nkNilLit:
     var count = 0
     for child in sonsReadonly(tree, n):
@@ -318,64 +311,28 @@ when false:
 
 template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind
 template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info
-template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand
+template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].uoperand
 
-template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand
+template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].soperand
 
 proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1)
 
-when false:
-  # xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below?
-  proc strLit*(tree: PackedTree; n: NodePos): lent string =
-    assert n.kind == nkStrLit
-    result = tree.sh.strings[LitId tree.nodes[n.int].operand]
-
-  proc strVal*(tree: PackedTree; n: NodePos): string =
-    assert n.kind == nkStrLit
-    result = tree.sh.strings[LitId tree.nodes[n.int].operand]
-    #result = cookedStrLit(raw)
-
-  proc filenameVal*(tree: PackedTree; n: NodePos): string =
-    case n.kind
-    of nkStrLit:
-      result = strVal(tree, n)
-    of nkIdent:
-      result = tree.sh.strings[n.litId]
-    of nkSym:
-      result = tree.sh.strings[tree.sh.syms[int n.symId].name]
-    else:
-      result = ""
-
-  proc identAsStr*(tree: PackedTree; n: NodePos): lent string =
-    assert n.kind == nkIdent
-    result = tree.sh.strings[LitId tree.nodes[n.int].operand]
-
 const
   externIntLit* = {nkCharLit,
     nkIntLit,
     nkInt8Lit,
     nkInt16Lit,
+    nkInt32Lit,
     nkInt64Lit,
     nkUIntLit,
     nkUInt8Lit,
     nkUInt16Lit,
     nkUInt32Lit,
-    nkUInt64Lit} # nkInt32Lit is missing by design!
+    nkUInt64Lit}
 
-  externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit}
+  externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit}
   externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit}
-  directIntLit* = nkInt32Lit
-
-when false:
-  proc identIdImpl(tree: PackedTree; n: NodePos): LitId =
-    if n.kind == nkIdent:
-      result = n.litId
-    elif n.kind == nkSym:
-      result = tree.sh.syms[int n.symId].name
-    else:
-      result = LitId(0)
-
-  template identId*(n: NodePos): LitId = identIdImpl(tree, n)
+  directIntLit* = nkNone
 
 template copyInto*(dest, n, body) =
   let patchPos = prepare(dest, tree, n)
@@ -387,28 +344,8 @@ template copyIntoKind*(dest, kind, info, body) =
   body
   patch dest, patchPos
 
-when false:
-  proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool =
-    let litId = tree.sh.strings.getKeyId(pragma)
-    if litId == LitId(0):
-      return false
-    assert n.kind == nkPragma
-    for ch0 in sonsReadonly(tree, n):
-      if ch0.kind == nkExprColonExpr:
-        if ch0.firstSon.identId == litId:
-          return true
-      elif ch0.identId == litId:
-        return true
-
 proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len
 
-when false:
-  proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) =
-    let patchPos = prepare(dest, nkError, n.info)
-    dest.add nkStrLit, msg, n.info
-    copyTree(dest, tree, n)
-    patch dest, patchPos
-
 iterator allNodes*(tree: PackedTree): NodePos =
   var p = 0
   while p < tree.len:
@@ -418,3 +355,13 @@ iterator allNodes*(tree: PackedTree): NodePos =
 
 proc toPackedItemId*(item: int32): PackedItemId {.inline.} =
   PackedItemId(module: LitId(0), item: item)
+
+proc load*(f: var RodFile; t: var PackedTree) =
+  loadSeq f, t.nodes
+  loadSeq f, t.withFlags
+  loadSeq f, t.withTypes
+
+proc store*(f: var RodFile; t: PackedTree) =
+  storeSeq f, t.nodes
+  storeSeq f, t.withFlags
+  storeSeq f, t.withTypes
diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim
index 0188eaee3..b244ec885 100644
--- a/compiler/ic/replayer.nim
+++ b/compiler/ic/replayer.nim
@@ -14,7 +14,7 @@
 import ".." / [ast, modulegraphs, trees, extccomp, btrees,
   msgs, lineinfos, pathutils, options, cgmeth]
 
-import tables
+import std/tables
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -89,6 +89,31 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) =
       else:
         internalAssert g.config, false
 
+proc replayBackendProcs*(g: ModuleGraph; module: int) =
+  for it in mitems(g.packed[module].fromDisk.attachedOps):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let op = it[1]
+    let tmp = translateId(it[2], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[2])
+    g.attachedOps[op][key] = LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let tmp = translateId(it[1], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[1])
+    g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.methodsPerType):
+    let key = translateId(it[0], g.packed, module, g.config)
+    let tmp = translateId(it[1], g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it[1])
+    g.methodsPerType.mgetOrPut(key, @[]).add LazySym(id: symId, sym: nil)
+
+  for it in mitems(g.packed[module].fromDisk.dispatchers):
+    let tmp = translateId(it, g.packed, module, g.config)
+    let symId = FullId(module: tmp.module, packed: it)
+    g.dispatchers.add LazySym(id: symId, sym: nil)
+
 proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
   ## We remember the generic instantiations a module performed
   ## in order to to avoid the code bloat that generic code tends
@@ -113,18 +138,14 @@ proc replayGenericCacheInformation*(g: ModuleGraph; module: int) =
       module: module, sym: FullId(module: sym.module, packed: it.sym),
       concreteTypes: concreteTypes, inst: nil)
 
-  for it in mitems(g.packed[module].fromDisk.methodsPerType):
+  for it in mitems(g.packed[module].fromDisk.methodsPerGenericType):
     let key = translateId(it[0], g.packed, module, g.config)
     let col = it[1]
     let tmp = translateId(it[2], g.packed, module, g.config)
     let symId = FullId(module: tmp.module, packed: it[2])
-    g.methodsPerType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
+    g.methodsPerGenericType.mgetOrPut(key, @[]).add (col, LazySym(id: symId, sym: nil))
 
-  for it in mitems(g.packed[module].fromDisk.enumToStringProcs):
-    let key = translateId(it[0], g.packed, module, g.config)
-    let tmp = translateId(it[1], g.packed, module, g.config)
-    let symId = FullId(module: tmp.module, packed: it[1])
-    g.enumToStringProcs[key] = LazySym(id: symId, sym: nil)
+  replayBackendProcs(g, module)
 
   for it in mitems(g.packed[module].fromDisk.methods):
     let sym = loadSymFromId(g.config, g.cache, g.packed, module,
diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim
index 496db0564..ac995dd2e 100644
--- a/compiler/ic/rodfiles.nim
+++ b/compiler/ic/rodfiles.nim
@@ -14,17 +14,19 @@
 ##     compiler works and less a storage format, you're probably looking for
 ##     the `ic` or `packed_ast` modules to understand the logical format.
 
-from typetraits import supportsCopyMem
+from std/typetraits import supportsCopyMem
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
 
+import std / tables
+
 ## Overview
 ## ========
 ## `RodFile` represents a Rod File (versioned binary format), and the
 ## associated data for common interactions such as IO and error tracking
 ## (`RodFileError`). The file format broken up into sections (`RodSection`)
-## and preceeded by a header (see: `cookie`). The precise layout, section
+## and preceded by a header (see: `cookie`). The precise layout, section
 ## ordering and data following the section are determined by the user. See
 ## `ic.loadRodFile`.
 ##
@@ -84,7 +86,6 @@ type
     convertersSection
     methodsSection
     pureEnumsSection
-    macroUsagesSection
     toReplaySection
     topLevelSection
     bodiesSection
@@ -93,11 +94,16 @@ type
     typeInstCacheSection
     procInstCacheSection
     attachedOpsSection
-    methodsPerTypeSection
+    methodsPerGenericTypeSection
     enumToStringProcsSection
+    methodsPerTypeSection
+    dispatchersSection
     typeInfoSection  # required by the backend
     backendFlagsSection
     aliveSymsSection # beware, this is stored in a `.alivesyms` file.
+    sideChannelSection
+    namespaceSection
+    symnamesSection
 
   RodFileError* = enum
     ok, tooBig, cannotOpen, ioFailure, wrongHeader, wrongSection, configMismatch,
@@ -110,8 +116,8 @@ type
                        # better than exceptions.
 
 const
-  RodVersion = 1
-  cookie = [byte(0), byte('R'), byte('O'), byte('D'),
+  RodVersion = 2
+  defaultCookie = [byte(0), byte('R'), byte('O'), byte('D'),
             byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)]
 
 proc setError(f: var RodFile; err: RodFileError) {.inline.} =
@@ -166,6 +172,18 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) =
     for i in 0..<s.len:
       storePrim(f, s[i])
 
+proc storeOrderedTable*[K, T](f: var RodFile; s: OrderedTable[K, T]) =
+  if f.err != ok: return
+  if s.len >= high(int32):
+    setError f, tooBig
+    return
+  var lenPrefix = int32(s.len)
+  if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    for _, v in s:
+      storePrim(f, v)
+
 proc loadPrim*(f: var RodFile; s: var string) =
   ## Read a string, the length was stored as a prefix
   if f.err != ok: return
@@ -207,16 +225,29 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) =
     for i in 0..<lenPrefix:
       loadPrim(f, s[i])
 
-proc storeHeader*(f: var RodFile) =
+proc loadOrderedTable*[K, T](f: var RodFile; s: var OrderedTable[K, T]) =
+  ## `T` must be compatible with `copyMem`, see `loadPrim`
+  if f.err != ok: return
+  var lenPrefix = int32(0)
+  if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix):
+    setError f, ioFailure
+  else:
+    s = initOrderedTable[K, T](lenPrefix)
+    for i in 0..<lenPrefix:
+      var x = default T
+      loadPrim(f, x)
+      s[x.id] = x
+
+proc storeHeader*(f: var RodFile; cookie = defaultCookie) =
   ## stores the header which is described by `cookie`.
   if f.err != ok: return
   if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len:
     setError f, ioFailure
 
-proc loadHeader*(f: var RodFile) =
+proc loadHeader*(f: var RodFile; cookie = defaultCookie) =
   ## Loads the header which is described by `cookie`.
   if f.err != ok: return
-  var thisCookie: array[cookie.len, byte]
+  var thisCookie: array[cookie.len, byte] = default(array[cookie.len, byte])
   if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len:
     setError f, ioFailure
   elif thisCookie != cookie:
@@ -232,13 +263,14 @@ proc storeSection*(f: var RodFile; s: RodSection) =
 proc loadSection*(f: var RodFile; expected: RodSection) =
   ## read the bytes value of s, sets and error if the section is incorrect.
   if f.err != ok: return
-  var s: RodSection
+  var s: RodSection = default(RodSection)
   loadPrim(f, s)
   if expected != s and f.err == ok:
     setError f, wrongSection
 
 proc create*(filename: string): RodFile =
   ## create the file and open it for writing
+  result = default(RodFile)
   if not open(result.f, filename, fmWrite):
     setError result, cannotOpen
 
@@ -246,5 +278,6 @@ proc close*(f: var RodFile) = close(f.f)
 
 proc open*(filename: string): RodFile =
   ## open the file for reading
+  result = default(RodFile)
   if not open(result.f, filename, fmRead):
     setError result, cannotOpen
diff --git a/compiler/idents.nim b/compiler/idents.nim
index f36ce09f3..34177e76d 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -11,8 +11,8 @@
 # An identifier is a shared immutable string that can be compared by its
 # id. This module is essential for the compiler's performance.
 
-import
-  hashes, wordrecg
+import wordrecg
+import std/hashes
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
diff --git a/compiler/importer.nim b/compiler/importer.nim
index c4e37c269..ffb7e0305 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -10,10 +10,12 @@
 ## This module implements the symbol importing mechanism.
 
 import
-  intsets, ast, astalgo, msgs, options, idents, lookups,
-  semdata, modulepaths, sigmatch, lineinfos, sets,
-  modulegraphs, wordrecg, tables
-from strutils import `%`
+  ast, astalgo, msgs, options, idents, lookups,
+  semdata, modulepaths, sigmatch, lineinfos,
+  modulegraphs, wordrecg
+from std/strutils import `%`, startsWith
+from std/sequtils import addUnique
+import std/[sets, tables, intsets]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -113,6 +115,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) =
 
 proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) =
   template bail = globalError(c.config, n.info, "invalid pragma")
+  result = (nil, @[])
   if n.kind == nkPragmaExpr:
     if n.len == 2 and n[1].kind == nkPragma:
       result[0] = n[0]
@@ -141,7 +144,7 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) =
     # for an enumeration we have to add all identifiers
     if multiImport:
       # for a overloadable syms add all overloaded routines
-      var it: ModuleIter
+      var it: ModuleIter = default(ModuleIter)
       var e = initModuleIter(it, c.graph, fromMod, s.name)
       while e != nil:
         if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3")
@@ -231,7 +234,7 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im
 proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym =
   result = realModule
   template createModuleAliasImpl(ident): untyped =
-    createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options)
+    createModuleAlias(realModule, c.idgen, ident, n.info, c.config.options)
   if n.kind != nkImportAs: discard
   elif n.len != 2 or n[1].kind != nkIdent:
     localError(c.config, n.info, "module alias must be an identifier")
@@ -243,11 +246,14 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool)
     result = createModuleAliasImpl(realModule.name)
   if importHidden:
     result.options.incl optImportHidden
-  c.unusedImports.add((result, n.info))
+  let moduleIdent = if n.kind == nkInfix: n[^1] else: n
+  c.unusedImports.add((result, moduleIdent.info))
   c.importModuleMap[result.id] = realModule.id
+  c.importModuleLookup.mgetOrPut(result.name.id, @[]).addUnique realModule.id
 
 proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] =
-  var ret: typeof(result)
+  result = (nil, false)
+  var ret = default(typeof(result))
   proc processPragma(n2: PNode): PNode =
     let (result2, kws) = splitPragmas(c, n2)
     result = result2
@@ -298,11 +304,26 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym =
       var prefix = ""
       if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; "
       message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated")
-    suggestSym(c.graph, n.info, result, c.graph.usageSym, false)
+    let moduleName = getModuleName(c.config, n)
+    if belongsToStdlib(c.graph, result) and not startsWith(moduleName, stdPrefix) and
+        not startsWith(moduleName, "system/") and not startsWith(moduleName, "packages/"):
+      message(c.config, n.info, warnStdPrefix, realModule.name.s)
+
+    proc suggestMod(n: PNode; s: PSym) =
+      if n.kind == nkImportAs:
+        suggestMod(n[0], realModule)
+      elif n.kind == nkInfix:
+        suggestMod(n[2], s)
+      else:
+        suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
+    suggestMod(n, result)
     importStmtResult.add newSymNode(result, n.info)
     #newStrNode(toFullPath(c.config, f), n.info)
+  else:
+    result = nil
 
 proc afterImport(c: PContext, m: PSym) =
+  if isCachedModule(c.graph, m): return
   # fixes bug #17510, for re-exported symbols
   let realModuleId = c.importModuleMap[m.id]
   for s in allSyms(c.graph, m):
@@ -323,22 +344,24 @@ proc evalImport*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkImportStmt, n.info)
   for i in 0..<n.len:
     let it = n[i]
-    if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = it[0]
-      let dir = it[1]
-      var imp = newNodeI(nkInfix, it.info)
-      imp.add sep
-      imp.add dir
-      imp.add sep # dummy entry, replaced in the loop
-      for x in it[2]:
+    if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
+      let lastPos = it.len - 1
+      var imp = copyNode(it)
+      newSons(imp, it.len)
+      for i in 0 ..< lastPos: imp[i] = it[i]
+      imp[lastPos] = imp[0] # dummy entry, replaced in the loop
+      for x in it[lastPos]:
         # transform `a/b/[c as d]` to `/a/b/c as d`
         if x.kind == nkInfix and x[0].ident.s == "as":
-          let impAs = copyTree(x)
-          imp[2] = x[1]
+          var impAs = copyNode(x)
+          newSons(impAs, 3)
+          impAs[0] = x[0]
+          imp[lastPos] = x[1]
           impAs[1] = imp
-          impMod(c, imp, result)
+          impAs[2] = x[2]
+          impMod(c, impAs, result)
         else:
-          imp[2] = x
+          imp[lastPos] = x
           impMod(c, imp, result)
     else:
       impMod(c, it, result)
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index 40ff7ab55..3dcc364a3 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -14,25 +14,31 @@
 ## See doc/destructors.rst for a spec of the implemented rewrite rules
 
 import
-  intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents,
-  strutils, options, dfa, lowerings, tables, modulegraphs,
+  ast, astalgo, msgs, renderer, magicsys, types, idents,
+  options, lowerings, modulegraphs,
   lineinfos, parampatterns, sighashes, liftdestructors, optimizer,
-  varpartitions
+  varpartitions, aliasanalysis, dfa, wordrecg
+
+import std/[strtabs, tables, strutils, intsets]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-from trees import exprStructuralEquivalent, getRoot
+from trees import exprStructuralEquivalent, getRoot, whichPragma
 
 type
   Con = object
     owner: PSym
-    g: ControlFlowGraph
+    when true:
+      g: ControlFlowGraph
     graph: ModuleGraph
     inLoop, inSpawn, inLoopCond: int
     uninit: IntSet # set of uninit'ed vars
-    uninitComputed: bool
     idgen: IdGenerator
+    body: PNode
+    otherUsage: TLineInfo
+    inUncheckedAssignSection: int
+    inEnsureMove: int
 
   Scope = object # we do scope-based memory management.
     # a scope is comparable to an nkStmtListExpr like
@@ -40,6 +46,8 @@ type
     vars: seq[PSym]
     wasMoved: seq[PNode]
     final: seq[PNode] # finally section
+    locals: seq[PSym]
+    body: PNode
     needsTry: bool
     parent: ptr Scope
 
@@ -61,169 +69,110 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} =
   result = ast.hasDestructor(t)
   when toDebug.len > 0:
     # for more effective debugging
-    if not result and c.graph.config.selectedGC in {gcArc, gcOrc}:
+    if not result and c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
       assert(not containsGarbageCollectedRef(t))
 
 proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode =
-  let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info)
+  let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.idgen, c.owner, info)
   sym.typ = typ
   s.vars.add(sym)
   result = newSymNode(sym)
 
-proc nestedScope(parent: var Scope): Scope =
-  Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent))
+proc nestedScope(parent: var Scope; body: PNode): Scope =
+  Scope(vars: @[], locals: @[], wasMoved: @[], final: @[], body: body, needsTry: false, parent: addr(parent))
 
-proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode
-proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode
+proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode
 
-import sets, hashes
+type
+  MoveOrCopyFlag = enum
+    IsDecl, IsExplicitSink, IsReturn
 
-proc hash(n: PNode): Hash = hash(cast[pointer](n))
+proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; flags: set[MoveOrCopyFlag] = {}): PNode
 
-type
-  State = ref object
-    lastReads: IntSet
-    potentialLastReads: IntSet
-    notLastReads: IntSet
-    alreadySeen: HashSet[PNode]
-
-proc preprocessCfg(cfg: var ControlFlowGraph) =
-  for i in 0..<cfg.len:
-    if cfg[i].kind in {goto, fork} and i + cfg[i].dest > cfg.len:
-      cfg[i].dest = cfg.len - i
-
-proc mergeStates(a: var State, b: sink State) =
-  # Inplace for performance:
-  #   lastReads = a.lastReads + b.lastReads
-  #   potentialLastReads = (a.potentialLastReads + b.potentialLastReads) - (a.notLastReads + b.notLastReads)
-  #   notLastReads = a.notLastReads + b.notLastReads
-  #   alreadySeen = a.alreadySeen + b.alreadySeen
-  # b is never nil
-  if a == nil:
-    a = b
+when false:
+  var
+    perfCounters: array[InstrKind, int]
+
+  proc showCounters*() =
+    for i in low(InstrKind)..high(InstrKind):
+      echo "INSTR ", i, " ", perfCounters[i]
+
+proc isLastReadImpl(n: PNode; c: var Con; scope: var Scope): bool =
+  let root = parampatterns.exprRoot(n, allowCalls=false)
+  if root == nil: return false
+
+  var s = addr(scope)
+  while s != nil:
+    if s.locals.contains(root): break
+    s = s.parent
+
+  c.g = constructCfg(c.owner, if s != nil: s.body else: c.body, root)
+  dbg:
+    echo "\n### ", c.owner.name.s, ":\nCFG:"
+    echoCfg(c.g)
+    #echo c.body
+
+  var j = 0
+  while j < c.g.len:
+    if c.g[j].kind == use and c.g[j].n == n: break
+    inc j
+  c.otherUsage = unknownLineInfo
+  if j < c.g.len:
+    var pcs = @[j+1]
+    var marked = initIntSet()
+    result = true
+    while pcs.len > 0:
+      var pc = pcs.pop()
+      if not marked.contains(pc):
+        let oldPc = pc
+        while pc < c.g.len:
+          dbg:
+            echo "EXEC ", c.g[pc].kind, " ", pc, " ", n
+          when false:
+            inc perfCounters[c.g[pc].kind]
+          case c.g[pc].kind
+          of loop:
+            let back = pc + c.g[pc].dest
+            if not marked.containsOrIncl(back):
+              pc = back
+            else:
+              break
+          of goto:
+            pc = pc + c.g[pc].dest
+          of fork:
+            if not marked.contains(pc+1):
+              pcs.add pc + 1
+            pc = pc + c.g[pc].dest
+          of use:
+            if c.g[pc].n.aliases(n) != no or n.aliases(c.g[pc].n) != no:
+              c.otherUsage = c.g[pc].n.info
+              return false
+            inc pc
+          of def:
+            if c.g[pc].n.aliases(n) == yes:
+              # the path leads to a redefinition of 's' --> sink 's'.
+              break
+            elif n.aliases(c.g[pc].n) != no:
+              # only partially writes to 's' --> can't sink 's', so this def reads 's'
+              # or maybe writes to 's' --> can't sink 's'
+              c.otherUsage = c.g[pc].n.info
+              return false
+            inc pc
+        marked.incl oldPc
   else:
-    a.lastReads.incl b.lastReads
-    a.potentialLastReads.incl b.potentialLastReads
-    a.potentialLastReads.excl a.notLastReads
-    a.potentialLastReads.excl b.notLastReads
-    a.notLastReads.incl b.notLastReads
-    a.alreadySeen.incl b.alreadySeen
-
-proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) =
-  template aliasesCached(obj, field: PNode): AliasKind =
-    aliases(obj, field)
-
-  var cfg = cfg
-  preprocessCfg(cfg)
-
-  var states = newSeq[State](cfg.len + 1)
-  states[0] = State()
-
-  for pc in 0..<cfg.len:
-    template state: State = states[pc]
-    if state != nil:
-      case cfg[pc].kind
-      of def:
-        var potentialLastReadsCopy = state.potentialLastReads
-        for r in potentialLastReadsCopy:
-          if cfg[pc].n.aliasesCached(cfg[r].n) == yes:
-            # the path leads to a redefinition of 's' --> sink 's'.
-            state.lastReads.incl r
-            state.potentialLastReads.excl r
-          elif cfg[r].n.aliasesCached(cfg[pc].n) != no:
-            # only partially writes to 's' --> can't sink 's', so this def reads 's'
-            # or maybe writes to 's' --> can't sink 's'
-            cfg[r].n.comment = '\n' & $pc
-            state.potentialLastReads.excl r
-            state.notLastReads.incl r
-
-        var alreadySeenThisNode = false
-        for s in state.alreadySeen:
-          if cfg[pc].n.aliasesCached(s) != no or s.aliasesCached(cfg[pc].n) != no:
-            alreadySeenThisNode = true; break
-        if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite
-        else: cfg[pc].n.flags.incl nfFirstWrite
-
-        state.alreadySeen.incl cfg[pc].n
-
-        mergeStates(states[pc + 1], move(states[pc]))
-      of use:
-        var potentialLastReadsCopy = state.potentialLastReads
-        for r in potentialLastReadsCopy:
-          if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no:
-            cfg[r].n.comment = '\n' & $pc
-            state.potentialLastReads.excl r
-            state.notLastReads.incl r
-
-        state.potentialLastReads.incl pc
-
-        state.alreadySeen.incl cfg[pc].n
-
-        mergeStates(states[pc + 1], move(states[pc]))
-      of goto:
-        mergeStates(states[pc + cfg[pc].dest], move(states[pc]))
-      of fork:
-        var copy = State()
-        copy[] = states[pc][]
-        mergeStates(states[pc + cfg[pc].dest], copy)
-        mergeStates(states[pc + 1], move(states[pc]))
-
-  let lastReads = (states[^1].lastReads + states[^1].potentialLastReads) - states[^1].notLastReads
-  var lastReadTable: Table[PNode, seq[int]]
-  for position, node in cfg:
-    if node.kind == use:
-      lastReadTable.mgetOrPut(node.n, @[]).add position
-  for node, positions in lastReadTable:
-    block checkIfAllPosLastRead:
-      for p in positions:
-        if p notin lastReads: break checkIfAllPosLastRead
-      node.flags.incl nfLastRead
-
-proc isLastRead(n: PNode; c: var Con): bool =
-  let m = dfa.skipConvDfa(n)
-  (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or nfLastRead in m.flags
+    result = false
+
+proc isLastRead(n: PNode; c: var Con; s: var Scope): bool =
+  # bug #23354; an object type could have a non-trival assignements when it is passed to a sink parameter
+  if not hasDestructor(c, n.typ) and (n.typ.kind != tyObject or isTrival(getAttachedOp(c.graph, n.typ, attachedAsgn))): return true
+
+  let m = skipConvDfa(n)
+  result = (m.kind == nkSym and sfSingleUsedTemp in m.sym.flags) or
+      isLastReadImpl(n, c, s)
 
 proc isFirstWrite(n: PNode; c: var Con): bool =
-  let m = dfa.skipConvDfa(n)
-  nfFirstWrite in m.flags
-
-proc initialized(code: ControlFlowGraph; pc: int,
-                 init, uninit: var IntSet; until: int): int =
-  ## Computes the set of definitely initialized variables across all code paths
-  ## as an IntSet of IDs.
-  var pc = pc
-  while pc < code.len:
-    case code[pc].kind
-    of goto:
-      pc += code[pc].dest
-    of fork:
-      var initA = initIntSet()
-      var initB = initIntSet()
-      var variantA = pc + 1
-      var variantB = pc + code[pc].dest
-      while variantA != variantB:
-        if max(variantA, variantB) > until:
-          break
-        if variantA < variantB:
-          variantA = initialized(code, variantA, initA, uninit, min(variantB, until))
-        else:
-          variantB = initialized(code, variantB, initB, uninit, min(variantA, until))
-      pc = min(variantA, variantB)
-      # we add vars if they are in both branches:
-      for v in initA:
-        if v in initB:
-          init.incl v
-    of use:
-      let v = code[pc].n.sym
-      if v.kind != skParam and v.id notin init:
-        # attempt to read an uninit'ed variable
-        uninit.incl v.id
-      inc pc
-    of def:
-      let v = code[pc].n.sym
-      init.incl v.id
-      inc pc
-  return pc
+  let m = skipConvDfa(n)
+  result = nfFirstWrite in m.flags
 
 proc isCursor(n: PNode): bool =
   case n.kind
@@ -239,39 +188,52 @@ proc isCursor(n: PNode): bool =
 template isUnpackedTuple(n: PNode): bool =
   ## we move out all elements of unpacked tuples,
   ## hence unpacked tuples themselves don't need to be destroyed
-  (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple)
+  ## except it's already a cursor
+  (n.kind == nkSym and n.sym.kind == skTemp and
+   n.sym.typ.kind == tyTuple and sfCursor notin n.sym.flags)
 
-proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) =
+proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string; inferredFromCopy = false) =
   var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">"
-  if (opname == "=" or opname == "=copy") and ri != nil:
+  if inferredFromCopy:
+    m.add ", which is inferred from unavailable '=copy'"
+
+  if (opname == "=" or opname == "=copy" or opname == "=dup") and ri != nil:
     m.add "; requires a copy because it's not the last read of '"
     m.add renderTree(ri)
     m.add '\''
-    if ri.comment.startsWith('\n'):
+    if c.otherUsage != unknownLineInfo:
+       # ri.comment.startsWith('\n'):
       m.add "; another read is done here: "
-      m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
+      m.add c.graph.config $ c.otherUsage
+      #m.add c.graph.config $ c.g[parseInt(ri.comment[1..^1])].n.info
     elif ri.kind == nkSym and ri.sym.kind == skParam and not isSinkType(ri.sym.typ):
       m.add "; try to make "
       m.add renderTree(ri)
       m.add " a 'sink' parameter"
   m.add "; routine: "
   m.add c.owner.name.s
+  #m.add "\n\n"
+  #m.add renderTree(c.body, {renderIds})
   localError(c.graph.config, ri.info, errGenerated, m)
 
 proc makePtrType(c: var Con, baseType: PType): PType =
-  result = newType(tyPtr, nextTypeId c.idgen, c.owner)
+  result = newType(tyPtr, c.idgen, c.owner)
   addSonSkipIntLit(result, baseType, c.idgen)
 
 proc genOp(c: var Con; op: PSym; dest: PNode): PNode =
-  let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
-  addrExp.add(dest)
+  var addrExp: PNode
+  if op.typ != nil and op.typ.signatureLen > 1 and op.typ.firstParamType.kind != tyVar:
+    addrExp = dest
+  else:
+    addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ))
+    addrExp.add(dest)
   result = newTree(nkCall, newSymNode(op), addrExp)
 
 proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode =
   var op = getAttachedOp(c.graph, t, kind)
   if op == nil or op.ast.isGenericRoutine:
     # give up and find the canonical type instead:
-    let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
+    let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct})
     let canon = c.graph.canonTypes.getOrDefault(h)
     if canon != nil:
       op = getAttachedOp(c.graph, canon, kind)
@@ -302,10 +264,20 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} =
 proc isNoInit(dest: PNode): bool {.inline.} =
   result = dest.kind == nkSym and sfNoInit in dest.sym.flags
 
-proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
-  if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or
+proc deepAliases(dest, ri: PNode): bool =
+  case ri.kind
+  of nkCallKinds, nkStmtListExpr, nkBracket, nkTupleConstr, nkObjConstr,
+     nkCast, nkConv, nkObjUpConv, nkObjDownConv:
+    for r in ri:
+      if deepAliases(dest, r): return true
+    return false
+  else:
+    return aliases(dest, ri) != no
+
+proc genSink(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
+  if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or IsDecl in flags or
       (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or
-      isNoInit(dest):
+      isNoInit(dest) or IsReturn in flags:
     # optimize sink call into a bitwise memcopy
     result = newTree(nkFastAsgn, dest, ri)
   else:
@@ -316,12 +288,19 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode =
     else:
       # the default is to use combination of `=destroy(dest)` and
       # and copyMem(dest, source). This is efficient.
-      result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
+      if deepAliases(dest, ri):
+        # consider: x = x + y, it is wrong to destroy the destination first!
+        # tmp to support self assignments
+        let tmp = c.getTemp(s, dest.typ, dest.info)
+        result = newTree(nkStmtList, newTree(nkFastAsgn, tmp, dest), newTree(nkFastAsgn, dest, ri),
+                         c.genDestroy(tmp))
+      else:
+        result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri))
 
 proc isCriticalLink(dest: PNode): bool {.inline.} =
   #[
   Lins's idea that only "critical" links can introduce a cycle. This is
-  critical for the performance gurantees that we strive for: If you
+  critical for the performance guarantees that we strive for: If you
   traverse a data structure, no tracing will be performed at all.
   ORC is about this promise: The GC only touches the memory that the
   mutator touches too.
@@ -338,16 +317,17 @@ proc isCriticalLink(dest: PNode): bool {.inline.} =
   ]#
   result = dest.kind != nkSym
 
-proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) =
-  if c.graph.config.selectedGC == gcOrc:
-    let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
-    if cyclicType(t):
+proc finishCopy(c: var Con; result, dest: PNode; flags: set[MoveOrCopyFlag]; isFromSink: bool) =
+  if c.graph.config.selectedGC == gcOrc and IsExplicitSink notin flags:
+    # add cyclic flag, but not to sink calls, which IsExplicitSink generates
+    let t = dest.typ.skipTypes(tyUserTypeClasses + {tyGenericInst, tyAlias, tySink, tyDistinct})
+    if cyclicType(c.graph, t):
       result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest))
 
 proc genMarkCyclic(c: var Con; result, dest: PNode) =
   if c.graph.config.selectedGC == gcOrc:
     let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
-    if cyclicType(t):
+    if cyclicType(c.graph, t):
       if t.kind == tyRef:
         result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest)
       else:
@@ -355,17 +335,24 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) =
         xenv.typ = getSysType(c.graph, dest.info, tyPointer)
         result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv)
 
-proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode =
+proc genCopyNoCheck(c: var Con; dest, ri: PNode; a: TTypeAttachedOp): PNode =
   let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink})
-  result = c.genOp(t, attachedAsgn, dest, ri)
+  result = c.genOp(t, a, dest, ri)
   assert ri.typ != nil
 
-proc genCopy(c: var Con; dest, ri: PNode): PNode =
+proc genCopy(c: var Con; dest, ri: PNode; flags: set[MoveOrCopyFlag]): PNode =
+  if c.inEnsureMove > 0:
+    localError(c.graph.config, ri.info, errFailedMove, "cannot move '" & $ri &
+                                                      "', which introduces an implicit copy")
   let t = dest.typ
   if tfHasOwned in t.flags and ri.kind != nkNilLit:
     # try to improve the error message here:
-    c.checkForErrorPragma(t, ri, "=copy")
-  result = c.genCopyNoCheck(dest, ri)
+    if IsExplicitSink in flags:
+      c.checkForErrorPragma(t, ri, "=sink")
+    else:
+      c.checkForErrorPragma(t, ri, "=copy")
+  let a = if IsExplicitSink in flags: attachedSink else: attachedAsgn
+  result = c.genCopyNoCheck(dest, ri, a)
   assert ri.typ != nil
 
 proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
@@ -385,30 +372,38 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode =
 
   if hasDestructor(c, objType):
     if getAttachedOp(c.graph, objType, attachedDestructor) != nil and
-        sfOverriden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
+        sfOverridden in getAttachedOp(c.graph, objType, attachedDestructor).flags:
       localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor.
 It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""")
       result.add newTree(nkFastAsgn, le, tmp)
       return
 
     # generate: if le != tmp: `=destroy`(le)
-    let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
-    let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
-    cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
-    cond.add le
-    cond.add tmp
-    let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
-    notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
-    notExpr.add cond
-    result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
+    if c.inUncheckedAssignSection != 0:
+      let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen)
+      let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
+      cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info))
+      cond.add le
+      cond.add tmp
+      let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool))
+      notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot))
+      notExpr.add cond
+      result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le)))
   result.add newTree(nkFastAsgn, le, tmp)
 
 proc genWasMoved(c: var Con, n: PNode): PNode =
-  result = newNodeI(nkCall, n.info)
-  result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved)))
-  result.add copyTree(n) #mWasMoved does not take the address
-  #if n.kind != nkSym:
-  #  message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
+  let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink})
+  let op = getAttachedOp(c.graph, n.typ, attachedWasMoved)
+  if op != nil:
+    if sfError in op.flags:
+      c.checkForErrorPragma(n.typ, n, "=wasMoved")
+    result = genOp(c, op, n)
+  else:
+    result = newNodeI(nkCall, n.info)
+    result.add(newSymNode(createMagic(c.graph, c.idgen, "`=wasMoved`", mWasMoved)))
+    result.add copyTree(n) #mWasMoved does not take the address
+    #if n.kind != nkSym:
+    #  message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")")
 
 proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
   result = newNodeI(nkCall, info)
@@ -417,13 +412,16 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode =
 
 proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
   # generate: (let tmp = v; reset(v); tmp)
-  if not hasDestructor(c, n.typ):
-    assert n.kind != nkSym or not hasDestructor(c, n.sym.typ)
+  if (not hasDestructor(c, n.typ)) and c.inEnsureMove == 0:
+    assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) or
+          (n.typ.kind == tyPtr and n.sym.typ.kind == tyRef)
+      # bug #23505; transformed by `transf`: addr (deref ref) -> ptr
+      # we know it's really a pointer; so here we assign it directly
     result = copyTree(n)
   else:
     result = newNodeIT(nkStmtListExpr, n.info, n.typ)
 
-    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info)
+    var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.idgen, c.owner, n.info)
     temp.typ = n.typ
     var v = newNodeI(nkLetSection, n.info)
     let tempAsNode = newSymNode(temp)
@@ -436,7 +434,8 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
 
     result.add v
     let nn = skipConv(n)
-    c.genMarkCyclic(result, nn)
+    if hasDestructor(c, n.typ):
+      c.genMarkCyclic(result, nn)
     let wasMovedCall = c.genWasMoved(nn)
     result.add wasMovedCall
     result.add tempAsNode
@@ -444,24 +443,50 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode =
 proc isCapturedVar(n: PNode): bool =
   let root = getRoot(n)
   if root != nil: result = root.name.s[0] == ':'
+  else: result = false
 
 proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
   result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-  let tmp = c.getTemp(s, n.typ, n.info)
-  if hasDestructor(c, n.typ):
-    result.add c.genWasMoved(tmp)
-    var m = c.genCopy(tmp, n)
-    m.add p(n, c, s, normal)
-    c.finishCopy(m, n, isFromSink = true)
-    result.add m
-    if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
+  let nTyp = n.typ.skipTypes(tyUserTypeClasses)
+  let tmp = c.getTemp(s, nTyp, n.info)
+  if hasDestructor(c, nTyp):
+    let typ = nTyp.skipTypes({tyGenericInst, tyAlias, tySink})
+    let op = getAttachedOp(c.graph, typ, attachedDup)
+    if op != nil and tfHasOwned notin typ.flags:
+      if sfError in op.flags:
+        c.checkForErrorPragma(nTyp, n, "=dup")
+      else:
+        let copyOp = getAttachedOp(c.graph, typ, attachedAsgn)
+        if copyOp != nil and sfError in copyOp.flags and
+           sfOverridden notin op.flags:
+          c.checkForErrorPragma(nTyp, n, "=dup", inferredFromCopy = true)
+
+      let src = p(n, c, s, normal)
+      var newCall = newTreeIT(nkCall, src.info, src.typ,
+            newSymNode(op),
+            src)
+      c.finishCopy(newCall, n, {}, isFromSink = true)
+      result.add newTreeI(nkFastAsgn,
+          src.info, tmp,
+          newCall
+      )
+    else:
+      result.add c.genWasMoved(tmp)
+      var m = c.genCopy(tmp, n, {})
+      m.add p(n, c, s, normal)
+      c.finishCopy(m, n, {}, isFromSink = true)
+      result.add m
+    if isLValue(n) and not isCapturedVar(n) and nTyp.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0:
       message(c.graph.config, n.info, hintPerformance,
         ("passing '$1' to a sink parameter introduces an implicit copy; " &
         "if possible, rearrange your program's control flow to prevent it") % $n)
+    if c.inEnsureMove > 0:
+      localError(c.graph.config, n.info, errFailedMove,
+        ("cannot move '$1', passing '$1' to a sink parameter introduces an implicit copy") % $n)
   else:
-    if c.graph.config.selectedGC in {gcArc, gcOrc}:
-      assert(not containsManagedMemory(n.typ))
-    if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
+    if c.graph.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+      assert(not containsManagedMemory(nTyp))
+    if nTyp.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}:
       localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter")
     result.add newTree(nkAsgn, tmp, p(n, c, s, normal))
   # Since we know somebody will take over the produced copy, there is
@@ -470,14 +495,14 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode =
 
 proc isDangerousSeq(t: PType): bool {.inline.} =
   let t = t.skipTypes(abstractInst)
-  result = t.kind == tySequence and tfHasOwned notin t[0].flags
+  result = t.kind == tySequence and tfHasOwned notin t.elementType.flags
 
 proc containsConstSeq(n: PNode): bool =
   if n.kind == nkBracket and n.len > 0 and n.typ != nil and isDangerousSeq(n.typ):
     return true
   result = false
   case n.kind
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
     result = containsConstSeq(n[1])
   of nkObjConstr, nkClosure:
     for i in 1..<n.len:
@@ -495,14 +520,14 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode =
     # This was already done in the sink parameter handling logic.
     result = newNodeIT(nkStmtListExpr, arg.info, arg.typ)
     let tmp = c.getTemp(s, arg.typ, arg.info)
-    result.add c.genSink(tmp, arg, isDecl = true)
+    result.add c.genSink(s, tmp, arg, {IsDecl})
     result.add tmp
     s.final.add c.genDestroy(tmp)
   else:
     result = arg
 
 proc cycleCheck(n: PNode; c: var Con) =
-  if c.graph.config.selectedGC != gcArc: return
+  if c.graph.config.selectedGC notin {gcArc, gcAtomicArc}: return
   var value = n[1]
   if value.kind == nkClosure:
     value = value[1]
@@ -540,7 +565,7 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; res: PNode) =
       res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info))
   elif sfThread notin v.sym.flags and sfCursor notin v.sym.flags:
     # do not destroy thread vars for now at all for consistency.
-    if sfGlobal in v.sym.flags and s.parent == nil: #XXX: Rethink this logic (see tarcmisc.test2)
+    if {sfGlobal, sfPure} <= v.sym.flags or sfGlobal in v.sym.flags and s.parent == nil:
       c.graph.globalDestructors.add c.genDestroy(v)
     else:
       s.final.add c.genDestroy(v)
@@ -567,17 +592,19 @@ proc processScope(c: var Con; s: var Scope; ret: PNode): PNode =
 
   if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry
 
-template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped): PNode =
+template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped, tmpFlags: TSymFlags): PNode =
   assert not ret.typ.isEmptyType
   var result = newNodeIT(nkStmtListExpr, ret.info, ret.typ)
   # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0
   # later and use it to eliminate the temporary when theres no need for it, but its
   # tricky because you would have to intercept moveOrCopy at a certain point
   let tmp = c.getTemp(s.parent[], ret.typ, ret.info)
-  tmp.sym.flags.incl sfSingleUsedTemp
-  let cpy = if hasDestructor(c, ret.typ):
+  tmp.sym.flags = tmpFlags
+  let cpy = if hasDestructor(c, ret.typ) and
+                ret.typ.kind notin {tyOpenArray, tyVarargs}:
+                # bug #23247 we don't own the data, so it's harmful to destroy it
               s.parent[].final.add c.genDestroy(tmp)
-              moveOrCopy(tmp, ret, c, s, isDecl = true)
+              moveOrCopy(tmp, ret, c, s, {IsDecl})
             else:
               newTree(nkFastAsgn, tmp, p(ret, c, s, normal))
 
@@ -601,7 +628,8 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt
 
   result
 
-template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
+template handleNestedTempl(n, processCall: untyped, willProduceStmt = false,
+                           tmpFlags = {sfSingleUsedTemp}) =
   template maybeVoid(child, s): untyped =
     if isEmptyType(child.typ): p(child, c, s, normal)
     else: processCall(child, s)
@@ -625,11 +653,11 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
       var branch = shallowCopy(it)
       for j in 0 ..< it.len-1:
         branch[j] = copyTree(it[j])
-      var ofScope = nestedScope(s)
-      branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
+      var ofScope = nestedScope(s, it.lastSon)
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
                      processScope(c, ofScope, maybeVoid(it[^1], ofScope))
                    else:
-                     processScopeExpr(c, ofScope, it[^1], processCall)
+                     processScopeExpr(c, ofScope, it[^1], processCall, tmpFlags)
       result.add branch
 
   of nkWhileStmt:
@@ -638,7 +666,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
     result = copyNode(n)
     result.add p(n[0], c, s, normal)
     dec c.inLoopCond
-    var bodyScope = nestedScope(s)
+    var bodyScope = nestedScope(s, n[1])
     let bodyResult = p(n[1], c, bodyScope, normal)
     result.add processScope(c, bodyScope, bodyResult)
     dec c.inLoop
@@ -650,7 +678,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
     for i in 0..<last-1:
       result[i] = n[i]
     result[last-1] = p(n[last-1], c, s, normal)
-    var bodyScope = nestedScope(s)
+    var bodyScope = nestedScope(s, n[1])
     let bodyResult = p(n[last], c, bodyScope, normal)
     result[last] = processScope(c, bodyScope, bodyResult)
     dec c.inLoop
@@ -658,51 +686,71 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) =
   of nkBlockStmt, nkBlockExpr:
     result = copyNode(n)
     result.add n[0]
-    var bodyScope = nestedScope(s)
+    var bodyScope = nestedScope(s, n[1])
     result.add if n[1].typ.isEmptyType or willProduceStmt:
                  processScope(c, bodyScope, processCall(n[1], bodyScope))
                else:
-                 processScopeExpr(c, bodyScope, n[1], processCall)
+                 processScopeExpr(c, bodyScope, n[1], processCall, tmpFlags)
 
   of nkIfStmt, nkIfExpr:
     result = copyNode(n)
     for i in 0..<n.len:
       let it = n[i]
       var branch = shallowCopy(it)
-      var branchScope = nestedScope(s)
+      var branchScope = nestedScope(s, it.lastSon)
       if it.kind in {nkElifBranch, nkElifExpr}:
         #Condition needs to be destroyed outside of the condition/branch scope
         branch[0] = p(it[0], c, s, normal)
 
-      branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt:
+      branch[^1] = if n.typ.isEmptyType or it[^1].typ.isEmptyType or willProduceStmt:
                      processScope(c, branchScope, maybeVoid(it[^1], branchScope))
                    else:
-                     processScopeExpr(c, branchScope, it[^1], processCall)
+                     processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
       result.add branch
 
   of nkTryStmt:
     result = copyNode(n)
-    var tryScope = nestedScope(s)
+    var tryScope = nestedScope(s, n[0])
     result.add if n[0].typ.isEmptyType or willProduceStmt:
                  processScope(c, tryScope, maybeVoid(n[0], tryScope))
                else:
-                 processScopeExpr(c, tryScope, n[0], maybeVoid)
+                 processScopeExpr(c, tryScope, n[0], maybeVoid, tmpFlags)
 
     for i in 1..<n.len:
       let it = n[i]
       var branch = copyTree(it)
-      var branchScope = nestedScope(s)
+      var branchScope = nestedScope(s, it[^1])
       branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally:
                      processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal)
                                                   else: maybeVoid(it[^1], branchScope))
                    else:
-                     processScopeExpr(c, branchScope, it[^1], processCall)
+                     processScopeExpr(c, branchScope, it[^1], processCall, tmpFlags)
       result.add branch
 
   of nkWhen: # This should be a "when nimvm" node.
     result = copyTree(n)
     result[1][0] = processCall(n[1][0], s)
-  else: assert(false)
+
+  of nkPragmaBlock:
+    var inUncheckedAssignSection = 0
+    let pragmaList = n[0]
+    for pi in pragmaList:
+      if whichPragma(pi) == wCast:
+        case whichPragma(pi[1])
+        of wUncheckedAssign:
+          inUncheckedAssignSection = 1
+        else:
+          discard
+    result = shallowCopy(n)
+    inc c.inUncheckedAssignSection, inUncheckedAssignSection
+    for i in 0 ..< n.len-1:
+      result[i] = p(n[i], c, s, normal)
+    result[^1] = maybeVoid(n[^1], s)
+    dec c.inUncheckedAssignSection, inUncheckedAssignSection
+
+  else:
+    result = nil
+    assert(false)
 
 proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
   if optOwnedRefs in c.graph.config.globalOptions and n[0].kind != nkEmpty:
@@ -712,9 +760,9 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
       result.add call
     else:
       let tmp = c.getTemp(s, n[0].typ, n.info)
-      var m = c.genCopyNoCheck(tmp, n[0])
+      var m = c.genCopyNoCheck(tmp, n[0], attachedAsgn)
       m.add p(n[0], c, s, normal)
-      c.finishCopy(m, n[0], isFromSink = false)
+      c.finishCopy(m, n[0], {}, isFromSink = false)
       result = newTree(nkStmtList, c.genWasMoved(tmp), m)
       var toDisarm = n[0]
       if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon
@@ -729,11 +777,23 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode =
       result.add copyNode(n[0])
   s.needsTry = true
 
-proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
+template isCustomDestructor(c: Con, t: PType): bool =
+  hasDestructor(c, t) and
+          getAttachedOp(c.graph, t, attachedDestructor) != nil and
+          sfOverridden in getAttachedOp(c.graph, t, attachedDestructor).flags
+
+proc hasCustomDestructor(c: Con, t: PType): bool =
+  result = isCustomDestructor(c, t)
+  var obj = t
+  while obj.baseClass != nil:
+    obj = skipTypes(obj.baseClass, abstractPtrs)
+    result = result or isCustomDestructor(c, obj)
+
+proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode; tmpFlags = {sfSingleUsedTemp}; inReturn = false): PNode =
   if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt,
-                nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt}:
+                nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkParForStmt, nkTryStmt, nkPragmaBlock}:
     template process(child, s): untyped = p(child, c, s, mode)
-    handleNestedTempl(n, process)
+    handleNestedTempl(n, process, tmpFlags = tmpFlags)
   elif mode == sinkArg:
     if n.containsConstSeq:
       # const sequences are not mutable and so we need to pass a copy to the
@@ -742,9 +802,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       result = passCopyToSink(n, c, s)
     elif n.kind in {nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkNilLit} +
          nkCallKinds + nkLiterals:
-      result = p(n, c, s, consumed)
+      if n.kind in nkCallKinds and n[0].kind == nkSym:
+        if n[0].sym.magic == mEnsureMove:
+          inc c.inEnsureMove
+          result = p(n[1], c, s, sinkArg)
+          dec c.inEnsureMove
+        else:
+          result = p(n, c, s, consumed)
+      else:
+        result = p(n, c, s, consumed)
     elif ((n.kind == nkSym and isSinkParam(n.sym)) or isAnalysableFieldAccess(n, c.owner)) and
-        isLastRead(n, c) and not (n.kind == nkSym and isCursor(n)):
+        isLastRead(n, c, s) and not (n.kind == nkSym and isCursor(n)):
       # Sinked params can be consumed only once. We need to reset the memory
       # to disable the destructor which we have not elided
       result = destructiveMoveVar(n, c, s)
@@ -762,6 +830,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
     elif n.kind in {nkObjDownConv, nkObjUpConv}:
       result = copyTree(n)
       result[0] = p(n[0], c, s, sinkArg)
+    elif n.kind == nkCast and n.typ.skipTypes(abstractInst).kind in {tyString, tySequence}:
+      result = copyTree(n)
+      result[1] = p(n[1], c, s, sinkArg)
     elif n.typ == nil:
       # 'raise X' can be part of a 'case' expression. Deal with it here:
       result = p(n, c, s, normal)
@@ -805,23 +876,31 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       for i in 1..<n.len:
         if n[i].kind == nkExprColonExpr:
           let field = lookupFieldAgain(t, n[i][0].sym)
-          if field != nil and sfCursor in field.flags:
+          if field != nil and (sfCursor in field.flags or field.typ.kind in {tyOpenArray, tyVarargs}):
+            # don't sink fields with openarray types
             result[i][1] = p(n[i][1], c, s, normal)
           else:
             result[i][1] = p(n[i][1], c, s, m)
         else:
           result[i] = p(n[i], c, s, m)
-      if mode == normal and isRefConstr:
+      if mode == normal and (isRefConstr or hasCustomDestructor(c, t)):
         result = ensureDestruction(result, n, c, s)
     of nkCallKinds:
+      if n[0].kind == nkSym and n[0].sym.magic == mEnsureMove:
+        inc c.inEnsureMove
+        result = p(n[1], c, s, sinkArg)
+        dec c.inEnsureMove
+        return
+
       let inSpawn = c.inSpawn
       if n[0].kind == nkSym and n[0].sym.magic == mSpawn:
         c.inSpawn.inc
       elif c.inSpawn > 0:
         c.inSpawn.dec
 
-      let parameters = n[0].typ
-      let L = if parameters != nil: parameters.len else: 0
+      # bug #23907; skips tyGenericInst for generic callbacks
+      let parameters = if n[0].typ != nil: n[0].typ.skipTypes(abstractInst) else: n[0].typ
+      let L = if parameters != nil: parameters.signatureLen else: 0
 
       when false:
         var isDangerous = false
@@ -844,14 +923,17 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
 
       if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}:
         result[0] = copyTree(n[0])
-        if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}:
+        if c.graph.config.selectedGC in {gcHooks, gcArc, gcAtomicArc, gcOrc}:
           let destroyOld = c.genDestroy(result[1])
           result = newTree(nkStmtList, destroyOld, result)
       else:
         result[0] = p(n[0], c, s, normal)
       if canRaise(n[0]): s.needsTry = true
       if mode == normal:
-        result = ensureDestruction(result, n, c, s)
+        if result.typ != nil and result.typ.kind notin {tyOpenArray, tyVarargs}:
+          # Returns of openarray types shouldn't be destroyed
+          # bug #19435; # bug #23247
+          result = ensureDestruction(result, n, c, s)
     of nkDiscardStmt: # Small optimization
       result = shallowCopy(n)
       if n[0].kind != nkEmpty:
@@ -864,32 +946,45 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       for it in n:
         var ri = it[^1]
         if it.kind == nkVarTuple and hasDestructor(c, ri.typ):
+          for i in 0..<it.len-2:
+            if it[i].kind == nkSym: s.locals.add it[i].sym
           let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner)
           result.add p(x, c, s, consumed)
-        elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ):
+        elif it.kind == nkIdentDefs and hasDestructor(c, skipPragmaExpr(it[0]).typ):
           for j in 0..<it.len-2:
-            let v = it[j]
+            let v = skipPragmaExpr(it[j])
             if v.kind == nkSym:
               if sfCompileTime in v.sym.flags: continue
+              s.locals.add v.sym
               pVarTopLevel(v, c, s, result)
             if ri.kind != nkEmpty:
-              result.add moveOrCopy(v, ri, c, s, isDecl = v.kind == nkSym)
+              result.add moveOrCopy(v, ri, c, s, if v.kind == nkSym: {IsDecl} else: {})
             elif ri.kind == nkEmpty and c.inLoop > 0:
-              result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = v.kind == nkSym)
+              let skipInit = v.kind == nkDotExpr and # Closure var
+                             sfNoInit in v[1].sym.flags
+              if not skipInit:
+                result.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, if v.kind == nkSym: {IsDecl} else: {})
         else: # keep the var but transform 'ri':
           var v = copyNode(n)
           var itCopy = copyNode(it)
           for j in 0..<it.len-1:
             itCopy.add it[j]
-          itCopy.add p(it[^1], c, s, normal)
+          var flags = {sfSingleUsedTemp}
+          if it.kind == nkIdentDefs and it.len == 3 and it[0].kind == nkSym and
+                                        sfGlobal in it[0].sym.flags:
+            flags.incl sfGlobal
+          itCopy.add p(it[^1], c, s, normal, tmpFlags = flags)
           v.add itCopy
           result.add v
-    of nkAsgn, nkFastAsgn:
+    of nkAsgn, nkFastAsgn, nkSinkAsgn:
       if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda}:
         if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}:
           cycleCheck(n, c)
-        assert n[1].kind notin {nkAsgn, nkFastAsgn}
-        result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s)
+        assert n[1].kind notin {nkAsgn, nkFastAsgn, nkSinkAsgn}
+        var flags = if n.kind == nkSinkAsgn: {IsExplicitSink} else: {}
+        if inReturn:
+          flags.incl(IsReturn)
+        result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s, flags)
       elif isDiscriminantField(n[0]):
         result = c.genDiscriminantAsgn(s, n)
       else:
@@ -908,7 +1003,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
        nkTypeOfExpr, nkMixinStmt, nkBindStmt:
       result = n
 
-    of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock:
+    of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange:
       result = shallowCopy(n)
       for i in 0 ..< n.len:
         result[i] = p(n[i], c, s, normal)
@@ -943,7 +1038,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       for i in 1 ..< n.len:
         result[i] = n[i]
       if mode == sinkArg and hasDestructor(c, n.typ):
-        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
+        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
           s.wasMoved.add c.genWasMoved(n)
         else:
           result = passCopyToSink(result, c, s)
@@ -953,7 +1048,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
       for i in 0 ..< n.len:
         result[i] = p(n[i], c, s, normal)
       if mode == sinkArg and hasDestructor(c, n.typ):
-        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c):
+        if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c, s):
           # consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])'
           # without the junk, hence 'c.genWasMoved(n)'
           # and not 'c.genWasMoved(result)':
@@ -972,7 +1067,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
     of nkReturnStmt:
       result = shallowCopy(n)
       for i in 0..<n.len:
-        result[i] = p(n[i], c, s, mode)
+        result[i] = p(n[i], c, s, mode, inReturn=true)
       s.needsTry = true
     of nkCast:
       result = shallowCopy(n)
@@ -986,11 +1081,12 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode =
     of nkGotoState, nkState, nkAsmStmt:
       result = n
     else:
+      result = nil
       internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind)
 
 proc sameLocation*(a, b: PNode): bool =
   proc sameConstant(a, b: PNode): bool =
-    a.kind in nkLiterals and a.intVal == b.intVal
+    a.kind in nkLiterals and b.kind in nkLiterals and a.intVal == b.intVal
 
   const nkEndPoint = {nkSym, nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}
   if a.kind in nkEndPoint and b.kind in nkEndPoint:
@@ -1014,9 +1110,9 @@ proc sameLocation*(a, b: PNode): bool =
     of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b)
     else: false
 
-proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode, isDecl: bool): PNode =
+proc genFieldAccessSideEffects(c: var Con; s: var Scope; dest, ri: PNode; flags: set[MoveOrCopyFlag] = {}): PNode =
   # with side effects
-  var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), nextSymId c.idgen, c.owner, ri[1].info)
+  var temp = newSym(skLet, getIdent(c.graph.cache, "bracketTmp"), c.idgen, c.owner, ri[1].info)
   temp.typ = ri[1].typ
   var v = newNodeI(nkLetSection, ri[1].info)
   let tempAsNode = newSymNode(temp)
@@ -1031,129 +1127,142 @@ proc genFieldAccessSideEffects(c: var Con; dest, ri: PNode, isDecl: bool): PNode
   newAccess.add ri[0]
   newAccess.add tempAsNode
 
-  var snk = c.genSink(dest, newAccess, isDecl)
+  var snk = c.genSink(s, dest, newAccess, flags)
   result = newTree(nkStmtList, v, snk, c.genWasMoved(newAccess))
 
-proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode =
+proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, flags: set[MoveOrCopyFlag] = {}): PNode =
+  var ri = ri
+  var isEnsureMove = 0
+  if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mEnsureMove:
+    ri = ri[1]
+    isEnsureMove = 1
   if sameLocation(dest, ri):
     # rule (self-assignment-removal):
     result = newNodeI(nkEmpty, dest.info)
-  elif isCursor(dest):
+  elif isCursor(dest) or dest.typ.kind in {tyOpenArray, tyVarargs}:
+    # hoisted openArray parameters might end up here
+    # openArray types don't have a lifted assignment operation (it's empty)
+    # bug #22132
     case ri.kind:
     of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
-      template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl)
+      template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
       # We know the result will be a stmt so we use that fact to optimize
       handleNestedTempl(ri, process, willProduceStmt = true)
     else:
       result = newTree(nkFastAsgn, dest, p(ri, c, s, normal))
   else:
-    case ri.kind
+    let ri2 = if ri.kind == nkWhen: ri[1][0] else: ri
+    case ri2.kind
     of nkCallKinds:
-      result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
+      result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
     of nkBracketExpr:
       if isUnpackedTuple(ri[0]):
         # unpacking of tuple: take over the elements
-        result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
-      elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c):
+        result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
+      elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s):
         if aliases(dest, ri) == no:
           # Rule 3: `=sink`(x, z); wasMoved(z)
           if isAtom(ri[1]):
-            var snk = c.genSink(dest, ri, isDecl)
+            var snk = c.genSink(s, dest, ri, flags)
             result = newTree(nkStmtList, snk, c.genWasMoved(ri))
           else:
-            result = genFieldAccessSideEffects(c, dest, ri, isDecl)
+            result = genFieldAccessSideEffects(c, s, dest, ri, flags)
         else:
-          result = c.genSink(dest, destructiveMoveVar(ri, c, s), isDecl)
+          result = c.genSink(s, dest, destructiveMoveVar(ri, c, s), flags)
       else:
-        result = c.genCopy(dest, ri)
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
     of nkBracket:
       # array constructor
       if ri.len > 0 and isDangerousSeq(ri.typ):
-        result = c.genCopy(dest, ri)
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
       else:
-        result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
+        result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
     of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit:
-      result = c.genSink(dest, p(ri, c, s, consumed), isDecl)
+      result = c.genSink(s, dest, p(ri, c, s, consumed), flags)
     of nkSym:
-      if isSinkParam(ri.sym) and isLastRead(ri, c):
+      if isSinkParam(ri.sym) and isLastRead(ri, c, s):
         # Rule 3: `=sink`(x, z); wasMoved(z)
-        let snk = c.genSink(dest, ri, isDecl)
+        let snk = c.genSink(s, dest, ri, flags)
         result = newTree(nkStmtList, snk, c.genWasMoved(ri))
-      elif ri.sym.kind != skParam and ri.sym.owner == c.owner and
-          isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri):
+      elif ri.sym.kind != skParam and
+          isAnalysableFieldAccess(ri, c.owner) and
+          isLastRead(ri, c, s) and canBeMoved(c, dest.typ):
         # Rule 3: `=sink`(x, z); wasMoved(z)
-        let snk = c.genSink(dest, ri, isDecl)
+        let snk = c.genSink(s, dest, ri, flags)
         result = newTree(nkStmtList, snk, c.genWasMoved(ri))
       else:
-        result = c.genCopy(dest, ri)
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
     of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast:
-      result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl)
+      result = c.genSink(s, dest, p(ri, c, s, sinkArg), flags)
     of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt:
-      template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl)
+      template process(child, s): untyped = moveOrCopy(dest, child, c, s, flags)
       # We know the result will be a stmt so we use that fact to optimize
       handleNestedTempl(ri, process, willProduceStmt = true)
     of nkRaiseStmt:
       result = pRaiseStmt(ri, c, s)
     else:
-      if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and
+      if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c, s) and
           canBeMoved(c, dest.typ):
         # Rule 3: `=sink`(x, z); wasMoved(z)
-        let snk = c.genSink(dest, ri, isDecl)
+        let snk = c.genSink(s, dest, ri, flags)
         result = newTree(nkStmtList, snk, c.genWasMoved(ri))
       else:
-        result = c.genCopy(dest, ri)
+        inc c.inEnsureMove, isEnsureMove
+        result = c.genCopy(dest, ri, flags)
+        dec c.inEnsureMove, isEnsureMove
         result.add p(ri, c, s, consumed)
-        c.finishCopy(result, dest, isFromSink = false)
+        c.finishCopy(result, dest, flags, isFromSink = false)
 
-proc computeUninit(c: var Con) =
-  if not c.uninitComputed:
-    c.uninitComputed = true
-    c.uninit = initIntSet()
-    var init = initIntSet()
-    discard initialized(c.g, pc = 0, init, c.uninit, int.high)
+when false:
+  proc computeUninit(c: var Con) =
+    if not c.uninitComputed:
+      c.uninitComputed = true
+      c.uninit = initIntSet()
+      var init = initIntSet()
+      discard initialized(c.g, pc = 0, init, c.uninit, int.high)
 
-proc injectDefaultCalls(n: PNode, c: var Con) =
-  case n.kind
-  of nkVarSection, nkLetSection:
-    for it in n:
-      if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
-        computeUninit(c)
-        for j in 0..<it.len-2:
-          let v = it[j]
-          doAssert v.kind == nkSym
-          if c.uninit.contains(v.sym.id):
-            it[^1] = genDefaultCall(v.sym.typ, c, v.info)
-            break
-  of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
-      nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
-    discard
-  else:
-    for i in 0..<n.safeLen:
-      injectDefaultCalls(n[i], c)
+  proc injectDefaultCalls(n: PNode, c: var Con) =
+    case n.kind
+    of nkVarSection, nkLetSection:
+      for it in n:
+        if it.kind == nkIdentDefs and it[^1].kind == nkEmpty:
+          computeUninit(c)
+          for j in 0..<it.len-2:
+            let v = skipPragmaExpr(it[j])
+            doAssert v.kind == nkSym
+            if c.uninit.contains(v.sym.id):
+              it[^1] = genDefaultCall(v.sym.typ, c, v.info)
+              break
+    of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef,
+        nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef:
+      discard
+    else:
+      for i in 0..<n.safeLen:
+        injectDefaultCalls(n[i], c)
 
 proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode =
   when toDebug.len > 0:
     shouldDebug = toDebug == owner.name.s or toDebug == "always"
   if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)):
     return n
-  var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen)
-  dbg:
-    echo "\n### ", owner.name.s, ":\nCFG:"
-    echoCfg(c.g)
-    echo n
+  var c = Con(owner: owner, graph: g, idgen: idgen, body: n, otherUsage: unknownLineInfo)
 
   if optCursorInference in g.config.options:
     computeCursors(owner, n, g)
 
-  computeLastReadsAndFirstWrites(c.g)
-
-  var scope: Scope
+  var scope = Scope(body: n)
   let body = p(n, c, scope, normal)
 
   if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}:
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 094b92a68..54a35dbee 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,11 +6,11 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64
+  linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64;loongarch64
   macosx: i386;amd64;powerpc64;arm64
   solaris: i386;amd64;sparc;sparc64
   freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el
-  netbsd: i386;amd64
+  netbsd: i386;amd64;arm64
   openbsd: i386;amd64;arm;arm64
   dragonfly: i386;amd64
   crossos: amd64
@@ -65,11 +65,13 @@ Files: "compiler"
 Files: "doc"
 Files: "doc/html"
 Files: "tools"
-Files: "tools/nim-gdb.py"
+Files: "tools/debug/nim-gdb.py"
 Files: "nimpretty"
 Files: "testament"
 Files: "nimsuggest"
 Files: "nimsuggest/tests/*.nim"
+Files: "changelogs/*.md"
+Files: "ci/funs.sh"
 
 [Lib]
 Files: "lib"
@@ -77,6 +79,7 @@ Files: "lib"
 [Other]
 Files: "examples"
 Files: "dist/nimble"
+Files: "dist/checksums"
 
 Files: "tests"
 
@@ -90,6 +93,7 @@ Files: "bin/nimgrab.exe"
 Files: "bin/nimpretty.exe"
 Files: "bin/testament.exe"
 Files: "bin/nim-gdb.bat"
+Files: "bin/atlas.exe"
 
 Files: "koch.exe"
 Files: "finish.exe"
@@ -120,6 +124,7 @@ Files: "bin/nim"
 InstallScript: "yes"
 UninstallScript: "yes"
 Files: "bin/nim-gdb"
+Files: "build_all.sh"
 
 
 [InnoSetup]
@@ -142,4 +147,4 @@ licenses: "bin/nim,MIT;lib/*,MIT;"
 
 [nimble]
 pkgName: "nim"
-pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css"
+pkgFiles: "compiler/*;doc/basicopt.txt;doc/advopt.txt;doc/nimdoc.css;doc/nimdoc.cls"
diff --git a/compiler/int128.nim b/compiler/int128.nim
index afa07094b..74e581cd5 100644
--- a/compiler/int128.nim
+++ b/compiler/int128.nim
@@ -3,7 +3,7 @@
 ## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This
 ## type is for that purpose.
 
-from math import trunc
+from std/math import trunc
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -33,39 +33,35 @@ template high*(t: typedesc[Int128]): Int128 = Max
 proc `$`*(a: Int128): string
 
 proc toInt128*[T: SomeInteger | bool](arg: T): Int128 =
-  when T is bool: result.sdata(0) = int32(arg)
-  elif T is SomeUnsignedInt:
-    when sizeof(arg) <= 4:
-      result.udata[0] = uint32(arg)
+  {.noSideEffect.}:
+    result = Zero
+    when T is bool: result.sdata(0) = int32(arg)
+    elif T is SomeUnsignedInt:
+      when sizeof(arg) <= 4:
+        result.udata[0] = uint32(arg)
+      else:
+        result.udata[0] = uint32(arg and T(0xffffffff))
+        result.udata[1] = uint32(arg shr 32)
+    elif sizeof(arg) <= 4:
+      result.sdata(0) = int32(arg)
+      if arg < 0: # sign extend
+        result.sdata(1) = -1
+        result.sdata(2) = -1
+        result.sdata(3) = -1
     else:
-      result.udata[0] = uint32(arg and T(0xffffffff))
-      result.udata[1] = uint32(arg shr 32)
-  elif sizeof(arg) <= 4:
-    result.sdata(0) = int32(arg)
-    if arg < 0: # sign extend
-      result.sdata(1) = -1
-      result.sdata(2) = -1
-      result.sdata(3) = -1
-  else:
-    let tmp = int64(arg)
-    result.udata[0] = uint32(tmp and 0xffffffff)
-    result.sdata(1) = int32(tmp shr 32)
-    if arg < 0: # sign extend
-      result.sdata(2) = -1
-      result.sdata(3) = -1
+      let tmp = int64(arg)
+      result.udata[0] = uint32(tmp and 0xffffffff)
+      result.sdata(1) = int32(tmp shr 32)
+      if arg < 0: # sign extend
+        result.sdata(2) = -1
+        result.sdata(3) = -1
 
 template isNegative(arg: Int128): bool =
   arg.sdata(3) < 0
 
-template isNegative(arg: int32): bool =
-  arg < 0
-
 proc bitconcat(a, b: uint32): uint64 =
   (uint64(a) shl 32) or uint64(b)
 
-proc bitsplit(a: uint64): (uint32, uint32) =
-  (cast[uint32](a shr 32), cast[uint32](a))
-
 proc toInt64*(arg: Int128): int64 =
   if isNegative(arg):
     assert(arg.sdata(3) == -1, "out of range")
@@ -176,6 +172,7 @@ proc addToHex*(result: var string; arg: Int128) =
     i -= 1
 
 proc toHex*(arg: Int128): string =
+  result = ""
   result.addToHex(arg)
 
 proc inc*(a: var Int128, y: uint32 = 1) =
@@ -211,37 +208,36 @@ proc `==`*(a, b: Int128): bool =
   if a.udata[3] != b.udata[3]: return false
   return true
 
-proc inplaceBitnot(a: var Int128) =
-  a.udata[0] = not a.udata[0]
-  a.udata[1] = not a.udata[1]
-  a.udata[2] = not a.udata[2]
-  a.udata[3] = not a.udata[3]
-
 proc bitnot*(a: Int128): Int128 =
+  result = Zero
   result.udata[0] = not a.udata[0]
   result.udata[1] = not a.udata[1]
   result.udata[2] = not a.udata[2]
   result.udata[3] = not a.udata[3]
 
 proc bitand*(a, b: Int128): Int128 =
+  result = Zero
   result.udata[0] = a.udata[0] and b.udata[0]
   result.udata[1] = a.udata[1] and b.udata[1]
   result.udata[2] = a.udata[2] and b.udata[2]
   result.udata[3] = a.udata[3] and b.udata[3]
 
 proc bitor*(a, b: Int128): Int128 =
+  result = Zero
   result.udata[0] = a.udata[0] or b.udata[0]
   result.udata[1] = a.udata[1] or b.udata[1]
   result.udata[2] = a.udata[2] or b.udata[2]
   result.udata[3] = a.udata[3] or b.udata[3]
 
 proc bitxor*(a, b: Int128): Int128 =
+  result = Zero
   result.udata[0] = a.udata[0] xor b.udata[0]
   result.udata[1] = a.udata[1] xor b.udata[1]
   result.udata[2] = a.udata[2] xor b.udata[2]
   result.udata[3] = a.udata[3] xor b.udata[3]
 
 proc `shr`*(a: Int128, b: int): Int128 =
+  result = Zero
   let b = b and 127
   if b < 32:
     result.sdata(3) = a.sdata(3) shr b
@@ -268,6 +264,7 @@ proc `shr`*(a: Int128, b: int): Int128 =
     result.sdata(0) = a.sdata(3) shr (b and 31)
 
 proc `shl`*(a: Int128, b: int): Int128 =
+  result = Zero
   let b = b and 127
   if b < 32:
     result.udata[0] = a.udata[0] shl b
@@ -291,6 +288,7 @@ proc `shl`*(a: Int128, b: int): Int128 =
     result.udata[3] = a.udata[0] shl (b and 31)
 
 proc `+`*(a, b: Int128): Int128 =
+  result = Zero
   let tmp0 = uint64(a.udata[0]) + uint64(b.udata[0])
   result.udata[0] = cast[uint32](tmp0)
   let tmp1 = uint64(a.udata[1]) + uint64(b.udata[1]) + (tmp0 shr 32)
@@ -323,6 +321,7 @@ proc abs(a: int32): int =
   if a < 0: -a else: a
 
 proc `*`(a: Int128, b: uint32): Int128 =
+  result = Zero
   let tmp0 = uint64(a.udata[0]) * uint64(b)
   let tmp1 = uint64(a.udata[1]) * uint64(b)
   let tmp2 = uint64(a.udata[2]) * uint64(b)
@@ -341,10 +340,11 @@ proc `*`*(a: Int128, b: int32): Int128 =
   if b < 0:
     result = -result
 
-proc `*=`*(a: var Int128, b: int32): Int128 =
-  result = result * b
+proc `*=`(a: var Int128, b: int32) =
+  a = a * b
 
 proc makeInt128(high, low: uint64): Int128 =
+  result = Zero
   result.udata[0] = cast[uint32](low)
   result.udata[1] = cast[uint32](low shr 32)
   result.udata[2] = cast[uint32](high)
@@ -357,23 +357,10 @@ proc low64(a: Int128): uint64 =
   bitconcat(a.udata[1], a.udata[0])
 
 proc `*`*(lhs, rhs: Int128): Int128 =
-  let
-    a = cast[uint64](lhs.udata[0])
-    b = cast[uint64](lhs.udata[1])
-    c = cast[uint64](lhs.udata[2])
-    d = cast[uint64](lhs.udata[3])
-
-    e = cast[uint64](rhs.udata[0])
-    f = cast[uint64](rhs.udata[1])
-    g = cast[uint64](rhs.udata[2])
-    h = cast[uint64](rhs.udata[3])
-
-
-  let a32 = cast[uint64](lhs.udata[1])
-  let a00 = cast[uint64](lhs.udata[0])
-  let b32 = cast[uint64](rhs.udata[1])
-  let b00 = cast[uint64](rhs.udata[0])
-
+  let a32 = uint64(lhs.udata[1])
+  let a00 = uint64(lhs.udata[0])
+  let b32 = uint64(rhs.udata[1])
+  let b00 = uint64(rhs.udata[0])
   result = makeInt128(high64(lhs) * low64(rhs) + low64(lhs) * high64(rhs) + a32 * b32, a00 * b00)
   result += toInt128(a32 * b00) shl 32
   result += toInt128(a00 * b32) shl 32
@@ -381,9 +368,10 @@ proc `*`*(lhs, rhs: Int128): Int128 =
 proc `*=`*(a: var Int128, b: Int128) =
   a = a * b
 
-import bitops
+import std/bitops
 
 proc fastLog2*(a: Int128): int =
+  result = 0
   if a.udata[3] != 0:
     return 96 + fastLog2(a.udata[3])
   if a.udata[2] != 0:
@@ -395,6 +383,8 @@ proc fastLog2*(a: Int128): int =
 
 proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
   assert(divisor != Zero)
+  result = (Zero, Zero)
+
   let isNegativeA = isNegative(dividend)
   let isNegativeB = isNegative(divisor)
 
@@ -444,17 +434,17 @@ proc divMod*(dividend, divisor: Int128): tuple[quotient, remainder: Int128] =
     result.remainder = dividend
 
 proc `div`*(a, b: Int128): Int128 =
-  let (a, b) = divMod(a, b)
+  let (a, _) = divMod(a, b)
   return a
 
 proc `mod`*(a, b: Int128): Int128 =
-  let (a, b) = divMod(a, b)
+  let (_, b) = divMod(a, b)
   return b
 
 proc addInt128*(result: var string; value: Int128) =
   let initialSize = result.len
   if value == Zero:
-    result.add "0"
+    result.add '0'
   elif value == low(Int128):
     result.add "-170141183460469231731687303715884105728"
   else:
@@ -475,6 +465,8 @@ proc addInt128*(result: var string; value: Int128) =
       j -= 1
 
 proc `$`*(a: Int128): string =
+  # "-170141183460469231731687303715884105728".len == 41
+  result = newStringOfCap(41)
   result.addInt128(a)
 
 proc parseDecimalInt128*(arg: string, pos: int = 0): Int128 =
@@ -559,24 +551,28 @@ proc toInt128*(arg: float64): Int128 =
     return res
 
 proc maskUInt64*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
   result.udata[0] = arg.udata[0]
   result.udata[1] = arg.udata[1]
   result.udata[2] = 0
   result.udata[3] = 0
 
 proc maskUInt32*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
   result.udata[0] = arg.udata[0]
   result.udata[1] = 0
   result.udata[2] = 0
   result.udata[3] = 0
 
 proc maskUInt16*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
   result.udata[0] = arg.udata[0] and 0xffff
   result.udata[1] = 0
   result.udata[2] = 0
   result.udata[3] = 0
 
 proc maskUInt8*(arg: Int128): Int128 {.noinit, inline.} =
+  result = Zero
   result.udata[0] = arg.udata[0] and 0xff
   result.udata[1] = 0
   result.udata[2] = 0
@@ -593,4 +589,4 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} =
   of 8:
     return maskUInt64(arg)
   else:
-    assert(false, "masking only implemented for 1, 2, 4 and 8 bytes")
+    raiseAssert "masking only implemented for 1, 2, 4 and 8 bytes"
diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim
index 2674605dc..17fbde29e 100644
--- a/compiler/isolation_check.nim
+++ b/compiler/isolation_check.nim
@@ -11,7 +11,9 @@
 ## https://github.com/nim-lang/RFCs/issues/244 for more details.
 
 import
-  ast, types, renderer, intsets
+  ast, types, renderer
+
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -21,6 +23,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool
 proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
   case n.kind
   of nkRecList:
+    result = false
     for i in 0..<n.len:
       result = canAliasN(arg, n[i], marker)
       if result: return
@@ -36,7 +39,7 @@ proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool =
       else: discard
   of nkSym:
     result = canAlias(arg, n.sym.typ, marker)
-  else: discard
+  else: result = false
 
 proc canAlias(arg, ret: PType; marker: var IntSet): bool =
   if containsOrIncl(marker, ret.id):
@@ -51,17 +54,18 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool =
   of tyObject:
     if isFinal(ret):
       result = canAliasN(arg, ret.n, marker)
-      if not result and ret.len > 0 and ret[0] != nil:
-        result = canAlias(arg, ret[0], marker)
+      if not result and ret.baseClass != nil:
+        result = canAlias(arg, ret.baseClass, marker)
     else:
       result = true
   of tyTuple:
-    for i in 0..<ret.len:
-      result = canAlias(arg, ret[i], marker)
+    result = false
+    for r in ret.kids:
+      result = canAlias(arg, r, marker)
       if result: break
   of tyArray, tySequence, tyDistinct, tyGenericInst,
      tyAlias, tyInferred, tySink, tyLent, tyOwned, tyRef:
-    result = canAlias(arg, ret.lastSon, marker)
+    result = canAlias(arg, ret.skipModifier, marker)
   of tyProc:
     result = ret.callConv == ccClosure
   else:
@@ -72,6 +76,71 @@ proc isValueOnlyType(t: PType): bool =
   proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent}
   result = not types.searchTypeFor(t, wrap)
 
+type
+  SearchResult = enum
+    NotFound, Abort, Found
+
+proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult
+
+proc containsDangerousRefAux(n: PNode; marker: var IntSet): SearchResult =
+  result = NotFound
+  case n.kind
+  of nkRecList:
+    for i in 0..<n.len:
+      result = containsDangerousRefAux(n[i], marker)
+      if result == Found: return result
+  of nkRecCase:
+    assert(n[0].kind == nkSym)
+    result = containsDangerousRefAux(n[0], marker)
+    if result == Found: return result
+    for i in 1..<n.len:
+      case n[i].kind
+      of nkOfBranch, nkElse:
+        result = containsDangerousRefAux(lastSon(n[i]), marker)
+        if result == Found: return result
+      else: discard
+  of nkSym:
+    result = containsDangerousRefAux(n.sym.typ, marker)
+  else: discard
+
+proc containsDangerousRefAux(t: PType; marker: var IntSet): SearchResult =
+  result = NotFound
+  if t == nil: return result
+  if containsOrIncl(marker, t.id): return result
+
+  if t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure):
+    result = Found
+  elif tfSendable in t.flags:
+    result = Abort
+  else:
+    # continue the type traversal:
+    result = NotFound
+
+  if result != NotFound: return result
+  case t.kind
+  of tyObject:
+    if t.baseClass != nil:
+      result = containsDangerousRefAux(t.baseClass.skipTypes(skipPtrs), marker)
+    if result == NotFound: result = containsDangerousRefAux(t.n, marker)
+  of tyGenericInst, tyDistinct, tyAlias, tySink:
+    result = containsDangerousRefAux(skipModifier(t), marker)
+  of tyArray, tySet, tySequence:
+    result = containsDangerousRefAux(t.elementType, marker)
+  of tyTuple:
+    for a in t.kids:
+      result = containsDangerousRefAux(a, marker)
+      if result == Found: return result
+  else:
+    discard
+
+proc containsDangerousRef(t: PType): bool =
+  # a `ref` type is "dangerous" if it occurs not within a type that is like `Isolated[T]`.
+  # For example:
+  # `ref int` # dangerous
+  # `Isolated[ref int]` # not dangerous
+  var marker = initIntSet()
+  result = containsDangerousRefAux(t, marker) == Found
+
 proc canAlias*(arg, ret: PType): bool =
   if isValueOnlyType(arg):
     # can alias only with addr(arg.x) and we don't care if it is not safe
@@ -80,12 +149,15 @@ proc canAlias*(arg, ret: PType): bool =
     var marker = initIntSet()
     result = canAlias(arg, ret, marker)
 
+const
+  SomeVar = {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
+
 proc containsVariable(n: PNode): bool =
   case n.kind
   of nodesToIgnoreSet:
     result = false
   of nkSym:
-    result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp}
+    result = n.sym.kind in SomeVar
   else:
     for ch in n:
       if containsVariable(ch): return true
@@ -109,7 +181,7 @@ proc checkIsolate*(n: PNode): bool =
           discard "fine, it is isolated already"
         else:
           let argType = n[i].typ
-          if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType):
+          if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
             if argType.canAlias(n.typ) or containsVariable(n[i]):
               # bug #19013: Alias information is not enough, we need to check for potential
               # "overlaps". I claim the problem can only happen by reading again from a location
@@ -118,10 +190,12 @@ proc checkIsolate*(n: PNode): bool =
               return false
       result = true
     of nkIfStmt, nkIfExpr:
+      result = false
       for it in n:
         result = checkIsolate(it.lastSon)
         if not result: break
     of nkCaseStmt:
+      result = false
       for i in 1..<n.len:
         result = checkIsolate(n[i].lastSon)
         if not result: break
@@ -131,6 +205,7 @@ proc checkIsolate*(n: PNode): bool =
         result = checkIsolate(n[i].lastSon)
         if not result: break
     of nkBracket, nkTupleConstr, nkPar:
+      result = false
       for it in n:
         result = checkIsolate(it)
         if not result: break
@@ -143,6 +218,12 @@ proc checkIsolate*(n: PNode): bool =
         result = checkIsolate(n[^1])
       else:
         result = false
+    of nkSym:
+      result = true
+      if n.sym.kind in SomeVar:
+        let argType = n.typ
+        if argType != nil and not isCompileTimeOnly(argType) and containsDangerousRef(argType):
+          result = false
     else:
       # unanalysable expression:
       result = false
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index dc58efb82..713944def 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -31,16 +31,20 @@ implements the required case distinction.
 import
   ast, trees, magicsys, options,
   nversion, msgs, idents, types,
-  ropes, passes, ccgutils, wordrecg, renderer,
-  cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
-  transf, injectdestructors, sourcemap, astmsgs
+  ropes, wordrecg, renderer,
+  cgmeth, lowerings, sighashes, modulegraphs, lineinfos,
+  transf, injectdestructors, sourcemap, astmsgs, pushpoppragmas,
+  mangleutils
 
-import json, sets, math, tables, intsets
-import strutils except addf
+import pipelineutils
+
+import std/[json, sets, math, tables, intsets]
+import std/strutils except addf
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio]
 
+import std/formatfloat
 
 type
   TJSGen = object of PPassContext
@@ -48,6 +52,7 @@ type
     graph: ModuleGraph
     config: ConfigRef
     sigConflicts: CountTable[SigHash]
+    initProc: PProc
 
   BModule = ref TJSGen
   TJSTypeKind = enum       # necessary JS "types"
@@ -98,27 +103,33 @@ type
     prc: PSym
     globals, locals, body: Rope
     options: TOptions
+    optionsStack: seq[(TOptions, TNoteKinds)]
     module: BModule
     g: PGlobals
-    generatedParamCopies: IntSet
     beforeRetNeeded: bool
     unique: int    # for temp identifier generation
     blocks: seq[TBlock]
     extraIndent: int
+    previousFileName: string  # For frameInfo inside templates.
+    # legacy: generatedParamCopies and up fields are used for jsNoLambdaLifting
+    generatedParamCopies: IntSet
     up: PProc     # up the call chain; required for closure support
-    declaredGlobals: IntSet
 
 template config*(p: PProc): ConfigRef = p.module.config
 
 proc indentLine(p: PProc, r: Rope): Rope =
   var p = p
-  var ind = 0
-  while true:
-    inc ind, p.blocks.len + p.extraIndent
-    if p.up == nil or p.up.prc != p.prc.owner:
-      break
-    p = p.up
-  result = repeat(' ', ind*2) & r
+  if jsNoLambdaLifting in p.config.legacyFeatures:
+    var ind = 0
+    while true:
+      inc ind, p.blocks.len + p.extraIndent
+      if p.up == nil or p.up.prc != p.prc.owner:
+        break
+      p = p.up
+    result = repeat(' ', ind*2) & r
+  else:
+    let ind = p.blocks.len + p.extraIndent
+    result = repeat(' ', ind*2) & r
 
 template line(p: PProc, added: string) =
   p.body.add(indentLine(p, rope(added)))
@@ -132,17 +143,15 @@ template nested(p, body) =
   dec p.extraIndent
 
 proc newGlobals(): PGlobals =
-  new(result)
-  result.forwarded = @[]
-  result.generatedSyms = initIntSet()
-  result.typeInfoGenerated = initIntSet()
+  result = PGlobals(forwarded: @[],
+        generatedSyms: initIntSet(),
+        typeInfoGenerated: initIntSet()
+        )
 
-proc initCompRes(r: var TCompRes) =
-  r.address = ""
-  r.res = ""
-  r.tmpLoc = ""
-  r.typ = etyNone
-  r.kind = resNone
+proc initCompRes(): TCompRes =
+  result = TCompRes(address: "", res: "",
+    tmpLoc: "", typ: etyNone, kind: resNone
+  )
 
 proc rdLoc(a: TCompRes): Rope {.inline.} =
   if a.typ != etyBaseIndex:
@@ -154,6 +163,8 @@ proc newProc(globals: PGlobals, module: BModule, procDef: PNode,
              options: TOptions): PProc =
   result = PProc(
     blocks: @[],
+    optionsStack: if module.initProc != nil: module.initProc.optionsStack
+                  else: @[],
     options: options,
     module: module,
     procDef: procDef,
@@ -169,10 +180,6 @@ proc initProcOptions(module: BModule): TOptions =
 proc newInitProc(globals: PGlobals, module: BModule): PProc =
   result = newProc(globals, module, nil, initProcOptions(module))
 
-proc declareGlobal(p: PProc; id: int; r: Rope) =
-  if p.prc != nil and not p.declaredGlobals.containsOrIncl(id):
-    p.locals.addf("global $1;$n", [r])
-
 const
   MappedToObject = {tyObject, tyArray, tyTuple, tyOpenArray,
     tySet, tyVarargs}
@@ -181,16 +188,16 @@ proc mapType(typ: PType): TJSTypeKind =
   let t = skipTypes(typ, abstractInst)
   case t.kind
   of tyVar, tyRef, tyPtr:
-    if skipTypes(t.lastSon, abstractInst).kind in MappedToObject:
+    if skipTypes(t.elementType, abstractInst).kind in MappedToObject:
       result = etyObject
     else:
       result = etyBaseIndex
   of tyPointer:
     # treat a tyPointer like a typed pointer to an array of bytes
     result = etyBaseIndex
-  of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent:
+  of tyRange, tyDistinct, tyOrdinal, tyError, tyLent:
     # tyLent is no-op as JS has pass-by-reference semantics
-    result = mapType(t[0])
+    result = mapType(skipModifier t)
   of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt
   of tyBool: result = etyBool
   of tyFloat..tyFloat128: result = etyFloat
@@ -206,13 +213,14 @@ proc mapType(typ: PType): TJSTypeKind =
     result = etyNone
   of tyGenericInst, tyInferred, tyAlias, tyUserTypeClass, tyUserTypeClassInst,
      tySink, tyOwned:
-    result = mapType(typ.lastSon)
+    result = mapType(typ.skipModifier)
   of tyStatic:
-    if t.n != nil: result = mapType(lastSon t)
+    if t.n != nil: result = mapType(skipModifier t)
     else: result = etyNone
   of tyProc: result = etyProc
   of tyCstring: result = etyString
-  of tyConcept, tyIterable: doAssert false
+  of tyConcept, tyIterable:
+    raiseAssert "unreachable"
 
 proc mapType(p: PProc; typ: PType): TJSTypeKind =
   result = mapType(typ)
@@ -238,7 +246,7 @@ proc mangleName(m: BModule, s: PSym): Rope =
     for chr in name:
       if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}:
         return false
-  result = s.loc.r
+  result = s.loc.snippet
   if result == "":
     if s.kind == skField and s.name.s.validJsName:
       result = rope(s.name.s)
@@ -261,11 +269,15 @@ proc mangleName(m: BModule, s: PSym): Rope =
       if m.config.hcrOn:
         # When hot reloading is enabled, we must ensure that the names
         # of functions and types will be preserved across rebuilds:
-        result.add(idOrSig(s, m.module.name.s, m.sigConflicts))
+        result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config))
+      elif s.kind == skParam:
+        result.add mangleParamExt(s)
+      elif s.kind in routineKinds:
+        result.add mangleProcNameExt(m.graph, s)
       else:
         result.add("_")
         result.add(rope(s.id))
-    s.loc.r = result
+    s.loc.snippet = result
 
 proc escapeJSString(s: string): string =
   result = newStringOfCap(s.len + s.len shr 2)
@@ -341,8 +353,7 @@ proc isSimpleExpr(p: PProc; n: PNode): bool =
       if n[i].kind notin {nkCommentStmt, nkEmpty}: return false
     result = isSimpleExpr(p, n.lastSon)
   else:
-    if n.isAtom:
-      result = true
+    result = n.isAtom
 
 proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
   inc(p.unique)
@@ -352,7 +363,7 @@ proc getTemp(p: PProc, defineInLocals: bool = true): Rope =
 
 proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   if p.isSimpleExpr(a) and p.isSimpleExpr(b):
     gen(p, a, x)
     gen(p, b, y)
@@ -379,7 +390,7 @@ proc genAnd(p: PProc, a, b: PNode, r: var TCompRes) =
 
 proc genOr(p: PProc, a, b: PNode, r: var TCompRes) =
   assert r.kind == resNone
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   if p.isSimpleExpr(a) and p.isSimpleExpr(b):
     gen(p, a, x)
     gen(p, b, y)
@@ -458,15 +469,13 @@ const # magic checked op; magic unchecked op;
     mUnaryMinusF64: ["", ""],
     mCharToStr: ["nimCharToStr", "nimCharToStr"],
     mBoolToStr: ["nimBoolToStr", "nimBoolToStr"],
-    mIntToStr: ["cstrToNimstr", "cstrToNimstr"],
-    mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"],
-    mFloatToStr: ["cstrToNimstr", "cstrToNimstr"],
     mCStrToStr: ["cstrToNimstr", "cstrToNimstr"],
     mStrToStr: ["", ""]]
 
 proc needsTemp(p: PProc; n: PNode): bool =
   # check if n contains a call to determine
   # if a temp should be made to prevent multiple evals
+  result = false
   if n.kind in nkCallKinds + {nkTupleConstr, nkObjConstr, nkBracket, nkCurly}:
     return true
   for c in n:
@@ -502,14 +511,14 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop
     elif x.tmpLoc != "" and n.kind == nkBracketExpr:
       # genArrayAddr
       var
-        address, index: TCompRes
-        first: Int128
+        address, index: TCompRes = default(TCompRes)
+        first: Int128 = Zero
       gen(p, n[0], address)
       gen(p, n[1], index)
       let (m1, tmp1) = maybeMakeTemp(p, n[0], address)
       let typ = skipTypes(n[0].typ, abstractPtrs)
       if typ.kind == tyArray:
-        first = firstOrd(p.config, typ[0])
+        first = firstOrd(p.config, typ.indexType)
       if optBoundsCheck in p.options:
         useMagic(p, "chckIndx")
         if first == 0: # save a couple chars
@@ -535,7 +544,7 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
   # $1 and $2 in the `frmt` string bind to lhs and rhs of the expr,
   # if $3 or $4 are present they will be substituted with temps for
   # lhs and rhs respectively
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   useMagic(p, magic)
   gen(p, n[1], x)
   gen(p, n[2], y)
@@ -552,28 +561,36 @@ template binaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string,
   r.res = frmt % [a, b, tmp, tmp2]
   r.kind = resExpr
 
-proc unsignedTrimmerJS(size: BiggestInt): Rope =
+proc unsignedTrimmer(size: BiggestInt): string =
   case size
-  of 1: rope"& 0xff"
-  of 2: rope"& 0xffff"
-  of 4: rope">>> 0"
-  else: rope""
-
+  of 1: "& 0xff"
+  of 2: "& 0xffff"
+  of 4: ">>> 0"
+  else: ""
 
-template unsignedTrimmer(size: BiggestInt): Rope =
-  size.unsignedTrimmerJS
+proc signedTrimmer(size: BiggestInt): string =
+  # sign extension is done by shifting to the left and then back to the right
+  "<< $1 >> $1" % [$(32 - size * 8)]
 
 proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
                     reassign: static[bool] = false) =
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   gen(p, n[1], x)
   gen(p, n[2], y)
-  let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size)
+  let size = n[1].typ.skipTypes(abstractRange).size
   when reassign:
     let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
-    r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
+    if size == 8 and optJsBigInt64 in p.config.globalOptions:
+      r.res = "$1 = BigInt.asUintN(64, ($4 $2 $3))" % [a, rope op, y.rdLoc, tmp]
+    else:
+      let trimmer = unsignedTrimmer(size)
+      r.res = "$1 = (($5 $2 $3) $4)" % [a, rope op, y.rdLoc, trimmer, tmp]
   else:
-    r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
+    if size == 8 and optJsBigInt64 in p.config.globalOptions:
+      r.res = "BigInt.asUintN(64, ($1 $2 $3))" % [x.rdLoc, rope op, y.rdLoc]
+    else:
+      let trimmer = unsignedTrimmer(size)
+      r.res = "(($1 $2 $3) $4)" % [x.rdLoc, rope op, y.rdLoc, trimmer]
   r.kind = resExpr
 
 template ternaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
@@ -594,10 +611,21 @@ template unaryExpr(p: PProc, n: PNode, r: var TCompRes, magic, frmt: string) =
   r.res = frmt % [a, tmp]
   r.kind = resExpr
 
+proc genBreakState(p: PProc, n: PNode, r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  # mangle `:state` properly somehow
+  if n.kind == nkClosure:
+    gen(p, n[1], a)
+    r.res = "(($1).HEX3Astate < 0)" % [rdLoc(a)]
+  else:
+    gen(p, n, a)
+    r.res = "((($1.ClE_0).HEX3Astate) < 0)" % [rdLoc(a)]
+  r.kind = resExpr
+
 proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   var
-    x, y: TCompRes
-    xLoc,yLoc: Rope
+    x, y: TCompRes = default(TCompRes)
+    xLoc, yLoc: Rope = ""
   let i = ord(optOverflowCheck notin p.options)
   useMagic(p, jsMagics[op][i])
   if n.len > 2:
@@ -614,32 +642,133 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   template applyFormat(frmtA, frmtB) =
     if i == 0: applyFormat(frmtA) else: applyFormat(frmtB)
 
-  case op:
-  of mAddI: applyFormat("addInt($1, $2)", "($1 + $2)")
-  of mSubI: applyFormat("subInt($1, $2)", "($1 - $2)")
-  of mMulI: applyFormat("mulInt($1, $2)", "($1 * $2)")
-  of mDivI: applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
-  of mModI: applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
-  of mSucc: applyFormat("addInt($1, $2)", "($1 + $2)")
-  of mPred: applyFormat("subInt($1, $2)", "($1 - $2)")
+  template bitwiseExpr(op: string) =
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind in {tyUInt, tyUInt32}:
+      r.res = "(($1 $2 $3) >>> 0)" % [xLoc, op, yLoc]
+    else:
+      r.res = "($1 $2 $3)" % [xLoc, op, yLoc]
+
+  case op
+  of mAddI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "addInt64")
+        applyFormat("addInt64($1, $2)")
+      else:
+        applyFormat("addInt($1, $2)")
+    else:
+      applyFormat("($1 + $2)")
+  of mSubI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "subInt64")
+        applyFormat("subInt64($1, $2)")
+      else:
+        applyFormat("subInt($1, $2)")
+    else:
+      applyFormat("($1 - $2)")
+  of mMulI:
+    if i == 0:
+      if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        useMagic(p, "mulInt64")
+        applyFormat("mulInt64($1, $2)")
+      else:
+        applyFormat("mulInt($1, $2)")
+    else:
+      applyFormat("($1 * $2)")
+  of mDivI:
+    if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "divInt64")
+      applyFormat("divInt64($1, $2)", "$1 / $2")
+    else:
+      applyFormat("divInt($1, $2)", "Math.trunc($1 / $2)")
+  of mModI:
+    if n[1].typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "modInt64")
+      applyFormat("modInt64($1, $2)", "$1 % $2")
+    else:
+      applyFormat("modInt($1, $2)", "Math.trunc($1 % $2)")
+  of mSucc:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
+      binaryUintExpr(p, n, r, "+")
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 + BigInt($2))")
+      else: binaryUintExpr(p, n, r, "+")
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        applyFormat("BigInt.asIntN(64, $1 + BigInt($2))")
+      else: binaryExpr(p, n, r, "addInt64", "addInt64($1, BigInt($2))")
+    else:
+      if optOverflowCheck notin p.options: applyFormat("$1 + $2")
+      else: binaryExpr(p, n, r, "addInt", "addInt($1, $2)")
+  of mPred:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
+      binaryUintExpr(p, n, r, "-")
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 - BigInt($2))")
+      else: binaryUintExpr(p, n, r, "-")
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        applyFormat("BigInt.asIntN(64, $1 - BigInt($2))")
+      else: binaryExpr(p, n, r, "subInt64", "subInt64($1, BigInt($2))")
+    else:
+      if optOverflowCheck notin p.options: applyFormat("$1 - $2")
+      else: binaryExpr(p, n, r, "subInt", "subInt($1, $2)")
   of mAddF64: applyFormat("($1 + $2)", "($1 + $2)")
   of mSubF64: applyFormat("($1 - $2)", "($1 - $2)")
   of mMulF64: applyFormat("($1 * $2)", "($1 * $2)")
   of mDivF64: applyFormat("($1 / $2)", "($1 / $2)")
-  of mShrI: applyFormat("", "")
+  of mShrI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      applyFormat("BigInt.asIntN(64, BigInt.asUintN(64, $1) >> BigInt($2))")
+    elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+      applyFormat("($1 >> BigInt($2))")
+    else:
+      if typ.kind in {tyInt..tyInt32}:
+        let trimmerU = unsignedTrimmer(typ.size)
+        let trimmerS = signedTrimmer(typ.size)
+        r.res = "((($1 $2) >>> $3) $4)" % [xLoc, trimmerU, yLoc, trimmerS]
+      else:
+        applyFormat("($1 >>> $2)")
   of mShlI:
-    if n[1].typ.size <= 4:
-      applyFormat("($1 << $2)", "($1 << $2)")
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.size == 8:
+      if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asIntN(64, $1 << BigInt($2))")
+      elif typ.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, $1 << BigInt($2))")
+      else:
+        applyFormat("($1 * Math.pow(2, $2))")
     else:
-      applyFormat("($1 * Math.pow(2, $2))", "($1 * Math.pow(2, $2))")
+      if typ.kind in {tyUInt..tyUInt32}:
+        let trimmer = unsignedTrimmer(typ.size)
+        r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
+      else:
+        let trimmer = signedTrimmer(typ.size)
+        r.res = "(($1 << $2) $3)" % [xLoc, yLoc, trimmer]
   of mAshrI:
-    if n[1].typ.size <= 4:
-      applyFormat("($1 >> $2)", "($1 >> $2)")
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.size == 8:
+      if optJsBigInt64 in p.config.globalOptions:
+        applyFormat("($1 >> BigInt($2))")
+      else:
+        applyFormat("Math.floor($1 / Math.pow(2, $2))")
     else:
-      applyFormat("Math.floor($1 / Math.pow(2, $2))", "Math.floor($1 / Math.pow(2, $2))")
-  of mBitandI: applyFormat("($1 & $2)", "($1 & $2)")
-  of mBitorI: applyFormat("($1 | $2)", "($1 | $2)")
-  of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)")
+      if typ.kind in {tyUInt..tyUInt32}:
+        applyFormat("($1 >>> $2)")
+      else:
+        applyFormat("($1 >> $2)")
+  of mBitandI: bitwiseExpr("&")
+  of mBitorI: bitwiseExpr("|")
+  of mBitxorI: bitwiseExpr("^")
   of mMinI: applyFormat("nimMin($1, $2)", "nimMin($1, $2)")
   of mMaxI: applyFormat("nimMax($1, $2)", "nimMax($1, $2)")
   of mAddU: applyFormat("", "")
@@ -672,16 +801,29 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   of mEqProc: applyFormat("($1 == $2)", "($1 == $2)")
   of mUnaryMinusI: applyFormat("negInt($1)", "-($1)")
   of mUnaryMinusI64: applyFormat("negInt64($1)", "-($1)")
-  of mAbsI: applyFormat("absInt($1)", "Math.abs($1)")
+  of mAbsI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      useMagic(p, "absInt64")
+      applyFormat("absInt64($1)", "absInt64($1)")
+    else:
+      applyFormat("absInt($1)", "Math.abs($1)")
   of mNot: applyFormat("!($1)", "!($1)")
   of mUnaryPlusI: applyFormat("+($1)", "+($1)")
-  of mBitnotI: applyFormat("~($1)", "~($1)")
+  of mBitnotI:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    if typ.kind in {tyUInt..tyUInt64}:
+      if typ.size == 8 and optJsBigInt64 in p.config.globalOptions:
+        applyFormat("BigInt.asUintN(64, ~($1))")
+      else:
+        let trimmer = unsignedTrimmer(typ.size)
+        r.res = "(~($1) $2)" % [xLoc, trimmer]
+    else:
+      applyFormat("~($1)")
   of mUnaryPlusF64: applyFormat("+($1)", "+($1)")
   of mUnaryMinusF64: applyFormat("-($1)", "-($1)")
   of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)")
   of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)")
-  of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
-  of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")")
   of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)")
   of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1")
   else:
@@ -694,27 +836,29 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) =
   of mMulU: binaryUintExpr(p, n, r, "*")
   of mDivU:
     binaryUintExpr(p, n, r, "/")
-    if n[1].typ.skipTypes(abstractRange).size == 8:
+    if optJsBigInt64 notin p.config.globalOptions and
+        n[1].typ.skipTypes(abstractRange).size == 8:
+      # bigint / already truncates
       r.res = "Math.trunc($1)" % [r.res]
   of mDivI:
     arithAux(p, n, r, op)
   of mModI:
     arithAux(p, n, r, op)
-  of mShrI:
-    var x, y: TCompRes
-    gen(p, n[1], x)
-    gen(p, n[2], y)
-    r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc]
-  of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr:
+  of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
     arithAux(p, n, r, op)
   of mEqRef:
     if mapType(n[1].typ) != etyBaseIndex:
       arithAux(p, n, r, op)
     else:
-      var x, y: TCompRes
+      var x, y: TCompRes = default(TCompRes)
       gen(p, n[1], x)
       gen(p, n[2], y)
       r.res = "($# == $# && $# == $#)" % [x.address, y.address, x.res, y.res]
+  of mEqProc:
+    if skipTypes(n[1].typ, abstractInst).callConv == ccClosure:
+      binaryExpr(p, n, r, "cmpClosures", "cmpClosures($1, $2)")
+    else:
+      arithAux(p, n, r, op)
   else:
     arithAux(p, n, r, op)
   r.kind = resExpr
@@ -724,20 +868,27 @@ proc hasFrameInfo(p: PProc): bool =
       ((p.prc == nil) or not (sfPure in p.prc.flags))
 
 proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope =
-  ropes.`%`("/* line $2 \"$1\" */$n",
-         [rope(toFullPath(config, info)), rope(line)])
+  "/* line $2:$3 \"$1\" */$n" % [
+    rope(toFullPath(config, info)), rope(line), rope(info.toColumn)
+  ]
 
 proc genLineDir(p: PProc, n: PNode) =
   let line = toLinenumber(n.info)
   if line < 0:
     return
+  if optEmbedOrigSrc in p.config.globalOptions:
+    lineF(p, "//$1$n", [sourceLine(p.config, n.info)])
   if optLineDir in p.options or optLineDir in p.config.options:
     lineF(p, "$1", [lineDir(p.config, n.info, line)])
   if hasFrameInfo(p):
     lineF(p, "F.line = $1;$n", [rope(line)])
+    let currentFileName = toFilename(p.config, n.info)
+    if p.previousFileName != currentFileName:
+      lineF(p, "F.filename = $1;$n", [makeJSString(currentFileName)])
+      p.previousFileName = currentFileName
 
 proc genWhileStmt(p: PProc, n: PNode) =
-  var cond: TCompRes
+  var cond: TCompRes = default(TCompRes)
   internalAssert p.config, isEmptyType(n.typ)
   genLineDir(p, n)
   inc(p.unique)
@@ -797,7 +948,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
     p.body.add("++excHandler;\L")
   var tmpFramePtr = rope"F"
   lineF(p, "try {$n", [])
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n[0], a)
   moveInto(p, a, r)
   var generalCatchBranchExists = false
@@ -832,6 +983,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         elif it.kind == nkType:
           throwObj = it
         else:
+          throwObj = nil
           internalError(p.config, n.info, "genTryStmt")
 
         if orExpr != "": orExpr.add("||")
@@ -840,7 +992,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
         # if isJsObject(throwObj.typ):
         if isImportedException(throwObj.typ, p.config):
           orExpr.addf("lastJSError instanceof $1",
-            [throwObj.typ.sym.loc.r])
+            [throwObj.typ.sym.loc.snippet])
         else:
           orExpr.addf("isObj(lastJSError.m_type, $1)",
                [genTypeInfo(p, throwObj.typ)])
@@ -850,8 +1002,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
       # If some branch requires a local alias introduce it here. This is needed
       # since JS cannot do ``catch x as y``.
       if excAlias != nil:
-        excAlias.sym.loc.r = mangleName(p.module, excAlias.sym)
-        lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.r)
+        excAlias.sym.loc.snippet = mangleName(p.module, excAlias.sym)
+        lineF(p, "var $1 = lastJSError;$n", excAlias.sym.loc.snippet)
       gen(p, n[i][^1], a)
       moveInto(p, a, r)
       lineF(p, "}$n", [])
@@ -872,7 +1024,7 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) =
 
 proc genRaiseStmt(p: PProc, n: PNode) =
   if n[0].kind != nkEmpty:
-    var a: TCompRes
+    var a: TCompRes = default(TCompRes)
     gen(p, n[0], a)
     let typ = skipTypes(n[0].typ, abstractPtrs)
     genLineDir(p, n)
@@ -886,18 +1038,17 @@ proc genRaiseStmt(p: PProc, n: PNode) =
 
 proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
   var
-    a, b, cond, stmt: TCompRes
-    totalRange = 0
+    a, b, cond, stmt: TCompRes = default(TCompRes)
   genLineDir(p, n)
   gen(p, n[0], cond)
-  let typeKind = skipTypes(n[0].typ, abstractVar).kind
+  let typeKind = skipTypes(n[0].typ, abstractVar+{tyRange}).kind
   var transferRange = false
   let anyString = typeKind in {tyString, tyCstring}
   case typeKind
   of tyString:
     useMagic(p, "toJSStr")
     lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc])
-  of tyFloat..tyFloat128:
+  of tyFloat..tyFloat128, tyInt..tyInt64, tyUInt..tyUInt64:
     transferRange = true
   else:
     lineF(p, "switch ($1) {$n", [cond.rdLoc])
@@ -926,10 +1077,6 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
               lineF(p, "$1 >= $2 && $1 <= $3", [cond.rdLoc, a.rdLoc, b.rdLoc])
           else:
             var v = copyNode(e[0])
-            inc(totalRange, int(e[1].intVal - v.intVal))
-            if totalRange > 65535:
-              localError(p.config, n.info,
-                        "Your case statement contains too many branches, consider using if/else instead!")
             while v.intVal <= e[1].intVal:
               gen(p, v, cond)
               lineF(p, "case $1:$n", [cond.rdLoc])
@@ -962,14 +1109,19 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) =
           lineF(p, "break;$n", [])
     of nkElse:
       if transferRange:
-         lineF(p, "else{$n", [])
+        if n.len == 2: # a dangling else for a case statement
+          transferRange = false
+          lineF(p, "switch ($1) {$n", [cond.rdLoc])
+          lineF(p, "default: $n", [])
+        else:
+          lineF(p, "else{$n", [])
       else:
         lineF(p, "default: $n", [])
       p.nested:
         gen(p, it[0], stmt)
         moveInto(p, stmt, r)
         if transferRange:
-           lineF(p, "}$n", [])
+          lineF(p, "}$n", [])
         else:
           lineF(p, "break;$n", [])
     else: internalError(p.config, it.info, "jsgen.genCaseStmt")
@@ -1011,10 +1163,14 @@ proc genBreakStmt(p: PProc, n: PNode) =
   p.blocks[idx].id = abs(p.blocks[idx].id) # label is used
   lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)])
 
-proc genAsmOrEmitStmt(p: PProc, n: PNode) =
+proc genAsmOrEmitStmt(p: PProc, n: PNode; isAsmStmt = false) =
   genLineDir(p, n)
   p.body.add p.indentLine("")
-  for i in 0..<n.len:
+  let offset =
+    if isAsmStmt: 1 # first son is pragmas
+    else: 0
+
+  for i in offset..<n.len:
     let it = n[i]
     case it.kind
     of nkStrLit..nkTripleStrLit:
@@ -1025,7 +1181,7 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
       if false:
         discard
       else:
-        var r: TCompRes
+        var r = default(TCompRes)
         gen(p, it, r)
 
         if it.typ.kind == tyPointer:
@@ -1041,13 +1197,13 @@ proc genAsmOrEmitStmt(p: PProc, n: PNode) =
 
         p.body.add(r.rdLoc)
     else:
-      var r: TCompRes
+      var r: TCompRes = default(TCompRes)
       gen(p, it, r)
       p.body.add(r.rdLoc)
   p.body.add "\L"
 
 proc genIf(p: PProc, n: PNode, r: var TCompRes) =
-  var cond, stmt: TCompRes
+  var cond, stmt: TCompRes = default(TCompRes)
   var toClose = 0
   if not isEmptyType(n.typ):
     r.kind = resVal
@@ -1069,8 +1225,17 @@ proc genIf(p: PProc, n: PNode, r: var TCompRes) =
     lineF(p, "}$n", [])
   line(p, repeat('}', toClose) & "\L")
 
-proc generateHeader(p: PProc, typ: PType): Rope =
+proc generateHeader(p: PProc, prc: PSym): Rope =
   result = ""
+  let typ = prc.typ
+  if jsNoLambdaLifting notin p.config.legacyFeatures:
+    if typ.callConv == ccClosure:
+      # we treat Env as the `this` parameter of the function
+      # to keep it simple
+      let env = prc.ast[paramsPos].lastSon
+      assert env.kind == nkSym, "env is missing"
+      env.sym.loc.snippet = "this"
+
   for i in 1..<typ.n.len:
     assert(typ.n[i].kind == nkSym)
     var param = typ.n[i].sym
@@ -1084,6 +1249,7 @@ proc generateHeader(p: PProc, typ: PType): Rope =
       result.add("_Idx")
 
 proc countJsParams(typ: PType): int =
+  result = 0
   for i in 1..<typ.n.len:
     assert(typ.n[i].kind == nkSym)
     var param = typ.n[i].sym
@@ -1102,12 +1268,13 @@ const
 
 proc needsNoCopy(p: PProc; y: PNode): bool =
   return y.kind in nodeKindsNeedNoCopy or
-        ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and
+        ((mapType(y.typ) != etyBaseIndex or
+          (jsNoLambdaLifting in p.config.legacyFeatures and y.kind == nkSym and y.sym.kind == skParam)) and
           (skipTypes(y.typ, abstractInst).kind in
-            {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes))
+            {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned, tyOpenArray} + IntegralTypes))
 
 proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
-  var a, b: TCompRes
+  var a, b: TCompRes = default(TCompRes)
   var xtyp = mapType(p, x.typ)
 
   # disable `[]=` for cstring
@@ -1131,7 +1298,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) =
       lineF(p, "$1 = nimCopy(null, $2, $3);$n",
                [a.rdLoc, b.res, genTypeInfo(p, y.typ)])
   of etyObject:
-    if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
+    if x.typ.kind in {tyVar, tyLent, tyOpenArray, tyVarargs} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded:
       lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc])
     else:
       useMagic(p, "nimCopy")
@@ -1186,28 +1353,21 @@ proc genFastAsgn(p: PProc, n: PNode) =
   genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy)
 
 proc genSwap(p: PProc, n: PNode) =
-  var a, b: TCompRes
-  gen(p, n[1], a)
-  gen(p, n[2], b)
-  var tmp = p.getTemp(false)
-  if mapType(p, skipTypes(n[1].typ, abstractVar)) == etyBaseIndex:
-    let tmp2 = p.getTemp(false)
-    if a.typ != etyBaseIndex or b.typ != etyBaseIndex:
-      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;",
-           [tmp, a.res, b.res])
+  let stmtList = lowerSwap(p.module.graph, n, p.module.idgen, if p.prc != nil: p.prc else: p.module.module)
+  assert stmtList.kind == nkStmtList
+  for i in 0..<stmtList.len:
+    genStmt(p, stmtList[i])
 
 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(p.config, f.info, "genFieldPosition")
+  else:
+    result = 0
+    internalError(p.config, f.info, "genFieldPosition")
 
 proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.typ = etyBaseIndex
   let b = if n.kind == nkHiddenAddr: n[0] else: n
   gen(p, b[0], a)
@@ -1216,8 +1376,8 @@ proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) =
   else:
     if b[1].kind != nkSym: internalError(p.config, b[1].info, "genFieldAddr")
     var f = b[1].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
-    r.res = makeJSString($f.loc.r)
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = makeJSString($f.loc.snippet)
   internalAssert p.config, a.typ != etyBaseIndex
   r.address = a.res
   r.kind = resExpr
@@ -1244,8 +1404,8 @@ proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) =
   else:
     if n[1].kind != nkSym: internalError(p.config, n[1].info, "genFieldAccess")
     var f = n[1].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
-    r.res = "$1.$2" % [r.res, f.loc.r]
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    r.res = "$1.$2" % [r.res, f.loc.snippet]
     mkTemp(1)
   r.kind = resExpr
 
@@ -1265,16 +1425,16 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
   # Field symbol
   var field = accessExpr[1].sym
   internalAssert p.config, field.kind == skField
-  if field.loc.r == "": field.loc.r = mangleName(p.module, field)
+  if field.loc.snippet == "": field.loc.snippet = mangleName(p.module, field)
   # Discriminant symbol
   let disc = checkExpr[2].sym
   internalAssert p.config, disc.kind == skField
-  if disc.loc.r == "": disc.loc.r = mangleName(p.module, disc)
+  if disc.loc.snippet == "": disc.loc.snippet = mangleName(p.module, disc)
 
-  var setx: TCompRes
+  var setx: TCompRes = default(TCompRes)
   gen(p, checkExpr[1], setx)
 
-  var obj: TCompRes
+  var obj: TCompRes = default(TCompRes)
   gen(p, accessExpr[0], obj)
   # Avoid evaluating the LHS twice (one to read the discriminant and one to read
   # the field)
@@ -1286,22 +1446,22 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) =
   useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen
   let msg = genFieldDefect(p.config, field.name.s, disc)
   lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n",
-    setx.res, tmp, disc.loc.r, if negCheck: "!==" else: "===",
+    setx.res, tmp, disc.loc.snippet, if negCheck: "!==" else: "===",
     makeJSString(msg), genTypeInfo(p, disc.typ))
 
   if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex:
     r.typ = etyBaseIndex
-    r.res = makeJSString($field.loc.r)
+    r.res = makeJSString($field.loc.snippet)
     r.address = tmp
   else:
     r.typ = etyNone
-    r.res = "$1.$2" % [tmp, field.loc.r]
+    r.res = "$1.$2" % [tmp, field.loc.snippet]
   r.kind = resExpr
 
 proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   var
-    a, b: TCompRes
-    first: Int128
+    a, b: TCompRes = default(TCompRes)
+    first: Int128 = Zero
   r.typ = etyBaseIndex
   let m = if n.kind == nkHiddenAddr: n[0] else: n
   gen(p, m[0], a)
@@ -1311,7 +1471,7 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   r.address = x
   var typ = skipTypes(m[0].typ, abstractPtrs)
   if typ.kind == tyArray:
-    first = firstOrd(p.config, typ[0])
+    first = firstOrd(p.config, typ.indexType)
   if optBoundsCheck in p.options:
     useMagic(p, "chckIndx")
     if first == 0: # save a couple chars
@@ -1326,8 +1486,8 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) =
   r.kind = resExpr
 
 proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) =
-  var ty = skipTypes(n[0].typ, abstractVarRange)
-  if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange)
+  var ty = skipTypes(n[0].typ, abstractVarRange+tyUserTypeClasses)
+  if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.elementType, abstractVarRange)
   case ty.kind
   of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
     genArrayAddr(p, n, r)
@@ -1362,10 +1522,10 @@ template isIndirect(x: PSym): bool =
 
 proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
   let s = n.sym
-  if s.loc.r == "": internalError(p.config, n.info, "genAddr: 3")
+  if s.loc.snippet == "": internalError(p.config, n.info, "genAddr: 3")
   case s.kind
   of skParam:
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     r.address = ""
     r.typ = etyNone
   of skVar, skLet, skResult:
@@ -1379,15 +1539,15 @@ proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) =
       # make addr() a no-op:
       r.typ = etyNone
       if isIndirect(s):
-        r.res = s.loc.r & "[0]"
+        r.res = s.loc.snippet & "[0]"
       else:
-        r.res = s.loc.r
+        r.res = s.loc.snippet
       r.address = ""
     elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex:
       # for ease of code generation, we do not distinguish between
       # sfAddrTaken and sfGlobal.
       r.typ = etyBaseIndex
-      r.address = s.loc.r
+      r.address = s.loc.snippet
       r.res = rope("0")
     else:
       # 'var openArray' for instance produces an 'addr' but this is harmless:
@@ -1414,7 +1574,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
       if ty.kind in MappedToObject:
         gen(p, n[0], r)
       else:
-        let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind
+        let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange+tyUserTypeClasses).kind
         case kindOfIndexedExpr
         of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs:
           genArrayAddr(p, n[0], r)
@@ -1427,6 +1587,13 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) =
       gen(p, n[0], r)
     of nkHiddenDeref:
       gen(p, n[0], r)
+    of nkDerefExpr:
+      var x = n[0]
+      if n.kind == nkHiddenAddr:
+        x = n[0][0]
+        if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+          x.typ = n.typ
+      gen(p, x, r)
     of nkHiddenAddr:
       gen(p, n[0], r)
     of nkConv:
@@ -1453,12 +1620,15 @@ proc attachProc(p: PProc; s: PSym) =
 
 proc genProcForSymIfNeeded(p: PProc, s: PSym) =
   if not p.g.generatedSyms.containsOrIncl(s.id):
-    let newp = genProc(p, s)
-    var owner = p
-    while owner != nil and owner.prc != s.owner:
-      owner = owner.up
-    if owner != nil: owner.locals.add(newp)
-    else: attachProc(p, newp, s)
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      let newp = genProc(p, s)
+      var owner = p
+      while owner != nil and owner.prc != s.owner:
+        owner = owner.up
+      if owner != nil: owner.locals.add(newp)
+      else: attachProc(p, newp, s)
+    else:
+      attachProc(p, s)
 
 proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
   let s = n.sym
@@ -1470,7 +1640,7 @@ proc genCopyForParamIfNeeded(p: PProc, n: PNode) =
       internalError(p.config, n.info, "couldn't find the owner proc of the closed over param: " & s.name.s)
     if owner.prc == s.owner:
       if not owner.generatedParamCopies.containsOrIncl(s.id):
-        let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.r, genTypeInfo(p, s.typ)]
+        let copy = "$1 = nimCopy(null, $1, $2);$n" % [s.loc.snippet, genTypeInfo(p, s.typ)]
         owner.locals.add(owner.indentLine(copy))
       return
     owner = owner.up
@@ -1481,40 +1651,40 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
   var s = n.sym
   case s.kind
   of skVar, skLet, skParam, skTemp, skResult, skForVar:
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     if sfCompileTime in s.flags:
-      genVarInit(p, s, if s.ast != nil: s.ast else: newNodeI(nkEmpty, s.info))
-    if s.kind == skParam:
+      genVarInit(p, s, if s.astdef != nil: s.astdef else: newNodeI(nkEmpty, s.info))
+    if jsNoLambdaLifting in p.config.legacyFeatures and s.kind == skParam:
       genCopyForParamIfNeeded(p, n)
     let k = mapType(p, s.typ)
     if k == etyBaseIndex:
       r.typ = etyBaseIndex
       if {sfAddrTaken, sfGlobal} * s.flags != {}:
         if isIndirect(s):
-          r.address = "$1[0][0]" % [s.loc.r]
-          r.res = "$1[0][1]" % [s.loc.r]
+          r.address = "$1[0][0]" % [s.loc.snippet]
+          r.res = "$1[0][1]" % [s.loc.snippet]
         else:
-          r.address = "$1[0]" % [s.loc.r]
-          r.res = "$1[1]" % [s.loc.r]
+          r.address = "$1[0]" % [s.loc.snippet]
+          r.res = "$1[1]" % [s.loc.snippet]
       else:
-        r.address = s.loc.r
-        r.res = s.loc.r & "_Idx"
+        r.address = s.loc.snippet
+        r.res = s.loc.snippet & "_Idx"
     elif isIndirect(s):
-      r.res = "$1[0]" % [s.loc.r]
+      r.res = "$1[0]" % [s.loc.snippet]
     else:
-      r.res = s.loc.r
+      r.res = s.loc.snippet
   of skConst:
     genConstant(p, s)
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
-    r.res = s.loc.r
-  of skProc, skFunc, skConverter, skMethod:
+    r.res = s.loc.snippet
+  of skProc, skFunc, skConverter, skMethod, skIterator:
     if sfCompileTime in s.flags:
       localError(p.config, n.info, "request to generate code for .compileTime proc: " &
           s.name.s)
     discard mangleName(p.module, s)
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     if lfNoDecl in s.loc.flags or s.magic notin generatedMagics or
        {sfImportc, sfInfixCall} * s.flags != {}:
       discard
@@ -1526,13 +1696,13 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) =
     else:
       genProcForSymIfNeeded(p, s)
   else:
-    if s.loc.r == "":
+    if s.loc.snippet == "":
       internalError(p.config, n.info, "symbol has no generated name: " & s.name.s)
     if mapType(p, s.typ) == etyBaseIndex:
-      r.address = s.loc.r
-      r.res = s.loc.r & "_Idx"
+      r.address = s.loc.snippet
+      r.res = s.loc.snippet & "_Idx"
     else:
-      r.res = s.loc.r
+      r.res = s.loc.snippet
   r.kind = resVal
 
 proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
@@ -1541,7 +1711,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
   if t == etyObject or it.typ.kind == tyLent:
     gen(p, it, r)
   else:
-    var a: TCompRes
+    var a: TCompRes = default(TCompRes)
     gen(p, it, a)
     r.kind = a.kind
     r.typ = mapType(p, n.typ)
@@ -1558,7 +1728,7 @@ proc genDeref(p: PProc, n: PNode, r: var TCompRes) =
       internalError(p.config, n.info, "genDeref")
 
 proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n, a)
   if a.typ == etyBaseIndex:
     r.res.add(a.address)
@@ -1568,7 +1738,7 @@ proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) =
     r.res.add(a.res)
 
 proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n, a)
   if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
       a.typ == etyBaseIndex:
@@ -1674,9 +1844,9 @@ proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType;
 proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
   # don't call '$' here for efficiency:
   let f = n[0].sym
-  if f.loc.r == "": f.loc.r = mangleName(p.module, f)
+  if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
   if sfInfixCall in f.flags:
-    let pat = $n[0].sym.loc.r
+    let pat = $n[0].sym.loc.snippet
     internalAssert p.config, pat.len > 0
     if pat.contains({'#', '(', '@'}):
       var typ = skipTypes(n[0].typ, abstractInst)
@@ -1692,7 +1862,7 @@ proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) =
       r.address = ""
       r.typ = etyNone
     r.res.add(".")
-  var op: TCompRes
+  var op: TCompRes = default(TCompRes)
   gen(p, n[0], op)
   r.res.add(op.res)
   genArgs(p, n, r, 2)
@@ -1754,17 +1924,27 @@ proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: v
   while t != nil:
     t = t.skipTypes(skipPtrs)
     createRecordVarAux(p, t.n, excludedFieldIDs, output)
-    t = t[0]
+    t = t.baseClass
 
-proc arrayTypeForElemType(typ: PType): string =
+proc arrayTypeForElemType(conf: ConfigRef; typ: PType): string =
   let typ = typ.skipTypes(abstractRange)
   case typ.kind
   of tyInt, tyInt32: "Int32Array"
   of tyInt16: "Int16Array"
   of tyInt8: "Int8Array"
+  of tyInt64:
+    if optJsBigInt64 in conf.globalOptions:
+      "BigInt64Array"
+    else:
+      ""
   of tyUInt, tyUInt32: "Uint32Array"
   of tyUInt16: "Uint16Array"
   of tyUInt8, tyChar, tyBool: "Uint8Array"
+  of tyUInt64:
+    if optJsBigInt64 in conf.globalOptions:
+      "BigUint64Array"
+    else:
+      ""
   of tyFloat32: "Float32Array"
   of tyFloat64, tyFloat: "Float64Array"
   of tyEnum:
@@ -1778,15 +1958,22 @@ proc arrayTypeForElemType(typ: PType): string =
 proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   var t = skipTypes(typ, abstractInst)
   case t.kind
-  of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar:
-    if $t.sym.loc.r == "bigint":
+  of tyInt8..tyInt32, tyUInt8..tyUInt32, tyEnum, tyChar:
+    result = putToSeq("0", indirect)
+  of tyInt, tyUInt:
+    if $t.sym.loc.snippet == "bigint":
+      result = putToSeq("0n", indirect)
+    else:
+      result = putToSeq("0", indirect)
+  of tyInt64, tyUInt64:
+    if optJsBigInt64 in p.config.globalOptions:
       result = putToSeq("0n", indirect)
     else:
       result = putToSeq("0", indirect)
   of tyFloat..tyFloat128:
     result = putToSeq("0.0", indirect)
   of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent:
-    result = createVar(p, lastSon(typ), indirect)
+    result = createVar(p, skipModifier(typ), indirect)
   of tySet:
     result = putToSeq("{}", indirect)
   of tyBool:
@@ -1796,7 +1983,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
   of tyArray:
     let length = toInt(lengthOrd(p.config, t))
     let e = elemType(t)
-    let jsTyp = arrayTypeForElemType(e)
+    let jsTyp = arrayTypeForElemType(p.config, e)
     if jsTyp.len > 0:
       result = "new $1($2)" % [rope(jsTyp), rope(length)]
     elif length > 32:
@@ -1823,7 +2010,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
     result.add("}")
     if indirect: result = "[$1]" % [result]
   of tyObject:
-    var initList: Rope
+    var initList: Rope = ""
     createObjInitList(p, t, initIntSet(), initList)
     result = ("({$1})") % [initList]
     if indirect: result = "[$1]" % [result]
@@ -1834,11 +2021,11 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope =
       result = putToSeq("null", indirect)
   of tySequence, tyString:
     result = putToSeq("[]", indirect)
-  of tyCstring, tyProc:
+  of tyCstring, tyProc, tyOpenArray:
     result = putToSeq("null", indirect)
   of tyStatic:
     if t.n != nil:
-      result = createVar(p, lastSon t, indirect)
+      result = createVar(p, skipModifier t, indirect)
     else:
       internalError(p.config, "createVar: " & $t.kind)
       result = ""
@@ -1850,7 +2037,7 @@ template returnType: untyped = ""
 
 proc genVarInit(p: PProc, v: PSym, n: PNode) =
   var
-    a: TCompRes
+    a: TCompRes = default(TCompRes)
     s: Rope
     varCode: string
     varName = mangleName(p.module, v)
@@ -1886,7 +2073,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
     gen(p, n, a)
     case mapType(p, v.typ)
     of etyObject, etySeq:
-      if needsNoCopy(p, n):
+      if v.typ.kind in {tyOpenArray, tyVarargs} or needsNoCopy(p, n):
         s = a.res
       else:
         useMagic(p, "nimCopy")
@@ -1896,33 +2083,44 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) =
       if a.typ == etyBaseIndex:
         if targetBaseIndex:
           line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n",
-                   [returnType, v.loc.r, a.address, a.res]))
+                   [returnType, v.loc.snippet, a.address, a.res]))
         else:
           if isIndirect(v):
             line(p, runtimeFormat(varCode & " = [[$3, $4]];$n",
-                     [returnType, v.loc.r, a.address, a.res]))
+                     [returnType, v.loc.snippet, a.address, a.res]))
           else:
             line(p, runtimeFormat(varCode & " = [$3, $4];$n",
-                     [returnType, v.loc.r, a.address, a.res]))
+                     [returnType, v.loc.snippet, a.address, a.res]))
       else:
         if targetBaseIndex:
           let tmp = p.getTemp
           lineF(p, "var $1 = $2, $3 = $1[0], $3_Idx = $1[1];$n",
-                   [tmp, a.res, v.loc.r])
+                   [tmp, a.res, v.loc.snippet])
         else:
-          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res]))
+          line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, a.res]))
       return
     else:
       s = a.res
     if isIndirect(v):
-      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s]))
+      line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.snippet, s]))
     else:
-      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s]))
+      line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.snippet, s]))
 
   if useReloadingGuard or useGlobalPragmas:
     dec p.extraIndent
     lineF(p, "}$n")
 
+proc genClosureVar(p: PProc, n: PNode) =
+  # assert n[2].kind != nkEmpty
+  # TODO: fixme transform `var env.x` into `var env.x = default()` after
+  # the order of transf and lambdalifting is fixed
+  if n[2].kind != nkEmpty:
+    genAsgnAux(p, n[0], n[2], false)
+  else:
+    var a: TCompRes = default(TCompRes)
+    gen(p, n[0], a)
+    line(p, runtimeFormat("$1 = $2;$n", [rdLoc(a), createVar(p, n[0].typ, false)]))
+
 proc genVarStmt(p: PProc, n: PNode) =
   for i in 0..<n.len:
     var a = n[i]
@@ -1932,26 +2130,28 @@ proc genVarStmt(p: PProc, n: PNode) =
         genStmt(p, unpacked)
       else:
         assert(a.kind == nkIdentDefs)
-        assert(a[0].kind == nkSym)
-        var v = a[0].sym
-        if lfNoDecl notin v.loc.flags and sfImportc notin v.flags:
-          genLineDir(p, a)
-          if sfCompileTime notin v.flags:
-            genVarInit(p, v, a[2])
-          else:
-            # lazy emit, done when it's actually used.
-            if v.ast == nil: v.ast = a[2]
+        if a[0].kind == nkSym:
+          var v = a[0].sym
+          if lfNoDecl notin v.loc.flags and sfImportc notin v.flags:
+            genLineDir(p, a)
+            if sfCompileTime notin v.flags:
+              genVarInit(p, v, a[2])
+            else:
+              # lazy emit, done when it's actually used.
+              if v.ast == nil: v.ast = a[2]
+        else: # closure
+          genClosureVar(p, a)
 
 proc genConstant(p: PProc, c: PSym) =
   if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id):
     let oldBody = move p.body
-    #genLineDir(p, c.ast)
-    genVarInit(p, c, c.ast)
+    #genLineDir(p, c.astdef)
+    genVarInit(p, c, c.astdef)
     p.g.constants.add(p.body)
     p.body = oldBody
 
 proc genNew(p: PProc, n: PNode) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   gen(p, n[1], a)
   var t = skipTypes(n[1].typ, abstractVar)[0]
   if mapType(t) == etyObject:
@@ -1962,7 +2162,7 @@ proc genNew(p: PProc, n: PNode) =
     lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)])
 
 proc genNewSeq(p: PProc, n: PNode) =
-  var x, y: TCompRes
+  var x, y: TCompRes = default(TCompRes)
   gen(p, n[1], x)
   gen(p, n[2], y)
   let t = skipTypes(n[1].typ, abstractVar)[0]
@@ -1971,37 +2171,41 @@ proc genNewSeq(p: PProc, n: PNode) =
 
 proc genOrd(p: PProc, n: PNode, r: var TCompRes) =
   case skipTypes(n[1].typ, abstractVar + abstractRange).kind
-  of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r)
+  of tyEnum, tyInt..tyInt32, tyUInt..tyUInt32, tyChar: gen(p, n[1], r)
+  of tyInt64, tyUInt64:
+    if optJsBigInt64 in p.config.globalOptions:
+      unaryExpr(p, n, r, "", "Number($1)")
+    else: gen(p, n[1], r)
   of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)")
   else: internalError(p.config, n.info, "genOrd")
 
 proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
 
   gen(p, n[1], a)
   r.kind = resExpr
   if skipTypes(n[1].typ, abstractVarRange).kind == tyChar:
     r.res.add("[$1].concat(" % [a.res])
   else:
-    r.res.add("($1 || []).concat(" % [a.res])
+    r.res.add("($1).concat(" % [a.res])
 
   for i in 2..<n.len - 1:
     gen(p, n[i], a)
     if skipTypes(n[i].typ, abstractVarRange).kind == tyChar:
       r.res.add("[$1]," % [a.res])
     else:
-      r.res.add("$1 || []," % [a.res])
+      r.res.add("$1," % [a.res])
 
   gen(p, n[^1], a)
   if skipTypes(n[^1].typ, abstractVarRange).kind == tyChar:
     r.res.add("[$1])" % [a.res])
   else:
-    r.res.add("$1 || [])" % [a.res])
+    r.res.add("$1)" % [a.res])
 
 proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = "") =
   useMagic(p, magic)
   r.res.add(magic & "(")
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
 
   gen(p, n[1], a)
   if magic == "reprAny":
@@ -2048,7 +2252,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) =
   r.kind = resExpr
 
 proc genOf(p: PProc, n: PNode, r: var TCompRes) =
-  var x: TCompRes
+  var x: TCompRes = default(TCompRes)
   let t = skipTypes(n[2].typ,
                     abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned})
   gen(p, n[1], x)
@@ -2063,28 +2267,36 @@ proc genDefault(p: PProc, n: PNode; r: var TCompRes) =
   r.res = createVar(p, n.typ, indirect = false)
   r.kind = resExpr
 
-proc genReset(p: PProc, n: PNode) =
-  var x: TCompRes
-  useMagic(p, "genericReset")
+proc genWasMoved(p: PProc, n: PNode) =
+  # TODO: it should be done by nir
+  var x: TCompRes = default(TCompRes)
   gen(p, n[1], x)
   if x.typ == etyBaseIndex:
     lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res])
   else:
-    let (a, tmp) = maybeMakeTempAssignable(p, n[1], x)
-    lineF(p, "$1 = genericReset($3, $2);$n", [a,
-                  genTypeInfo(p, n[1].typ), tmp])
+    var y: TCompRes = default(TCompRes)
+    genDefault(p, n[1], y)
+    let (a, _) = maybeMakeTempAssignable(p, n[1], x)
+    lineF(p, "$1 = $2;$n", [a, y.rdLoc])
 
 proc genMove(p: PProc; n: PNode; r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.kind = resVal
   r.res = p.getTemp()
   gen(p, n[1], a)
   lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
-  genReset(p, n)
+  genWasMoved(p, n)
   #lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc])
 
+proc genDup(p: PProc; n: PNode; r: var TCompRes) =
+  var a: TCompRes = default(TCompRes)
+  r.kind = resVal
+  r.res = p.getTemp()
+  gen(p, n[1], a)
+  lineF(p, "$1 = $2;$n", [r.rdLoc, a.rdLoc])
+
 proc genJSArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.res = rope("[")
   r.kind = resExpr
   for i in 0 ..< n.len:
@@ -2115,7 +2327,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     binaryExpr(p, n, r, "addChar",
         "addChar($1, $2);")
   of mAppendStrStr:
-    var lhs, rhs: TCompRes
+    var lhs, rhs: TCompRes = default(TCompRes)
     gen(p, n[1], lhs)
     gen(p, n[2], rhs)
 
@@ -2128,7 +2340,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
       r.res = "$1.push.apply($3, $2);" % [a, rhs.rdLoc, tmp]
     r.kind = resExpr
   of mAppendSeqElem:
-    var x, y: TCompRes
+    var x, y: TCompRes = default(TCompRes)
     gen(p, n[1], x)
     gen(p, n[2], y)
     if mapType(n[2].typ) == etyBaseIndex:
@@ -2156,7 +2368,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     if mapType(n[1].typ) != etyBaseIndex:
       unaryExpr(p, n, r, "", "($1 == null)")
     else:
-      var x: TCompRes
+      var x: TCompRes = default(TCompRes)
       gen(p, n[1], x)
       r.res = "($# == null && $# === 0)" % [x.address, x.res]
   of mEnumToStr: genRepr(p, n, r)
@@ -2167,7 +2379,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     if n[1].kind == nkBracket:
       genJSArrayConstr(p, n[1], r)
     else:
-      var x: TCompRes
+      var x: TCompRes = default(TCompRes)
       gen(p, n[1], x)
       useMagic(p, "nimCopy")
       r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)]
@@ -2176,7 +2388,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mDestroy, mTrace: discard "ignore calls to the default destructor"
   of mOrd: genOrd(p, n, r)
   of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray:
-    var x: TCompRes
+    var x: TCompRes = default(TCompRes)
     gen(p, n[1], x)
     if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
       let (a, tmp) = maybeMakeTemp(p, n[1], x)
@@ -2185,7 +2397,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
       r.res = "($1).length" % [x.rdLoc]
     r.kind = resExpr
   of mHigh:
-    var x: TCompRes
+    var x: TCompRes = default(TCompRes)
     gen(p, n[1], x)
     if skipTypes(n[1].typ, abstractInst).kind == tyCstring:
       let (a, tmp) = maybeMakeTemp(p, n[1], x)
@@ -2194,21 +2406,43 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
       r.res = "($1).length - 1" % [x.rdLoc]
     r.kind = resExpr
   of mInc:
-    if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
       binaryUintExpr(p, n, r, "+", true)
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 + BigInt($2))", true)
+      else: binaryUintExpr(p, n, r, "+", true)
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 + BigInt($2))", true)
+      else: binaryExpr(p, n, r, "addInt64", "$1 = addInt64($3, BigInt($2))", true)
     else:
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2")
       else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)", true)
   of ast.mDec:
-    if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}:
+    let typ = n[1].typ.skipTypes(abstractVarRange)
+    case typ.kind
+    of tyUInt..tyUInt32:
       binaryUintExpr(p, n, r, "-", true)
+    of tyUInt64:
+      if optJsBigInt64 in p.config.globalOptions:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asUintN(64, $3 - BigInt($2))", true)
+      else: binaryUintExpr(p, n, r, "-", true)
+    elif typ.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+      if optOverflowCheck notin p.options:
+        binaryExpr(p, n, r, "", "$1 = BigInt.asIntN(64, $3 - BigInt($2))", true)
+      else: binaryExpr(p, n, r, "subInt64", "$1 = subInt64($3, BigInt($2))", true)
     else:
       if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2")
       else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true)
   of mSetLengthStr:
-    binaryExpr(p, n, r, "mnewString", "($1.length = $2)")
+    binaryExpr(p, n, r, "mnewString",
+      """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); }
+         else {$3.length = $4; }""")
   of mSetLengthSeq:
-    var x, y: TCompRes
+    var x, y: TCompRes = default(TCompRes)
     gen(p, n[1], x)
     gen(p, n[2], y)
     let t = skipTypes(n[1].typ, abstractVar)[0]
@@ -2231,8 +2465,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
   of mNewSeq: genNewSeq(p, n)
   of mNewSeqOfCap: unaryExpr(p, n, r, "", "[]")
   of mOf: genOf(p, n, r)
-  of mDefault: genDefault(p, n, r)
-  of mReset, mWasMoved: genReset(p, n)
+  of mDefault, mZeroDefault: genDefault(p, n, r)
+  of mWasMoved: genWasMoved(p, n)
   of mEcho: genEcho(p, n, r)
   of mNLen..mNError, mSlurp, mStaticExec:
     localError(p.config, n.info, errXMustBeCompileTime % n[0].sym.name.s)
@@ -2247,7 +2481,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     genCall(p, n, r)
   of mSlice:
     # arr.slice([begin[, end]]): 'end' is exclusive
-    var x, y, z: TCompRes
+    var x, y, z: TCompRes = default(TCompRes)
     gen(p, n[1], x)
     gen(p, n[2], y)
     gen(p, n[3], z)
@@ -2255,13 +2489,17 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
     r.kind = resExpr
   of mMove:
     genMove(p, n, r)
+  of mDup:
+    genDup(p, n, r)
+  of mEnsureMove:
+    gen(p, n[1], r)
   else:
     genCall(p, n, r)
     #else internalError(p.config, e.info, 'genMagic: ' + magicToStr[op]);
 
 proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
   var
-    a, b: TCompRes
+    a, b: TCompRes = default(TCompRes)
   useMagic(p, "setConstr")
   r.res = rope("setConstr(")
   r.kind = resExpr
@@ -2293,12 +2531,12 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
   ## Nim sequence maps to JS array.
   var t = skipTypes(n.typ, abstractInst)
   let e = elemType(t)
-  let jsTyp = arrayTypeForElemType(e)
+  let jsTyp = arrayTypeForElemType(p.config, e)
   if skipTypes(n.typ, abstractVarRange).kind != tySequence and jsTyp.len > 0:
     # generate typed array
     # for example Nim generates `new Uint8Array([1, 2, 3])` for `[byte(1), 2, 3]`
     # TODO use `set` or loop to initialize typed array which improves performances in some situations
-    var a: TCompRes
+    var a: TCompRes = default(TCompRes)
     r.res = "new $1([" % [rope(jsTyp)]
     r.kind = resExpr
     for i in 0 ..< n.len:
@@ -2310,7 +2548,7 @@ proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
     genJSArrayConstr(p, n, r)
 
 proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.res = rope("{")
   r.kind = resExpr
   for i in 0..<n.len:
@@ -2329,9 +2567,9 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) =
   r.res.add("}")
 
 proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
-  var a: TCompRes
+  var a: TCompRes = default(TCompRes)
   r.kind = resExpr
-  var initList : Rope
+  var initList : Rope = ""
   var fieldIDs = initIntSet()
   let nTyp = n.typ.skipTypes(abstractInst)
   for i in 1..<n.len:
@@ -2341,17 +2579,17 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) =
     let val = it[1]
     gen(p, val, a)
     var f = it[0].sym
-    if f.loc.r == "": f.loc.r = mangleName(p.module, f)
-    fieldIDs.incl(lookupFieldAgain(nTyp, f).id)
+    if f.loc.snippet == "": f.loc.snippet = mangleName(p.module, f)
+    fieldIDs.incl(lookupFieldAgain(n.typ.skipTypes({tyDistinct}), f).id)
 
     let typ = val.typ.skipTypes(abstractInst)
     if a.typ == etyBaseIndex:
-      initList.addf("$#: [$#, $#]", [f.loc.r, a.address, a.res])
+      initList.addf("$#: [$#, $#]", [f.loc.snippet, a.address, a.res])
     else:
       if not needsNoCopy(p, val):
         useMagic(p, "nimCopy")
         a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)]
-      initList.addf("$#: $#", [f.loc.r, a.res])
+      initList.addf("$#: $#", [f.loc.snippet, a.res])
   let t = skipTypes(n.typ, abstractInst + skipPtrs)
   createObjInitList(p, t, fieldIDs, initList)
   r.res = ("{$1}") % [initList]
@@ -2374,7 +2612,29 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) =
     r.res = "(!!($1))" % [r.res]
     r.kind = resExpr
   elif toInt:
-    r.res = "(($1) | 0)" % [r.res]
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
+    else:
+      r.res = "(($1) | 0)" % [r.res]
+  elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt(Math.trunc($1))" % [r.res]
+    elif src.kind == tyUInt64:
+      r.res = "BigInt.asIntN(64, $1)" % [r.res]
+  elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif fromInt: # could be negative
+      r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
+    elif src.kind == tyInt64:
+      r.res = "BigInt.asUintN(64, $1)" % [r.res]
+  elif toUint or dest.kind in tyFloat..tyFloat64:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
   else:
     # TODO: What types must we handle here?
     discard
@@ -2383,12 +2643,29 @@ proc upConv(p: PProc, n: PNode, r: var TCompRes) =
   gen(p, n[0], r)        # XXX
 
 proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) =
-  var a, b: TCompRes
+  var a, b: TCompRes = default(TCompRes)
   gen(p, n[0], r)
-  if optRangeCheck notin p.options or (skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64} and
-      checkUnsignedConversions notin p.config.legacyFeatures):
-    discard "XXX maybe emit masking instructions here"
+  let src = skipTypes(n[0].typ, abstractVarRange)
+  let dest = skipTypes(n.typ, abstractVarRange)
+  if optRangeCheck notin p.options:
+    if optJsBigInt64 in p.config.globalOptions and
+          dest.kind in {tyUInt..tyUInt32, tyInt..tyInt32} and
+          src.kind in {tyInt64, tyUInt64}:
+      # conversions to Number are kept
+      r.res = "Number($1)" % [r.res]
+    else:
+      discard
+  elif dest.kind in {tyUInt..tyUInt64} and checkUnsignedConversions notin p.config.legacyFeatures:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "BigInt.asUintN($1, $2)" % [$(dest.size * 8), r.res]
+    else:
+      r.res = "BigInt.asUintN($1, BigInt($2))" % [$(dest.size * 8), r.res]
+    if not (dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions):
+      r.res = "Number($1)" % [r.res]
   else:
+    if src.kind in {tyInt64, tyUInt64} and dest.kind notin {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      # we do a range check anyway, so it's ok if the number gets rounded
+      r.res = "Number($1)" % [r.res]
     gen(p, n[1], a)
     gen(p, n[2], b)
     useMagic(p, "chckRange")
@@ -2465,9 +2742,10 @@ proc optionalLine(p: Rope): Rope =
 
 proc genProc(oldProc: PProc, prc: PSym): Rope =
   ## Generate a JS procedure ('function').
+  result = ""
   var
     resultSym: PSym
-    a: TCompRes
+    a: TCompRes = default(TCompRes)
   #if gVerbosity >= 3:
   #  echo "BEGIN generating code for: " & prc.name.s
   var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options)
@@ -2475,26 +2753,27 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   var returnStmt: Rope = ""
   var resultAsgn: Rope = ""
   var name = mangleName(p.module, prc)
-  let header = generateHeader(p, prc.typ)
-  if prc.typ[0] != nil and sfPure notin prc.flags:
+  let header = generateHeader(p, prc)
+  if prc.typ.returnType != nil and sfPure notin prc.flags:
     resultSym = prc.ast[resultPos].sym
     let mname = mangleName(p.module, resultSym)
-    let returnAddress = not isIndirect(resultSym) and
+    # otherwise uses "fat pointers"
+    let useRawPointer = not isIndirect(resultSym) and
       resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and
         mapType(p, resultSym.typ) == etyBaseIndex
-    if returnAddress:
+    if useRawPointer:
       resultAsgn = p.indentLine(("var $# = null;$n") % [mname])
       resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname])
     else:
       let resVar = createVar(p, resultSym.typ, isIndirect(resultSym))
       resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar])
     gen(p, prc.ast[resultPos], a)
-    if returnAddress:
+    if mapType(p, resultSym.typ) == etyBaseIndex:
       returnStmt = "return [$#, $#];$n" % [a.address, a.res]
     else:
       returnStmt = "return $#;$n" % [a.res]
 
-  var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, cache = false)
+  var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, {})
   if sfInjectDestructors in prc.flags:
     transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody)
 
@@ -2546,14 +2825,19 @@ proc genProc(oldProc: PProc, prc: PSym): Rope =
   #  echo "END   generated code for: " & prc.name.s
 
 proc genStmt(p: PProc, n: PNode) =
-  var r: TCompRes
+  var r: TCompRes = default(TCompRes)
   gen(p, n, r)
   if r.res != "": lineF(p, "$#;$n", [r.res])
 
 proc genPragma(p: PProc, n: PNode) =
-  for it in n.sons:
+  for i in 0..<n.len:
+    let it = n[i]
     case whichPragma(it)
     of wEmit: genAsmOrEmitStmt(p, it[1])
+    of wPush:
+      processPushBackendOption(p.config, p.optionsStack, p.options, n, i+1)
+    of wPop:
+      processPopBackendOption(p.config, p.optionsStack, p.options)
     else: discard
 
 proc genCast(p: PProc, n: PNode, r: var TCompRes) =
@@ -2568,24 +2852,35 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) =
   let fromInt = (src.kind in tyInt..tyInt32)
   let fromUint = (src.kind in tyUInt..tyUInt32)
 
-  if toUint and (fromInt or fromUint):
-    let trimmer = unsignedTrimmer(dest.size)
-    r.res = "($1 $2)" % [r.res, trimmer]
+  if toUint:
+    if fromInt or fromUint:
+      r.res = "Number(BigInt.asUintN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
+    elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number(BigInt.asUintN($1, $2))" % [$(dest.size * 8), r.res]
   elif toInt:
-    if fromInt:
-      return
-    elif fromUint:
-      if src.size == 4 and dest.size == 4:
-        # XXX prevent multi evaluations
-        r.res = "($1 | 0)" % [r.res]
-      else:
-        let trimmer = unsignedTrimmer(dest.size)
-        let minuend = case dest.size
-          of 1: "0xfe"
-          of 2: "0xfffe"
-          of 4: "0xfffffffe"
-          else: ""
-        r.res = "($1 - ($2 $3))" % [rope minuend, r.res, trimmer]
+    if fromInt or fromUint:
+      r.res = "Number(BigInt.asIntN($1, BigInt($2)))" % [$(dest.size * 8), r.res]
+    elif src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number(BigInt.asIntN($1, $2))" % [$(dest.size * 8), r.res]
+  elif dest.kind == tyInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromInt or fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt(Math.trunc($1))" % [r.res]
+    elif src.kind == tyUInt64:
+      r.res = "BigInt.asIntN(64, $1)" % [r.res]
+  elif dest.kind == tyUInt64 and optJsBigInt64 in p.config.globalOptions:
+    if fromUint or src.kind in {tyBool, tyChar, tyEnum}:
+      r.res = "BigInt($1)" % [r.res]
+    elif fromInt: # could be negative
+      r.res = "BigInt.asUintN(64, BigInt($1))" % [r.res]
+    elif src.kind in {tyFloat..tyFloat64}:
+      r.res = "BigInt.asUintN(64, BigInt(Math.trunc($1)))" % [r.res]
+    elif src.kind == tyInt64:
+      r.res = "BigInt.asUintN(64, $1)" % [r.res]
+  elif dest.kind in tyFloat..tyFloat64:
+    if src.kind in {tyInt64, tyUInt64} and optJsBigInt64 in p.config.globalOptions:
+      r.res = "Number($1)" % [r.res]
   elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer:
     r.address = r.res
     r.res = "null"
@@ -2604,10 +2899,25 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkSym:
     genSym(p, n, r)
   of nkCharLit..nkUInt64Lit:
-    if n.typ.kind == tyBool:
+    case n.typ.skipTypes(abstractVarRange).kind
+    of tyBool:
       r.res = if n.intVal == 0: rope"false" else: rope"true"
+    of tyUInt64:
+      r.res = rope($cast[BiggestUInt](n.intVal))
+      if optJsBigInt64 in p.config.globalOptions:
+        r.res.add('n')
+    of tyInt64:
+      let wrap = n.intVal < 0 # wrap negative integers with parens
+      if wrap: r.res.add '('
+      r.res.addInt n.intVal
+      if optJsBigInt64 in p.config.globalOptions:
+        r.res.add('n')
+      if wrap: r.res.add ')'
     else:
-      r.res = rope(n.intVal)
+      let wrap = n.intVal < 0 # wrap negative integers with parens
+      if wrap: r.res.add '('
+      r.res.addInt n.intVal
+      if wrap: r.res.add ')'
     r.kind = resExpr
   of nkNilLit:
     if isEmptyType(n.typ):
@@ -2646,7 +2956,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       r.res = rope"Infinity"
     of fcNegInf:
       r.res = rope"-Infinity"
-    else: r.res = rope(f.toStrMaxPrecision)
+    else:
+      if n.typ.skipTypes(abstractVarRange).kind == tyFloat32:
+        r.res.addFloatRoundtrip(f.float32)
+      else:
+        r.res.addFloatRoundtrip(f)
     r.kind = resExpr
   of nkCallKinds:
     if isEmptyType(n.typ):
@@ -2658,7 +2972,18 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       genInfixCall(p, n, r)
     else:
       genCall(p, n, r)
-  of nkClosure: gen(p, n[0], r)
+  of nkClosure:
+    if jsNoLambdaLifting in p.config.legacyFeatures:
+      gen(p, n[0], r)
+    else:
+      let tmp = getTemp(p)
+      var a: TCompRes = default(TCompRes)
+      var b: TCompRes = default(TCompRes)
+      gen(p, n[0], a)
+      gen(p, n[1], b)
+      lineF(p, "$1 = $2.bind($3); $1.ClP_0 = $2; $1.ClE_0 = $3;$n", [tmp, a.rdLoc, b.rdLoc])
+      r.res = tmp
+      r.kind = resVal
   of nkCurly: genSetConstr(p, n, r)
   of nkBracket: genArrayConstr(p, n, r)
   of nkPar, nkTupleConstr: genTupleConstr(p, n, r)
@@ -2689,7 +3014,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkLambdaKinds:
     let s = n[namePos].sym
     discard mangleName(p.module, s)
-    r.res = s.loc.r
+    r.res = s.loc.snippet
     if lfNoDecl in s.loc.flags or s.magic notin generatedMagics: discard
     elif not p.g.generatedSyms.containsOrIncl(s.id):
       p.locals.add(genProc(p, s))
@@ -2716,22 +3041,21 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
   of nkReturnStmt: genReturnStmt(p, n)
   of nkBreakStmt: genBreakStmt(p, n)
   of nkAsgn: genAsgn(p, n)
-  of nkFastAsgn: genFastAsgn(p, n)
+  of nkFastAsgn, nkSinkAsgn: genFastAsgn(p, n)
   of nkDiscardStmt:
     if n[0].kind != nkEmpty:
       genLineDir(p, n)
       gen(p, n[0], r)
-      r.res = "var _ = " & r.res
-  of nkAsmStmt: genAsmOrEmitStmt(p, n)
+      r.res = "(" & r.res & ")"
+  of nkAsmStmt:
+    warningDeprecated(p.config, n.info, "'asm' for the JS target is deprecated, use the 'emit' pragma")
+    genAsmOrEmitStmt(p, n, true)
   of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r)
   of nkRaiseStmt: genRaiseStmt(p, n)
   of nkTypeSection, nkCommentStmt, nkIncludeStmt,
      nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt,
-     nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt,
+     nkFromStmt, nkTemplateDef, nkMacroDef, nkIteratorDef, nkStaticStmt,
      nkMixinStmt, nkBindStmt: discard
-  of nkIteratorDef:
-    if n[0].sym.typ.callConv == TCallingConvention.ccClosure:
-      globalError(p.config, n.info, "Closure iterators are not supported by JS backend!")
   of nkPragma: genPragma(p, n)
   of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef:
     var s = n[namePos].sym
@@ -2739,7 +3063,9 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
       genSym(p, n[namePos], r)
       r.res = ""
   of nkGotoState, nkState:
-    globalError(p.config, n.info, "First class iterators not implemented")
+    globalError(p.config, n.info, "not implemented")
+  of nkBreakState:
+    genBreakState(p, n[0], r)
   of nkPragmaBlock: gen(p, n.lastSon, r)
   of nkComesFrom:
     discard "XXX to implement for better stack traces"
@@ -2747,13 +3073,11 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
 
 proc newModule(g: ModuleGraph; module: PSym): BModule =
   ## Create a new JS backend module node.
-  new(result)
-  result.module = module
-  result.sigConflicts = initCountTable[SigHash]()
   if g.backend == nil:
     g.backend = newGlobals()
-  result.graph = g
-  result.config = g.config
+  result = BModule(module: module, sigConflicts: initCountTable[SigHash](),
+                   graph: g, config: g.config
+  )
   if sfSystemModule in module.flags:
     PGlobals(g.backend).inSystem = true
 
@@ -2801,7 +3125,7 @@ proc genModule(p: PProc, n: PNode) =
   if p.config.hcrOn and n.kind == nkStmtList:
     let moduleSym = p.module.module
     var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" &
-                          idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts)
+                          idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts, p.config)
     lineF(p, "var $1;$n", [moduleLoadedVar])
     var inGuardedBlock = false
 
@@ -2818,14 +3142,15 @@ proc genModule(p: PProc, n: PNode) =
   if optStackTrace in p.options:
     p.body.add(frameDestroy(p))
 
-proc myProcess(b: PPassContext, n: PNode): PNode =
+proc processJSCodeGen*(b: PPassContext, n: PNode): PNode =
   ## Generate JS code for a node.
   result = n
   let m = BModule(b)
-  if passes.skipCodegen(m.config, n): return n
+  if pipelineutils.skipCodegen(m.config, n): return n
   if m.module == nil: internalError(m.config, n.info, "myProcess")
   let globals = PGlobals(m.graph.backend)
   var p = newInitProc(globals, m)
+  m.initProc = p
   p.unique = globals.unique
   genModule(p, n)
   p.g.code.add(p.locals)
@@ -2839,25 +3164,15 @@ proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
       var p = newInitProc(globals, m)
       attachProc(p, prc)
 
-  var disp = generateMethodDispatchers(graph)
-  for i in 0..<disp.len:
-    let prc = disp[i].sym
+  generateIfMethodDispatchers(graph, m.idgen)
+  for prc in getDispatchers(graph):
     if not globals.generatedSyms.containsOrIncl(prc.id):
       var p = newInitProc(globals, m)
       attachProc(p, prc)
 
   result = globals.typeInfo & globals.constants & globals.code
 
-proc getClassName(t: PType): Rope =
-  var s = t.sym
-  if s.isNil or sfAnon in s.flags:
-    s = skipTypes(t, abstractPtrs).sym
-  if s.isNil or sfAnon in s.flags:
-    doAssert(false, "cannot retrieve class name")
-  if s.loc.r != "": result = s.loc.r
-  else: result = rope(s.name.s)
-
-proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
+proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
   ## Finalize JS code generation of a Nim module.
   ## Param `n` may contain nodes returned from the last module close call.
   var m = BModule(b)
@@ -2867,14 +3182,14 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
     for i in countdown(high(graph.globalDestructors), 0):
       n.add graph.globalDestructors[i]
   # Process any nodes left over from the last call to `myClose`.
-  result = myProcess(b, n)
+  result = processJSCodeGen(b, n)
   # Some codegen is different (such as no stacktraces; see `initProcOptions`)
   # when `std/system` is being processed.
   if sfSystemModule in m.module.flags:
     PGlobals(graph.backend).inSystem = false
   # Check if codegen should continue before any files are generated.
   # It may bail early is if too many errors have been raised.
-  if passes.skipCodegen(m.config, n): return n
+  if pipelineutils.skipCodegen(m.config, n): return n
   # Nim modules are compiled into a single JS file.
   # If this is the main module, then this is the final call to `myClose`.
   if sfMainModule in m.module.flags:
@@ -2883,7 +3198,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
     # Generate an optional source map.
     if optSourcemap in m.config.globalOptions:
       var map: SourceMap
-      (code, map) = genSourceMap($(code), outFile.string)
+      map = genSourceMap($code, outFile.string)
+      code &= "\n//# sourceMappingURL=$#.map" % [outFile.string]
       writeFile(outFile.string & ".map", $(%map))
     # Check if the generated JS code matches the output file, or else
     # write it to the file.
@@ -2891,9 +3207,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode =
       if not writeRope(code, outFile):
         rawMessage(m.config, errCannotOpenFile, outFile.string)
 
-proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
-  ## Create the JS backend pass context `BModule` for a Nim module.
+proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext =
   result = newModule(graph, s)
   result.idgen = idgen
-
-const JSgenPass* = makePass(myOpen, myProcess, myClose)
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index 56d075af7..d980f9989 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -69,7 +69,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
   else: internalError(p.config, n.info, "genObjectFields")
 
 proc objHasTypeField(t: PType): bool {.inline.} =
-  tfInheritable in t.flags or t[0] != nil
+  tfInheritable in t.flags or t.baseClass != nil
 
 proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
   let kind = if objHasTypeField(typ): tyObject else: tyTuple
@@ -79,9 +79,9 @@ proc genObjectInfo(p: PProc, typ: PType, name: Rope) =
   p.g.typeInfo.addf("var NNI$1 = $2;$n",
        [rope(typ.id), genObjectFields(p, typ, typ.n)])
   p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
-  if (typ.kind == tyObject) and (typ[0] != nil):
+  if (typ.kind == tyObject) and (typ.baseClass != nil):
     p.g.typeInfo.addf("$1.base = $2;$n",
-         [name, genTypeInfo(p, typ[0].skipTypes(skipPtrs))])
+         [name, genTypeInfo(p, typ.baseClass.skipTypes(skipPtrs))])
 
 proc genTupleFields(p: PProc, typ: PType): Rope =
   var s: Rope = ""
@@ -117,9 +117,9 @@ proc genEnumInfo(p: PProc, typ: PType, name: Rope) =
   prepend(p.g.typeInfo, s)
   p.g.typeInfo.add(n)
   p.g.typeInfo.addf("$1.node = NNI$2;$n", [name, rope(typ.id)])
-  if typ[0] != nil:
+  if typ.baseClass != nil:
     p.g.typeInfo.addf("$1.base = $2;$n",
-         [name, genTypeInfo(p, typ[0])])
+         [name, genTypeInfo(p, typ.baseClass)])
 
 proc genTypeInfo(p: PProc, typ: PType): Rope =
   let t = typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
@@ -127,30 +127,30 @@ proc genTypeInfo(p: PProc, typ: PType): Rope =
   if containsOrIncl(p.g.typeInfoGenerated, t.id): return
   case t.kind
   of tyDistinct:
-    result = genTypeInfo(p, t[0])
+    result = genTypeInfo(p, t.skipModifier)
   of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64:
     var s =
       "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" %
       [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
-  of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet:
+  of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray:
     var s =
       "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
     p.g.typeInfo.addf("$1.base = $2;$n",
-         [result, genTypeInfo(p, t.lastSon)])
+         [result, genTypeInfo(p, t.elementType)])
   of tyArray:
     var s =
       "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" %
               [result, rope(ord(t.kind))]
     prepend(p.g.typeInfo, s)
     p.g.typeInfo.addf("$1.base = $2;$n",
-         [result, genTypeInfo(p, t[1])])
+         [result, genTypeInfo(p, t.elementType)])
   of tyEnum: genEnumInfo(p, t, result)
   of tyObject: genObjectInfo(p, t, result)
   of tyTuple: genTupleInfo(p, t, result)
   of tyStatic:
-    if t.n != nil: result = genTypeInfo(p, lastSon t)
+    if t.n != nil: result = genTypeInfo(p, skipModifier t)
     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 96edba8c8..54cdfc5bc 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -10,10 +10,12 @@
 # This file implements lambda lifting for the transformator.
 
 import
-  intsets, strutils, options, ast, astalgo, msgs,
-  idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
+  options, ast, astalgo, msgs,
+  idents, renderer, types, magicsys, lowerings, modulegraphs, lineinfos,
   transf, liftdestructors, typeallowed
 
+import std/[strutils, tables, intsets]
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -133,29 +135,31 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator)
   var n = newNodeI(nkRange, iter.info)
   n.add newIntNode(nkIntLit, -1)
   n.add newIntNode(nkIntLit, 0)
-  result = newType(tyRange, nextTypeId(idgen), iter)
+  result = newType(tyRange, idgen, iter)
   result.n = n
   var intType = nilOrSysInt(g)
-  if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter)
+  if intType.isNil: intType = newType(tyInt, idgen, iter)
   rawAddSon(result, intType)
 
 proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
-  result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info)
+  result = newSym(skField, getIdent(g.cache, ":state"), idgen, iter, iter.info)
   result.typ = createClosureIterStateType(g, iter, idgen)
 
+template isIterator*(owner: PSym): bool =
+  owner.kind == skIterator and owner.typ.callConv == ccClosure
+
 proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; 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(g, idgen, owner, info, final=false)
-  rawAddField(result, createStateField(g, owner, idgen))
+  if owner.isIterator or not isDefined(g.config, "nimOptIters"):
+    rawAddField(result, createStateField(g, owner, idgen))
 
 proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym =
   if resultPos < iter.ast.len:
     result = iter.ast[resultPos].sym
   else:
     # XXX a bit hacky:
-    result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {})
-    result.typ = iter.typ[0]
+    result = newSym(skResult, getIdent(g.cache, ":result"), idgen, iter, iter.info, {})
+    result.typ = iter.typ.returnType
     incl(result.flags, sfUsed)
     iter.ast.add newSymNode(result)
 
@@ -170,24 +174,22 @@ proc addHiddenParam(routine: PSym, param: PSym) =
   assert sfFromGeneric in param.flags
   #echo "produced environment: ", param.id, " for ", routine.id
 
-proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
+proc getEnvParam*(routine: PSym): PSym =
   let params = routine.ast[paramsPos]
   let hidden = lastSon(params)
   if hidden.kind == nkSym and hidden.sym.kind == skParam and hidden.sym.name.s == paramName:
     result = hidden.sym
     assert sfFromGeneric in result.flags
   else:
+    result = nil
+
+proc getHiddenParam(g: ModuleGraph; routine: PSym): PSym =
+  result = getEnvParam(routine)
+  if result.isNil:
     # writeStackTrace()
     localError(g.config, routine.info, "internal error: could not find env param for " & routine.name.s)
     result = routine
 
-proc getEnvParam*(routine: PSym): PSym =
-  let params = routine.ast[paramsPos]
-  let hidden = lastSon(params)
-  if hidden.kind == nkSym and hidden.sym.name.s == paramName:
-    result = hidden.sym
-    assert sfFromGeneric in result.flags
-
 proc interestingVar(s: PSym): bool {.inline.} =
   result = s.kind in {skVar, skLet, skTemp, skForVar, skParam, skResult} and
     sfGlobal notin s.flags and
@@ -199,6 +201,8 @@ proc illegalCapture(s: PSym): bool {.inline.} =
 proc isInnerProc(s: PSym): bool =
   if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone:
     result = s.skipGenericOwner.kind in routineKinds
+  else:
+    result = false
 
 proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode =
   # Bugfix: unfortunately we cannot use 'nkFastAsgn' here as that would
@@ -225,34 +229,38 @@ proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; inf
     prc.flags.incl sfInjectDestructors
 
 proc interestingIterVar(s: PSym): bool {.inline.} =
+  # unused with -d:nimOptIters
   # XXX optimization: Only lift the variable if it lives across
   # yield/return boundaries! This can potentially speed up
   # closure iterators quite a bit.
   result = s.kind in {skResult, skVar, skLet, skTemp, skForVar} and sfGlobal notin s.flags
 
-template isIterator*(owner: PSym): bool =
-  owner.kind == skIterator and owner.typ.callConv == ccClosure
-
-proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} =
+template liftingHarmful(conf: ConfigRef; owner: PSym): bool =
   ## lambda lifting can be harmful for JS-like code generators.
   let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro
-  result = conf.backend == backendJs and not isCompileTime
+  jsNoLambdaLifting in conf.legacyFeatures and conf.backend == backendJs and not isCompileTime
 
 proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) =
   if owner.kind != skMacro:
-    createTypeBoundOps(g, nil, refType.lastSon, info, idgen)
+    createTypeBoundOps(g, nil, refType.elementType, info, idgen)
     createTypeBoundOps(g, nil, refType, info, idgen)
     if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions:
       owner.flags.incl sfInjectDestructors
 
+proc genCreateEnv(env: PNode): PNode =
+  var c = newNodeIT(nkObjConstr, env.info, env.typ)
+  c.add newNodeIT(nkType, env.info, env.typ)
+  let e = copyTree(env)
+  e.flags.incl nfFirstWrite
+  result = newAsgnStmt(e, c)
+
 proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
   # transforms  (iter)  to  (let env = newClosure[iter](); (iter, env))
   if liftingHarmful(g.config, owner): return n
   let iter = n.sym
   assert iter.isIterator
 
-  result = newNodeIT(nkStmtListExpr, n.info, n.typ)
-
+  result = newNodeIT(nkStmtListExpr, n.info, iter.typ)
   let hp = getHiddenParam(g, iter)
   var env: PNode
   if owner.isIterator:
@@ -260,7 +268,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
     addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen)
     env = indirectAccess(newSymNode(it), hp, hp.info)
   else:
-    let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info)
+    let e = newSym(skLet, iter.name, idgen, owner, n.info)
     e.typ = hp.typ
     e.flags = hp.flags
     env = newSymNode(e)
@@ -268,11 +276,13 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN
     addVar(v, env)
     result.add(v)
   # add 'new' statement:
-  result.add newCall(getSysSym(g, n.info, "internalNew"), env)
+  #result.add newCall(getSysSym(g, n.info, "internalNew"), env)
+  result.add genCreateEnv(env)
   createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner)
   result.add makeClosure(g, idgen, iter, env, n.info)
 
 proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode =
+  # unused with -d:nimOptIters
   let envParam = getHiddenParam(g, owner)
   let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr})
   let field = addField(obj, s, g.cache, idgen)
@@ -285,33 +295,34 @@ proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner:
 
 proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) =
   let s = n.sym
+  let isEnv = s.name.id == getIdent(g.cache, ":env").id
   if illegalCapture(s):
     localError(g.config, n.info,
       ("'$1' is of type <$2> which cannot be captured as it would violate memory" &
        " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." &
-       " Consider using a <ref $2> which can be captured.") %
-      [s.name.s, typeToString(s.typ), g.config$s.info])
-  elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags):
+       " Consider using a <ref T> which can be captured.") %
+      [s.name.s, typeToString(s.typ.skipTypes({tyVar})), g.config$s.info])
+  elif not (owner.typ.isClosure or owner.isNimcall and not owner.isExplicitCallConv or isEnv):
     localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" %
       [s.name.s, owner.name.s, $owner.typ.callConv])
   incl(owner.typ.flags, tfCapturesEnv)
-  owner.typ.callConv = ccClosure
+  if not isEnv:
+    owner.typ.callConv = ccClosure
 
 type
   DetectionPass = object
     processed, capturedVars: IntSet
     ownerToType: Table[int, PType]
     somethingToDo: bool
+    inTypeOf: bool
     graph: ModuleGraph
     idgen: IdGenerator
 
 proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass =
-  result.processed = initIntSet()
-  result.capturedVars = initIntSet()
-  result.ownerToType = initTable[int, PType]()
-  result.processed.incl(fn.id)
-  result.graph = g
-  result.idgen = idgen
+  result = DetectionPass(processed: toIntSet([fn.id]),
+    capturedVars: initIntSet(), ownerToType: initTable[int, PType](),
+    graph: g, idgen: idgen
+  )
 
 discard """
 proc outer =
@@ -327,15 +338,19 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym;
                         info: TLineInfo): PType =
   result = c.ownerToType.getOrDefault(owner.id)
   if result.isNil:
-    result = newType(tyRef, nextTypeId(c.idgen), owner)
-    let obj = createEnvObj(c.graph, c.idgen, owner, info)
-    rawAddSon(result, obj)
+    let env = getEnvParam(owner)
+    if env.isNil or not owner.isIterator or not isDefined(c.graph.config, "nimOptIters"):
+      result = newType(tyRef, c.idgen, owner)
+      let obj = createEnvObj(c.graph, c.idgen, owner, info)
+      rawAddSon(result, obj)
+    else:
+      result = env.typ
     c.ownerToType[owner.id] = result
 
 proc asOwnedRef(c: var DetectionPass; t: PType): PType =
   if optOwnedRefs in c.graph.config.globalOptions:
     assert t.kind == tyRef
-    result = newType(tyOwned, nextTypeId(c.idgen), t.owner)
+    result = newType(tyOwned, c.idgen, t.owner)
     result.flags.incl tfHasOwned
     result.rawAddSon t
   else:
@@ -344,7 +359,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType =
 proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym;
                           info: TLineInfo): PType =
   var r = c.getEnvTypeForOwner(owner, info)
-  result = newType(tyPtr, nextTypeId(c.idgen), owner)
+  result = newType(tyPtr, c.idgen, owner)
   rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr}))
 
 proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
@@ -372,7 +387,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) =
       if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags:
         localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor")
   else:
-    let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info)
+    let result = newSym(skField, upIdent, c.idgen, obj.owner, obj.owner.info)
     result.typ = fieldType
     when false:
       if c.graph.config.selectedGC == gcDestructors:
@@ -405,12 +420,15 @@ Consider:
 
 """
 
+proc isTypeOf(n: PNode): bool =
+  n.kind == nkSym and n.sym.magic in {mTypeOf, mType}
+
 proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) =
   var cp = getEnvParam(fn)
   let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner
   let t = c.getEnvTypeForOwner(owner, info)
   if cp == nil:
-    cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info)
+    cp = newSym(skParam, getIdent(c.graph.cache, paramName), c.idgen, fn, fn.info)
     incl(cp.flags, sfFromGeneric)
     cp.typ = t
     addHiddenParam(fn, cp)
@@ -433,24 +451,28 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
     if innerProc:
       if s.isIterator: c.somethingToDo = true
       if not c.processed.containsOrIncl(s.id):
-        let body = transformBody(c.graph, c.idgen, s, cache = true)
+        let body = transformBody(c.graph, c.idgen, s, {useCache})
         detectCapturedVars(body, s, c)
     let ow = s.skipGenericOwner
+    let innerClosure = innerProc and s.typ.callConv == ccClosure and not s.isIterator
+    let interested = interestingVar(s)
     if ow == owner:
       if owner.isIterator:
         c.somethingToDo = true
         addClosureParam(c, owner, n.info)
-        if interestingIterVar(s):
-          if not c.capturedVars.containsOrIncl(s.id):
+        if not isDefined(c.graph.config, "nimOptIters") and interestingIterVar(s):
+          if not c.capturedVars.contains(s.id):
+            if not c.inTypeOf: c.capturedVars.incl(s.id)
             let obj = getHiddenParam(c.graph, owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
             #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr})
 
             if s.name.id == getIdent(c.graph.cache, ":state").id:
+              obj.n[0].sym.flags.incl sfNoInit
               obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
             else:
               discard addField(obj, s, c.graph.cache, c.idgen)
     # direct or indirect dependency:
-    elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s):
+    elif innerClosure or interested:
       discard """
         proc outer() =
           var x: int
@@ -467,10 +489,12 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       addClosureParam(c, owner, n.info)
       #echo "capturing ", n.info
       # variable 's' is actually captured:
-      if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id):
-        let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
-        #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
-        discard addField(obj, s, c.graph.cache, c.idgen)
+      if interestingVar(s):
+        if not c.capturedVars.contains(s.id):
+          if not c.inTypeOf: c.capturedVars.incl(s.id)
+          let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr})
+          #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr})
+          discard addField(obj, s, c.graph.cache, c.idgen)
       # create required upFields:
       var w = owner.skipGenericOwner
       if isInnerProc(w) or owner.isIterator:
@@ -502,9 +526,14 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) =
       detectCapturedVars(n[namePos], owner, c)
   of nkReturnStmt:
     detectCapturedVars(n[0], owner, c)
+  of nkIdentDefs:
+    detectCapturedVars(n[^1], owner, c)
   else:
+    if n.isCallExpr and n[0].isTypeOf:
+      c.inTypeOf = true
     for i in 0..<n.len:
       detectCapturedVars(n[i], owner, c)
+    c.inTypeOf = false
 
 type
   LiftingPass = object
@@ -514,9 +543,8 @@ type
     unownedEnvVars: Table[int, PNode] # only required for --newruntime
 
 proc initLiftingPass(fn: PSym): LiftingPass =
-  result.processed = initIntSet()
-  result.processed.incl(fn.id)
-  result.envVars = initTable[int, PNode]()
+  result = LiftingPass(processed: toIntSet([fn.id]),
+                envVars: initTable[int, PNode]())
 
 proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let s = n.sym
@@ -524,8 +552,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
   let envParam = getHiddenParam(g, owner)
   if not envParam.isNil:
     var access = newSymNode(envParam)
+    var obj = access.typ.elementType
     while true:
-      let obj = access.typ[0]
       assert obj.kind == tyObject
       let field = getFieldFromObj(obj, s)
       if field != nil:
@@ -533,18 +561,19 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode =
       let upField = lookupInRecord(obj.n, getIdent(g.cache, upName))
       if upField == nil: break
       access = rawIndirectAccess(access, upField, n.info)
+      obj = access.typ.baseClass
   localError(g.config, n.info, "internal error: environment misses: " & s.name.s)
   result = n
 
 proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode =
-  var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info)
+  var v = newSym(skVar, getIdent(cache, envName), idgen, owner, info)
   v.flags = {sfShadowed, sfGeneratedOp}
   v.typ = typ
   result = newSymNode(v)
   when false:
     if owner.kind == skIterator and owner.typ.callConv == ccClosure:
       let it = getHiddenParam(owner)
-      addUniqueField(it.typ[0], v)
+      addUniqueField(it.typ.elementType, v)
       result = indirectAccess(newSymNode(it), v, v.info)
     else:
       result = newSymNode(v)
@@ -561,7 +590,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass;
     result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen)
     c.envVars[owner.id] = result
     if optOwnedRefs in d.graph.config.globalOptions:
-      var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info)
+      var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), d.idgen, owner, info)
       v.flags = {sfShadowed, sfGeneratedOp}
       v.typ = envVarType
       c.unownedEnvVars[owner.id] = newSymNode(v)
@@ -596,7 +625,7 @@ proc rawClosureCreation(owner: PSym;
         addVar(v, unowned)
 
     # add 'new' statement:
-    result.add(newCall(getSysSym(d.graph, env.info, "internalNew"), env))
+    result.add genCreateEnv(env)
     if optOwnedRefs in d.graph.config.globalOptions:
       let unowned = c.unownedEnvVars[owner.id]
       assert unowned != nil
@@ -645,7 +674,7 @@ proc closureCreationForIter(iter: PNode;
                             d: var DetectionPass; c: var LiftingPass): PNode =
   result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ)
   let owner = iter.sym.skipGenericOwner
-  var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info)
+  var v = newSym(skVar, getIdent(d.graph.cache, envName), d.idgen, owner, iter.info)
   incl(v.flags, sfShadowed)
   v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ)
   var vnode: PNode
@@ -658,7 +687,7 @@ proc closureCreationForIter(iter: PNode;
     var vs = newNodeI(nkVarSection, iter.info)
     addVar(vs, vnode)
     result.add(vs)
-  result.add(newCall(getSysSym(d.graph, iter.info, "internalNew"), vnode))
+  result.add genCreateEnv(vnode)
   createTypeBoundOpsLL(d.graph, vnode.typ, iter.info, d.idgen, owner)
 
   let upField = lookupInRecord(v.typ.skipTypes({tyOwned, tyRef, tyPtr}).n, getIdent(d.graph.cache, upName))
@@ -703,6 +732,7 @@ proc symToClosure(n: PNode; owner: PSym; d: var DetectionPass;
     # direct dependency, so use the outer's env variable:
     result = makeClosure(d.graph, d.idgen, s, setupEnvVar(owner, d, c, n.info), n.info)
   else:
+    result = nil
     let available = getHiddenParam(d.graph, owner)
     let wanted = getHiddenParam(d.graph, s).typ
     # ugh: call through some other inner proc;
@@ -729,7 +759,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
         #  echo renderTree(s.getBody, {renderIds})
         let oldInContainer = c.inContainer
         c.inContainer = 0
-        var body = transformBody(d.graph, d.idgen, s, cache = false)
+        var body = transformBody(d.graph, d.idgen, s, {})
         body = liftCapturedVars(body, s, d, c)
         if c.envVars.getOrDefault(s.id).isNil:
           s.transformedBody = body
@@ -744,7 +774,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
     elif s.id in d.capturedVars:
       if s.owner != owner:
         result = accessViaEnvParam(d.graph, n, owner)
-      elif owner.isIterator and interestingIterVar(s):
+      elif owner.isIterator and not isDefined(d.graph.config, "nimOptIters") and interestingIterVar(s):
         result = accessViaEnvParam(d.graph, n, owner)
       else:
         result = accessViaEnvVar(n, owner, d, c)
@@ -774,7 +804,7 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
       n[1] = liftCapturedVars(n[1], owner, d, c)
       if n[1].kind == nkClosure: result = n[1]
   of nkReturnStmt:
-    if n[0].kind in {nkAsgn, nkFastAsgn}:
+    if n[0].kind in {nkAsgn, nkFastAsgn, nkSinkAsgn}:
       # we have a `result = result` expression produced by the closure
       # transform, let's not touch the LHS in order to make the lifting pass
       # correct when `result` is lifted
@@ -784,6 +814,8 @@ proc liftCapturedVars(n: PNode; owner: PSym; d: var DetectionPass;
   of nkTypeOfExpr:
     result = n
   else:
+    if n.isCallExpr and n[0].isTypeOf:
+      return
     if owner.isIterator:
       if nfLL in n.flags:
         # special case 'when nimVm' due to bug #3636:
@@ -851,20 +883,19 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType;
   fn.typ.callConv = oldCC
 
 proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool;
-                  idgen: IdGenerator): PNode =
-  # XXX backend == backendJs 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.
+                  idgen: IdGenerator; flags: TransformFlags): PNode =
   let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro
 
-  if body.kind == nkEmpty or (
+  if body.kind == nkEmpty or (jsNoLambdaLifting in g.config.legacyFeatures and
       g.config.backend == backendJs and not isCompileTime) or
-      fn.skipGenericOwner.kind != skModule:
+      (fn.skipGenericOwner.kind != skModule and force notin flags):
 
     # ignore forward declaration:
     result = body
     tooEarly = true
+    if fn.isIterator and isDefined(g.config, "nimOptIters"):
+      var d = initDetectionPass(g, fn, idgen)
+      addClosureParam(d, fn, body.info)
   else:
     var d = initDetectionPass(g, fn, idgen)
     detectCapturedVars(body, fn, d)
@@ -928,14 +959,14 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
   result = newNodeI(nkStmtList, body.info)
 
   # static binding?
-  var env: PSym
+  var env: PSym = nil
   let op = call[0]
   if op.kind == nkSym and op.sym.isIterator:
     # createClosure()
     let iter = op.sym
 
     let hp = getHiddenParam(g, iter)
-    env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info)
+    env = newSym(skLet, iter.name, idgen, owner, body.info)
     env.typ = hp.typ
     env.flags = hp.flags
 
@@ -943,7 +974,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
     addVar(v, newSymNode(env))
     result.add(v)
     # add 'new' statement:
-    result.add(newCall(getSysSym(g, env.info, "internalNew"), env.newSymNode))
+    result.add genCreateEnv(env.newSymNode)
     createTypeBoundOpsLL(g, env.typ, body.info, idgen, owner)
 
   elif op.kind == nkStmtListExpr:
@@ -963,12 +994,15 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym):
   # gather vars in a tuple:
   var v2 = newNodeI(nkLetSection, body.info)
   var vpart = newNodeI(if body.len == 3: nkIdentDefs else: nkVarTuple, body.info)
-  for i in 0..<body.len-2:
-    if body[i].kind == nkSym:
-      body[i].sym.transitionToLet()
-    vpart.add body[i]
+  if body.len == 3 and body[0].kind == nkVarTuple:
+    vpart = body[0] # fixes for (i,j) in walk() # bug #15924
+  else:
+    for i in 0..<body.len-2:
+      if body[i].kind == nkSym:
+        body[i].sym.transitionToLet()
+      vpart.add body[i]
 
-  vpart.add newNodeI(nkEmpty, body.info) # no explicit type
+    vpart.add newNodeI(nkEmpty, body.info) # no explicit type
   if not env.isNil:
     call[0] = makeClosure(g, idgen, call[0].sym, env.newSymNode, body.info)
   vpart.add call
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index ec9db6aad..0121b1185 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -9,7 +9,7 @@
 
 ## Layouter for nimpretty.
 
-import idents, lexer, lineinfos, llstream, options, msgs, strutils, pathutils
+import idents, lexer, ast, lineinfos, llstream, options, msgs, strutils, pathutils
 
 const
   MinLineLen = 15
@@ -243,23 +243,28 @@ proc renderTokens*(em: var Emitter): string =
 
   return content
 
-proc writeOut*(em: Emitter, content: string)  =
+type
+  FinalCheck = proc (content: string; origAst: PNode): bool {.nimcall.}
+
+proc writeOut*(em: Emitter; content: string; origAst: PNode; check: FinalCheck) =
   ## Write to disk
   let outFile = em.config.absOutFile
   if fileExists(outFile) and readFile(outFile.string) == content:
     discard "do nothing, see #9499"
     return
-  var f = llStreamOpen(outFile, fmWrite)
-  if f == nil:
-    rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
-    return
-  f.llStreamWrite content
-  llStreamClose(f)
 
-proc closeEmitter*(em: var Emitter) =
+  if check(content, origAst):
+    var f = llStreamOpen(outFile, fmWrite)
+    if f == nil:
+      rawMessage(em.config, errGenerated, "cannot open file: " & outFile.string)
+      return
+    f.llStreamWrite content
+    llStreamClose(f)
+
+proc closeEmitter*(em: var Emitter; origAst: PNode; check: FinalCheck) =
   ## Renders emitter tokens and write to a file
   let content = renderTokens(em)
-  em.writeOut(content)
+  em.writeOut(content, origAst, check)
 
 proc wr(em: var Emitter; x: string; lt: LayoutToken) =
   em.tokens.add x
@@ -510,7 +515,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
     rememberSplit(splitComma)
     wrSpace em
   of openPars:
-    if tok.strongSpaceA > 0 and not em.endsInWhite and
+    if tsLeading in tok.spacing and not em.endsInWhite and
         (not em.wasExportMarker or tok.tokType == tkCurlyDotLe):
       wrSpace em
     wr(em, $tok.tokType, ltSomeParLe)
@@ -528,7 +533,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
     wr(em, $tok.tokType, ltOther)
     if not em.inquote: wrSpace(em)
   of tkOpr, tkDotDot:
-    if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and
+    if em.inquote or (tok.spacing == {} and
         tok.ident.s notin ["<", ">", "<=", ">=", "==", "!="]):
       # bug #9504: remember to not spacify a keyword:
       lastTokWasTerse = true
@@ -538,7 +543,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
       if not em.endsInWhite: wrSpace(em)
       wr(em, tok.ident.s, ltOpr)
       template isUnary(tok): bool =
-        tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+        tok.spacing == {tsLeading}
 
       if not isUnary(tok):
         rememberSplit(splitBinary)
@@ -551,11 +556,15 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) =
     if not preventComment:
       emitComment(em, tok, dontIndent = false)
   of tkIntLit..tkStrLit, tkRStrLit, tkTripleStrLit, tkGStrLit, tkGTripleStrLit, tkCharLit:
-    let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
-    if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
-    em.lineSpan = countNewlines(lit)
-    if em.lineSpan > 0: calcCol(em, lit)
-    wr em, lit, ltLit
+    if not em.inquote:
+      let lit = fileSection(em.config, em.fid, tok.offsetA, tok.offsetB)
+      if endsInAlpha(em) and tok.tokType notin {tkGStrLit, tkGTripleStrLit}: wrSpace(em)
+      em.lineSpan = countNewlines(lit)
+      if em.lineSpan > 0: calcCol(em, lit)
+      wr em, lit, ltLit
+    else:
+      if endsInAlpha(em): wrSpace(em)
+      wr em, tok.literal, ltLit
   of tkEof: discard
   else:
     let lit = if tok.ident != nil: tok.ident.s else: tok.literal
diff --git a/compiler/lexer.nim b/compiler/lexer.nim
index 06a6cea67..ad5dd560c 100644
--- a/compiler/lexer.nim
+++ b/compiler/lexer.nim
@@ -16,14 +16,15 @@
 # DOS or Macintosh text files, even when it is not the native format.
 
 import
-  hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream,
-  wordrecg, lineinfos, pathutils, parseutils
+  options, msgs, platform, idents, nimlexbase, llstream,
+  wordrecg, lineinfos, pathutils
+
+import std/[hashes, parseutils, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, formatfloat]
 
 const
-  MaxLineLength* = 80         # lines longer than this lead to a warning
   numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'}
   SymChars*: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
   SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'}
@@ -93,19 +94,21 @@ type
                               # so that it is the correct default value
     base2, base8, base16
 
-  Token* = object             # a Nim token
-    tokType*: TokType         # the type of the token
-    indent*: int              # the indentation; != -1 if the token has been
-                              # preceded with indentation
-    ident*: PIdent            # the parsed identifier
-    iNumber*: BiggestInt      # the parsed integer literal
-    fNumber*: BiggestFloat    # the parsed floating point literal
-    base*: NumericalBase      # the numerical base; only valid for int
-                              # or float literals
-    strongSpaceA*: int8       # leading spaces of an operator
-    strongSpaceB*: int8       # trailing spaces of an operator
-    literal*: string          # the parsed (string) literal; and
-                              # documentation comments are here too
+  TokenSpacing* = enum
+    tsLeading, tsTrailing, tsEof
+
+  Token* = object                # a Nim token
+    tokType*: TokType            # the type of the token
+    base*: NumericalBase         # the numerical base; only valid for int
+                                 # or float literals
+    spacing*: set[TokenSpacing]  # spaces around token
+    indent*: int                 # the indentation; != -1 if the token has been
+                                 # preceded with indentation
+    ident*: PIdent               # the parsed identifier
+    iNumber*: BiggestInt         # the parsed integer literal
+    fNumber*: BiggestFloat       # the parsed floating point literal
+    literal*: string             # the parsed (string) literal; and
+                                 # documentation comments are here too
     line*, col*: int
     when defined(nimpretty):
       offsetA*, offsetB*: int # used for pretty printing so that literals
@@ -119,11 +122,12 @@ type
                               # this is needed because scanning comments
                               # needs so much look-ahead
     currLineIndent*: int
-    strongSpaces*, allowTabs*: bool
     errorHandler*: ErrorHandler
     cache*: IdentCache
     when defined(nimsuggest):
       previousToken: TLineInfo
+      tokenEnd*: TLineInfo
+      previousTokenEnd*: TLineInfo
     config*: ConfigRef
 
 proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} =
@@ -145,9 +149,11 @@ proc isNimIdentifier*(s: string): bool =
     var i = 1
     while i < sLen:
       if s[i] == '_': inc(i)
-      if i < sLen and s[i] notin SymChars: return
+      if i < sLen and s[i] notin SymChars: return false
       inc(i)
     result = true
+  else:
+    result = false
 
 proc `$`*(tok: Token): string =
   case tok.tokType
@@ -169,32 +175,6 @@ proc printTok*(conf: ConfigRef; tok: Token) =
   # xxx factor with toLocation
   msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok)
 
-proc initToken*(L: var Token) =
-  L.tokType = tkInvalid
-  L.iNumber = 0
-  L.indent = 0
-  L.strongSpaceA = 0
-  L.literal = ""
-  L.fNumber = 0.0
-  L.base = base10
-  L.ident = nil
-  when defined(nimpretty):
-    L.commentOffsetA = 0
-    L.commentOffsetB = 0
-
-proc fillToken(L: var Token) =
-  L.tokType = tkInvalid
-  L.iNumber = 0
-  L.indent = 0
-  L.strongSpaceA = 0
-  setLen(L.literal, 0)
-  L.fNumber = 0.0
-  L.base = base10
-  L.ident = nil
-  when defined(nimpretty):
-    L.commentOffsetA = 0
-    L.commentOffsetB = 0
-
 proc openLexer*(lex: var Lexer, fileIdx: FileIndex, inputstream: PLLStream;
                  cache: IdentCache; config: ConfigRef) =
   openBaseLexer(lex, inputstream)
@@ -320,8 +300,7 @@ proc getNumber(L: var Lexer, result: var Token) =
     # Used to get slightly human friendlier err messages.
     const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''}
     var msgPos = L.bufpos
-    var t: Token
-    t.literal = ""
+    var t = Token(literal: "")
     L.bufpos = startpos # Use L.bufpos as pos because of matchChars
     matchChars(L, t, literalishChars)
     # We must verify +/- specifically so that we're not past the literal
@@ -505,11 +484,11 @@ proc getNumber(L: var Lexer, result: var Token) =
         of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff
         of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff
         of tkFloat32Lit:
-          setNumber result.fNumber, (cast[PFloat32](addr(xi)))[]
+          setNumber result.fNumber, (cast[ptr float32](addr(xi)))[]
           # note: this code is endian neutral!
           # XXX: Test this on big endian machine!
         of tkFloat64Lit, tkFloatLit:
-          setNumber result.fNumber, (cast[PFloat64](addr(xi)))[]
+          setNumber result.fNumber, (cast[ptr float64](addr(xi)))[]
         else: internalError(L.config, getLineInfo(L), "getNumber")
 
         # Bounds checks. Non decimal literals are allowed to overflow the range of
@@ -534,8 +513,8 @@ proc getNumber(L: var Lexer, result: var Token) =
         of floatTypes:
           result.fNumber = parseFloat(result.literal)
         of tkUInt64Lit, tkUIntLit:
-          var iNumber: uint64
-          var len: int
+          var iNumber: uint64 = uint64(0)
+          var len: int = 0
           try:
             len = parseBiggestUInt(result.literal, iNumber)
           except ValueError:
@@ -544,8 +523,8 @@ proc getNumber(L: var Lexer, result: var Token) =
             raise newException(ValueError, "invalid integer: " & result.literal)
           result.iNumber = cast[int64](iNumber)
         else:
-          var iNumber: int64
-          var len: int
+          var iNumber: int64 = int64(0)
+          var len: int = 0
           try:
             len = parseBiggestInt(result.literal, iNumber)
           except ValueError:
@@ -732,10 +711,6 @@ proc handleCRLF(L: var Lexer, pos: int): int =
   template registerLine =
     let col = L.getColNumber(pos)
 
-    when not defined(nimpretty):
-      if col > MaxLineLength:
-        lexMessagePos(L, hintLineTooLong, pos)
-
   case L.buf[pos]
   of CR:
     registerLine()
@@ -795,7 +770,7 @@ proc getString(L: var Lexer, tok: var Token, mode: StringMode) =
     if mode != normal: tok.tokType = tkRStrLit
     else: tok.tokType = tkStrLit
     while true:
-      var c = L.buf[pos]
+      let c = L.buf[pos]
       if c == '\"':
         if mode != normal and L.buf[pos+1] == '\"':
           inc(pos, 2)
@@ -821,7 +796,7 @@ proc getCharacter(L: var Lexer; tok: var Token) =
   tokenBegin(tok, L.bufpos)
   let startPos = L.bufpos
   inc(L.bufpos)               # skip '
-  var c = L.buf[L.bufpos]
+  let c = L.buf[L.bufpos]
   case c
   of '\0'..pred(' '), '\'':
     lexMessage(L, errGenerated, "invalid character literal")
@@ -907,7 +882,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
       inc(pos)
       suspicious = true
     of '\x80'..'\xFF':
-      if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and unicodeOprLen(L.buf, pos)[0] != 0:
+      if c in UnicodeOperatorStartChars and unicodeOprLen(L.buf, pos)[0] != 0:
         break
       else:
         h = h !& ord(c)
@@ -915,7 +890,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
     else: break
   tokenEnd(tok, pos-1)
   h = !$h
-  tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
+  tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
   if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or
       (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)):
     tok.tokType = tkSymbol
@@ -929,7 +904,7 @@ proc getSymbol(L: var Lexer, tok: var Token) =
 proc endOperator(L: var Lexer, tok: var Token, pos: int,
                  hash: Hash) {.inline.} =
   var h = !$hash
-  tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h)
+  tok.ident = L.cache.getIdent(cast[cstring](addr(L.buf[L.bufpos])), pos - L.bufpos, h)
   if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr
   else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon))
   L.bufpos = pos
@@ -939,11 +914,11 @@ proc getOperator(L: var Lexer, tok: var Token) =
   tokenBegin(tok, pos)
   var h: Hash = 0
   while true:
-    var c = L.buf[pos]
+    let c = L.buf[pos]
     if c in OpChars:
       h = h !& ord(c)
       inc(pos)
-    elif c in UnicodeOperatorStartChars and unicodeOperators in L.config.features:
+    elif c in UnicodeOperatorStartChars:
       let oprLen = unicodeOprLen(L.buf, pos)[0]
       if oprLen == 0: break
       for i in 0..<oprLen:
@@ -955,12 +930,15 @@ proc getOperator(L: var Lexer, tok: var Token) =
   tokenEnd(tok, pos-1)
   # advance pos but don't store it in L.bufpos so the next token (which might
   # be an operator too) gets the preceding spaces:
-  tok.strongSpaceB = 0
+  tok.spacing = tok.spacing - {tsTrailing, tsEof}
+  var trailing = false
   while L.buf[pos] == ' ':
     inc pos
-    inc tok.strongSpaceB
+    trailing = true
   if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}:
-    tok.strongSpaceB = -1
+    tok.spacing.incl(tsEof)
+  elif trailing:
+    tok.spacing.incl(tsTrailing)
 
 proc getPrecedence*(tok: Token): int =
   ## Calculates the precedence of the given token.
@@ -1004,22 +982,6 @@ proc getPrecedence*(tok: Token): int =
   of tkOr, tkXor, tkPtr, tkRef: result = 3
   else: return -10
 
-proc newlineFollows*(L: Lexer): bool =
-  var pos = L.bufpos
-  while true:
-    case L.buf[pos]
-    of ' ', '\t':
-      inc(pos)
-    of CR, LF:
-      result = true
-      break
-    of '#':
-      inc(pos)
-      if L.buf[pos] == '#': inc(pos)
-      if L.buf[pos] != '[': return true
-    else:
-      break
-
 proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
                           isDoc: bool) =
   var pos = start
@@ -1071,7 +1033,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
       when defined(nimpretty): tok.literal.add "\L"
       if isDoc:
         when not defined(nimpretty): tok.literal.add "\n"
-        inc tok.iNumber
         var c = toStrip
         while L.buf[pos] == ' ' and c > 0:
           inc pos
@@ -1090,8 +1051,6 @@ proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int;
 proc scanComment(L: var Lexer, tok: var Token) =
   var pos = L.bufpos
   tok.tokType = tkComment
-  # iNumber contains the number of '\n' in the token
-  tok.iNumber = 0
   assert L.buf[pos+1] == '#'
   when defined(nimpretty):
     tok.commentOffsetA = L.offsetBase + pos
@@ -1114,9 +1073,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
         toStrip = 0
       else:  # found first non-whitespace character
         stripInit = true
-    var lastBackslash = -1
     while L.buf[pos] notin {CR, LF, nimlexbase.EndOfFile}:
-      if L.buf[pos] == '\\': lastBackslash = pos+1
       tok.literal.add(L.buf[pos])
       inc(pos)
     tokenEndIgnore(tok, pos)
@@ -1134,7 +1091,6 @@ proc scanComment(L: var Lexer, tok: var Token) =
         while L.buf[pos] == ' ' and c > 0:
           inc pos
           dec c
-        inc tok.iNumber
     else:
       if L.buf[pos] > ' ':
         L.indentAhead = indent
@@ -1147,7 +1103,7 @@ proc scanComment(L: var Lexer, tok: var Token) =
 proc skip(L: var Lexer, tok: var Token) =
   var pos = L.bufpos
   tokenBegin(tok, pos)
-  tok.strongSpaceA = 0
+  tok.spacing.excl(tsLeading)
   when defined(nimpretty):
     var hasComment = false
     var commentIndent = L.currLineIndent
@@ -1158,9 +1114,9 @@ proc skip(L: var Lexer, tok: var Token) =
     case L.buf[pos]
     of ' ':
       inc(pos)
-      inc(tok.strongSpaceA)
+      tok.spacing.incl(tsLeading)
     of '\t':
-      if not L.allowTabs: lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
+      lexMessagePos(L, errGenerated, pos, "tabs are not allowed, use spaces instead")
       inc(pos)
     of CR, LF:
       tokenEndPrevious(tok, pos)
@@ -1180,7 +1136,7 @@ proc skip(L: var Lexer, tok: var Token) =
           pos = L.bufpos
         else:
           break
-      tok.strongSpaceA = 0
+      tok.spacing.excl(tsLeading)
       when defined(nimpretty):
         if L.buf[pos] == '#' and tok.line < 0: commentIndent = indent
       if L.buf[pos] > ' ' and (L.buf[pos] != '#' or L.buf[pos+1] == '#'):
@@ -1219,12 +1175,16 @@ proc skip(L: var Lexer, tok: var Token) =
 proc rawGetTok*(L: var Lexer, tok: var Token) =
   template atTokenEnd() {.dirty.} =
     when defined(nimsuggest):
+      L.previousTokenEnd.line = L.tokenEnd.line
+      L.previousTokenEnd.col = L.tokenEnd.col
+      L.tokenEnd.line = tok.line.uint16
+      L.tokenEnd.col = getColNumber(L, L.bufpos).int16
       # we attach the cursor to the last *strong* token
       if tok.tokType notin weakTokens:
         L.previousToken.line = tok.line.uint16
         L.previousToken.col = tok.col.int16
 
-  fillToken(tok)
+  reset(tok)
   if L.indentAhead >= 0:
     tok.indent = L.indentAhead
     L.currLineIndent = L.indentAhead
@@ -1236,7 +1196,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
     if tok.tokType == tkComment:
       L.indentAhead = L.currLineIndent
       return
-  var c = L.buf[L.bufpos]
+  let c = L.buf[L.bufpos]
   tok.line = L.lineNumber
   tok.col = getColNumber(L, L.bufpos)
   if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars:
@@ -1244,7 +1204,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
   else:
     case c
     of UnicodeOperatorStartChars:
-      if unicodeOperators in L.config.features and unicodeOprLen(L.buf, L.bufpos)[0] != 0:
+      if unicodeOprLen(L.buf, L.bufpos)[0] != 0:
         getOperator(L, tok)
       else:
         getSymbol(L, tok)
@@ -1355,7 +1315,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
       getNumber(L, tok)
       let c = L.buf[L.bufpos]
       if c in SymChars+{'_'}:
-        if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
+        if c in UnicodeOperatorStartChars and
             unicodeOprLen(L.buf, L.bufpos)[0] != 0:
           discard
         else:
@@ -1370,7 +1330,7 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
         getNumber(L, tok)
         let c = L.buf[L.bufpos]
         if c in SymChars+{'_'}:
-          if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and
+          if c in UnicodeOperatorStartChars and
               unicodeOprLen(L.buf, L.bufpos)[0] != 0:
             discard
           else:
@@ -1392,9 +1352,9 @@ proc rawGetTok*(L: var Lexer, tok: var Token) =
 
 proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
                      cache: IdentCache; config: ConfigRef): int =
-  var lex: Lexer
-  var tok: Token
-  initToken(tok)
+  result = 0
+  var lex: Lexer = default(Lexer)
+  var tok: Token = default(Token)
   openLexer(lex, fileIdx, inputstream, cache, config)
   var prevToken = tkEof
   while tok.tokType != tkEof:
@@ -1407,11 +1367,11 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream;
 
 proc getPrecedence*(ident: PIdent): int =
   ## assumes ident is binary operator already
-  var tok: Token
-  initToken(tok)
-  tok.ident = ident
-  tok.tokType =
-    if tok.ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
-      TokType(tok.ident.id + ord(tkSymbol))
-    else: tkOpr
+  let
+    tokType =
+      if ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol):
+        TokType(ident.id + ord(tkSymbol))
+      else: tkOpr
+    tok = Token(ident: ident, tokType: tokType)
+
   getPrecedence(tok)
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 5174a908f..9ff5c0a9d 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -8,11 +8,12 @@
 #
 
 ## This module implements lifting for type-bound operations
-## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``).
+## (`=sink`, `=copy`, `=destroy`, `=deepCopy`, `=wasMoved`, `=dup`).
 
 import modulegraphs, lineinfos, idents, ast, renderer, semdata,
-  sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils
+  sighashes, lowerings, options, types, msgs, magicsys, ccgutils
 
+import std/tables
 from trees import isCaseObj
 
 when defined(nimPreviewSlimSystem):
@@ -34,11 +35,12 @@ type
 
 template destructor*(t: PType): PSym = getAttachedOp(c.g, t, attachedDestructor)
 template assignment*(t: PType): PSym = getAttachedOp(c.g, t, attachedAsgn)
+template dup*(t: PType): PSym = getAttachedOp(c.g, t, attachedDup)
 template asink*(t: PType): PSym = getAttachedOp(c.g, t, attachedSink)
 
 proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode)
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator): PSym
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym
 
 proc createTypeBoundOps*(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
                          idgen: IdGenerator)
@@ -49,15 +51,15 @@ proc at(a, i: PNode, elemType: PType): PNode =
   result[1] = i
   result.typ = elemType
 
-proc destructorOverriden(g: ModuleGraph; t: PType): bool =
+proc destructorOverridden(g: ModuleGraph; t: PType): bool =
   let op = getAttachedOp(g, t, attachedDestructor)
-  op != nil and sfOverriden in op.flags
+  op != nil and sfOverridden in op.flags
 
 proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  for i in 0..<t.len:
+  for i, a in t.ikids:
     let lit = lowerings.newIntLit(c.g, x.info, i)
-    let b = if c.kind == attachedTrace: y else: y.at(lit, t[i])
-    fillBody(c, t[i], body, x.at(lit, t[i]), b)
+    let b = if c.kind == attachedTrace: y else: y.at(lit, a)
+    fillBody(c, a, body, x.at(lit, a), b)
 
 proc dotField(x: PNode, f: PSym): PNode =
   result = newNodeI(nkDotExpr, x.info, 2)
@@ -82,12 +84,14 @@ proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode =
   result = genBuiltin(c.g, c.idgen, magic, name, i)
 
 proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
-  if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
+  if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink, attachedDup}:
     body.add newAsgnStmt(x, y)
   elif c.kind == attachedDestructor and c.addMemReset:
     let call = genBuiltin(c, mDefault, "default", x)
     call.typ = t
     body.add newAsgnStmt(x, call)
+  elif c.kind == attachedWasMoved:
+    body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc genAddr(c: var TLiftCtx; x: PNode): PNode =
   if x.kind == nkHiddenDeref:
@@ -135,27 +139,36 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode =
   result.add minusExpr
 
 proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
-  var destroy = newNodeIT(nkCall, x.info, op.typ[0])
+  var destroy = newNodeIT(nkCall, x.info, op.typ.returnType)
   destroy.add(newSymNode(op))
-  destroy.add genAddr(c, x)
+  if op.typ.firstParamType.kind != tyVar:
+    destroy.add x
+  else:
+    destroy.add genAddr(c, x)
   if sfNeverRaises notin op.flags:
     c.canRaise = true
   if c.addMemReset:
-    result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved,  "wasMoved", x))
+    result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved,  "`=wasMoved`", x))
   else:
     result = destroy
 
-proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) =
+proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
+  result = newNodeIT(nkCall, x.info, op.typ.returnType)
+  result.add(newSymNode(op))
+  result.add genAddr(c, x)
+
+proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool, enforceWasMoved = false) =
   case n.kind
   of nkSym:
     if c.filterDiscriminator != nil: return
     let f = n.sym
     let b = if c.kind == attachedTrace: y else: y.dotField(f)
-    if (sfCursor in f.flags and f.typ.skipTypes(abstractInst).kind in {tyRef, tyProc} and
-        c.g.config.selectedGC in {gcArc, gcOrc, gcHooks}) or
+    if (sfCursor in f.flags and c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc, gcHooks}) or
         enforceDefaultOp:
       defaultOp(c, f.typ, body, x.dotField(f), b)
     else:
+      if enforceWasMoved:
+        body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x.dotField(f))
       fillBody(c, f.typ, body, x.dotField(f), b)
   of nkNilLit: discard
   of nkRecCase:
@@ -194,7 +207,7 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
       branch[^1] = newNodeI(nkStmtList, c.info)
 
       fillBodyObj(c, n[i].lastSon, branch[^1], x, y,
-                  enforceDefaultOp = localEnforceDefaultOp)
+                  enforceDefaultOp = localEnforceDefaultOp, enforceWasMoved = c.kind == attachedAsgn)
       if branch[^1].len == 0: inc emptyBranches
       caseStmt.add(branch)
     if emptyBranches != n.len-1:
@@ -205,20 +218,29 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool)
       fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false)
     c.filterDiscriminator = oldfilterDiscriminator
   of nkRecList:
-    for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp)
+    for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp, enforceWasMoved)
   else:
     illFormedAstLocal(n, c.g.config)
 
 proc fillBodyObjTImpl(c: var TLiftCtx; t: PType, body, x, y: PNode) =
-  if t.len > 0 and t[0] != nil:
-    fillBody(c, skipTypes(t[0], abstractPtrs), body, x, y)
+  if t.baseClass != nil:
+    let dest = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+    dest.add newNodeI(nkEmpty, c.info)
+    dest.add x
+    var src = y
+    if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}:
+      src = newNodeIT(nkHiddenSubConv, c.info, t.baseClass)
+      src.add newNodeI(nkEmpty, c.info)
+      src.add y
+
+    fillBody(c, skipTypes(t.baseClass, abstractPtrs), body, dest, src)
   fillBodyObj(c, t.n, body, x, y, enforceDefaultOp = false)
 
 proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
   var hasCase = isCaseObj(t.n)
   var obj = t
-  while obj.len > 0 and obj[0] != nil:
-    obj = skipTypes(obj[0], abstractPtrs)
+  while obj.baseClass != nil:
+    obj = skipTypes(obj.baseClass, abstractPtrs)
     hasCase = hasCase or isCaseObj(obj.n)
 
   if hasCase and c.kind in {attachedAsgn, attachedDeepCopy}:
@@ -236,7 +258,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
     # for every field (dependent on dest.kind):
     #   `=` dest.field, src.field
     # =destroy(blob)
-    var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
+    var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
     dummy.typ = y.typ
     if ccgIntroducedPtr(c.g.config, dummy, y.typ):
       # Because of potential aliasing when the src param is passed by ref, we need to check for equality here,
@@ -245,7 +267,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
         newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y))
       cond.typ = getSysType(c.g, x.info, tyBool)
       body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
-    var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info)
+    var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
     temp.typ = x.typ
     incl(temp.flags, sfFromGeneric)
     var v = newNodeI(nkVarSection, c.info)
@@ -255,7 +277,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
     #body.add newAsgnStmt(blob, x)
 
     var wasMovedCall = newNodeI(nkCall, c.info)
-    wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved)))
+    wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "`=wasMoved`", mWasMoved)))
     wasMovedCall.add x # mWasMoved does not take the address
     body.add wasMovedCall
 
@@ -268,6 +290,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) =
     c.kind = attachedDestructor
     fillBodyObjTImpl(c, t, body, blob, y)
     c.kind = prevKind
+
   else:
     fillBodyObjTImpl(c, t, body, x, y)
 
@@ -276,8 +299,8 @@ proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode =
   result.typ = getSysType(g, info, tyBool)
 
 proc getCycleParam(c: TLiftCtx): PNode =
-  assert c.kind == attachedAsgn
-  if c.fn.typ.len == 4:
+  assert c.kind in {attachedAsgn, attachedDup}
+  if c.fn.typ.len == 3 + ord(c.kind == attachedAsgn):
     result = c.fn.typ.n.lastSon
     assert result.kind == nkSym
     assert result.sym.name.s == "cyclic"
@@ -291,30 +314,41 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
   result.add newSymNode(op)
   if sfNeverRaises notin op.flags:
     c.canRaise = true
-  if op.typ.sons[1].kind == tyVar:
+  if op.typ.firstParamType.kind == tyVar:
     result.add genAddr(c, x)
   else:
     result.add x
   if y != nil:
     result.add y
-  if op.typ.len == 4:
+  if op.typ.signatureLen == 4:
     assert y != nil
-    if c.fn.typ.len == 4:
+    if c.fn.typ.signatureLen == 4:
       result.add getCycleParam(c)
     else:
       # assume the worst: A cycle is created:
       result.add boolLit(c.g, y.info, true)
 
 proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode =
-  result = newNodeIT(nkCall, x.info, op.typ[0])
+  result = newNodeIT(nkCall, x.info, op.typ.returnType)
   result.add(newSymNode(op))
   result.add x
   if sfNeverRaises notin op.flags:
     c.canRaise = true
 
+  if c.kind == attachedDup and op.typ.len == 3:
+    assert x != nil
+    if c.fn.typ.len == 3:
+      result.add getCycleParam(c)
+    else:
+      # assume the worst: A cycle is created:
+      result.add boolLit(c.g, x.info, true)
+
 proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
   result = newAsgnStmt(x, newOpCall(c, op, y))
 
+proc newDupCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode =
+  result = newAsgnStmt(x, newOpCall(c, op, y))
+
 proc usesBuiltinArc(t: PType): bool =
   proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t)
   result = types.searchTypeFor(t, wrap)
@@ -339,9 +373,9 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
                         field: var PSym): bool =
   if optSeqDestructors in c.g.config.globalOptions:
     var op = field
-    let destructorOverriden = destructorOverriden(c.g, t)
+    let destructorOverridden = destructorOverridden(c.g, t)
     if op != nil and op != c.fn and
-        (sfOverriden in op.flags or destructorOverriden):
+        (sfOverridden in op.flags or destructorOverridden):
       if sfError in op.flags:
         incl c.fn.flags, sfError
       #else:
@@ -349,10 +383,12 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
       onUse(c.info, op)
       body.add newHookCall(c, op, x, y)
       result = true
-    elif op == nil and destructorOverriden:
+    elif op == nil and destructorOverridden:
       op = produceSym(c.g, c.c, t, c.kind, c.info, c.idgen)
       body.add newHookCall(c, op, x, y)
       result = true
+    else:
+      result = false
   elif tfHasAsgn in t.flags:
     var op: PSym
     if sameType(t, c.asgnForType):
@@ -382,12 +418,14 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode;
     assert op.ast[genericParamsPos].kind == nkEmpty
     body.add newHookCall(c, op, x, y)
     result = true
+  else:
+    result = false
 
 proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) =
   let t = orig.skipTypes(abstractInst - {tyDistinct})
   var op = t.destructor
 
-  if op != nil and sfOverriden in op.flags:
+  if op != nil and sfOverridden in op.flags:
     if op.ast.isGenericRoutine:
       # patch generic destructor:
       op = instantiateGeneric(c, op, t, t.typeInst)
@@ -410,7 +448,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
   case c.kind
   of attachedDestructor:
     var op = t.destructor
-    if op != nil and sfOverriden in op.flags:
+    if op != nil and sfOverridden in op.flags:
 
       if op.ast.isGenericRoutine:
         # patch generic destructor:
@@ -421,10 +459,12 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
       onUse(c.info, op)
       body.add destructorCall(c, op, x)
       result = true
+    else:
+      result = false
     #result = addDestructorCall(c, t, body, x)
   of attachedAsgn, attachedSink, attachedTrace:
     var op = getAttachedOp(c.g, t, c.kind)
-    if op != nil and sfOverriden in op.flags:
+    if op != nil and sfOverridden in op.flags:
       if op.ast.isGenericRoutine:
         # patch generic =trace:
         op = instantiateGeneric(c, op, t, t.typeInst)
@@ -441,9 +481,43 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool =
       onUse(c.info, op)
       body.add newDeepCopyCall(c, op, x, y)
       result = true
+    else:
+      result = false
+
+  of attachedWasMoved:
+    var op = getAttachedOp(c.g, t, attachedWasMoved)
+    if op != nil and sfOverridden in op.flags:
+
+      if op.ast.isGenericRoutine:
+        # patch generic destructor:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op)
+
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add genWasMovedCall(c, op, x)
+      result = true
+    else:
+      result = false
+
+  of attachedDup:
+    var op = getAttachedOp(c.g, t, attachedDup)
+    if op != nil and sfOverridden in op.flags:
+
+      if op.ast.isGenericRoutine:
+        # patch generic destructor:
+        op = instantiateGeneric(c, op, t, t.typeInst)
+        setAttachedOp(c.g, c.idgen.module, t, attachedDup, op)
+
+      #markUsed(c.g.config, c.info, op, c.g.usageSym)
+      onUse(c.info, op)
+      body.add newDupCall(c, op, x, y)
+      result = true
+    else:
+      result = false
 
 proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
-  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
   temp.typ = getSysType(c.g, body.info, tyInt)
   incl(temp.flags, sfFromGeneric)
 
@@ -453,7 +527,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode =
   body.add v
 
 proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode =
-  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info)
+  var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), c.idgen, c.fn, c.info)
   temp.typ = value.typ
   incl(temp.flags, sfFromGeneric)
 
@@ -491,7 +565,7 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   let counterIdx = body.len
   let i = declareCounter(c, body, toInt64(firstOrd(c.g.config, t)))
   let whileLoop = genWhileLoop(c, i, x)
-  let elemType = t.lastSon
+  let elemType = t.elementType
   let b = if c.kind == attachedTrace: y else: y.at(i, elemType)
   fillBody(c, elemType, whileLoop[1], x.at(i, elemType), b)
   if whileLoop[1].len > 0:
@@ -500,14 +574,28 @@ proc forallElements(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   else:
     body.sons.setLen counterIdx
 
+proc checkSelfAssignment(c: var TLiftCtx; t: PType; body, x, y: PNode) =
+  var cond = callCodegenProc(c.g, "sameSeqPayload", c.info,
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x),
+      newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)
+      )
+  cond.typ = getSysType(c.g, c.info, tyBool)
+  body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info)))
+
 proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
+  of attachedDup:
+    body.add setLenSeqCall(c, t, x, y)
+    forallElements(c, t, body, x, y)
   of attachedAsgn, attachedDeepCopy:
     # we generate:
+    # if x.p == y.p:
+    #   return
     # setLen(dest, y.len)
     # var i = 0
     # while i < y.len: dest[i] = y[i]; inc(i)
     # This is usually more efficient than a destroy/create pair.
+    checkSelfAssignment(c, t, body, x, y)
     body.add setLenSeqCall(c, t, x, y)
     forallElements(c, t, body, x, y)
   of attachedSink:
@@ -521,17 +609,18 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     forallElements(c, t, body, x, y)
     body.add genBuiltin(c, mDestroy, "destroy", x)
   of attachedTrace:
-    if canFormAcycle(t.elemType):
+    if canFormAcycle(c.g, t.elemType):
       # follow all elements:
       forallElements(c, t, body, x, y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   createTypeBoundOps(c.g, c.c, t, body.info, c.idgen)
   # recursions are tricky, so we might need to forward the generated
   # operation here:
   var t = t
-  if t.assignment == nil or t.destructor == nil:
-    let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct})
+  if t.assignment == nil or t.destructor == nil or t.dup == nil:
+    let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct})
     let canon = c.g.canonTypes.getOrDefault(h)
     if canon != nil: t = canon
 
@@ -556,15 +645,22 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     doAssert t.destructor != nil
     body.add destructorCall(c, t.destructor, x)
   of attachedTrace:
-    if t.kind != tyString and canFormAcycle(t.elemType):
+    if t.kind != tyString and canFormAcycle(c.g, t.elemType):
       let op = getAttachedOp(c.g, t, c.kind)
       if op == nil:
         return # protect from recursion
       body.add newHookCall(c, op, x, y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+  of attachedDup:
+    # XXX: replace these with assertions.
+    let op = getAttachedOp(c.g, t, c.kind)
+    if op == nil:
+      return # protect from recursion
+    body.add newDupCall(c, op, x, y)
 
 proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
-  of attachedAsgn, attachedDeepCopy:
+  of attachedAsgn, attachedDeepCopy, attachedDup:
     body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y)
   of attachedSink:
     let moveCall = genBuiltin(c, mMove, "move", x)
@@ -576,10 +672,11 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add genBuiltin(c, mDestroy, "destroy", x)
   of attachedTrace:
     discard "strings are atomic and have no inner elements that are to trace"
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
-proc cyclicType*(t: PType): bool =
+proc cyclicType*(g: ModuleGraph, t: PType): bool =
   case t.kind
-  of tyRef: result = types.canFormAcycle(t.lastSon)
+  of tyRef: result = types.canFormAcycle(g, t.elementType)
   of tyProc: result = t.callConv == ccClosure
   else: result = false
 
@@ -604,13 +701,18 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 
   ]#
   var actions = newNodeI(nkStmtList, c.info)
-  let elemType = t.lastSon
+  let elemType = t.elementType
 
   createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen)
-  let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType)
+  let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(c.g, elemType)
+
+  let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and
+                      (not isPureObject(elemType)) and
+                      tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags
+  # dynamic Acyclic refs need to use dyn decRef
 
   let tmp =
-    if isCyclic and c.kind in {attachedAsgn, attachedSink}:
+    if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
       declareTempOf(c, body, x)
     else:
       x
@@ -632,6 +734,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo)
     else:
       cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp)
+  elif isInheritableAcyclicRef:
+    cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x)
   else:
     cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x)
   cond.typ = getSysType(c.g, x.info, tyBool)
@@ -667,6 +771,16 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
         # If the ref is polymorphic we have to account for this
         body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y)
       #echo "can follow ", elemType, " static ", isFinal(elemType)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+  of attachedDup:
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, y, callCodegenProc(c.g,
+          "nimIncRefCyclic", c.info, y, getCycleParam(c)))
+    else:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, y, callCodegenProc(c.g,
+          "nimIncRef", c.info, y))
 
 proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   ## Closures are really like refs except they always use a virtual destructor
@@ -676,7 +790,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 
   let isCyclic = c.g.config.selectedGC == gcOrc
   let tmp =
-    if isCyclic and c.kind in {attachedAsgn, attachedSink}:
+    if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:
       declareTempOf(c, body, xenv)
     else:
       xenv
@@ -710,11 +824,21 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
 
       body.add genIf(c, cond, actions)
       body.add newAsgnStmt(x, y)
+  of attachedDup:
+    let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y)
+    yenv.typ = getSysType(c.g, c.info, tyPointer)
+    if isCyclic:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c)))
+    else:
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv))
   of attachedDestructor:
     body.add genIf(c, cond, actions)
   of attachedDeepCopy: assert(false, "cannot happen")
   of attachedTrace:
     body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y)
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case c.kind
@@ -727,6 +851,9 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
     body.add genIf(c, x, callCodegenProc(c.g, "nimDecWeakRef", c.info, x))
     body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
+    body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y))
   of attachedDestructor:
     # it's better to prepend the destruction of weak refs in order to
     # prevent wrong "dangling refs exist" problems:
@@ -739,11 +866,12 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       body.sons.insert(des, 0)
   of attachedDeepCopy: assert(false, "cannot happen")
   of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   var actions = newNodeI(nkStmtList, c.info)
 
-  let elemType = t.lastSon
+  let elemType = t.skipModifier
   #fillBody(c, elemType, actions, genDeref(x), genDeref(y))
   #var disposeCall = genBuiltin(c, mDispose, "dispose", x)
 
@@ -760,10 +888,13 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of attachedSink, attachedAsgn:
     body.add genIf(c, x, actions)
     body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
   of attachedDestructor:
     body.add genIf(c, x, actions)
   of attachedDeepCopy: assert(false, "cannot happen")
   of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   if c.kind == attachedDeepCopy:
@@ -775,7 +906,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     call[1] = y
     body.add newAsgnStmt(x, call)
   elif (optOwnedRefs in c.g.config.globalOptions and
-      optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}:
+      optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
     let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
     xx.typ = getSysType(c.g, c.info, tyPointer)
     case c.kind
@@ -790,6 +921,11 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
       body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
       body.add newAsgnStmt(x, y)
+    of attachedDup:
+      let yy = genBuiltin(c, mAccessEnv, "accessEnv", y)
+      yy.typ = getSysType(c.g, c.info, tyPointer)
+      body.add newAsgnStmt(x, y)
+      body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy))
     of attachedDestructor:
       let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx))
       if body.len == 0:
@@ -798,6 +934,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
         body.sons.insert(des, 0)
     of attachedDeepCopy: assert(false, "cannot happen")
     of attachedTrace: discard
+    of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   let xx = genBuiltin(c, mAccessEnv, "accessEnv", x)
@@ -809,10 +946,13 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   of attachedSink, attachedAsgn:
     body.add genIf(c, xx, actions)
     body.add newAsgnStmt(x, y)
+  of attachedDup:
+    body.add newAsgnStmt(x, y)
   of attachedDestructor:
     body.add genIf(c, xx, actions)
   of attachedDeepCopy: assert(false, "cannot happen")
   of attachedTrace: discard
+  of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
 
 proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   case t.kind
@@ -821,7 +961,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       tyPtr, tyUncheckedArray, tyVar, tyLent:
     defaultOp(c, t, body, x, y)
   of tyRef:
-    if c.g.config.selectedGC in {gcArc, gcOrc}:
+    if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
       atomicRefOp(c, t, body, x, y)
     elif (optOwnedRefs in c.g.config.globalOptions and
         optRefCheck in c.g.config.options):
@@ -830,7 +970,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       defaultOp(c, t, body, x, y)
   of tyProc:
     if t.callConv == ccClosure:
-      if c.g.config.selectedGC in {gcArc, gcOrc}:
+      if c.g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
         atomicClosureOp(c, t, body, x, y)
       else:
         closureOp(c, t, body, x, y)
@@ -876,13 +1016,28 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
       defaultOp(c, t, body, x, y)
   of tyObject:
     if not considerUserDefinedOp(c, t, body, x, y):
-      if c.kind in {attachedAsgn, attachedSink} and t.sym != nil and sfImportc in t.sym.flags:
-        body.add newAsgnStmt(x, y)
+      if t.sym != nil and sfImportc in t.sym.flags:
+        case c.kind
+        of {attachedAsgn, attachedSink, attachedDup}:
+          body.add newAsgnStmt(x, y)
+        of attachedWasMoved:
+          body.add genBuiltin(c, mWasMoved, "`=wasMoved`", x)
+        else:
+          fillBodyObjT(c, t, body, x, y)
       else:
-        fillBodyObjT(c, t, body, x, y)
+        if c.kind == attachedDup:
+          var op2 = getAttachedOp(c.g, t, attachedAsgn)
+          if op2 != nil and sfOverridden in op2.flags:
+            #markUsed(c.g.config, c.info, op, c.g.usageSym)
+            onUse(c.info, op2)
+            body.add newHookCall(c, t.assignment, x, y)
+          else:
+            fillBodyObjT(c, t, body, x, y)
+        else:
+          fillBodyObjT(c, t, body, x, y)
   of tyDistinct:
     if not considerUserDefinedOp(c, t, body, x, y):
-      fillBody(c, t[0], body, x, y)
+      fillBody(c, t.elementType, body, x, y)
   of tyTuple:
     fillBodyTup(c, t, body, x, y)
   of tyVarargs, tyOpenArray:
@@ -891,7 +1046,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     else:
       discard "cannot copy openArray"
 
-  of tyFromExpr, tyProxy, tyBuiltInTypeClass, tyUserTypeClass,
+  of tyFromExpr, tyError, tyBuiltInTypeClass, tyUserTypeClass,
      tyUserTypeClassInst, tyCompositeTypeClass, tyAnd, tyOr, tyNot, tyAnything,
      tyGenericParam, tyGenericBody, tyNil, tyUntyped, tyTyped,
      tyTypeDesc, tyGenericInvocation, tyForward, tyStatic:
@@ -899,42 +1054,86 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) =
     discard
   of tyOrdinal, tyRange, tyInferred,
      tyGenericInst, tyAlias, tySink:
-    fillBody(c, lastSon(t), body, x, y)
-  of tyConcept, tyIterable: doAssert false
+    fillBody(c, skipModifier(t), body, x, y)
+  of tyConcept, tyIterable: raiseAssert "unreachable"
 
 proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType;
                             kind: TTypeAttachedOp; info: TLineInfo;
                             idgen: IdGenerator): PSym =
   assert typ.kind == tyDistinct
-  let baseType = typ[0]
+  let baseType = typ.elementType
   if getAttachedOp(g, baseType, kind) == nil:
-    discard produceSym(g, c, baseType, kind, info, idgen)
+    # TODO: fixme `isDistinct` is a fix for #23552; remove it after
+    # `-d:nimPreviewNonVarDestructor` becomes the default
+    discard produceSym(g, c, baseType, kind, info, idgen, isDistinct = true)
   result = getAttachedOp(g, baseType, kind)
   setAttachedOp(g, idgen.module, typ, kind, result)
 
-proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+proc symDupPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
               info: TLineInfo; idgen: IdGenerator): PSym =
+  let procname = getIdent(g.cache, AttachedOpToStr[kind])
+  result = newSym(skProc, procname, idgen, owner, info)
+  let res = newSym(skResult, getIdent(g.cache, "result"), idgen, result, info)
+  let src = newSym(skParam, getIdent(g.cache, "src"),
+                   idgen, result, info)
+  res.typ = typ
+  src.typ = typ
+
+  result.typ = newType(tyProc, idgen, owner)
+  result.typ.n = newNodeI(nkFormalParams, info)
+  rawAddSon(result.typ, res.typ)
+  result.typ.n.add newNodeI(nkEffectList, info)
+
+  result.typ.addParam src
+
+  if g.config.selectedGC == gcOrc and
+    cyclicType(g, typ.skipTypes(abstractInst)):
+    let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
+                            idgen, result, info)
+    cycleParam.typ = getSysType(g, info, tyBool)
+    result.typ.addParam cycleParam
+
+  var n = newNodeI(nkProcDef, info, bodyPos+2)
+  for i in 0..<n.len: n[i] = newNodeI(nkEmpty, info)
+  n[namePos] = newSymNode(result)
+  n[paramsPos] = result.typ.n
+  n[bodyPos] = newNodeI(nkStmtList, info)
+  n[resultPos] = newSymNode(res)
+  result.ast = n
+  incl result.flags, sfFromGeneric
+  incl result.flags, sfGeneratedOp
+
+proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp;
+              info: TLineInfo; idgen: IdGenerator; isDiscriminant = false; isDistinct = false): PSym =
+  if kind == attachedDup:
+    return symDupPrototype(g, typ, owner, kind, info, idgen)
 
   let procname = getIdent(g.cache, AttachedOpToStr[kind])
-  result = newSym(skProc, procname, nextSymId(idgen), owner, info)
-  let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
+  result = newSym(skProc, procname, idgen, owner, info)
+  let dest = newSym(skParam, getIdent(g.cache, "dest"), idgen, result, info)
   let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"),
-                   nextSymId(idgen), result, info)
-  dest.typ = makeVarType(typ.owner, typ, idgen)
+                   idgen, result, info)
+
+  if kind == attachedDestructor and g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+     ((g.config.isDefined("nimPreviewNonVarDestructor") and not isDiscriminant) or (typ.kind in {tyRef, tyString, tySequence} and not isDistinct)):
+    dest.typ = typ
+  else:
+    dest.typ = makeVarType(typ.owner, typ, idgen)
+
   if kind == attachedTrace:
     src.typ = getSysType(g, info, tyPointer)
   else:
     src.typ = typ
 
-  result.typ = newProcType(info, nextTypeId(idgen), owner)
+  result.typ = newProcType(info, idgen, owner)
   result.typ.addParam dest
-  if kind != attachedDestructor:
+  if kind notin {attachedDestructor, attachedWasMoved}:
     result.typ.addParam src
 
   if kind == attachedAsgn and g.config.selectedGC == gcOrc and
-      cyclicType(typ.skipTypes(abstractInst)):
+      cyclicType(g, typ.skipTypes(abstractInst)):
     let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"),
-                            nextSymId(idgen), result, info)
+                            idgen, result, info)
     cycleParam.typ = getSysType(g, info, tyBool)
     result.typ.addParam cycleParam
 
@@ -946,6 +1145,9 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp
   result.ast = n
   incl result.flags, sfFromGeneric
   incl result.flags, sfGeneratedOp
+  if kind == attachedWasMoved:
+    incl result.flags, sfNoSideEffect
+    incl result.typ.flags, tfNoSideEffect
 
 proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x)
@@ -955,33 +1157,36 @@ proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) =
   body.add newAsgnStmt(xx, yy)
 
 proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
-              info: TLineInfo; idgen: IdGenerator): PSym =
+              info: TLineInfo; idgen: IdGenerator; isDistinct = false): PSym =
   if typ.kind == tyDistinct:
     return produceSymDistinctType(g, c, typ, kind, info, idgen)
 
   result = getAttachedOp(g, typ, kind)
   if result == nil:
-    result = symPrototype(g, typ, typ.owner, kind, info, idgen)
+    result = symPrototype(g, typ, typ.owner, kind, info, idgen, isDistinct = isDistinct)
 
   var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType: typ, idgen: idgen,
                    fn: result)
 
-  let dest = result.typ.n[1].sym
-  let d = newDeref(newSymNode(dest))
-  let src = if kind == attachedDestructor: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
+  let dest = if kind == attachedDup: result.ast[resultPos].sym else: result.typ.n[1].sym
+  let d = if result.typ.firstParamType.kind == tyVar: newDeref(newSymNode(dest)) else: newSymNode(dest)
+  let src = case kind
+            of {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer))
+            of attachedDup: newSymNode(result.typ.n[1].sym)
             else: newSymNode(result.typ.n[2].sym)
 
   # register this operation already:
   setAttachedOpPartial(g, idgen.module, typ, kind, result)
 
-  if kind == attachedSink and destructorOverriden(g, typ):
+  if kind == attachedSink and destructorOverridden(g, typ):
     ## compiler can use a combination of `=destroy` and memCopy for sink op
     dest.flags.incl sfCursor
-    result.ast[bodyPos].add newOpCall(a, getAttachedOp(g, typ, attachedDestructor), d[0])
+    let op = getAttachedOp(g, typ, attachedDestructor)
+    result.ast[bodyPos].add newOpCall(a, op, if op.typ.firstParamType.kind == tyVar: d[0] else: d)
     result.ast[bodyPos].add newAsgnStmt(d, src)
   else:
     var tk: TTypeKind
-    if g.config.selectedGC in {gcArc, gcOrc, gcHooks}:
+    if g.config.selectedGC in {gcArc, gcOrc, gcHooks, gcAtomicArc}:
       tk = skipTypes(typ, {tyOrdinal, tyRange, tyInferred, tyGenericInst, tyStatic, tyAlias, tySink}).kind
     else:
       tk = tyNone # no special casing for strings and seqs
@@ -992,18 +1197,24 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp;
       fillStrOp(a, typ, result.ast[bodyPos], d, src)
     else:
       fillBody(a, typ, result.ast[bodyPos], d, src)
-      if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy} and not lacksMTypeField(typ):
+      if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy, attachedDup} and not lacksMTypeField(typ):
         # bug #19205: Do not forget to also copy the hidden type field:
         genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src)
 
-  if not a.canRaise: incl result.flags, sfNeverRaises
+  if not a.canRaise:
+    incl result.flags, sfNeverRaises
+    result.ast[pragmasPos] = newNodeI(nkPragma, info)
+    result.ast[pragmasPos].add newTree(nkExprColonExpr,
+        newIdentNode(g.cache.getIdent("raises"),  info), newNodeI(nkBracket, info))
+
   completePartialOp(g, idgen.module, typ, kind, result)
 
 
 proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
                                         info: TLineInfo; idgen: IdGenerator): PSym =
   assert(typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject)
-  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen)
+  # discrimantor assignments needs pointers to destroy fields; alas, we cannot use non-var destructor here
+  result = symPrototype(g, field.typ, typ.owner, attachedDestructor, info, idgen, isDiscriminant = true)
   var a = TLiftCtx(info: info, g: g, kind: attachedDestructor, asgnForType: typ, idgen: idgen,
                    fn: result)
   a.asgnForType = typ
@@ -1011,7 +1222,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym,
   a.addMemReset = true
   let discrimantDest = result.typ.n[1].sym
 
-  let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info)
+  let dst = newSym(skVar, getIdent(g.cache, "dest"), idgen, result, info)
   dst.typ = makePtrType(typ.owner, typ, idgen)
   let dstSym = newSymNode(dst)
   let d = newDeref(dstSym)
@@ -1037,7 +1248,7 @@ proc patchBody(g: ModuleGraph; c: PContext; n: PNode; info: TLineInfo; idgen: Id
       if op != nil:
         if op.ast.isGenericRoutine:
           internalError(g.config, info, "resolved destructor is generic")
-        if op.magic == mDestroy:
+        if op.magic == mDestroy and t.kind != tyString:
           internalError(g.config, info, "patching mDestroy with mDestroy?")
         n[0] = newSymNode(op)
   for x in n: patchBody(g, c, x, info, idgen)
@@ -1047,13 +1258,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
   let op = getAttachedOp(g, t, kind)
   if op != nil and op.ast != nil and op.ast.isGenericRoutine:
     if t.typeInst != nil:
-      var a: TLiftCtx
-      a.info = info
-      a.g = g
-      a.kind = kind
-      a.c = c
-      a.idgen = idgen
-
+      var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen)
       let opInst = instantiateGeneric(a, op, t, t.typeInst)
       if opInst.ast != nil:
         patchBody(g, c, opInst.ast, info, a.idgen)
@@ -1061,7 +1266,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I
     else:
       localError(g.config, info, "unresolved generic parameter")
 
-proc isTrival(s: PSym): bool {.inline.} =
+proc isTrival*(s: PSym): bool {.inline.} =
   s == nil or (s.ast != nil and s.ast[bodyPos].len == 0)
 
 proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo;
@@ -1075,7 +1280,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
   let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink})
   if isEmptyContainer(skipped) or skipped.kind == tyStatic: return
 
-  let h = sighashes.hashType(skipped, {CoType, CoConsiderOwned, CoDistinct})
+  let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct})
   var canon = g.canonTypes.getOrDefault(h)
   if canon == nil:
     g.canonTypes[h] = skipped
@@ -1096,15 +1301,15 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf
   # bug #15122: We need to produce all prototypes before entering the
   # mind boggling recursion. Hacks like these imply we should rewrite
   # this module.
-  var generics: array[attachedDestructor..attachedTrace, bool]
-  for k in attachedDestructor..lastAttached:
+  var generics: array[attachedWasMoved..attachedTrace, bool] = default(array[attachedWasMoved..attachedTrace, bool])
+  for k in attachedWasMoved..lastAttached:
     generics[k] = getAttachedOp(g, canon, k) != nil
     if not generics[k]:
       setAttachedOp(g, idgen.module, canon, k,
           symPrototype(g, canon, canon.owner, k, info, idgen))
 
   # we generate the destructor first so that other operators can depend on it:
-  for k in attachedDestructor..lastAttached:
+  for k in attachedWasMoved..lastAttached:
     if not generics[k]:
       discard produceSym(g, c, canon, k, info, idgen)
     else:
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index 7ca46ab1b..aaa0707e0 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -10,9 +10,11 @@
 ## This module implements the '.liftLocals' pragma.
 
 import
-  strutils, options, ast, msgs,
+  options, ast, msgs,
   idents, renderer, types, lowerings, lineinfos
 
+import std/strutils
+
 from pragmas import getPragmaVal
 from wordrecg import wLiftLocals
 
@@ -49,6 +51,7 @@ proc liftLocals(n: PNode; i: int; c: var Ctx) =
       liftLocals(it, i, c)
 
 proc lookupParam(params, dest: PNode): PSym =
+  result = nil
   if dest.kind != nkIdent: return nil
   for i in 1..<params.len:
     if params[i].kind == nkSym and params[i].sym.name.id == dest.ident.id:
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index ec1643960..94a483299 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -7,10 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-## This module contains the ``TMsgKind`` enum as well as the
-## ``TLineInfo`` object.
+## This module contains the `TMsgKind` enum as well as the
+## `TLineInfo` object.
 
-import ropes, tables, pathutils, hashes
+import ropes, pathutils
+import std/[hashes, tables]
 
 const
   explanationsBaseUrl* = "https://nim-lang.github.io/Nim"
@@ -44,6 +45,7 @@ type
     errRstSandboxedDirective,
     errProveInit, # deadcode
     errGenerated,
+    errFailedMove,
     errUser,
     # warnings
     warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
@@ -57,6 +59,7 @@ type
     warnRstBrokenLink = "BrokenLink",
     warnRstLanguageXNotSupported = "LanguageXNotSupported",
     warnRstFieldXNotSupported = "FieldXNotSupported",
+    warnRstUnusedImportdoc = "UnusedImportdoc",
     warnRstStyle = "warnRstStyle",
     warnCommentXIgnored = "CommentXIgnored",
     warnTypelessParam = "TypelessParam",
@@ -68,7 +71,8 @@ type
     warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
     warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
     warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
-    warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
+    warnLockLevel = "LockLevel", # deadcode
+    warnResultShadowed = "ResultShadowed",
     warnInconsistentSpacing = "Spacing",  warnCaseTransition = "CaseTransition",
     warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
     warnStrictNotNil = "StrictNotNil",
@@ -79,14 +83,22 @@ type
     warnAnyEnumConv = "AnyEnumConv",
     warnHoleEnumConv = "HoleEnumConv",
     warnCstringConv = "CStringConv",
+    warnPtrToCstringConv = "PtrToCstringConv",
     warnEffect = "Effect",
-    warnCastSizes = "CastSizes"
-    warnTemplateRedefinition = "TemplateRedefinition",
+    warnCastSizes = "CastSizes", # deadcode
+    warnAboveMaxSizeSet = "AboveMaxSizeSet",
+    warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition",
+    warnUnnamedBreak = "UnnamedBreak",
+    warnStmtListLambda = "StmtListLambda",
+    warnBareExcept = "BareExcept",
+    warnImplicitDefaultValue = "ImplicitDefaultValue",
+    warnIgnoredSymbolInjection = "IgnoredSymbolInjection",
+    warnStdPrefix = "StdPrefix"
     warnUser = "User",
+    warnGlobalVarConstructorTemporary = "GlobalVarConstructorTemporary",
     # hints
     hintSuccess = "Success", hintSuccessX = "SuccessX",
     hintCC = "CC",
-    hintLineTooLong = "LineTooLong",
     hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport",
     hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
     hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
@@ -99,6 +111,7 @@ type
     hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
     hintMsgOrigin = "MsgOrigin", # since 1.3.5
     hintDeclaredLoc = "DeclaredLoc", # since 1.5.1
+    hintUnknownHint = "UnknownHint"
 
 const
   MsgKindToStr*: array[TMsgKind, string] = [
@@ -120,6 +133,7 @@ const
     errRstSandboxedDirective: "disabled directive: '$1'",
     errProveInit: "Cannot prove that '$1' is initialized.",  # deadcode
     errGenerated: "$1",
+    errFailedMove: "$1",
     errUser: "$1",
     warnCannotOpenFile: "cannot open '$1'",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
@@ -136,6 +150,7 @@ const
     warnRstBrokenLink: "broken link '$1'",
     warnRstLanguageXNotSupported: "language '$1' not supported",
     warnRstFieldXNotSupported: "field '$1' not supported",
+    warnRstUnusedImportdoc: "importdoc for '$1' is not used",
     warnRstStyle: "RST style: $1",
     warnCommentXIgnored: "comment '$1' ignored",
     warnTypelessParam: "", # deadcode
@@ -160,7 +175,7 @@ const
     warnUninit: "use explicit initialization of '$1' for clarity",
     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",
+    warnLockLevel: "$1", # deadcode
     warnResultShadowed: "Special variable 'result' is shadowed.",
     warnInconsistentSpacing: "Number of spaces around '$#' is not consistent",
     warnCaseTransition: "Potential object case transition, instantiate new object instead",
@@ -174,15 +189,23 @@ const
     warnAnyEnumConv: "$1",
     warnHoleEnumConv: "$1",
     warnCstringConv: "$1",
+    warnPtrToCstringConv: "unsafe conversion to 'cstring' from '$1'; Use a `cast` operation like `cast[cstring](x)`; this will become a compile time error in the future",
     warnEffect: "$1",
-    warnCastSizes: "$1",
-    warnTemplateRedefinition: "template '$1' is implicitly redefined, consider adding an explicit .redefine pragma",
+    warnCastSizes: "$1", # deadcode
+    warnAboveMaxSizeSet: "$1",
+    warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma",
+    warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead",
+    warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead",
+    warnBareExcept: "$1",
+    warnImplicitDefaultValue: "$1",
+    warnIgnoredSymbolInjection: "$1",
+    warnStdPrefix: "$1 needs the 'std' prefix",
     warnUser: "$1",
+    warnGlobalVarConstructorTemporary: "global variable '$1' initialization requires a temporary variable",
     hintSuccess: "operation successful: $#",
     # keep in sync with `testament.isSuccess`
     hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output",
     hintCC: "CC: $1",
-    hintLineTooLong: "line too long",
     hintXDeclaredButNotUsed: "'$1' is declared but not used",
     hintDuplicateModuleImport: "$1",
     hintXCannotRaiseY: "$1",
@@ -214,6 +237,7 @@ const
     hintExtendedContext: "$1",
     hintMsgOrigin: "$1",
     hintDeclaredLoc: "$1",
+    hintUnknownHint: "unknown hint: $1"
   ]
 
 const
@@ -231,8 +255,9 @@ type
   TNoteKinds* = set[TNoteKind]
 
 proc computeNotesVerbosity(): array[0..3, TNoteKinds] =
-  result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv}
-  result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
+  result = default(array[0..3, TNoteKinds])
+  result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv, warnBareExcept, warnStdPrefix}
+  result[2] = result[3] - {hintStackTrace, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt}
   result[1] = result[2] - {warnProveField, warnProveIndex,
     warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd,
     hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance}
@@ -243,6 +268,7 @@ const
   NotesVerbosity* = computeNotesVerbosity()
   errXMustBeCompileTime* = "'$1' can only be used in compile-time context"
   errArgsNeedRunOption* = "arguments can only be given if the '--run' option is selected"
+  errFloatToString* = "cannot convert '$1' to '$2'"
 
 type
   TFileInfo* = object
@@ -262,7 +288,7 @@ type
                                # and parsed; usually "" but is used
                                # for 'nimsuggest'
     hash*: string              # the checksum of the file
-    dirty*: bool               # for 'nimfix' / 'nimpretty' like tooling
+    dirty*: bool               # for 'nimpretty' like tooling
     when defined(nimpretty):
       fullContent*: string
   FileIndex* = distinct int32
@@ -293,7 +319,7 @@ proc `==`*(a, b: FileIndex): bool {.borrow.}
 proc hash*(i: TLineInfo): Hash =
   hash (i.line.int, i.col.int, i.fileIndex.int)
 
-proc raiseRecoverableError*(msg: string) {.noinline.} =
+proc raiseRecoverableError*(msg: string) {.noinline, noreturn.} =
   raise newException(ERecoverableError, msg)
 
 const
@@ -324,9 +350,8 @@ type
 
 
 proc initMsgConfig*(): MsgConfig =
-  result.msgContext = @[]
-  result.lastError = unknownLineInfo
-  result.filenameToIndexTbl = initTable[string, FileIndex]()
-  result.fileInfos = @[]
-  result.errorOutputs = {eStdOut, eStdErr}
+  result = MsgConfig(msgContext: @[], lastError: unknownLineInfo,
+                     filenameToIndexTbl: initTable[string, FileIndex](),
+                     fileInfos: @[], errorOutputs: {eStdOut, eStdErr}
+  )
   result.filenameToIndexTbl["???"] = FileIndex(-1)
diff --git a/compiler/linter.nim b/compiler/linter.nim
index 0c2aaef79..a80c377e9 100644
--- a/compiler/linter.nim
+++ b/compiler/linter.nim
@@ -19,10 +19,12 @@ const
   Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
 
 proc identLen*(line: string, start: int): int =
+  result = 0
   while start+result < line.len and line[start+result] in Letters:
     inc result
 
 proc `=~`(s: string, a: openArray[string]): bool =
+  result = false
   for x in a:
     if s.startsWith(x): return true
 
@@ -95,7 +97,7 @@ template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind)
   if optStyleCheck in ctx.config.options and # ignore if styleChecks are off
      {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # check only if hint/error is enabled
      hintName in ctx.config.notes and # ignore if name checks are not requested
-     ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
+     ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
      optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage
      sym.kind != skResult and # ignore `result`
      sym.kind != skTemp and # ignore temporary variables created by the compiler
@@ -136,7 +138,7 @@ template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) =
   ## Check symbol uses match their definition's style.
   if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
      hintName in ctx.config.notes and # ignore if name checks are not requested
-     ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages
+     ctx.config.belongsToProjectPackage(sym) and # ignore foreign packages
      sym.kind != skTemp and # ignore temporary variables created by the compiler
      sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols???
      sfAnon notin sym.flags: # ignore temporary variables created by the compiler
@@ -147,6 +149,10 @@ proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragm
   if pragmaName != wanted:
     lintReport(conf, info, wanted, pragmaName)
 
-template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) =
-  if {optStyleHint, optStyleError} * conf.globalOptions != {}:
-    checkPragmaUseImpl(conf, info, w, pragmaName)
+template checkPragmaUse*(ctx: PContext; info: TLineInfo; w: TSpecialWord; pragmaName: string, sym: PSym) =
+  ## Check builtin pragma uses match their definition's style.
+  ## Note: This only applies to builtin pragmas, not user pragmas.
+  if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off
+     hintName in ctx.config.notes and # ignore if name checks are not requested
+     (sym != nil and ctx.config.belongsToProjectPackage(sym)): # ignore foreign packages
+    checkPragmaUseImpl(ctx.config, info, w, pragmaName)
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 004d990fa..cc8148483 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
 const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
   not defined(windows)
 
-when hasRstdin: import rdstdin
+when hasRstdin: import std/rdstdin
 
 type
   TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
@@ -40,33 +40,22 @@ type
 
   PLLStream* = ref TLLStream
 
-proc llStreamOpen*(data: string): PLLStream =
-  new(result)
-  result.s = data
-  result.kind = llsString
+proc llStreamOpen*(data: sink string): PLLStream =
+  PLLStream(kind: llsString, s: data)
 
 proc llStreamOpen*(f: File): PLLStream =
-  new(result)
-  result.f = f
-  result.kind = llsFile
+  PLLStream(kind: llsFile, f: f)
 
 proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
-  new(result)
-  result.kind = llsFile
+  result = PLLStream(kind: llsFile)
   if not open(result.f, filename.string, mode): result = nil
 
 proc llStreamOpen*(): PLLStream =
-  new(result)
-  result.kind = llsNone
+  PLLStream(kind: llsNone)
 
 proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
 proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
-  new(result)
-  result.kind = llsStdIn
-  result.s = ""
-  result.lineOffset = -1
-  result.repl = r
-  result.onPrompt = onPrompt
+  PLLStream(kind: llsStdIn, s: "", lineOffset: -1, repl: r, onPrompt: onPrompt)
 
 proc llStreamClose*(s: PLLStream) =
   case s.kind
@@ -79,6 +68,7 @@ when not declared(readLineFromStdin):
   # fallback implementation:
   proc readLineFromStdin(prompt: string, line: var string): bool =
     stdout.write(prompt)
+    stdout.flushFile()
     result = readLine(stdin, line)
     if not result:
       stdout.write("\n")
@@ -89,6 +79,8 @@ proc endsWith*(x: string, s: set[char]): bool =
   while i >= 0 and x[i] == ' ': dec(i)
   if i >= 0 and x[i] in s:
     result = true
+  else:
+    result = false
 
 const
   LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
@@ -104,6 +96,7 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
         line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs))
 
 proc countTriples(s: string): int =
+  result = 0
   var i = 0
   while i+2 < s.len:
     if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 63e4920f7..d8fcf73e0 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -8,14 +8,16 @@
 #
 
 # This module implements lookup helpers.
-import std/[algorithm, strutils]
+import std/[algorithm, strutils, tables]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import
-  intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs
+  ast, astalgo, idents, semdata, types, msgs, options,
+  renderer, lineinfos, modulegraphs, astmsgs, wordrecg
+
+import std/[intsets, sets]
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
@@ -48,6 +50,11 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
         case x.kind
         of nkIdent: id.add(x.ident.s)
         of nkSym: id.add(x.sym.name.s)
+        of nkSymChoices, nkOpenSym:
+          if x[0].kind == nkSym:
+            id.add(x[0].sym.name.s)
+          else:
+            handleError(n, origin)
         of nkLiterals - nkFloatLiterals: id.add(x.renderTree)
         else: handleError(n, origin)
       result = getIdent(c.cache, id)
@@ -56,6 +63,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent =
       result = n[0].sym.name
     else:
       handleError(n, origin)
+  of nkOpenSym:
+    result = considerQuotedIdent(c, n[0], origin)
   else:
     handleError(n, origin)
 
@@ -67,7 +76,7 @@ proc addUniqueSym*(scope: PScope, s: PSym): PSym =
 
 proc openScope*(c: PContext): PScope {.discardable.} =
   result = PScope(parent: c.currentScope,
-                  symbols: newStrTable(),
+                  symbols: initStrTable(),
                   depthLevel: c.scopeDepth + 1)
   c.currentScope = result
 
@@ -89,17 +98,6 @@ iterator localScopesFrom*(c: PContext; scope: PScope): PScope =
     if s == c.topLevelScope: break
     yield s
 
-proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym =
-  if s == nil or s.kind != skAlias:
-    result = s
-  else:
-    result = s.owner
-    if conf.cmd == cmdNimfix:
-      prettybase.replaceDeprecated(conf, n.info, s, result)
-    else:
-      message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " &
-              s.name.s & " is deprecated")
-
 proc isShadowScope*(s: PScope): bool {.inline.} =
   s.parent != nil and s.parent.depthLevel == s.depthLevel
 
@@ -141,7 +139,7 @@ proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule;
         return result
 
 iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym =
-  var ti: ModuleIter
+  var ti: ModuleIter = default(ModuleIter)
   var candidate = initIdentIter(ti, marked, im, name, g)
   while candidate != nil:
     yield candidate
@@ -154,7 +152,7 @@ iterator importedItems*(c: PContext; name: PIdent): PSym =
       yield s
 
 proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] =
-  var ti: TIdentIter
+  var ti: TIdentIter = default(TIdentIter)
   result = @[]
   var res = initIdentIter(ti, c.pureEnumFields, name)
   while res != nil:
@@ -165,6 +163,7 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
   # really iterate over all symbols in all the scopes. This is expensive
   # and only used by suggest.nim.
   var isLocal = true
+
   var scopeN = 0
   for scope in allScopes(c.currentScope):
     if scope == c.topLevelScope: isLocal = false
@@ -179,6 +178,17 @@ iterator allSyms*(c: PContext): (PSym, int, bool) =
       assert s != nil
       yield (s, scopeN, isLocal)
 
+iterator uniqueSyms*(c: PContext): (PSym, int, bool) =
+  ## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name)
+  # Track seen symbols so we don't duplicate them.
+  # The int is for the symbols name, and line info is
+  # to be able to tell apart symbols with same name but on different lines
+  var seen = initHashSet[(TLineInfo, int)]()
+  for res in allSyms(c):
+    if not seen.containsOrIncl((res[0].info, res[0].name.id)):
+      yield res
+
+
 proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym =
   var marked = initIntSet()
   var symSet = OverloadableSyms
@@ -211,11 +221,28 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} =
     if i == limit: return
     inc i
 
+proc searchInScopesAllCandidatesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
+  result = @[]
+  for scope in allScopes(c.currentScope):
+    var ti: TIdentIter = default(TIdentIter)
+    var candidate = initIdentIter(ti, scope.symbols, s)
+    while candidate != nil:
+      if candidate.kind in filter:
+        result.add candidate
+      candidate = nextIdentIter(ti, scope.symbols)
+
+  if result.len == 0:
+    var marked = initIntSet()
+    for im in c.imports.mitems:
+      for s in symbols(im, marked, s, c.graph):
+        if s.kind in filter:
+          result.add s
+
 proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] =
   result = @[]
   block outer:
     for scope in allScopes(c.currentScope):
-      var ti: TIdentIter
+      var ti: TIdentIter = default(TIdentIter)
       var candidate = initIdentIter(ti, scope.symbols, s)
       while candidate != nil:
         if candidate.kind in filter:
@@ -231,8 +258,63 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy
         if s.kind in filter:
           result.add s
 
-proc errorSym*(c: PContext, n: PNode): PSym =
+proc cmpScopes*(ctx: PContext, s: PSym): int =
+  # Do not return a negative number
+  if s.originatingModule == ctx.module:
+    result = 2
+    var owner = s
+    while true:
+      owner = owner.skipGenericOwner
+      if owner.kind == skModule: break
+      inc result
+  else:
+    result = 1
+
+proc isAmbiguous*(c: PContext, s: PIdent, filter: TSymKinds, sym: var PSym): bool =
+  result = false
+  block outer:
+    for scope in allScopes(c.currentScope):
+      var ti: TIdentIter = default(TIdentIter)
+      var candidate = initIdentIter(ti, scope.symbols, s)
+      var scopeHasCandidate = false
+      while candidate != nil:
+        if candidate.kind in filter:
+          if scopeHasCandidate:
+            # 2 candidates in same scope, ambiguous
+            return true
+          else:
+            scopeHasCandidate = true
+            sym = candidate
+        candidate = nextIdentIter(ti, scope.symbols)
+      if scopeHasCandidate:
+        # scope had a candidate but wasn't ambiguous
+        return false
+
+  var importsHaveCandidate = false
+  var marked = initIntSet()
+  for im in c.imports.mitems:
+    for s in symbols(im, marked, s, c.graph):
+      if s.kind in filter:
+        if importsHaveCandidate:
+          # 2 candidates among imports, ambiguous
+          return true
+        else:
+          importsHaveCandidate = true
+          sym = s
+  if importsHaveCandidate:
+    # imports had a candidate but wasn't ambiguous
+    return false
+
+proc errorSym*(c: PContext, ident: PIdent, info: TLineInfo): PSym =
   ## creates an error symbol to avoid cascading errors (for IDE support)
+  result = newSym(skError, ident, c.idgen, getCurrOwner(c), info, {})
+  result.typ = errorType(c)
+  incl(result.flags, sfDiscardable)
+  # pretend it's from the top level scope to prevent cascading errors:
+  if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
+    c.moduleScope.addSym(result)
+
+proc errorSym*(c: PContext, n: PNode): PSym =
   var m = n
   # ensure that 'considerQuotedIdent' can't fail:
   if m.kind == nkDotExpr: m = m[1]
@@ -240,12 +322,7 @@ proc errorSym*(c: PContext, n: PNode): PSym =
       considerQuotedIdent(c, m)
     else:
       getIdent(c.cache, "err:" & renderTree(m))
-  result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {})
-  result.typ = errorType(c)
-  incl(result.flags, sfDiscardable)
-  # pretend it's from the top level scope to prevent cascading errors:
-  if c.config.cmd != cmdInteractive and c.compilesContextId == 0:
-    c.moduleScope.addSym(result)
+  result = errorSym(c, ident, n.info)
 
 type
   TOverloadIterMode* = enum
@@ -272,10 +349,10 @@ proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string =
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) =
   # check if all symbols have been used and defined:
-  var it: TTabIter
+  var it: TTabIter = default(TTabIter)
   var s = initTabIter(it, scope.symbols)
   var missingImpls = 0
-  var unusedSyms: seq[tuple[sym: PSym, key: string]]
+  var unusedSyms: seq[tuple[sym: PSym, key: string]] = @[]
   while s != nil:
     if sfForward in s.flags and s.kind notin {skType, skModule}:
       # too many 'implementation of X' errors are annoying
@@ -306,15 +383,18 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string;
 # xxx pending bootstrap >= 1.4, replace all those overloads with a single one:
 # proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} =
 proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) =
+  if sym.name.id == ord(wUnderscore): return
   let conflict = scope.addUniqueSym(sym)
   if conflict != nil:
-    if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner:
+    if sym.kind == skModule and conflict.kind == skModule:
       # e.g.: import foo; import foo
       # xxx we could refine this by issuing a different hint for the case
       # where a duplicate import happens inside an include.
-      localError(c.config, info, hintDuplicateModuleImport,
-        "duplicate import of '$1'; previous import here: $2" %
-        [sym.name.s, c.config $ conflict.info])
+      if c.importModuleMap[sym.id] == c.importModuleMap[conflict.id]:
+        #only hints if the conflict is the actual module not just a shared name
+        localError(c.config, info, hintDuplicateModuleImport,
+          "duplicate import of '$1'; previous import here: $2" %
+          [sym.name.s, c.config $ conflict.info])
     else:
       wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated)
 
@@ -360,11 +440,12 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) =
   if fn.kind notin OverloadableSyms:
     internalError(c.config, fn.info, "addOverloadableSymAt")
     return
-  let check = strTableGet(scope.symbols, fn.name)
-  if check != nil and check.kind notin OverloadableSyms:
-    wrongRedefinition(c, fn.info, fn.name.s, check.info)
-  else:
-    scope.addSym(fn)
+  if fn.name.id != ord(wUnderscore):
+    let check = strTableGet(scope.symbols, fn.name)
+    if check != nil and check.kind notin OverloadableSyms:
+      wrongRedefinition(c, fn.info, fn.name.s, check.info)
+    else:
+      scope.addSym(fn)
 
 proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) =
   ## adds an overloadable symbol on the scope and the interface if appropriate
@@ -377,7 +458,7 @@ proc openShadowScope*(c: PContext) =
   ## opens a shadow scope, just like any other scope except the depth is the
   ## same as the parent -- see `isShadowScope`.
   c.currentScope = PScope(parent: c.currentScope,
-                          symbols: newStrTable(),
+                          symbols: initStrTable(),
                           depthLevel: c.scopeDepth)
 
 proc closeShadowScope*(c: PContext) =
@@ -401,15 +482,8 @@ proc mergeShadowScope*(c: PContext) =
     else:
       c.addInterfaceDecl(sym)
 
-when false:
-  # `nimfix` used to call `altSpelling` and prettybase.replaceDeprecated(n.info, ident, alt)
-  proc altSpelling(c: PContext, x: PIdent): PIdent =
-    case x.s[0]
-    of 'A'..'Z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
-    of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1))
-    else: result = x
 
-import std/editdistance, heapqueue
+import std/[editdistance, heapqueue]
 
 type SpellCandidate = object
   dist: int
@@ -417,7 +491,7 @@ type SpellCandidate = object
   msg: string
   sym: PSym
 
-template toOrderTup(a: SpellCandidate): auto =
+template toOrderTup(a: SpellCandidate): (int, int, string) =
   # `dist` is first, to favor nearby matches
   # `depth` is next, to favor nearby enclosing scopes among ties
   # `sym.name.s` is last, to make the list ordered and deterministic among ties
@@ -430,7 +504,7 @@ proc mustFixSpelling(c: PContext): bool {.inline.} =
   result = c.config.spellSuggestMax != 0 and c.compilesContextId == 0
     # don't slowdown inside compiles()
 
-proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
+proc fixSpelling(c: PContext, ident: PIdent, result: var string) =
   ## when we cannot find the identifier, suggest nearby spellings
   var list = initHeapQueue[SpellCandidate]()
   let name0 = ident.s.nimIdentNormalize
@@ -438,36 +512,37 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) =
   for (sym, depth, isLocal) in allSyms(c):
     let depth = -depth - 1
     let dist = editDistance(name0, sym.name.s.nimIdentNormalize)
-    var msg: string
+    var msg: string = ""
     msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s]
-    addDeclaredLoc(msg, c.config, sym) # `msg` needed for deterministic ordering.
     list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym)
 
   if list.len == 0: return
   let e0 = list[0]
-  var count = 0
+  var
+    count = 0
+    last: PIdent = nil
   while true:
     # pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`.
     if list.len == 0: break
     let e = list.pop()
     if c.config.spellSuggestMax == spellSuggestSecretSauce:
       const
-        smallThres = 2
-        maxCountForSmall = 4
-        # avoids ton of operator matches when mis-matching short symbols such as `i`
-        # other heuristics could be devised, such as only suggesting operators if `name0`
-        # is an operator (likewise with non-operators).
-      if e.dist > e0.dist or (name0.len <= smallThres and count >= maxCountForSmall): break
+        minLengthForSuggestion = 4
+        maxCount = 3 # avoids ton of matches; three counts for equal distances
+      if e.dist > e0.dist or count >= maxCount or name0.len < minLengthForSuggestion: break
     elif count >= c.config.spellSuggestMax: break
     if count == 0:
       result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': "
-    result.add e.msg
-    count.inc
+    if e.sym.name != last:
+      result.add e.msg
+      count.inc
+      last = e.sym.name
 
 proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym =
   var err = "ambiguous identifier: '" & s.name.s & "'"
   var i = 0
   var ignoredModules = 0
+  result = nil
   for candidate in importedItems(c, s.name):
     if i == 0: err.add " -- use one of the following:\n"
     else: err.add "\n"
@@ -485,51 +560,76 @@ proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PS
     amb = false
 
 proc errorUseQualifier*(c: PContext; info: TLineInfo; s: PSym) =
-  var amb: bool
+  var amb: bool = false
   discard errorUseQualifier(c, info, s, amb)
 
-proc errorUseQualifier(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
-  var err = "ambiguous identifier: '" & candidates[0].name.s & "'"
+proc ambiguousIdentifierMsg*(candidates: seq[PSym], prefix = "use one of", indent = 0): string =
+  result = ""
+  for i in 0 ..< indent:
+    result.add(' ')
+  result.add "ambiguous identifier: '" & candidates[0].name.s & "'"
   var i = 0
   for candidate in candidates:
-    if i == 0: err.add " -- use one of the following:\n"
-    else: err.add "\n"
-    err.add "  " & candidate.owner.name.s & "." & candidate.name.s
-    err.add ": " & typeToString(candidate.typ)
+    if i == 0: result.add " -- $1 the following:\n" % prefix
+    else: result.add "\n"
+    for i in 0 ..< indent:
+      result.add(' ')
+    result.add "  " & candidate.owner.name.s & "." & candidate.name.s
+    result.add ": " & typeToString(candidate.typ)
     inc i
-  localError(c.config, info, errGenerated, err)
+
+proc errorUseQualifier*(c: PContext; info: TLineInfo; candidates: seq[PSym]) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(candidates))
+
+proc ambiguousIdentifierMsg*(choices: PNode, indent = 0): string =
+  var candidates = newSeq[PSym](choices.len)
+  let prefix = if choices[0].typ.kind != tyProc: "use one of" else: "you need a helper proc to disambiguate"
+  for i, n in choices:
+    candidates[i] = n.sym
+  result = ambiguousIdentifierMsg(candidates, prefix, indent)
+
+proc errorUseQualifier*(c: PContext; info:TLineInfo; choices: PNode) =
+  localError(c.config, info, errGenerated, ambiguousIdentifierMsg(choices))
 
 proc errorUndeclaredIdentifier*(c: PContext; info: TLineInfo; name: string, extra = "") =
-  var err = "undeclared identifier: '" & name & "'" & extra
-  if c.recursiveDep.len > 0:
-    err.add "\nThis might be caused by a recursive module dependency:\n"
-    err.add c.recursiveDep
-    # prevent excessive errors for 'nim check'
-    c.recursiveDep = ""
+  var err: string
+  if name == "_":
+    err = "the special identifier '_' is ignored in declarations and cannot be used"
+  else:
+    err = "undeclared identifier: '" & name & "'"
+    if "`gensym" in name:
+      err.add "; if declared in a template, this identifier may be inconsistently marked inject or gensym"
+    if extra.len != 0:
+      err.add extra
+    if c.recursiveDep.len > 0:
+      err.add "\nThis might be caused by a recursive module dependency:\n"
+      err.add c.recursiveDep
+      # prevent excessive errors for 'nim check'
+      c.recursiveDep = ""
   localError(c.config, info, errGenerated, err)
 
-proc errorUndeclaredIdentifierHint*(c: PContext; n: PNode, ident: PIdent): PSym =
+proc errorUndeclaredIdentifierHint*(c: PContext; ident: PIdent; info: TLineInfo): PSym =
   var extra = ""
-  if c.mustFixSpelling: fixSpelling(c, n, ident, extra)
-  errorUndeclaredIdentifier(c, n.info, ident.s, extra)
-  result = errorSym(c, n)
+  if c.mustFixSpelling: fixSpelling(c, ident, extra)
+  errorUndeclaredIdentifier(c, info, ident.s, extra)
+  result = errorSym(c, ident, info)
 
 proc lookUp*(c: PContext, n: PNode): PSym =
   # Looks up a symbol. Generates an error in case of nil.
   var amb = false
   case n.kind
   of nkIdent:
-    result = searchInScopes(c, n.ident, amb).skipAlias(n, c.config)
-    if result == nil: result = errorUndeclaredIdentifierHint(c, n, n.ident)
+    result = searchInScopes(c, n.ident, amb)
+    if result == nil: result = errorUndeclaredIdentifierHint(c, n.ident, n.info)
   of nkSym:
     result = n.sym
   of nkAccQuoted:
     var ident = considerQuotedIdent(c, n)
-    result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
-    if result == nil: result = errorUndeclaredIdentifierHint(c, n, ident)
+    result = searchInScopes(c, ident, amb)
+    if result == nil: result = errorUndeclaredIdentifierHint(c, ident, n.info)
   else:
     internalError(c.config, n.info, "lookUp")
-    return
+    return nil
   if amb:
     #contains(c.ambiguousSymbols, result.id):
     result = errorUseQualifier(c, n.info, result, amb)
@@ -540,54 +640,78 @@ type
   TLookupFlag* = enum
     checkAmbiguity, checkUndeclared, checkModule, checkPureEnumFields
 
+const allExceptModule = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
+
+proc lookUpCandidates*(c: PContext, ident: PIdent, filter: set[TSymKind],
+                       includePureEnum = false): seq[PSym] =
+  result = searchInScopesFilterBy(c, ident, filter)
+  if skEnumField in filter and (result.len == 0 or includePureEnum):
+    result.add allPureEnumFields(c, ident)
+
 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 amb = false
     var ident = considerQuotedIdent(c, n)
     if checkModule in flags:
-      result = searchInScopes(c, ident, amb).skipAlias(n, c.config)
+      result = searchInScopes(c, ident, amb)
+      if result == nil:
+        let candidates = allPureEnumFields(c, ident)
+        if candidates.len > 0:
+          result = candidates[0]
+          amb = candidates.len > 1
+          if amb and checkAmbiguity in flags:
+            errorUseQualifier(c, n.info, candidates)
     else:
-      let candidates = searchInScopesFilterBy(c, ident, allExceptModule) #.skipAlias(n, c.config)
+      let candidates = lookUpCandidates(c, ident, allExceptModule)
       if candidates.len > 0:
         result = candidates[0]
         amb = candidates.len > 1
         if amb and checkAmbiguity in flags:
           errorUseQualifier(c, n.info, candidates)
-    if result == nil:
-      let candidates = allPureEnumFields(c, ident)
-      if candidates.len > 0:
-        result = candidates[0]
-        amb = candidates.len > 1
-        if amb and checkAmbiguity in flags:
-          errorUseQualifier(c, n.info, candidates)
-
+      else:
+        result = nil
     if result == nil and checkUndeclared in flags:
-      result = errorUndeclaredIdentifierHint(c, n, ident)
+      result = errorUndeclaredIdentifierHint(c, ident, n.info)
     elif checkAmbiguity in flags and result != nil and amb:
       result = errorUseQualifier(c, n.info, result, amb)
     c.isAmbiguous = amb
   of nkSym:
     result = n.sym
+  of nkOpenSym:
+    result = qualifiedLookUp(c, n[0], flags)
   of nkDotExpr:
     result = nil
     var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule})
     if m != nil and m.kind == skModule:
       var ident: PIdent = nil
-      if n[1].kind == nkIdent:
-        ident = n[1].ident
-      elif n[1].kind == nkAccQuoted:
+      if n[1].kind == nkAccQuoted:
         ident = considerQuotedIdent(c, n[1])
+      else:
+        # this includes sym and symchoice nodes, but since we are looking in
+        # a module, it shouldn't matter what was captured
+        ident = n[1].getPIdent
       if ident != nil:
         if m == c.module:
-          result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config)
+          var ti: TIdentIter = default(TIdentIter)
+          result = initIdentIter(ti, c.topLevelScope.symbols, ident)
+          if result != nil and nextIdentIter(ti, c.topLevelScope.symbols) != nil:
+            # another symbol exists with same name
+            c.isAmbiguous = true
         else:
-          result = someSym(c.graph, m, ident).skipAlias(n, c.config)
+          var amb: bool = false
+          if c.importModuleLookup.getOrDefault(m.name.id).len > 1:
+            result = errorUseQualifier(c, n.info, m, amb)
+          else:
+            result = someSymAmb(c.graph, m, ident, amb)
+            if amb: c.isAmbiguous = true
         if result == nil and checkUndeclared in flags:
-          result = errorUndeclaredIdentifierHint(c, n[1], ident)
+          result = errorUndeclaredIdentifierHint(c, ident, n[1].info)
       elif n[1].kind == nkSym:
         result = n[1].sym
+        if result.owner != nil and result.owner != m and checkUndeclared in flags:
+          # dotExpr in templates can end up here
+          result = errorUndeclaredIdentifierHint(c, result.name, n[1].info)
       elif checkUndeclared in flags and
            n[1].kind notin {nkOpenSymChoice, nkClosedSymChoice}:
         localError(c.config, n[1].info, "identifier expected, but got: " &
@@ -599,15 +723,20 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym =
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
+  if n.kind == nkOpenSym:
+    # maybe the logic in semexprs should be mirrored here instead
+    # for now it only seems this is called for `pickSym` in `getTypeIdent` 
+    return initOverloadIter(o, c, n[0])
   o.importIdx = -1
   o.marked = initIntSet()
   case n.kind
   of nkIdent, nkAccQuoted:
+    result = nil
     var ident = considerQuotedIdent(c, n)
     var scope = c.currentScope
     o.mode = oimNoQualifier
     while true:
-      result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config)
+      result = initIdentIter(o.it, scope.symbols, ident)
       if result != nil:
         o.currentScope = scope
         break
@@ -615,7 +744,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         scope = scope.parent
         if scope == nil:
           for i in 0..c.imports.high:
-            result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config)
+            result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph)
             if result != nil:
               o.currentScope = nil
               o.importIdx = i
@@ -626,6 +755,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     result = n.sym
     o.mode = oimDone
   of nkDotExpr:
+    result = nil
     o.mode = oimOtherModule
     o.m = qualifiedLookUp(c, n[0], {checkUndeclared, checkModule})
     if o.m != nil and o.m.kind == skModule:
@@ -638,10 +768,10 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         if o.m == c.module:
           # a module may access its private members:
           result = initIdentIter(o.it, c.topLevelScope.symbols,
-                                 ident).skipAlias(n, c.config)
+                                 ident)
           o.mode = oimSelfModule
         else:
-          result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config)
+          result = initModuleIter(o.mit, c.graph, o.m, ident)
       else:
         noidentError(c.config, n[1], n)
         result = errorSym(c, n[1])
@@ -655,7 +785,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
     o.symChoiceIndex = 1
     o.marked = initIntSet()
     incl(o.marked, result.id)
-  else: discard
+  else: result = nil
   when false:
     if result != nil and result.kind == skStub: loadStub(result)
 
@@ -670,11 +800,12 @@ proc lastOverloadScope*(o: TOverloadIter): int =
   else: result = -1
 
 proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym =
+  result = nil
   assert o.currentScope == nil
   var idx = o.importIdx+1
   o.importIdx = c.imports.len # assume the other imported modules lack this symbol too
   while idx < c.imports.len:
-    result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config)
+    result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph)
     if result != nil:
       # oh, we were wrong, some other module had the symbol, so remember that:
       o.importIdx = idx
@@ -682,9 +813,10 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym
     inc idx
 
 proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym =
+  result = nil
   assert o.currentScope == nil
   while o.importIdx < c.imports.len:
-    result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
+    result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
     #while result != nil and result.id in o.marked:
     #  result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx])
     if result != nil:
@@ -699,29 +831,29 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
   of oimNoQualifier:
     if o.currentScope != nil:
       assert o.importIdx < 0
-      result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config)
+      result = nextIdentIter(o.it, o.currentScope.symbols)
       while result == nil:
         o.currentScope = o.currentScope.parent
         if o.currentScope != nil:
-          result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config)
+          result = initIdentIter(o.it, o.currentScope.symbols, o.it.name)
           # BUGFIX: o.it.name <-> n.ident
         else:
           o.importIdx = 0
           if c.imports.len > 0:
-            result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config)
+            result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph)
             if result == nil:
               result = nextOverloadIterImports(o, c, n)
           break
     elif o.importIdx < c.imports.len:
-      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
       if result == nil:
         result = nextOverloadIterImports(o, c, n)
     else:
       result = nil
   of oimSelfModule:
-    result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config)
+    result = nextIdentIter(o.it, c.topLevelScope.symbols)
   of oimOtherModule:
-    result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config)
+    result = nextModuleIter(o.mit, c.graph)
   of oimSymChoice:
     if o.symChoiceIndex < n.len:
       result = n[o.symChoiceIndex].sym
@@ -732,26 +864,28 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
       o.mode = oimSymChoiceLocalLookup
       o.currentScope = c.currentScope
       result = firstIdentExcluding(o.it, o.currentScope.symbols,
-                                   n[0].sym.name, o.marked).skipAlias(n, c.config)
+                                   n[0].sym.name, o.marked)
       while result == nil:
         o.currentScope = o.currentScope.parent
         if o.currentScope != nil:
           result = firstIdentExcluding(o.it, o.currentScope.symbols,
-                                      n[0].sym.name, o.marked).skipAlias(n, c.config)
+                                      n[0].sym.name, o.marked)
         else:
           o.importIdx = 0
           result = symChoiceExtension(o, c, n)
           break
       if result != nil:
         incl o.marked, result.id
+    else:
+      result = nil
   of oimSymChoiceLocalLookup:
     if o.currentScope != nil:
-      result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config)
+      result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked)
       while result == nil:
         o.currentScope = o.currentScope.parent
         if o.currentScope != nil:
           result = firstIdentExcluding(o.it, o.currentScope.symbols,
-                                      n[0].sym.name, o.marked).skipAlias(n, c.config)
+                                      n[0].sym.name, o.marked)
         else:
           o.importIdx = 0
           result = symChoiceExtension(o, c, n)
@@ -760,20 +894,23 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
         incl o.marked, result.id
 
     elif o.importIdx < c.imports.len:
-      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config)
+      result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph)
       #assert result.id notin o.marked
       #while result != nil and result.id in o.marked:
-      #  result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config)
+      #  result = nextIdentIter(o.it, c.imports[o.importIdx])
       if result == nil:
         inc o.importIdx
         result = symChoiceExtension(o, c, n)
+    else:
+      result = nil
 
   when false:
     if result != nil and result.kind == skStub: loadStub(result)
 
 proc pickSym*(c: PContext, n: PNode; kinds: set[TSymKind];
               flags: TSymFlags = {}): PSym =
-  var o: TOverloadIter
+  result = nil
+  var o: TOverloadIter = default(TOverloadIter)
   var a = initOverloadIter(o, c, n)
   while a != nil:
     if a.kind in kinds and flags <= a.flags:
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index fc66fc9fa..2c9c4cb32 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -19,7 +19,7 @@ when defined(nimPreviewSlimSystem):
   import std/assertions
 
 proc newDeref*(n: PNode): PNode {.inline.} =
-  result = newNodeIT(nkHiddenDeref, n.info, n.typ[0])
+  result = newNodeIT(nkHiddenDeref, n.info, n.typ.elementType)
   result.add n
 
 proc newTupleAccess*(g: ModuleGraph; tup: PNode, i: int): PNode =
@@ -79,7 +79,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P
   if avoidTemp:
     tempAsNode = value
   else:
-    var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
+    var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
                   owner, value.info, g.config.options)
     temp.typ = skipTypes(value.typ, abstractInst)
     incl(temp.flags, sfFromGeneric)
@@ -100,7 +100,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P
   ## freely, multiple times. This is frequently required and such a builtin would also be
   ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'!
   result = newNodeIT(nkStmtListExpr, value.info, value.typ)
-  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen),
+  var temp = newSym(skTemp, getIdent(g.cache, genPrefix), idgen,
                     owner, value.info, g.config.options)
   temp.typ = skipTypes(value.typ, abstractInst)
   incl(temp.flags, sfFromGeneric)
@@ -122,29 +122,10 @@ proc newTupleAccessRaw*(tup: PNode, i: int): PNode =
 proc newTryFinally*(body, final: PNode): PNode =
   result = newTree(nkHiddenTryStmt, body, newTree(nkFinally, final))
 
-proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode =
-  let value = n.lastSon
-  result = newNodeI(nkStmtList, n.info)
-
-  var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options)
-  var v = newNodeI(nkLetSection, value.info)
-  let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
-
-  var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
-  vpart[0] = tempAsNode
-  vpart[1] = newNodeI(nkEmpty, value.info)
-  vpart[2] = value
-  v.add vpart
-  result.add(v)
-
-  let lhs = n[0]
-  for i in 0..<lhs.len:
-    result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempAsNode, i))
-
 proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; 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(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options)
+  var temp = newSym(skVar, getIdent(g.cache, genPrefix), idgen, owner, n.info, owner.options)
   temp.typ = n[1].typ
   incl(temp.flags, sfFromGeneric)
   incl(temp.flags, sfGenSym)
@@ -163,7 +144,7 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod
   result.add newFastAsgnStmt(n[2], tempAsNode)
 
 proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType =
-  result = newType(tyObject, nextTypeId(idgen), owner)
+  result = newType(tyObject, idgen, owner)
   if final:
     rawAddSon(result, nil)
     incl result.flags, tfFinal
@@ -171,8 +152,7 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo
     rawAddSon(result, getCompilerProc(g, "RootObj").typ)
   result.n = newNodeI(nkRecList, info)
   let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s),
-                  nextSymId(idgen),
-                  owner, info, owner.options)
+                  idgen, owner, info, owner.options)
   incl s.flags, sfAnon
   s.typ = result
   result.sym = s
@@ -234,10 +214,13 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): 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(cache, s.name.s & $obj.n.len),
-                     nextSymId(idgen), s.owner, s.info, s.options)
+                     idgen, s.owner, s.info, s.options)
   field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
   let t = skipIntLit(s.typ, idgen)
   field.typ = t
+  if s.kind in {skLet, skVar, skField, skForVar}:
+    #field.bitsize = s.bitsize
+    field.alignment = s.alignment
   assert t.kind != tyTyped
   propagateToOwner(obj, t)
   field.position = obj.n.len
@@ -250,7 +233,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym
 proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} =
   result = lookupInRecord(obj.n, s.itemId)
   if result == nil:
-    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen),
+    var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), idgen,
                        s.owner, s.info, s.options)
     field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item)
     let t = skipIntLit(s.typ, idgen)
@@ -272,14 +255,14 @@ proc newDotExpr*(obj, b: PSym): PNode =
 proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst)[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
   while true:
     assert t.kind == tyObject
     field = lookupInRecord(t.n, b)
     if field != nil: break
-    t = t[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
@@ -295,7 +278,7 @@ proc indirectAccess*(a: PNode, b: ItemId, info: TLineInfo): PNode =
 proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
-  deref.typ = a.typ.skipTypes(abstractInst)[0]
+  deref.typ = a.typ.skipTypes(abstractInst).elementType
   var t = deref.typ.skipTypes(abstractInst)
   var field: PSym
   let bb = getIdent(cache, b)
@@ -303,7 +286,7 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo; cache: IdentCache): P
     assert t.kind == tyObject
     field = getSymFromList(t.n, bb)
     if field != nil: break
-    t = t[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
   #if field == nil:
@@ -323,7 +306,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
     assert t.kind == tyObject
     result = lookupInRecord(t.n, v.itemId)
     if result != nil: break
-    t = t[0]
+    t = t.baseClass
     if t == nil: break
     t = t.skipTypes(skipPtrs)
 
@@ -337,17 +320,18 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
 proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode =
   result = newNodeI(nkAddr, n.info, 1)
   result[0] = n
-  result.typ = newType(typeKind, nextTypeId(idgen), n.typ.owner)
+  result.typ = newType(typeKind, idgen, n.typ.owner)
   result.typ.rawAddSon(n.typ)
 
 proc genDeref*(n: PNode; k = nkHiddenDeref): PNode =
   result = newNodeIT(k, n.info,
-                     n.typ.skipTypes(abstractInst)[0])
+                     n.typ.skipTypes(abstractInst).elementType)
   result.add n
 
 proc callCodegenProc*(g: ModuleGraph; name: string;
                       info: TLineInfo = unknownLineInfo;
-                      arg1, arg2, arg3, optionalArgs: PNode = nil): PNode =
+                      arg1: PNode = nil, arg2: PNode = nil,
+                      arg3: PNode = nil, optionalArgs: PNode = nil): PNode =
   result = newNodeI(nkCall, info)
   let sym = magicsys.getCompilerProc(g, name)
   if sym == nil:
@@ -360,7 +344,7 @@ proc callCodegenProc*(g: ModuleGraph; name: string;
     if optionalArgs != nil:
       for i in 1..<optionalArgs.len-2:
         result.add optionalArgs[i]
-    result.typ = sym.typ[0]
+    result.typ = sym.typ.returnType
 
 proc newIntLit*(g: ModuleGraph; info: TLineInfo; value: BiggestInt): PNode =
   result = nkIntLit.newIntNode(value)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index ab234a2a8..1ec6b9a69 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -18,7 +18,7 @@ export createMagic
 proc nilOrSysInt*(g: ModuleGraph): PType = g.sysTypes[tyInt]
 
 proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType =
-  result = newType(kind, nextTypeId(g.idgen), g.systemModule)
+  result = newType(kind, g.idgen, g.systemModule)
   result.size = size
   result.align = size.int16
 
@@ -26,21 +26,21 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym =
   result = systemModuleSym(g, getIdent(g.cache, name))
   if result == nil:
     localError(g.config, info, "system module needs: " & name)
-    result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
-    result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
-  if result.kind == skAlias: result = result.owner
+    result = newSym(skError, getIdent(g.cache, name), g.idgen, g.systemModule, g.systemModule.info, {})
+    result.typ = newType(tyError, g.idgen, g.systemModule)
 
 proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym =
+  result = nil
   let id = getIdent(g.cache, name)
   for r in systemModuleSyms(g, id):
     if r.magic == m:
       # prefer the tyInt variant:
-      if r.typ[0] != nil and r.typ[0].kind == tyInt: return r
+      if r.typ.returnType != nil and r.typ.returnType.kind == tyInt: return r
       result = r
   if result != nil: return result
   localError(g.config, info, "system module needs: " & name)
-  result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {})
-  result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule)
+  result = newSym(skError, id, g.idgen, g.systemModule, g.systemModule.info, {})
+  result.typ = newType(tyError, g.idgen, g.systemModule)
 
 proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType =
   result = getSysSym(g, info, name).typ
@@ -81,8 +81,8 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType =
 
 proc resetSysTypes*(g: ModuleGraph) =
   g.systemModule = nil
-  initStrTable(g.compilerprocs)
-  initStrTable(g.exposed)
+  g.compilerprocs = initStrTable()
+  g.exposed = initStrTable()
   for i in low(g.sysTypes)..high(g.sysTypes):
     g.sysTypes[i] = nil
 
@@ -93,16 +93,23 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType =
 
 proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} =
   if t.n != nil and t.kind in {tyInt, tyFloat}:
-    result = copyType(t, nextTypeId(id), t.owner)
+    result = copyType(t, id, t.owner)
     result.n = nil
   else:
     result = t
 
 proc addSonSkipIntLit*(father, son: PType; id: IdGenerator) =
   let s = son.skipIntLit(id)
-  father.sons.add(s)
+  father.add(s)
   propagateToOwner(father, s)
 
+proc makeVarType*(owner: PSym; baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
+  if baseType.kind == kind:
+    result = baseType
+  else:
+    result = newType(kind, idgen, owner)
+    addSonSkipIntLit(result, baseType, idgen)
+
 proc getCompilerProc*(g: ModuleGraph; name: string): PSym =
   let ident = getIdent(g.cache, name)
   result = strTableGet(g.compilerprocs, ident)
@@ -124,7 +131,7 @@ proc registerNimScriptSymbol*(g: ModuleGraph; s: PSym) =
 proc getNimScriptSymbol*(g: ModuleGraph; name: string): PSym =
   strTableGet(g.exposed, getIdent(g.cache, name))
 
-proc resetNimScriptSymbols*(g: ModuleGraph) = initStrTable(g.exposed)
+proc resetNimScriptSymbols*(g: ModuleGraph) = g.exposed = initStrTable()
 
 proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
   case t.kind
@@ -146,7 +153,17 @@ proc getMagicEqSymForType*(g: ModuleGraph; t: PType; info: TLineInfo): PSym =
   of tyProc:
     result = getSysMagic(g, info, "==", mEqProc)
   else:
+    result = nil
     globalError(g.config, info,
       "can't find magic equals operator for type kind " & $t.kind)
 
+proc makePtrType*(baseType: PType; idgen: IdGenerator): PType =
+  result = newType(tyPtr, idgen, baseType.owner)
+  addSonSkipIntLit(result, baseType, idgen)
 
+proc makeAddr*(n: PNode; idgen: IdGenerator): PNode =
+  if n.kind == nkHiddenAddr:
+    result = n
+  else:
+    result = newTree(nkHiddenAddr, n)
+    result.typ = makePtrType(n.typ, idgen)
diff --git a/compiler/main.nim b/compiler/main.nim
index 171521c7a..4c52317cf 100644
--- a/compiler/main.nim
+++ b/compiler/main.nim
@@ -13,12 +13,12 @@ when not defined(nimcore):
   {.error: "nimcore MUST be defined for Nim's core tooling".}
 
 import
-  std/[strutils, os, times, tables, sha1, with, json],
+  std/[strutils, os, times, tables, with, json],
   llstream, ast, lexer, syntaxes, options, msgs,
   condsyms,
-  sem, idents, passes, extccomp,
+  idents, extccomp,
   cgen, nversion,
-  platform, nimconf, passaux, depends, vm,
+  platform, nimconf, depends,
   modules,
   modulegraphs, lineinfos, pathutils, vmprofiler
 
@@ -26,15 +26,14 @@ import
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
 
-import ic / [cbackend, integrity, navigator]
-from ic / ic import rodViewer
+import ic / [cbackend, integrity, navigator, ic]
 
-when not defined(leanCompiler):
-  import jsgen, docgen, docgen2
+import ../dist/checksums/src/checksums/sha1
+
+import pipelines
 
-proc semanticPasses(g: ModuleGraph) =
-  registerPass g, verbosePass
-  registerPass g, semPass
+when not defined(leanCompiler):
+  import docgen
 
 proc writeDepsFile(g: ModuleGraph) =
   let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps")
@@ -47,13 +46,39 @@ proc writeDepsFile(g: ModuleGraph) =
       f.writeLine(toFullPath(g.config, k))
   f.close()
 
+proc writeCMakeDepsFile(conf: ConfigRef) =
+  ## write a list of C files for build systems like CMake.
+  ## only updated when the C file list changes.
+  let fname = getNimcacheDir(conf) / conf.outFile.changeFileExt("cdeps")
+  # generate output files list
+  var cfiles: seq[string] = @[]
+  for it in conf.toCompile: cfiles.add(it.cname.string)
+  let fileset = cfiles.toCountTable()
+  # read old cfiles list
+  var fl: File = default(File)
+  var prevset = initCountTable[string]()
+  if open(fl, fname.string, fmRead):
+    for line in fl.lines: prevset.inc(line)
+    fl.close()
+  # write cfiles out
+  if fileset != prevset:
+    fl = open(fname.string, fmWrite)
+    for line in cfiles: fl.writeLine(line)
+    fl.close()
+
 proc commandGenDepend(graph: ModuleGraph) =
-  semanticPasses(graph)
-  registerPass(graph, gendependPass)
-  compileProject(graph)
+  setPipeLinePass(graph, GenDependPass)
+  compilePipelineProject(graph)
   let project = graph.config.projectFull
   writeDepsFile(graph)
   generateDot(graph, project)
+
+  # dot in graphivz tool kit is required
+  let graphvizDotPath = findExe("dot")
+  if graphvizDotPath.len == 0:
+    quit("gendepend: Graphviz's tool dot is required," &
+    "see https://graphviz.org/download for downloading")
+
   execExternalProgram(graph.config, "dot -Tpng -o" &
       changeFileExt(project, "png").string &
       ' ' & changeFileExt(project, "dot").string)
@@ -67,8 +92,8 @@ proc commandCheck(graph: ModuleGraph) =
     defineSymbol(conf.symbols, "nimconfig")
   elif conf.backend == backendJs:
     setTarget(conf.target, osJS, cpuJS)
-  semanticPasses(graph)  # use an empty backend for semantic checking only
-  compileProject(graph)
+  setPipeLinePass(graph, SemPass)
+  compilePipelineProject(graph)
 
   if conf.symbolFiles != disabledSf:
     case conf.ideCmd
@@ -82,22 +107,20 @@ when not defined(leanCompiler):
   proc commandDoc2(graph: ModuleGraph; ext: string) =
     handleDocOutputOptions graph.config
     graph.config.setErrorMaxHighMaybe
-    semanticPasses(graph)
     case ext:
-    of TexExt:  registerPass(graph, docgen2TexPass)
-    of JsonExt: registerPass(graph, docgen2JsonPass)
-    of HtmlExt: registerPass(graph, docgen2Pass)
-    else: doAssert false, $ext
-    compileProject(graph)
-    finishDoc2Pass(graph.config.projectName)
+    of TexExt:
+      setPipeLinePass(graph, Docgen2TexPass)
+    of JsonExt:
+      setPipeLinePass(graph, Docgen2JsonPass)
+    of HtmlExt:
+      setPipeLinePass(graph, Docgen2Pass)
+    else: raiseAssert $ext
+    compilePipelineProject(graph)
 
 proc commandCompileToC(graph: ModuleGraph) =
   let conf = graph.config
   extccomp.initVars(conf)
-  semanticPasses(graph)
   if conf.symbolFiles == disabledSf:
-    registerPass(graph, cgenPass)
-
     if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"):
       if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile):
         # nothing changed
@@ -107,7 +130,11 @@ proc commandCompileToC(graph: ModuleGraph) =
   if not extccomp.ccHasSaneOverflow(conf):
     conf.symbols.defineSymbol("nimEmulateOverflowChecks")
 
-  compileProject(graph)
+  if conf.symbolFiles == disabledSf:
+    setPipeLinePass(graph, CgenPass)
+  else:
+    setPipeLinePass(graph, SemPass)
+  compilePipelineProject(graph)
   if graph.config.errorCounter > 0:
     return # issue #9933
   if conf.symbolFiles == disabledSf:
@@ -123,9 +150,11 @@ proc commandCompileToC(graph: ModuleGraph) =
     extccomp.callCCompiler(conf)
     # for now we do not support writing out a .json file with the build instructions when HCR is on
     if not conf.hcrOn:
-      extccomp.writeJsonBuildInstructions(conf)
+      extccomp.writeJsonBuildInstructions(conf, graph.cachedFiles)
     if optGenScript in graph.config.globalOptions:
       writeDepsFile(graph)
+    if optGenCDeps in graph.config.globalOptions:
+      writeCMakeDepsFile(conf)
 
 proc commandJsonScript(graph: ModuleGraph) =
   extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile)
@@ -138,42 +167,35 @@ proc commandCompileToJS(graph: ModuleGraph) =
     conf.exc = excCpp
     setTarget(conf.target, osJS, cpuJS)
     defineSymbol(conf.symbols, "ecmascript") # For backward compatibility
-    semanticPasses(graph)
-    registerPass(graph, JSgenPass)
-    compileProject(graph)
+    setPipeLinePass(graph, JSgenPass)
+    compilePipelineProject(graph)
     if optGenScript in conf.globalOptions:
       writeDepsFile(graph)
 
-proc interactivePasses(graph: ModuleGraph) =
+proc commandInteractive(graph: ModuleGraph) =
+  graph.config.setErrorMaxHighMaybe
   initDefines(graph.config.symbols)
   defineSymbol(graph.config.symbols, "nimscript")
   # note: seems redundant with -d:nimHasLibFFI
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  registerPass(graph, verbosePass)
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
-
-proc commandInteractive(graph: ModuleGraph) =
-  graph.config.setErrorMaxHighMaybe
-  interactivePasses(graph)
-  compileSystemModule(graph)
+  setPipeLinePass(graph, InterpreterPass)
+  compilePipelineSystemModule(graph)
   if graph.config.commandArgs.len > 0:
-    discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
+    discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {})
   else:
     var m = graph.makeStdinModule()
     incl(m.flags, sfMainModule)
     var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0)
     let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config))
-    processModule(graph, m, idgen, s)
+    discard processPipelineModule(graph, m, idgen, s)
 
 proc commandScan(cache: IdentCache, config: ConfigRef) =
   var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt)
   var stream = llStreamOpen(f, fmRead)
   if stream != nil:
     var
-      L: Lexer
-      tok: Token
-    initToken(tok)
+      L: Lexer = default(Lexer)
+      tok: Token = default(Token)
     openLexer(L, f, stream, cache, config)
     while true:
       rawGetTok(L, tok)
@@ -219,8 +241,6 @@ proc mainCommand*(graph: ModuleGraph) =
   let conf = graph.config
   let cache = graph.cache
 
-  # In "nim serve" scenario, each command must reset the registered passes
-  clearPasses(graph)
   conf.lastCmdTime = epochTime()
   conf.searchPaths.add(conf.libpath)
 
@@ -245,7 +265,7 @@ proc mainCommand*(graph: ModuleGraph) =
         # and it has added this define implictly, so we must undo that here.
         # A better solution might be to fix system.nim
         undefSymbol(conf.symbols, "useNimRtl")
-    of backendInvalid: doAssert false
+    of backendInvalid: raiseAssert "unreachable"
 
   proc compileToBackend() =
     customizeForBackend(conf.backend)
@@ -255,7 +275,7 @@ proc mainCommand*(graph: ModuleGraph) =
     of backendCpp: commandCompileToC(graph)
     of backendObjc: commandCompileToC(graph)
     of backendJs: commandCompileToJS(graph)
-    of backendInvalid: doAssert false
+    of backendInvalid: raiseAssert "unreachable"
 
   template docLikeCmd(body) =
     when defined(leanCompiler):
@@ -275,14 +295,18 @@ proc mainCommand*(graph: ModuleGraph) =
     # so by default should not end up in $PWD nor in $projectPath.
     var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
               else: conf.projectPath
-    doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
+    if not ret.string.isAbsolute: # `AbsoluteDir` is not a real guarantee
+      rawMessage(conf, errCannotOpenFile, ret.string & "/")
     if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}:
       ret = ret / htmldocsDir
     conf.outDir = ret
 
   ## process all commands
   case conf.cmd
-  of cmdBackends: compileToBackend()
+  of cmdBackends:
+    compileToBackend()
+    when BenchIC:
+      echoTimes graph.packed
   of cmdTcc:
     when hasTinyCBackend:
       extccomp.setCC(conf, "tcc", unknownLineInfo)
@@ -294,7 +318,6 @@ proc mainCommand*(graph: ModuleGraph) =
   of cmdDoc0: docLikeCmd commandDoc(cache, conf)
   of cmdDoc:
     docLikeCmd():
-      conf.setNoteDefaults(warnLockLevel, false) # issue #13218
       conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218
         # because currently generates lots of false positives due to conflation
         # of labels links in doc comments, e.g. for random.rand:
@@ -379,6 +402,10 @@ proc mainCommand*(graph: ModuleGraph) =
       for it in conf.searchPaths: msgWriteln(conf, it.string)
   of cmdCheck:
     commandCheck(graph)
+  of cmdM:
+    graph.config.symbolFiles = v2Sf
+    setUseIc(graph.config.symbolFiles != disabledSf)
+    commandCheck(graph)
   of cmdParse:
     wantMainModule(conf)
     discard parseFile(conf.projectMainIdx, cache, conf)
@@ -396,7 +423,7 @@ proc mainCommand*(graph: ModuleGraph) =
   of cmdJsonscript:
     setOutFile(graph.config)
     commandJsonScript(graph)
-  of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix:
+  of cmdUnknown, cmdNone, cmdIdeTools:
     rawMessage(conf, errGenerated, "invalid command: " & conf.command)
 
   if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}:
diff --git a/compiler/mangleutils.nim b/compiler/mangleutils.nim
new file mode 100644
index 000000000..2ae954518
--- /dev/null
+++ b/compiler/mangleutils.nim
@@ -0,0 +1,59 @@
+import std/strutils
+import ast, modulegraphs
+
+proc mangle*(name: string): string =
+  result = newStringOfCap(name.len)
+  var start = 0
+  if name[0] in Digits:
+    result.add("X" & name[0])
+    start = 1
+  var requiresUnderscore = false
+  template special(x) =
+    result.add x
+    requiresUnderscore = true
+  for i in start..<name.len:
+    let c = name[i]
+    case c
+    of 'a'..'z', '0'..'9', 'A'..'Z':
+      result.add(c)
+    of '_':
+      # we generate names like 'foo_9' for scope disambiguations and so
+      # disallow this here:
+      if i > 0 and i < name.len-1 and name[i+1] in Digits:
+        discard
+      else:
+        result.add(c)
+    of '$': special "dollar"
+    of '%': special "percent"
+    of '&': special "amp"
+    of '^': special "roof"
+    of '!': special "emark"
+    of '?': special "qmark"
+    of '*': special "star"
+    of '+': special "plus"
+    of '-': special "minus"
+    of '/': special "slash"
+    of '\\': special "backslash"
+    of '=': special "eq"
+    of '<': special "lt"
+    of '>': special "gt"
+    of '~': special "tilde"
+    of ':': special "colon"
+    of '.': special "dot"
+    of '@': special "at"
+    of '|': special "bar"
+    else:
+      result.add("X" & toHex(ord(c), 2))
+      requiresUnderscore = true
+  if requiresUnderscore:
+    result.add "_"
+
+proc mangleParamExt*(s: PSym): string =
+  result = "_p"
+  result.addInt s.position
+
+proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string =
+  result = "__"
+  result.add graph.ifaces[s.itemId.module].uniqueName
+  result.add "_u"
+  result.addInt s.itemId.item # s.disamb #
diff --git a/compiler/md5_old.nim b/compiler/md5_old.nim
deleted file mode 100644
index da4a51198..000000000
--- a/compiler/md5_old.nim
+++ /dev/null
@@ -1,297 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# `std/md5` without VM and JavaScript support, to circumvent a bug with
-# openarrays on Nim < 1.4.
-
-when defined(nimHasStyleChecks):
-  {.push styleChecks: off.}
-
-type
-  MD5State = array[0..3, uint32]
-  MD5Block = array[0..15, uint32]
-  MD5CBits = array[0..7, uint8]
-  MD5Digest* = array[0..15, uint8]
-    ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_.
-  MD5Buffer = array[0..63, uint8]
-  MD5Context* {.final.} = object
-    state: MD5State
-    count: array[0..1, uint32]
-    buffer: MD5Buffer
-
-const
-  padding: array[0..63, uint8] = [
-    0x80'u8, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0
-  ]
-
-proc F(x, y, z: uint32): uint32 {.inline.} =
-  result = (x and y) or ((not x) and z)
-
-proc G(x, y, z: uint32): uint32 {.inline.} =
-  result = (x and z) or (y and (not z))
-
-proc H(x, y, z: uint32): uint32 {.inline.} =
-  result = x xor y xor z
-
-proc I(x, y, z: uint32): uint32 {.inline.} =
-  result = y xor (x or (not z))
-
-proc rot(x: var uint32, n: uint8) {.inline.} =
-  x = (x shl n) or (x shr (32'u32 - n))
-
-proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
-  a = a + F(b, c, d) + x + ac
-  rot(a, s)
-  a = a + b
-
-proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
-  a = a + G(b, c, d) + x + ac
-  rot(a, s)
-  a = a + b
-
-proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
-  a = a + H(b, c, d) + x + ac
-  rot(a, s)
-  a = a + b
-
-proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
-  a = a + I(b, c, d) + x + ac
-  rot(a, s)
-  a = a + b
-
-proc encode(dest: var MD5Block, src: openArray[uint8]) =
-  var j = 0
-  for i in 0..high(dest):
-    dest[i] = uint32(ord(src[j])) or
-              uint32(ord(src[j+1])) shl 8 or
-              uint32(ord(src[j+2])) shl 16 or
-              uint32(ord(src[j+3])) shl 24
-    inc(j, 4)
-
-proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
-  var i = 0
-  for j in 0..high(src):
-    dest[i] = uint8(src[j] and 0xff'u32)
-    dest[i+1] = uint8(src[j] shr 8 and 0xff'u32)
-    dest[i+2] = uint8(src[j] shr 16 and 0xff'u32)
-    dest[i+3] = uint8(src[j] shr 24 and 0xff'u32)
-    inc(i, 4)
-
-template slice(s: string | cstring, a, b): openArray[uint8] =
-  s.toOpenArrayByte(a, b)
-
-template slice(s: openArray[uint8], a, b): openArray[uint8] =
-  s.toOpenArray(a, b)
-
-proc transform(buffer: openArray[uint8], state: var MD5State) =
-  var
-    myBlock: MD5Block
-  encode(myBlock, buffer)
-  var a = state[0]
-  var b = state[1]
-  var c = state[2]
-  var d = state[3]
-  FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32)
-  FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32)
-  FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32)
-  FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32)
-  FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32)
-  FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32)
-  FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32)
-  FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32)
-  FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32)
-  FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32)
-  FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32)
-  FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32)
-  FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32)
-  FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32)
-  FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32)
-  FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32)
-  GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32)
-  GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32)
-  GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32)
-  GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32)
-  GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32)
-  GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32)
-  GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32)
-  GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32)
-  GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32)
-  GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32)
-  GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32)
-  GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32)
-  GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32)
-  GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32)
-  GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32)
-  GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32)
-  HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32)
-  HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32)
-  HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32)
-  HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32)
-  HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32)
-  HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32)
-  HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32)
-  HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32)
-  HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32)
-  HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32)
-  HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32)
-  HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32)
-  HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32)
-  HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32)
-  HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32)
-  HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32)
-  II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32)
-  II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32)
-  II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32)
-  II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32)
-  II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32)
-  II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32)
-  II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32)
-  II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32)
-  II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32)
-  II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32)
-  II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32)
-  II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32)
-  II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32)
-  II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32)
-  II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32)
-  II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32)
-  state[0] = state[0] + a
-  state[1] = state[1] + b
-  state[2] = state[2] + c
-  state[3] = state[3] + d
-
-proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.}
-proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [],
-    tags: [], gcsafe.}
-proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.}
-
-proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [],
-    tags: [], gcsafe.} =
-  ## Updates the `MD5Context` with the `input` data of length `len`.
-  ##
-  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
-  ## function explicitly.
-  md5Update(c, input.slice(0, len - 1))
-
-
-proc toMD5*(s: string): MD5Digest =
-  ## Computes the `MD5Digest` value for a string `s`.
-  ##
-  ## **See also:**
-  ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation
-  ##   of the `MD5Digest`
-  ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string
-  runnableExamples:
-    assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
-
-  var c: MD5Context
-  md5Init(c)
-  md5Update(c, s.slice(0, s.len - 1))
-  md5Final(c, result)
-
-proc `$`*(d: MD5Digest): string =
-  ## Converts a `MD5Digest` value into its string representation.
-  const digits = "0123456789abcdef"
-  result = ""
-  for i in 0..15:
-    add(result, digits[(d[i].int shr 4) and 0xF])
-    add(result, digits[d[i].int and 0xF])
-
-proc getMD5*(s: string): string =
-  ## Computes an MD5 value of `s` and returns its string representation.
-  ##
-  ## **See also:**
-  ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string
-  runnableExamples:
-    assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72"
-
-  var
-    c: MD5Context
-    d: MD5Digest
-  md5Init(c)
-  md5Update(c, s.slice(0, s.len - 1))
-  md5Final(c, d)
-  result = $d
-
-proc `==`*(D1, D2: MD5Digest): bool =
-  ## Checks if two `MD5Digest` values are identical.
-  for i in 0..15:
-    if D1[i] != D2[i]: return false
-  return true
-
-
-proc clearBuffer(c: var MD5Context) {.inline.} =
-  zeroMem(addr(c.buffer), sizeof(MD5Buffer))
-
-proc md5Init*(c: var MD5Context) =
-  ## Initializes an `MD5Context`.
-  ##
-  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
-  ## function explicitly.
-  c.state[0] = 0x67452301'u32
-  c.state[1] = 0xEFCDAB89'u32
-  c.state[2] = 0x98BADCFE'u32
-  c.state[3] = 0x10325476'u32
-  c.count[0] = 0'u32
-  c.count[1] = 0'u32
-  clearBuffer(c)
-
-proc writeBuffer(c: var MD5Context, index: int,
-                 input: openArray[uint8], inputIndex, len: int) {.inline.} =
-  copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len)
-
-proc md5Update*(c: var MD5Context, input: openArray[uint8]) =
-  ## Updates the `MD5Context` with the `input` data.
-  ##
-  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
-  ## function explicitly.
-  var Index = int((c.count[0] shr 3) and 0x3F)
-  c.count[0] = c.count[0] + (uint32(input.len) shl 3)
-  if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32
-  c.count[1] = c.count[1] + (uint32(input.len) shr 29)
-  var PartLen = 64 - Index
-  if input.len >= PartLen:
-    writeBuffer(c, Index, input, 0, PartLen)
-    transform(c.buffer, c.state)
-    var i = PartLen
-    while i + 63 < input.len:
-      transform(input.slice(i, i + 63), c.state)
-      inc(i, 64)
-    if i < input.len:
-      writeBuffer(c, 0, input, i, input.len - i)
-  elif input.len > 0:
-    writeBuffer(c, Index, input, 0, input.len)
-
-proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
-  ## Finishes the `MD5Context` and stores the result in `digest`.
-  ##
-  ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this
-  ## function explicitly.
-  var
-    Bits: MD5CBits
-    PadLen: int
-  decode(Bits, c.count)
-  var Index = int((c.count[0] shr 3) and 0x3F)
-  if Index < 56: PadLen = 56 - Index
-  else: PadLen = 120 - Index
-  md5Update(c, padding.slice(0, PadLen - 1))
-  md5Update(c, Bits)
-  decode(digest, c.state)
-  clearBuffer(c)
-
-
-when defined(nimHasStyleChecks):
-  {.pop.} #{.push styleChecks: off.}
diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim
index 0df72c43b..77762d23a 100644
--- a/compiler/modulegraphs.nim
+++ b/compiler/modulegraphs.nim
@@ -11,10 +11,12 @@
 ## represents a complete Nim project. Single modules can either be kept in RAM
 ## or stored in a rod-file.
 
-import intsets, tables, hashes, md5_old, sequtils
-import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages
+import std/[intsets, tables, hashes, strtabs, algorithm, os, strutils, parseutils]
+import ../dist/checksums/src/checksums/md5
+import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages, suggestsymdb
 import ic / [packed_ast, ic]
 
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -53,9 +55,17 @@ type
     concreteTypes*: seq[FullId]
     inst*: PInstantiation
 
-  SymInfoPair* = object
-    sym*: PSym
-    info*: TLineInfo
+  PipelinePass* = enum
+    NonePass
+    SemPass
+    JSgenPass
+    CgenPass
+    EvalPass
+    InterpreterPass
+    GenDependPass
+    Docgen2TexPass
+    Docgen2JsonPass
+    Docgen2Pass
 
   ModuleGraph* {.acyclic.} = ref object
     ifaces*: seq[Iface]  ## indexed by int32 fileIdx
@@ -64,8 +74,10 @@ type
 
     typeInstCache*: Table[ItemId, seq[LazyType]] # A symbol's ItemId.
     procInstCache*: Table[ItemId, seq[LazyInstantiation]] # A symbol's ItemId.
-    attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc.
-    methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
+    attachedOps*: array[TTypeAttachedOp, Table[ItemId, LazySym]] # Type ID, destructors, etc.
+    methodsPerGenericType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods
+    memberProcsPerType*: Table[ItemId, seq[PSym]] # Type ID, attached member procs (only c++, virtual,member and ctor so far).
+    initializersPerType*: Table[ItemId, PNode] # Type ID, AST call to the default ctor (c++ only)
     enumToStringProcs*: Table[ItemId, LazySym]
     emittedTypeInfo*: Table[string, FileIndex]
 
@@ -75,6 +87,7 @@ type
     importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies
     suggestMode*: bool # whether we are in nimsuggest mode or not.
     invalidTransitiveClosure: bool
+    interactive*: bool
     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
@@ -84,12 +97,18 @@ type
     cache*: IdentCache
     vm*: RootRef # unfortunately the 'vm' state is shared project-wise, this will
                  # be clarified in later compiler implementations.
+    repl*: RootRef # REPL state is shared project-wise.
     doStopCompile*: proc(): bool {.closure.}
     usageSym*: PSym # for nimsuggest
     owners*: seq[PSym]
-    suggestSymbols*: Table[FileIndex, seq[SymInfoPair]]
+    suggestSymbols*: SuggestSymbolDatabase
     suggestErrors*: Table[FileIndex, seq[Suggest]]
     methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization!
+    bucketTable*: CountTable[ItemId]
+    objectTree*: Table[ItemId, seq[tuple[depth: int, value: PType]]]
+    methodsPerType*: Table[ItemId, seq[LazySym]]
+    dispatchers*: seq[LazySym]
+
     systemModule*: PSym
     sysTypes*: array[TTypeKind, PType]
     compilerprocs*: TStrTable
@@ -104,6 +123,7 @@ type
     cacheCounters*: Table[string, BiggestInt] # IC: implemented
     cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented
     passes*: seq[TPass]
+    pipelinePass*: PipelinePass
     onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
     onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
     onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.}
@@ -113,6 +133,8 @@ type
     idgen*: IdGenerator
     operators*: Operators
 
+    cachedFiles*: StringTableRef
+
   TPassContext* = object of RootObj # the pass's context
     idgen*: IdGenerator
   PPassContext* = ref TPassContext
@@ -127,13 +149,15 @@ type
                  isFrontend: bool]
 
 proc resetForBackend*(g: ModuleGraph) =
-  initStrTable(g.compilerprocs)
+  g.compilerprocs = initStrTable()
   g.typeInstCache.clear()
   g.procInstCache.clear()
   for a in mitems(g.attachedOps):
     a.clear()
-  g.methodsPerType.clear()
+  g.methodsPerGenericType.clear()
   g.enumToStringProcs.clear()
+  g.dispatchers.setLen(0)
+  g.methodsPerType.clear()
 
 const
   cb64 = [
@@ -181,8 +205,8 @@ template semtabAll*(g: ModuleGraph, m: PSym): TStrTable =
   g.ifaces[m.position].interfHidden
 
 proc initStrTables*(g: ModuleGraph, m: PSym) =
-  initStrTable(semtab(g, m))
-  initStrTable(semtabAll(g, m))
+  semtab(g, m) = initStrTable()
+  semtabAll(g, m) = initStrTable()
 
 proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
   strTableAdd(semtab(g, m), s)
@@ -191,10 +215,10 @@ proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) =
 proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} =
   result = module < g.packed.len and g.packed[module].status == loaded
 
-proc isCachedModule(g: ModuleGraph; m: PSym): bool {.inline.} =
+proc isCachedModule*(g: ModuleGraph; m: PSym): bool {.inline.} =
   isCachedModule(g, m.position)
 
-proc simulateCachedModule*(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
+proc simulateCachedModule(g: ModuleGraph; moduleSym: PSym; m: PackedModule) =
   when false:
     echo "simulating ", moduleSym.name.s, " ", moduleSym.position
   simulateLoadedModule(g.packed, g.config, g.cache, moduleSym, m)
@@ -233,7 +257,7 @@ proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym =
 iterator allSyms*(g: ModuleGraph; m: PSym): PSym =
   let importHidden = optImportHidden in m.options
   if isCachedModule(g, m):
-    var rodIt: RodIter
+    var rodIt: RodIter = default(RodIter)
     var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden)
     while r != nil:
       yield r
@@ -250,11 +274,30 @@ proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym =
   else:
     result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name)
 
+proc someSymAmb*(g: ModuleGraph; m: PSym; name: PIdent; amb: var bool): PSym =
+  let importHidden = optImportHidden in m.options
+  if isCachedModule(g, m):
+    result = nil
+    for s in interfaceSymbols(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden):
+      if result == nil:
+        # set result to the first symbol
+        result = s
+      else:
+        # another symbol found
+        amb = true
+        break
+  else:
+    var ti: TIdentIter = default(TIdentIter)
+    result = initIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden), name)
+    if result != nil and nextIdentIter(ti, g.ifaces[m.position].interfSelect(importHidden)) != nil:
+      # another symbol exists with same name
+      amb = true
+
 proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym =
   result = someSym(g, g.systemModule, name)
 
 iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym =
-  var mi: ModuleIter
+  var mi: ModuleIter = default(ModuleIter)
   var r = initModuleIter(mi, g, g.systemModule, name)
   while r != nil:
     yield r
@@ -285,6 +328,13 @@ proc resolveInst(g: ModuleGraph; t: var LazyInstantiation): PInstantiation =
     t.inst = result
   assert result != nil
 
+proc resolveAttachedOp*(g: ModuleGraph; t: var LazySym): PSym =
+  result = t.sym
+  if result == nil:
+    result = loadSymFromId(g.config, g.cache, g.packed, t.id.module, t.id.packed)
+    t.sym = result
+  assert result != nil
+
 iterator typeInstCacheItems*(g: ModuleGraph; s: PSym): PType =
   if g.typeInstCache.contains(s.itemId):
     let x = addr(g.typeInstCache[s.itemId])
@@ -297,25 +347,50 @@ iterator procInstCacheItems*(g: ModuleGraph; s: PSym): PInstantiation =
     for t in mitems(x[]):
       yield resolveInst(g, t)
 
+
 proc getAttachedOp*(g: ModuleGraph; t: PType; op: TTypeAttachedOp): PSym =
   ## returns the requested attached operation for type `t`. Can return nil
   ## if no such operation exists.
-  result = g.attachedOps[op].getOrDefault(t.itemId)
+  if g.attachedOps[op].contains(t.itemId):
+    result = resolveAttachedOp(g, g.attachedOps[op][t.itemId])
+  else:
+    result = nil
 
 proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
   ## we also need to record this to the packed module.
-  g.attachedOps[op][t.itemId] = value
+  g.attachedOps[op][t.itemId] = LazySym(sym: value)
 
 proc setAttachedOpPartial*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
   ## we also need to record this to the packed module.
-  g.attachedOps[op][t.itemId] = value
-  # XXX Also add to the packed module!
+  g.attachedOps[op][t.itemId] = LazySym(sym: value)
 
 proc completePartialOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp; value: PSym) =
   if g.config.symbolFiles != disabledSf:
     assert module < g.encoders.len
     assert isActive(g.encoders[module])
     toPackedGeneratedProcDef(value, g.encoders[module], g.packed[module].fromDisk)
+    #storeAttachedProcDef(t, op, value, g.encoders[module], g.packed[module].fromDisk)
+
+iterator getDispatchers*(g: ModuleGraph): PSym =
+  for i in g.dispatchers.mitems:
+    yield resolveSym(g, i)
+
+proc addDispatchers*(g: ModuleGraph, value: PSym) =
+  # TODO: add it for packed modules
+  g.dispatchers.add LazySym(sym: value)
+
+iterator resolveLazySymSeq(g: ModuleGraph, list: var seq[LazySym]): PSym =
+  for it in list.mitems:
+    yield resolveSym(g, it)
+
+proc setMethodsPerType*(g: ModuleGraph; id: ItemId, methods: seq[LazySym]) =
+  # TODO: add it for packed modules
+  g.methodsPerType[id] = methods
+
+iterator getMethodsPerType*(g: ModuleGraph; t: PType): PSym =
+  if g.methodsPerType.contains(t.itemId):
+    for it in mitems g.methodsPerType[t.itemId]:
+      yield resolveSym(g, it)
 
 proc getToStringProc*(g: ModuleGraph; t: PType): PSym =
   result = resolveSym(g, g.enumToStringProcs[t.itemId])
@@ -325,12 +400,12 @@ proc setToStringProc*(g: ModuleGraph; t: PType; value: PSym) =
   g.enumToStringProcs[t.itemId] = LazySym(sym: value)
 
 iterator methodsForGeneric*(g: ModuleGraph; t: PType): (int, PSym) =
-  if g.methodsPerType.contains(t.itemId):
-    for it in mitems g.methodsPerType[t.itemId]:
+  if g.methodsPerGenericType.contains(t.itemId):
+    for it in mitems g.methodsPerGenericType[t.itemId]:
       yield (it[0], resolveSym(g, it[1]))
 
 proc addMethodToGeneric*(g: ModuleGraph; module: int; t: PType; col: int; m: PSym) =
-  g.methodsPerType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
+  g.methodsPerGenericType.mgetOrPut(t.itemId, @[]).add (col, LazySym(sym: m))
 
 proc hasDisabledAsgn*(g: ModuleGraph; t: PType): bool =
   let op = getAttachedOp(g, t, attachedAsgn)
@@ -343,10 +418,11 @@ proc copyTypeProps*(g: ModuleGraph; module: int; dest, src: PType) =
       setAttachedOp(g, module, dest, k, op)
 
 proc loadCompilerProc*(g: ModuleGraph; name: string): PSym =
+  result = nil
   if g.config.symbolFiles == disabledSf: return nil
 
   # slow, linear search, but the results are cached:
-  for module in 0..high(g.packed):
+  for module in 0..<len(g.packed):
     #if isCachedModule(g, module):
     let x = searchForCompilerproc(g.packed[module], name)
     if x >= 0:
@@ -388,13 +464,56 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} =
   result = g.doStopCompile != nil and g.doStopCompile()
 
 proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym =
-  result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {})
+  result = newSym(skProc, getIdent(g.cache, name), idgen, nil, unknownLineInfo, {})
   result.magic = m
   result.flags = {sfNeverRaises}
 
 proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym =
   result = createMagic(g, g.idgen, name, m)
 
+proc uniqueModuleName*(conf: ConfigRef; m: PSym): string =
+  ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
+  ## so that it is useful as a C identifier snippet.
+  let fid = FileIndex(m.position)
+  let path = AbsoluteFile toFullPath(conf, fid)
+  var isLib = false
+  var rel = ""
+  if path.string.startsWith(conf.libpath.string):
+    isLib = true
+    rel = relativeTo(path, conf.libpath).string
+  else:
+    rel = relativeTo(path, conf.projectPath).string
+
+  if not isLib and not belongsToProjectPackage(conf, m):
+    # special handlings for nimble packages
+    when DirSep == '\\':
+      let rel2 = replace(rel, '\\', '/')
+    else:
+      let rel2 = rel
+    const pkgs2 = "pkgs2/"
+    var start = rel2.find(pkgs2)
+    if start >= 0:
+      start += pkgs2.len
+      start += skipUntil(rel2, {'/'}, start)
+      if start+1 < rel2.len:
+        rel = "pkg/" & rel2[start+1..<rel.len] # strips paths
+
+  let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
+  result = newStringOfCap(trunc)
+  for i in 0..<trunc:
+    let c = rel[i]
+    case c
+    of 'a'..'z', '0'..'9':
+      result.add c
+    of {os.DirSep, os.AltSep}:
+      result.add 'Z' # because it looks a bit like '/'
+    of '.':
+      result.add 'O' # a circle
+    else:
+      # We mangle upper letters too so that there cannot
+      # be clashes with our special meanings of 'Z' and 'O'
+      result.addInt ord(c)
+
 proc registerModule*(g: ModuleGraph; m: PSym) =
   assert m != nil
   assert m.kind == skModule
@@ -403,10 +522,10 @@ proc registerModule*(g: ModuleGraph; m: PSym) =
     setLen(g.ifaces, m.position + 1)
 
   if m.position >= g.packed.len:
-    setLen(g.packed, m.position + 1)
+    setLen(g.packed.pm, m.position + 1)
 
   g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[],
-                               uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position))))
+                               uniqueName: rope(uniqueModuleName(g.config, m)))
   initStrTables(g, m)
 
 proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
@@ -415,37 +534,39 @@ proc registerModuleById*(g: ModuleGraph; m: FileIndex) =
 proc initOperators*(g: ModuleGraph): Operators =
   # These are safe for IC.
   # Public because it's used by DrNim.
-  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)
+  result = Operators(
+    opLe: createMagic(g, "<=", mLeI),
+    opLt: createMagic(g, "<", mLtI),
+    opAnd: createMagic(g, "and", mAnd),
+    opOr: createMagic(g, "or", mOr),
+    opIsNil: createMagic(g, "isnil", mIsNil),
+    opEq: createMagic(g, "==", mEqI),
+    opAdd: createMagic(g, "+", mAddI),
+    opSub: createMagic(g, "-", mSubI),
+    opMul: createMagic(g, "*", mMulI),
+    opDiv: createMagic(g, "div", mDivI),
+    opLen: createMagic(g, "len", mLengthSeq),
+    opNot: createMagic(g, "not", mNot),
+    opContains: createMagic(g, "contains", mInSet)
+  )
 
 proc initModuleGraphFields(result: ModuleGraph) =
   # A module ID of -1 means that the symbol is not attached to a module at all,
   # but to the module graph:
   result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32)
-  initStrTable(result.packageSyms)
+  result.packageSyms = initStrTable()
   result.deps = initIntSet()
   result.importDeps = initTable[FileIndex, seq[FileIndex]]()
   result.ifaces = @[]
   result.importStack = @[]
   result.inclToMod = initTable[FileIndex, FileIndex]()
   result.owners = @[]
-  result.suggestSymbols = initTable[FileIndex, seq[SymInfoPair]]()
+  result.suggestSymbols = initTable[FileIndex, SuggestFileSymbolDatabase]()
   result.suggestErrors = initTable[FileIndex, seq[Suggest]]()
   result.methods = @[]
-  initStrTable(result.compilerprocs)
-  initStrTable(result.exposed)
-  initStrTable(result.packageTypes)
+  result.compilerprocs = initStrTable()
+  result.exposed = initStrTable()
+  result.packageTypes = initStrTable()
   result.emptyNode = newNode(nkEmpty)
   result.cacheSeqs = initTable[string, PNode]()
   result.cacheCounters = initTable[string, BiggestInt]()
@@ -454,6 +575,7 @@ proc initModuleGraphFields(result: ModuleGraph) =
   result.symBodyHashes = initTable[int, SigHash]()
   result.operators = initOperators(result)
   result.emittedTypeInfo = initTable[string, FileIndex]()
+  result.cachedFiles = newStringTable()
 
 proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   result = ModuleGraph()
@@ -462,7 +584,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph =
   initModuleGraphFields(result)
 
 proc resetAllModules*(g: ModuleGraph) =
-  initStrTable(g.packageSyms)
+  g.packageSyms = initStrTable()
   g.deps = initIntSet()
   g.ifaces = @[]
   g.importStack = @[]
@@ -470,11 +592,12 @@ proc resetAllModules*(g: ModuleGraph) =
   g.usageSym = nil
   g.owners = @[]
   g.methods = @[]
-  initStrTable(g.compilerprocs)
-  initStrTable(g.exposed)
+  g.compilerprocs = initStrTable()
+  g.exposed = initStrTable()
   initModuleGraphFields(g)
 
 proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym =
+  result = nil
   if fileIdx.int32 >= 0:
     if isCachedModule(g, fileIdx.int32):
       result = g.packed[fileIdx.int32].module
@@ -580,6 +703,7 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) =
 
 proc needsCompilation*(g: ModuleGraph): bool =
   # every module that *depends* on this file is also dirty:
+  result = false
   for i in 0i32..<g.ifaces.len.int32:
     let m = g.ifaces[i].module
     if m != nil:
@@ -587,6 +711,7 @@ proc needsCompilation*(g: ModuleGraph): bool =
         return true
 
 proc needsCompilation*(g: ModuleGraph, fileIdx: FileIndex): bool =
+  result = false
   let module = g.getModule(fileIdx)
   if module != nil and g.isDirty(module):
     return true
@@ -608,12 +733,12 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex;
   ## Returns 'nil' if the module needs to be recompiled.
   if g.config.symbolFiles in {readOnlySf, v2Sf, stressTest}:
     result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx, cachedModules)
+  else:
+    result = nil
 
 proc configComplete*(g: ModuleGraph) =
   rememberStartupConfig(g.startupPackedConfig, g.config)
 
-from std/strutils import repeat, `%`
-
 proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) =
   let conf = graph.config
   let isNimscript = conf.isDefined("nimscript")
@@ -639,16 +764,14 @@ func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool =
   ## Check if symbol belongs to the 'stdlib' package.
   sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId
 
-proc `==`*(a, b: SymInfoPair): bool =
-  result = a.sym == b.sym and a.info.exactEquals(b.info)
-
-proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): seq[SymInfoPair] =
-  result = graph.suggestSymbols.getOrDefault(fileIdx, @[]).deduplicate
+proc fileSymbols*(graph: ModuleGraph, fileIdx: FileIndex): SuggestFileSymbolDatabase =
+  result = graph.suggestSymbols.getOrDefault(fileIdx, newSuggestFileSymbolDatabase(fileIdx, optIdeExceptionInlayHints in graph.config.globalOptions))
+  doAssert(result.fileIndex == fileIdx)
 
 iterator suggestSymbolsIter*(g: ModuleGraph): SymInfoPair =
   for xs in g.suggestSymbols.values:
-    for x in xs.deduplicate:
-      yield x
+    for i in xs.lineInfo.low..xs.lineInfo.high:
+      yield xs.getSymInfoPair(i)
 
 iterator suggestErrorsIter*(g: ModuleGraph): Suggest =
   for xs in g.suggestErrors.values:
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index e80ea3fa6..c9e6060e5 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -7,9 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
+import ast, renderer, msgs, options, idents, lineinfos,
   pathutils
 
+import std/[strutils, os]
+
 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"
@@ -36,11 +38,18 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string =
           localError(n.info, "only '/' supported with $package notation")
           result = ""
     else:
-      let modname = getModuleName(conf, n[2])
-      # hacky way to implement 'x / y /../ z':
-      result = getModuleName(conf, n1)
-      result.add renderTree(n0, {renderNoComments}).replace(" ")
-      result.add modname
+      if n0.kind in nkIdentKinds:
+        let ident = n0.getPIdent
+        if ident != nil and ident.s[0] == '/':
+          let modname = getModuleName(conf, n[2])
+          # hacky way to implement 'x / y /../ z':
+          result = getModuleName(conf, n1)
+          result.add renderTree(n0, {renderNoComments}).replace(" ")
+          result.add modname
+        else:
+          result = ""
+      else:
+        result = ""
   of nkPrefix:
     when false:
       if n[0].kind == nkIdent and n[0].ident.s == "$":
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 2becef38f..6e2af8bcc 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,14 +10,12 @@
 ## Implements the module handling, including the caching of modules.
 
 import
-  ast, astalgo, magicsys, msgs, options,
-  idents, lexer, passes, syntaxes, llstream, modulegraphs,
-  lineinfos, pathutils, tables, packages
+  ast, magicsys, msgs, options,
+  idents, lexer, syntaxes, modulegraphs,
+  lineinfos, pathutils
 
-when defined(nimPreviewSlimSystem):
-  import std/[syncio, assertions]
-
-import ic / replayer
+import ../dist/checksums/src/checksums/sha1
+import std/strtabs
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
   magicsys.resetSysTypes(g)
@@ -25,12 +23,12 @@ proc resetSystemArtifacts*(g: ModuleGraph) =
 template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent =
   getIdent(graph.cache, splitFile(filename).name)
 
-proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
+proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) =
   let packSym = getPackage(graph, fileIdx)
   result.owner = packSym
   result.position = int fileIdx
 
-proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
+proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym =
   let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
   # 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.
@@ -38,103 +36,23 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym =
                 name: getModuleIdent(graph, filename),
                 info: newLineInfo(fileIdx, 1, 1))
   if not isNimIdentifier(result.name.s):
-    rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s)
+    rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s &
+              "'; a module name must be a valid Nim identifier.")
   partialInitModule(result, graph, fileIdx, filename)
   graph.registerModule(result)
 
-proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
-  var flags = flags
-  if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
-  result = graph.getModule(fileIdx)
-
-  template processModuleAux(moduleStatus) =
-    onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
-    var s: PLLStream
-    if sfMainModule in flags:
-      if graph.config.projectIsStdin: s = stdin.llStreamOpen
-      elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
-    discard processModule(graph, result, idGeneratorFromModule(result), s)
-  if result == nil:
-    var cachedModules: seq[FileIndex]
-    result = moduleFromRodFile(graph, fileIdx, cachedModules)
-    let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
-    if result == nil:
-      result = newModule(graph, fileIdx)
-      result.flags.incl flags
-      registerModule(graph, result)
-      processModuleAux("import")
-    else:
-      if sfSystemModule in flags:
-        graph.systemModule = result
-      partialInitModule(result, graph, fileIdx, filename)
-    for m in cachedModules:
-      registerModuleById(graph, m)
-      replayStateChanges(graph.packed[m.int].module, graph)
-      replayGenericCacheInformation(graph, m.int)
-  elif graph.isDirty(result):
-    result.flags.excl sfDirty
-    # reset module fields:
-    initStrTables(graph, result)
-    result.ast = nil
-    processModuleAux("import(dirty)")
-    graph.markClientsDirty(fileIdx)
-
-proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
-  # this is called by the semantic checking phase
-  assert graph.config != nil
-  result = compileModule(graph, fileIdx, {}, s)
-  graph.addDep(s, fileIdx)
-  # keep track of import relationships
-  if graph.config.hcrOn:
-    graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
-  #if sfSystemModule in result.flags:
-  #  localError(result.info, errAttemptToRedefine, result.name.s)
-  # restore the notes for outer module:
-  graph.config.notes =
-    if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
-    else: graph.config.foreignPackageNotes
-
 proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode =
   result = syntaxes.parseFile(fileIdx, graph.cache, graph.config)
   graph.addDep(s, fileIdx)
   graph.addIncludeDep(s.position.FileIndex, fileIdx)
-
-proc connectCallbacks*(graph: ModuleGraph) =
-  graph.includeFileCallback = includeModule
-  graph.importModuleCallback = importModule
-
-proc compileSystemModule*(graph: ModuleGraph) =
-  if graph.systemModule == nil:
-    connectCallbacks(graph)
-    graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
-        graph.config.libpath / RelativeFile"system.nim")
-    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
+  let path = toFullPath(graph.config, fileIdx)
+  graph.cachedFiles[path] = $secureHashFile(path)
 
 proc wantMainModule*(conf: ConfigRef) =
   if conf.projectFull.isEmpty:
     fatal(conf, gCmdLineInfo, "command expects a filename")
   conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt))
 
-proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
-  connectCallbacks(graph)
-  let conf = graph.config
-  wantMainModule(conf)
-  configComplete(graph)
-
-  let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
-  let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
-  conf.projectMainIdx2 = projectFile
-
-  let packSym = getPackage(graph, projectFile)
-  graph.config.mainPackageId = packSym.getPackageId
-  graph.importStack.add projectFile
-
-  if projectFile == systemFileIdx:
-    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
-  else:
-    graph.compileSystemModule()
-    discard graph.compileModule(projectFile, {sfMainModule})
-
 proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym =
   result = graph.newModule(fileInfoIdx(graph.config, filename))
   registerModule(graph, result)
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index c7610b630..c49ca8c9b 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -10,7 +10,7 @@
 import
   std/[strutils, os, tables, terminal, macros, times],
   std/private/miscdollars,
-  options, lineinfos, pathutils, strutils2
+  options, lineinfos, pathutils
 
 import ropes except `%`
 
@@ -24,6 +24,10 @@ template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true)
 template toStdOrrKind(stdOrr): untyped =
   if stdOrr == stdout: stdOrrStdout else: stdOrrStderr
 
+proc toLowerAscii(a: var string) {.inline.} =
+  for c in mitems(a):
+    if isUpperAscii(c): c = char(uint8(c) xor 0b0010_0000'u8)
+
 proc flushDot*(conf: ConfigRef) =
   ## safe to call multiple times
   # xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`.
@@ -57,14 +61,12 @@ proc makeCString*(s: string): Rope =
   result.add('\"')
 
 proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo =
-  result.fullPath = fullPath
-  #shallow(result.fullPath)
-  result.projPath = projPath
-  #shallow(result.projPath)
-  result.shortName = fullPath.extractFilename
+  result = TFileInfo(fullPath: fullPath, projPath: projPath,
+                    shortName: fullPath.extractFilename,
+                    quotedFullName: fullPath.string.makeCString,
+                    lines: @[]
+  )
   result.quotedName = result.shortName.makeCString
-  result.quotedFullName = fullPath.string.makeCString
-  result.lines = @[]
   when defined(nimpretty):
     if not result.fullPath.isEmpty:
       try:
@@ -78,7 +80,7 @@ when defined(nimpretty):
   proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string =
     substr(conf.m.fileInfos[fid.int].fullContent, a, b)
 
-proc canonicalCase(path: var string) =
+proc canonicalCase(path: var string) {.inline.} =
   ## the idea is to only use this for checking whether a path is already in
   ## the table but otherwise keep the original case
   when FileSystemCaseSensitive: discard
@@ -101,16 +103,13 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
 
   try:
     canon = canonicalizePath(conf, filename)
-    when not defined(nimSeqsV2):
-      shallow(canon.string)
   except OSError:
     canon = filename
     # The compiler uses "filenames" such as `command line` or `stdin`
     # This flag indicates that we are working with such a path here
     pseudoPath = true
 
-  var canon2: string
-  forceCopy(canon2, canon.string) # because `canon` may be shallow
+  var canon2 = canon.string
   canon2.canonicalCase
 
   if conf.m.filenameToIndexTbl.hasKey(canon2):
@@ -119,17 +118,23 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool
   else:
     isKnownFile = false
     result = conf.m.fileInfos.len.FileIndex
-    #echo "ID ", result.int, " ", canon2
     conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename
                                             else: relativeTo(canon, conf.projectPath)))
     conf.m.filenameToIndexTbl[canon2] = result
 
 proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex =
-  var dummy: bool
+  var dummy: bool = false
   result = fileInfoIdx(conf, filename, dummy)
 
+proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex =
+  fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile)
+
+proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex =
+  var dummy: bool = false
+  fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy)
+
 proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo =
-  result.fileIndex = fileInfoIdx
+  result = TLineInfo(fileIndex: fileInfoIdx)
   if line < int high(uint16):
     result.line = uint16(line)
   else:
@@ -219,7 +224,7 @@ proc setDirtyFile*(conf: ConfigRef; fileIdx: FileIndex; filename: AbsoluteFile)
 
 proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
   assert fileIdx.int32 >= 0
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     conf.m.fileInfos[fileIdx.int32].hash = hash
   else:
     shallowCopy(conf.m.fileInfos[fileIdx.int32].hash, hash)
@@ -227,7 +232,7 @@ proc setHash*(conf: ConfigRef; fileIdx: FileIndex; hash: string) =
 
 proc getHash*(conf: ConfigRef; fileIdx: FileIndex): string =
   assert fileIdx.int32 >= 0
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     result = conf.m.fileInfos[fileIdx.int32].hash
   else:
     shallowCopy(result, conf.m.fileInfos[fileIdx.int32].hash)
@@ -287,9 +292,11 @@ proc toColumn*(info: TLineInfo): int {.inline.} =
   result = info.col
 
 proc toFileLineCol(info: InstantiationInfo): string {.inline.} =
+  result = ""
   result.toLocation(info.filename, info.line, info.column + ColOffset)
 
 proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} =
+  result = ""
   result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset)
 
 proc `$`*(conf: ConfigRef; info: TLineInfo): string = toFileLineCol(conf, info)
@@ -401,7 +408,7 @@ proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) %
 type TErrorHandling* = enum doNothing, doAbort, doRaise
 
 proc log*(s: string) =
-  var f: File
+  var f: File = default(File)
   if open(f, getHomeDir() / "nimsuggest.log", fmAppend):
     f.writeLine(s)
     close(f)
@@ -422,7 +429,8 @@ To create a stacktrace, rerun compilation with './koch temp $1 <file>', see $2 f
 proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string, ignoreMsg: bool) =
   if msg in fatalMsgs:
     if conf.cmd == cmdIdeTools: log(s)
-    quit(conf, msg)
+    if conf.cmd != cmdIdeTools or msg != errFatal:
+      quit(conf, msg)
   if msg >= errMin and msg <= errMax or
       (msg in warnMin..hintMax and msg in conf.warningAsErrors and not ignoreMsg):
     inc(conf.errorCounter)
@@ -430,7 +438,11 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string,
     if conf.errorCounter >= conf.errorMax:
       # only really quit when we're not in the new 'nim check --def' mode:
       if conf.ideCmd == ideNone:
-        quit(conf, msg)
+        when defined(nimsuggest):
+          #we need to inform the user that something went wrong when initializing NimSuggest
+          raiseRecoverableError(s)
+        else:
+          quit(conf, msg)
     elif eh == doAbort and conf.cmd != cmdIdeTools:
       quit(conf, msg)
     elif eh == doRaise:
@@ -495,6 +507,8 @@ proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string =
     result = "\n" & indent & $sourceLine(conf, info)
     if info.col >= 0:
       result.add "\n" & indent & spaces(info.col) & '^'
+  else:
+    result = ""
 
 proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string =
   let title = case msg
@@ -504,7 +518,8 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s
   conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg)
 
 proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
-               eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} =
+               eh: TErrorHandling, info2: InstantiationInfo, isRaw = false,
+               ignoreError = false) {.gcsafe, noinline.} =
   var
     title: string
     color: ForegroundColor
@@ -534,19 +549,21 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
     ignoreMsg = not conf.hasWarn(msg)
     if not ignoreMsg and msg in conf.warningAsErrors:
       title = ErrorTitle
+      color = ErrorColor
     else:
       title = WarningTitle
+      color = WarningColor
     if not ignoreMsg: writeContext(conf, info)
-    color = WarningColor
     inc(conf.warnCounter)
   of hintMin..hintMax:
     sev = Severity.Hint
     ignoreMsg = not conf.hasHint(msg)
     if not ignoreMsg and msg in conf.warningAsErrors:
       title = ErrorTitle
+      color = ErrorColor
     else:
       title = HintTitle
-    color = HintColor
+      color = HintColor
     inc(conf.hintCounter)
 
   let s = if isRaw: arg else: getMessageStr(msg, arg)
@@ -568,7 +585,8 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
             " compiler msg initiated here", KindColor,
             KindFormat % $hintMsgOrigin,
             resetStyle, conf.unitSep)
-  handleError(conf, msg, eh, s, ignoreMsg)
+  if not ignoreError:
+    handleError(conf, msg, eh, s, ignoreMsg)
   if msg in fatalMsgs:
     # most likely would have died here but just in case, we restore state
     conf.m.errorOutputs = errorOutputsOld
@@ -611,7 +629,7 @@ proc warningDeprecated*(conf: ConfigRef, info: TLineInfo = gCmdLineInfo, msg = "
   message(conf, info, warnDeprecated, msg)
 
 proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) =
-  if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return
+  if conf.cmd in {cmdIdeTools, cmdCheck} and conf.structuredErrorHook.isNil: return
   writeContext(conf, info)
   liMessage(conf, info, errInternal, errMsg, doAbort, info2)
 
@@ -633,13 +651,16 @@ template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraM
   let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName
   liMessage(conf, info, msg, m, doNothing, instLoc())
 
-proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
-  if i.fileIndex.int32 < 0:
+proc quotedFilename*(conf: ConfigRef; fi: FileIndex): Rope =
+  if fi.int32 < 0:
     result = makeCString "???"
   elif optExcessiveStackTrace in conf.globalOptions:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedFullName
+    result = conf.m.fileInfos[fi.int32].quotedFullName
   else:
-    result = conf.m.fileInfos[i.fileIndex.int32].quotedName
+    result = conf.m.fileInfos[fi.int32].quotedName
+
+proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope =
+  quotedFilename(conf, i.fileIndex)
 
 template listMsg(title, r) =
   msgWriteln(conf, title, {msgNoUnitSep})
@@ -648,31 +669,6 @@ template listMsg(title, r) =
 proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax)
 proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax)
 
-proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string =
-  ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'}
-  ## so that it is useful as a C identifier snippet.
-  let path = AbsoluteFile toFullPath(conf, fid)
-  let rel =
-    if path.string.startsWith(conf.libpath.string):
-      relativeTo(path, conf.libpath).string
-    else:
-      relativeTo(path, conf.projectPath).string
-  let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len
-  result = newStringOfCap(trunc)
-  for i in 0..<trunc:
-    let c = rel[i]
-    case c
-    of 'a'..'z':
-      result.add c
-    of {os.DirSep, os.AltSep}:
-      result.add 'Z' # because it looks a bit like '/'
-    of '.':
-      result.add 'O' # a circle
-    else:
-      # We mangle upper letters and digits too so that there cannot
-      # be clashes with our special meanings of 'Z' and 'O'
-      result.addInt ord(c)
-
 proc genSuccessX*(conf: ConfigRef) =
   let mem =
     when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem"
@@ -711,6 +707,8 @@ proc genSuccessX*(conf: ConfigRef) =
   elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends:
     # for some cmd we expect a valid absOutFile
     output = "unknownOutput"
+  elif optStdout in conf.globalOptions:
+    output = "stdout"
   else:
     output = $conf.absOutFile
   if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename
diff --git a/compiler/ndi.nim b/compiler/ndi.nim
index a9d9cfe79..cc18ab39f 100644
--- a/compiler/ndi.nim
+++ b/compiler/ndi.nim
@@ -29,7 +29,7 @@ proc doWrite(f: var NdiFile; s: PSym; conf: ConfigRef) =
   f.buf.add "\t"
   f.buf.addInt s.info.col.int
   f.f.write(s.name.s, "\t")
-  f.f.writeRope(s.loc.r)
+  f.f.writeRope(s.loc.snippet)
   f.f.writeLine("\t", toFullPath(conf, s.info), "\t", f.buf)
 
 template writeMangledName*(f: NdiFile; s: PSym; conf: ConfigRef) =
diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim
index 49ceb8942..7e0efc34b 100644
--- a/compiler/nilcheck.nim
+++ b/compiler/nilcheck.nim
@@ -7,8 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes
-import sequtils, strutils, sets
+import ast, renderer, msgs, options, lineinfos, idents, treetab
+import std/[intsets, tables, sequtils, strutils, sets, strformat, hashes]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -309,6 +309,7 @@ proc symbol(n: PNode): Symbol =
   # echo "symbol ", n, " ", n.kind, " ", result.int
 
 func `$`(map: NilMap): string =
+  result = ""
   var now = map
   var stack: seq[NilMap] = @[]
   while not now.isNil:
@@ -416,7 +417,7 @@ proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) =
   if targetSetIndex != noSetIndex:
     var targetSet = map.sets[targetSetIndex]
     if targetSet.len > 1:
-      var other: ExprIndex
+      var other: ExprIndex = default(ExprIndex)
 
       for element in targetSet:
         if element.ExprIndex != targetIndex:
@@ -497,7 +498,7 @@ proc checkCall(n, ctx, map): Check =
   # check args and handle possible mutations
 
   var isNew = false
-  result.map = map
+  result = Check(map: map)
   for i, child in n:
     discard check(child, ctx, map)
 
@@ -506,7 +507,7 @@ proc checkCall(n, ctx, map): Check =
       # as it might have been mutated
       # TODO similar for normal refs and fields: find dependent exprs: brackets
 
-      if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef:
+      if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ.elementType.kind == tyRef:
         if not isNew:
           result.map = newNilMap(map)
           isNew = true
@@ -561,7 +562,7 @@ proc derefWarning(n, ctx, map; kind: Nilability) =
   if n.info in ctx.warningLocations:
     return
   ctx.warningLocations.incl(n.info)
-  var a: seq[History]
+  var a: seq[History] = @[]
   if n.kind == nkSym:
     a = history(map, ctx.index(n))
   var res = ""
@@ -752,6 +753,7 @@ proc checkReturn(n, ctx, map): Check =
 
 proc checkIf(n, ctx, map): Check =
   ## check branches based on condition
+  result = default(Check)
   var mapIf: NilMap = map
 
   # first visit the condition
@@ -765,7 +767,7 @@ proc checkIf(n, ctx, map): Check =
   # the state of the conditions: negating conditions before the current one
   var layerHistory = newNilMap(mapIf)
   # the state after branch effects
-  var afterLayer: NilMap
+  var afterLayer: NilMap = nil
   # the result nilability for expressions
   var nilability = Safe
 
@@ -824,7 +826,7 @@ proc checkFor(n, ctx, map): Check =
   var check2 = check(n.sons[2], ctx, m)
   var map2 = check2.map
 
-  result.map = ctx.union(map0, m)
+  result = Check(map: ctx.union(map0, m))
   result.map = ctx.union(result.map, map2)
   result.nilability = Safe
 
@@ -852,7 +854,7 @@ proc checkWhile(n, ctx, map): Check =
   var check2 = check(n.sons[1], ctx, m)
   var map2 = check2.map
 
-  result.map = ctx.union(map0, map1)
+  result = Check(map: ctx.union(map0, map1))
   result.map = ctx.union(result.map, map2)
   result.nilability = Safe
 
@@ -862,9 +864,10 @@ proc checkInfix(n, ctx, map): Check =
   ##   a or b : map is an union of a and b's
   ##   a == b : use checkCondition
   ##   else: no change, just check args
+  result = default(Check)
   if n[0].kind == nkSym:
-    var mapL: NilMap
-    var mapR: NilMap
+    var mapL: NilMap = nil
+    var mapR: NilMap = nil
     if n[0].sym.magic notin {mAnd, mEqRef}:
       mapL = checkCondition(n[1], ctx, map, false, false)
       mapR = checkCondition(n[2], ctx, map, false, false)
@@ -897,7 +900,7 @@ proc checkInfix(n, ctx, map): Check =
 proc checkIsNil(n, ctx, map; isElse: bool = false): Check =
   ## check isNil calls
   ## update the map depending on if it is not isNil or isNil
-  result.map = newNilMap(map)
+  result = Check(map: newNilMap(map))
   let value = n[1]
   result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n)
 
@@ -909,24 +912,24 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode =
     else: ""
 
   var cache = newIdentCache()
-  var op = newSym(skVar, cache.getIdent(name), nextSymId ctx.idgen, nil, r.info)
+  var op = newSym(skVar, cache.getIdent(name), ctx.idgen, nil, r.info)
 
   op.magic = magic
   result = nkInfix.newTree(
     newSymNode(op, r.info),
     l,
     r)
-  result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
+  result.typ = newType(tyBool, ctx.idgen, nil)
 
 proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode =
   var cache = newIdentCache()
-  var op = newSym(skVar, cache.getIdent("not"), nextSymId ctx.idgen, nil, node.info)
+  var op = newSym(skVar, cache.getIdent("not"), ctx.idgen, nil, node.info)
 
   op.magic = mNot
   result = nkPrefix.newTree(
     newSymNode(op, node.info),
     node)
-  result.typ = newType(tyBool, nextTypeId ctx.idgen, nil)
+  result.typ = newType(tyBool, ctx.idgen, nil)
 
 proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode =
   infix(ctx, l, r, mEqRef)
@@ -945,9 +948,9 @@ proc checkCase(n, ctx, map): Check =
   #   c2
   # also a == true is a , a == false is not a
   let base = n[0]
-  result.map = map.copyMap()
+  result = Check(map: map.copyMap())
   result.nilability = Safe
-  var a: PNode
+  var a: PNode = nil
   for child in n:
     case child.kind:
     of nkOfBranch:
@@ -1217,12 +1220,12 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
     result = check(n.sons[1], ctx, map)
   of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange,
      nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse:
-    result.map = map
+    result = Check(map: map)
     if n.kind in {nkObjConstr, nkTupleConstr}:
       # TODO deeper nested elements?
       # A(field: B()) #
       # field: Safe ->
-      var elements: seq[(PNode, Nilability)]
+      var elements: seq[(PNode, Nilability)] = @[]
       for i, child in n:
         result = check(child, ctx, result.map)
         if i > 0:
@@ -1242,12 +1245,12 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
     result = check(n.sons[0], ctx, map)
   of nkIfStmt, nkIfExpr:
     result = checkIf(n, ctx, map)
-  of nkAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     result = checkAsgn(n[0], n[1], ctx, map)
-  of nkVarSection:
-    result.map = map
+  of nkVarSection, nkLetSection:
+    result = Check(map: map)
     for child in n:
-      result = checkAsgn(child[0], child[2], ctx, result.map)
+      result = checkAsgn(child[0].skipPragmaExpr, child[2], ctx, result.map)
   of nkForStmt:
     result = checkFor(n, ctx, map)
   of nkCaseStmt:
@@ -1271,8 +1274,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check =
   else:
 
     var elementMap = map.copyMap()
-    var elementCheck: Check
-    elementCheck.map = elementMap
+    var elementCheck = Check(map: elementMap)
     for element in n:
       elementCheck = check(element, ctx, elementCheck.map)
 
@@ -1333,7 +1335,7 @@ proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) =
   ctx.symbolIndices = {resultId: resultExprIndex}.toTable()
   var cache = newIdentCache()
   ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)])
-  var emptySet: IntSet # set[ExprIndex]
+  var emptySet: IntSet = initIntSet() # set[ExprIndex]
   ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet])
   for i, arg in s.typ.n.sons:
     if i > 0:
@@ -1365,7 +1367,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
         continue
       map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child)
 
-  map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info)
+  map.store(context, resultExprIndex, if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef: Nil else: Safe, TResult, s.ast.info)
 
   # echo "checking ", s.name.s, " ", filename
 
@@ -1381,5 +1383,5 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) =
   # (ANotNil, BNotNil) :
   # do we check on asgn nilability at all?
 
-  if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags:
+  if not s.typ.returnType.isNil and s.typ.returnType.kind == tyRef and tfNotNil in s.typ.returnType.flags:
     checkResult(s.ast, context, res.map)
diff --git a/compiler/nim.cfg b/compiler/nim.cfg
index 5e8471f70..ce5a22ad2 100644
--- a/compiler/nim.cfg
+++ b/compiler/nim.cfg
@@ -6,13 +6,16 @@ define:booting
 define:nimcore
 define:nimPreviewFloatRoundtrip
 define:nimPreviewSlimSystem
-gc:refc
+define:nimPreviewCstringConversion
+define:nimPreviewProcConversion
+define:nimPreviewRangeDefault
+define:nimPreviewNonVarDestructor
+threads:off
 
 #import:"$projectpath/testability"
 
 @if windows:
   cincludes: "$lib/wrappers/libffi/common"
-  tlsEmulation:off
 @end
 
 define:useStdoutAsStdmsg
@@ -26,10 +29,35 @@ define:useStdoutAsStdmsg
 #gc:markAndSweep
 
 @if nimHasWarningObservableStores:
-  warning[ObservableStores]: off
+  warning[ObservableStores]:off
 @end
 
-@if nimHasEffectsOf:
-  experimental:strictEffects
-  warningAsError:Effect:on
+
+@if nimHasWarningAsError:
+  warningAsError[GcUnsafe2]:on
+@end
+
+@if nimHasWarnUnnamedBreak:
+  warningAserror[UnnamedBreak]:on
+@end
+
+@if nimHasWarnBareExcept:
+  warning[BareExcept]:on
+  warningAserror[BareExcept]:on
+@end
+
+
+@if nimUseStrictDefs:
+  experimental:strictDefs
+  warningAsError[Uninit]:on
+  warningAsError[ProveInit]:on
+@end
+
+@if nimHasWarnStdPrefix:
+  warning[StdPrefix]:on
+  warningAsError[StdPrefix]:on
+@end
+
+@if nimHasVtables:
+  experimental:vtables
 @end
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 2a6b3bc1b..005f11a58 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -12,10 +12,7 @@ import std/[os, strutils, parseopt]
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-when defined(windows) and not defined(nimKochBootstrap):
-  # remove workaround pending bootstrap >= 1.5.1
-  # refs https://github.com/nim-lang/Nim/issues/18334#issuecomment-867114536
-  # alternative would be to prepend `currentSourcePath.parentDir.quoteShell`
+when defined(windows):
   when defined(gcc):
     when defined(x86):
       {.link: "../icons/nim.res".}
@@ -31,7 +28,7 @@ import
   commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper,
   pathutils, modulegraphs
 
-from browsers import openDefaultBrowser
+from std/browsers import openDefaultBrowser
 from nodejs import findNodeJs
 
 when hasTinyCBackend:
@@ -41,6 +38,15 @@ when defined(profiler) or defined(memProfiler):
   {.hint: "Profiling support is turned on!".}
   import nimprof
 
+proc nimbleLockExists(config: ConfigRef): bool =
+  const nimbleLock = "nimble.lock"
+  let pd = if not config.projectPath.isEmpty: config.projectPath else: AbsoluteDir(getCurrentDir())
+  if optSkipParentConfigFiles notin config.globalOptions:
+    for dir in parentDirs(pd.string, fromRoot=true, inclusive=false):
+      if fileExists(dir / nimbleLock):
+        return true
+  return fileExists(pd.string / nimbleLock)
+
 proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
   var p = parseopt.initOptParser(cmd)
   var argsCount = 0
@@ -74,12 +80,20 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) =
         config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}:
       rawMessage(config, errGenerated, errArgsNeedRunOption)
 
+  if config.nimbleLockExists:
+    # disable nimble path if nimble.lock is present.
+    # see https://github.com/nim-lang/nimble/issues/1004
+    disableNimblePath(config)
+
 proc getNimRunExe(conf: ConfigRef): string =
   # xxx consider defining `conf.getConfigVar("nimrun.exe")` to allow users to
   # customize the binary to run the command with, e.g. for custom `nodejs` or `wine`.
   if conf.isDefined("mingw"):
     if conf.isDefined("i386"): result = "wine"
     elif conf.isDefined("amd64"): result = "wine64"
+    else: result = ""
+  else:
+    result = ""
 
 proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   let self = NimProg(
@@ -97,8 +111,14 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
   if not self.loadConfigsAndProcessCmdLine(cache, conf, graph):
     return
 
+  if conf.cmd == cmdCheck and optWasNimscript notin conf.globalOptions and
+       conf.backend == backendInvalid:
+    conf.backend = backendC
+
   if conf.selectedGC == gcUnselected:
-    if conf.backend in {backendC, backendCpp, backendObjc}:
+    if conf.backend in {backendC, backendCpp, backendObjc} or
+        (conf.cmd in cmdDocLike and conf.backend != backendJs) or
+        conf.cmd == cmdGendepend:
       initOrcDefines(conf)
 
   mainCommand(graph)
@@ -113,7 +133,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
     case conf.cmd
     of cmdBackends, cmdTcc:
       let nimRunExe = getNimRunExe(conf)
-      var cmdPrefix: string
+      var cmdPrefix = ""
       if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell
       case conf.backend
       of backendC, backendCpp, backendObjc: discard
@@ -122,7 +142,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
         # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode
         if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell
         cmdPrefix.add " --unhandled-rejections=strict"
-      else: doAssert false, $conf.backend
+      else: raiseAssert $conf.backend
       if cmdPrefix.len > 0: cmdPrefix.add " "
         # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic:
         # `The parameter is incorrect`
diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim
index b6b08ccd4..a5324ea76 100644
--- a/compiler/nimblecmd.nim
+++ b/compiler/nimblecmd.nim
@@ -9,12 +9,15 @@
 
 ## Implements some helper procs for Nimble (Nim's package manager) support.
 
-import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils,
-  std/sha1, tables
+import options, msgs, lineinfos, pathutils
+
+import std/[parseutils, strutils, os, tables, sequtils]
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
 
+import ../dist/checksums/src/checksums/sha1
+
 proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) =
   if not conf.searchPaths.contains(path):
     conf.searchPaths.insert(path, 0)
@@ -35,11 +38,16 @@ proc isSpecial(ver: Version): bool =
 
 proc isValidVersion(v: string): bool =
   if v.len > 0:
-    if v[0] in {'#'} + Digits: return true
+    if v[0] in {'#'} + Digits:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc `<`*(ver: Version, ver2: Version): bool =
   ## This is synced from Nimble's version module.
-
+  result = false
   # Handling for special versions such as "#head" or "#branch".
   if ver.isSpecial or ver2.isSpecial:
     if ver2.isSpecial and ($ver2).normalize == "#head":
@@ -71,6 +79,8 @@ proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string]
   ## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into
   ## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")``
 
+  result = ("", "", "")
+
   const checksumSeparator = '-'
   const versionSeparator = '-'
   const specialVersionSepartator = "-#"
@@ -143,7 +153,7 @@ proc addNimblePath(conf: ConfigRef; p: string, info: TLineInfo) =
     conf.lazyPaths.insert(AbsoluteDir path, 0)
 
 proc addPathRec(conf: ConfigRef; dir: string, info: TLineInfo) =
-  var packages: PackageInfo
+  var packages: PackageInfo = initTable[string, tuple[version, checksum: string]]()
   var pos = dir.len-1
   if dir[pos] in {DirSep, AltSep}: inc(pos)
   for k,p in os.walkDir(dir):
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index fceedb2c4..5417cd1e9 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -10,8 +10,10 @@
 # This module handles the reading of the config file.
 
 import
-  llstream, commands, os, strutils, msgs, lexer, ast,
-  options, idents, wordrecg, strtabs, lineinfos, pathutils, scriptconfig
+  llstream, commands, msgs, lexer, ast,
+  options, idents, wordrecg, lineinfos, pathutils, scriptconfig
+
+import std/[os, strutils, strtabs]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -162,7 +164,7 @@ proc checkSymbol(L: Lexer, tok: Token) =
     lexMessage(L, errGenerated, "expected identifier, but got: " & $tok)
 
 proc parseAssignment(L: var Lexer, tok: var Token;
-                     config: ConfigRef; condStack: var seq[bool]) =
+                     config: ConfigRef; filename: AbsoluteFile; condStack: var seq[bool]) =
   if tok.ident != nil:
     if tok.ident.s == "-" or tok.ident.s == "--":
       confTok(L, tok, config, condStack)           # skip unnecessary prefix
@@ -205,6 +207,7 @@ proc parseAssignment(L: var Lexer, tok: var Token;
       checkSymbol(L, tok)
       val.add($tok)
       confTok(L, tok, config, condStack)
+  config.currentConfigDir = parentDir(filename.string)
   if percent:
     processSwitch(s, strtabs.`%`(val, config.configVars,
                                 {useEnvironment, useEmpty}), passPP, info, config)
@@ -214,20 +217,21 @@ proc parseAssignment(L: var Lexer, tok: var Token;
 proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache;
                     config: ConfigRef): bool =
   var
-    L: Lexer
+    L: Lexer = default(Lexer)
     tok: Token
     stream: PLLStream
   stream = llStreamOpen(filename, fmRead)
   if stream != nil:
-    initToken(tok)
     openLexer(L, filename, stream, cache, config)
-    tok.tokType = tkEof       # to avoid a pointless warning
+    tok = Token(tokType: tkEof)       # to avoid a pointless warning
     var condStack: seq[bool] = @[]
     confTok(L, tok, config, condStack)           # read in the first token
-    while tok.tokType != tkEof: parseAssignment(L, tok, config, condStack)
+    while tok.tokType != tkEof: parseAssignment(L, tok, config, filename, condStack)
     if condStack.len > 0: lexMessage(L, errGenerated, "expected @end")
     closeLexer(L)
     return true
+  else:
+    result = false
 
 proc getUserConfigPath*(filename: RelativeFile): AbsoluteFile =
   result = getConfigDir().AbsoluteDir / RelativeDir"nim" / filename
@@ -250,7 +254,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen:
 
   template runNimScriptIfExists(path: AbsoluteFile, isMain = false) =
     let p = path # eval once
-    var s: PLLStream
+    var s: PLLStream = nil
     if isMain and optWasNimscript in conf.globalOptions:
       if conf.projectIsStdin: s = stdin.llStreamOpen
       elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput)
diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim
index 82e2f0812..0833cfeb3 100644
--- a/compiler/nimeval.nim
+++ b/compiler/nimeval.nim
@@ -9,10 +9,16 @@
 
 ## exposes the Nim VM to clients.
 import
-  ast, astalgo, modules, passes, condsyms,
-  options, sem, llstream, lineinfos, vm,
-  vmdef, modulegraphs, idents, os, pathutils,
-  passaux, scriptconfig, std/compilesettings
+  ast, modules, condsyms,
+  options, llstream, lineinfos, vm,
+  vmdef, modulegraphs, idents, pathutils,
+  scriptconfig, std/[compilesettings, tables, os]
+
+import pipelines
+
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 type
   Interpreter* = ref object ## Use Nim as an interpreter with this object
@@ -34,7 +40,7 @@ proc selectUniqueSymbol*(i: Interpreter; name: string;
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
   let n = getIdent(i.graph.cache, name)
-  var it: ModuleIter
+  var it: ModuleIter = default(ModuleIter)
   var s = initModuleIter(it, i.graph, i.mainModule, n)
   result = nil
   while s != nil:
@@ -72,11 +78,14 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) =
   assert i != nil
   assert i.mainModule != nil, "no main module selected"
   initStrTables(i.graph, i.mainModule)
+  i.graph.cacheSeqs.clear()
+  i.graph.cacheCounters.clear()
+  i.graph.cacheTables.clear()
   i.mainModule.ast = nil
 
   let s = if scriptStream != nil: scriptStream
           else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead)
-  processModule(i.graph, i.mainModule, i.idgen, s)
+  discard processPipelineModule(i.graph, i.mainModule, i.idgen, s)
 
 proc findNimStdLib*(): string =
   ## Tries to find a path to a valid "system.nim" file.
@@ -109,12 +118,10 @@ proc createInterpreter*(scriptName: string;
   var conf = newConfigRef()
   var cache = newIdentCache()
   var graph = newModuleGraph(cache, conf)
-  connectCallbacks(graph)
+  connectPipelineCallbacks(graph)
   initDefines(conf.symbols)
   for define in defines:
     defineSymbol(conf.symbols, define[0], define[1])
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
 
   for p in searchPaths:
     conf.searchPaths.add(AbsoluteDir p)
@@ -129,7 +136,8 @@ proc createInterpreter*(scriptName: string;
   if registerOps:
     vm.registerAdditionalOps() # Required to register parts of stdlib modules
   graph.vm = vm
-  graph.compileSystemModule()
+  setPipeLinePass(graph, EvalPass)
+  graph.compilePipelineSystemModule()
   result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen)
 
 proc destroyInterpreter*(i: Interpreter) =
@@ -159,13 +167,11 @@ proc runRepl*(r: TLLRepl;
   defineSymbol(conf.symbols, "nimscript")
   if supportNimscript: defineSymbol(conf.symbols, "nimconfig")
   when hasFFI: defineSymbol(graph.config.symbols, "nimffi")
-  registerPass(graph, verbosePass)
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
   var m = graph.makeStdinModule()
   incl(m.flags, sfMainModule)
   var idgen = idGeneratorFromModule(m)
 
   if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen)
-  graph.compileSystemModule()
-  processModule(graph, m, idgen, llStreamOpenStdIn(r))
+  setPipeLinePass(graph, InterpreterPass)
+  graph.compilePipelineSystemModule()
+  discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r))
diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim
deleted file mode 100644
index 30c138f79..000000000
--- a/compiler/nimfix/nimfix.nim
+++ /dev/null
@@ -1,111 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Nimfix is a tool that helps to convert old-style Nimrod code to Nim code.
-
-import strutils, os, parseopt
-import compiler/[options, commands, modules, sem,
-  passes, passaux, linter,
-  msgs, nimconf,
-  extccomp, condsyms,
-  modulegraphs, idents]
-
-const Usage = """
-Nimfix - Tool to patch Nim code
-Usage:
-  nimfix [options] projectfile.nim
-
-Options:
-  --overwriteFiles:on|off          overwrite the original nim files.
-                                   DEFAULT is ON!
-  --wholeProject                   overwrite every processed file.
-  --checkExtern:on|off             style check also extern names
-  --styleCheck:on|off|auto         performs style checking for identifiers
-                                   and suggests an alternative spelling;
-                                   'auto' corrects the spelling.
-  --bestEffort                     try to fix the code even when there
-                                   are errors.
-
-In addition, all command line options of Nim are supported.
-"""
-
-proc mainCommand =
-  registerPass verbosePass
-  registerPass semPass
-  conf.setCmd cmdNimfix
-  searchPaths.add options.libpath
-  if gProjectFull.len != 0:
-    # current path is always looked first for modules
-    searchPaths.insert(gProjectPath, 0)
-
-  compileProject(newModuleGraph(), newIdentCache())
-  pretty.overwriteFiles()
-
-proc processCmdLine*(pass: TCmdLinePass, cmd: string, config: ConfigRef) =
-  var p = parseopt.initOptParser(cmd)
-  var argsCount = 0
-  gOnlyMainfile = true
-  while true:
-    parseopt.next(p)
-    case p.kind
-    of cmdEnd: break
-    of cmdLongoption, cmdShortOption:
-      case p.key.normalize
-      of "overwritefiles":
-        case p.val.normalize
-        of "on": gOverWrite = true
-        of "off": gOverWrite = false
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "checkextern":
-        case p.val.normalize
-        of "on": gCheckExtern = true
-        of "off": gCheckExtern = false
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "stylecheck":
-        case p.val.normalize
-        of "off": gStyleCheck = StyleCheck.None
-        of "on": gStyleCheck = StyleCheck.Warn
-        of "auto": gStyleCheck = StyleCheck.Auto
-        else: localError(gCmdLineInfo, errOnOrOffExpected)
-      of "wholeproject": gOnlyMainfile = false
-      of "besteffort": msgs.gErrorMax = high(int) # don't stop after first error
-      else:
-        processSwitch(pass, p, config)
-    of cmdArgument:
-      options.gProjectName = unixToNativePath(p.key)
-      # if processArgument(pass, p, argsCount): break
-
-proc handleCmdLine(config: ConfigRef) =
-  if paramCount() == 0:
-    stdout.writeLine(Usage)
-  else:
-    processCmdLine(passCmd1, "", config)
-    if gProjectName != "":
-      try:
-        gProjectFull = canonicalizePath(gProjectName)
-      except OSError:
-        gProjectFull = gProjectName
-      var p = splitFile(gProjectFull)
-      gProjectPath = p.dir
-      gProjectName = p.name
-    else:
-      gProjectPath = getCurrentDir()
-    loadConfigs(DefaultConfig, config) # load all config files
-    # now process command line arguments again, because some options in the
-    # command line can overwrite the config file's settings
-    extccomp.initVars()
-    processCmdLine(passCmd2, "", config)
-    mainCommand()
-
-when compileOption("gc", "refc"):
-  GC_disableMarkAndSweep()
-
-condsyms.initDefines()
-defineSymbol "nimfix"
-handleCmdline newConfigRef()
diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg
deleted file mode 100644
index 0d9dbfa4b..000000000
--- a/compiler/nimfix/nimfix.nim.cfg
+++ /dev/null
@@ -1,17 +0,0 @@
-# Special configuration file for the Nim project
-# gc:markAndSweep
-
-hint[XDeclaredButNotUsed]:off
-path:"$projectPath/.."
-
-path:"$lib/packages/docutils"
-path:"$nim"
-
-define:useStdoutAsStdmsg
-symbol:nimfix
-define:nimfix
-
-cs:partial
-#define:useNodeIds
-define:booting
-define:noDocgen
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
deleted file mode 100644
index 78c24bae3..000000000
--- a/compiler/nimfix/prettybase.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-#
-#           The Nim Compiler
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-import strutils except Letters
-import ".." / [ast, msgs, lineinfos, idents, options, linter]
-
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
-  let line = sourceLine(conf, info)
-  var first = min(info.col.int, line.len)
-  if first < 0: return
-  #inc first, skipIgnoreCase(line, "proc ", first)
-  while first > 0 and line[first-1] in Letters: dec first
-  if first < 0: return
-  if line[first] == '`': inc first
-
-  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)
-    when defined(gcArc) or defined(gcOrc):
-      conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
-    else:
-      system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
-    conf.m.fileInfos[info.fileIndex.int32].dirty = true
-    #if newSym.s == "File": writeStackTrace()
-
-proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PSym) =
-  replaceDeprecated(conf, info, oldSym.name, newSym.name)
-
-proc replaceComment*(conf: ConfigRef; info: TLineInfo) =
-  let line = sourceLine(conf, info)
-  var first = info.col.int
-  if line[first] != '#': inc first
-
-  var x = line.substr(0, first-1) & "discard " & line.substr(first+1).escape
-  when defined(gcArc) or defined(gcOrc):
-    conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1] = move x
-  else:
-    system.shallowCopy(conf.m.fileInfos[info.fileIndex.int32].lines[info.line.int-1], x)
-  conf.m.fileInfos[info.fileIndex.int32].dirty = true
diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim
index 3bc9af9c9..6708b57f8 100644
--- a/compiler/nimlexbase.nim
+++ b/compiler/nimlexbase.nim
@@ -12,8 +12,9 @@
 # handling that exists! Only at line endings checks are necessary
 # if the buffer needs refilling.
 
-import
-  llstream, strutils
+import llstream
+
+import std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim
index 3756f956b..0a66c3c1f 100644
--- a/compiler/nimpaths.nim
+++ b/compiler/nimpaths.nim
@@ -17,7 +17,7 @@ interpolation variables:
 Unstable API
 ]##
 
-import os, strutils
+import std/[os, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index 49c80065a..7edf55278 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -62,8 +62,10 @@ proc someInSet*(s: PNode, a, b: PNode): bool =
   result = false
 
 proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet =
-  var first, j: Int128
-  first = firstOrd(conf, s.typ[0])
+  result = @[]
+  var first: Int128 = Zero
+  var j: Int128 = Zero
+  first = firstOrd(conf, s.typ.elementType)
   bitSetInit(result, int(getSize(conf, s.typ)))
   for i in 0..<s.len:
     if s[i].kind == nkRange:
diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim
index 283643e8d..9753e1c99 100644
--- a/compiler/nodejs.nim
+++ b/compiler/nodejs.nim
@@ -1,4 +1,4 @@
-import os
+import std/os
 
 proc findNodeJs*(): string {.inline.} =
   ## Find NodeJS executable and return it as a string.
@@ -7,4 +7,4 @@ proc findNodeJs*(): string {.inline.} =
     result = findExe("node")
   if result.len == 0:
     echo "Please install NodeJS first, see https://nodejs.org/en/download"
-    raise newException(IOError, "NodeJS not found in PATH: " & result)
+    raise newException(IOError, "NodeJS not found in PATH")
diff --git a/compiler/nodekinds.nim b/compiler/nodekinds.nim
new file mode 100644
index 000000000..ccdbbd26d
--- /dev/null
+++ b/compiler/nodekinds.nim
@@ -0,0 +1,211 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## NodeKind enum.
+
+type
+  TNodeKind* = enum # order is extremely important, because ranges are used
+                    # to check whether a node belongs to a certain class
+    nkNone,               # unknown node kind: indicates an error
+                          # Expressions:
+                          # Atoms:
+    nkEmpty,              # the node is empty
+    nkIdent,              # node is an identifier
+    nkSym,                # node is a symbol
+    nkType,               # node is used for its typ field
+
+    nkCharLit,            # a character literal ''
+    nkIntLit,             # an integer literal
+    nkInt8Lit,
+    nkInt16Lit,
+    nkInt32Lit,
+    nkInt64Lit,
+    nkUIntLit,            # an unsigned integer literal
+    nkUInt8Lit,
+    nkUInt16Lit,
+    nkUInt32Lit,
+    nkUInt64Lit,
+    nkFloatLit,           # a floating point literal
+    nkFloat32Lit,
+    nkFloat64Lit,
+    nkFloat128Lit,
+    nkStrLit,             # a string literal ""
+    nkRStrLit,            # a raw string literal r""
+    nkTripleStrLit,       # a triple string literal """
+    nkNilLit,             # the nil literal
+                          # end of atoms
+    nkComesFrom,          # "comes from" template/macro information for
+                          # better stack trace generation
+    nkDotCall,            # used to temporarily flag a nkCall node;
+                          # this is used
+                          # for transforming ``s.len`` to ``len(s)``
+
+    nkCommand,            # a call like ``p 2, 4`` without parenthesis
+    nkCall,               # a call like p(x, y) or an operation like +(a, b)
+    nkCallStrLit,         # a call with a string literal
+                          # x"abc" has two sons: nkIdent, nkRStrLit
+                          # x"""abc""" has two sons: nkIdent, nkTripleStrLit
+    nkInfix,              # a call like (a + b)
+    nkPrefix,             # a call like !a
+    nkPostfix,            # something like a! (also used for visibility)
+    nkHiddenCallConv,     # an implicit type conversion via a type converter
+
+    nkExprEqExpr,         # a named parameter with equals: ''expr = expr''
+    nkExprColonExpr,      # a named parameter with colon: ''expr: expr''
+    nkIdentDefs,          # a definition like `a, b: typeDesc = expr`
+                          # either typeDesc or expr may be nil; used in
+                          # formal parameters, var statements, etc.
+    nkVarTuple,           # a ``var (a, b) = expr`` construct
+    nkPar,                # syntactic (); may be a tuple constructor
+    nkObjConstr,          # object constructor: T(a: 1, b: 2)
+    nkCurly,              # syntactic {}
+    nkCurlyExpr,          # an expression like a{i}
+    nkBracket,            # syntactic []
+    nkBracketExpr,        # an expression like a[i..j, k]
+    nkPragmaExpr,         # an expression like a{.pragmas.}
+    nkRange,              # an expression like i..j
+    nkDotExpr,            # a.b
+    nkCheckedFieldExpr,   # a.b, but b is a field that needs to be checked
+    nkDerefExpr,          # a^
+    nkIfExpr,             # if as an expression
+    nkElifExpr,
+    nkElseExpr,
+    nkLambda,             # lambda expression
+    nkDo,                 # lambda block appering as trailing proc param
+    nkAccQuoted,          # `a` as a node
+
+    nkTableConstr,        # a table constructor {expr: expr}
+    nkBind,               # ``bind expr`` node
+    nkClosedSymChoice,    # symbol choice node; a list of nkSyms (closed)
+    nkOpenSymChoice,      # symbol choice node; a list of nkSyms (open)
+    nkHiddenStdConv,      # an implicit standard type conversion
+    nkHiddenSubConv,      # an implicit type conversion from a subtype
+                          # to a supertype
+    nkConv,               # a type conversion
+    nkCast,               # a type cast
+    nkStaticExpr,         # a static expr
+    nkAddr,               # a addr expression
+    nkHiddenAddr,         # implicit address operator
+    nkHiddenDeref,        # implicit ^ operator
+    nkObjDownConv,        # down conversion between object types
+    nkObjUpConv,          # up conversion between object types
+    nkChckRangeF,         # range check for floats
+    nkChckRange64,        # range check for 64 bit ints
+    nkChckRange,          # range check for ints
+    nkStringToCString,    # string to cstring
+    nkCStringToString,    # cstring to string
+                          # end of expressions
+
+    nkAsgn,               # a = b
+    nkFastAsgn,           # internal node for a fast ``a = b``
+                          # (no string copy)
+    nkGenericParams,      # generic parameters
+    nkFormalParams,       # formal parameters
+    nkOfInherit,          # inherited from symbol
+
+    nkImportAs,           # a 'as' b in an import statement
+    nkProcDef,            # a proc
+    nkMethodDef,          # a method
+    nkConverterDef,       # a converter
+    nkMacroDef,           # a macro
+    nkTemplateDef,        # a template
+    nkIteratorDef,        # an iterator
+
+    nkOfBranch,           # used inside case statements
+                          # for (cond, action)-pairs
+    nkElifBranch,         # used in if statements
+    nkExceptBranch,       # an except section
+    nkElse,               # an else part
+    nkAsmStmt,            # an assembler block
+    nkPragma,             # a pragma statement
+    nkPragmaBlock,        # a pragma with a block
+    nkIfStmt,             # an if statement
+    nkWhenStmt,           # a when expression or statement
+    nkForStmt,            # a for statement
+    nkParForStmt,         # a parallel for statement
+    nkWhileStmt,          # a while statement
+    nkCaseStmt,           # a case statement
+    nkTypeSection,        # a type section (consists of type definitions)
+    nkVarSection,         # a var section
+    nkLetSection,         # a let section
+    nkConstSection,       # a const section
+    nkConstDef,           # a const definition
+    nkTypeDef,            # a type definition
+    nkYieldStmt,          # the yield statement as a tree
+    nkDefer,              # the 'defer' statement
+    nkTryStmt,            # a try statement
+    nkFinally,            # a finally section
+    nkRaiseStmt,          # a raise statement
+    nkReturnStmt,         # a return statement
+    nkBreakStmt,          # a break statement
+    nkContinueStmt,       # a continue statement
+    nkBlockStmt,          # a block statement
+    nkStaticStmt,         # a static statement
+    nkDiscardStmt,        # a discard statement
+    nkStmtList,           # a list of statements
+    nkImportStmt,         # an import statement
+    nkImportExceptStmt,   # an import x except a statement
+    nkExportStmt,         # an export statement
+    nkExportExceptStmt,   # an 'export except' statement
+    nkFromStmt,           # a from * import statement
+    nkIncludeStmt,        # an include statement
+    nkBindStmt,           # a bind statement
+    nkMixinStmt,          # a mixin statement
+    nkUsingStmt,          # an using statement
+    nkCommentStmt,        # a comment statement
+    nkStmtListExpr,       # a statement list followed by an expr; this is used
+                          # to allow powerful multi-line templates
+    nkBlockExpr,          # a statement block ending in an expr; this is used
+                          # to allow powerful multi-line templates that open a
+                          # temporary scope
+    nkStmtListType,       # a statement list ending in a type; for macros
+    nkBlockType,          # a statement block ending in a type; for macros
+                          # types as syntactic trees:
+
+    nkWith,               # distinct with `foo`
+    nkWithout,            # distinct without `foo`
+
+    nkTypeOfExpr,         # type(1+2)
+    nkObjectTy,           # object body
+    nkTupleTy,            # tuple body
+    nkTupleClassTy,       # tuple type class
+    nkTypeClassTy,        # user-defined type class
+    nkStaticTy,           # ``static[T]``
+    nkRecList,            # list of object parts
+    nkRecCase,            # case section of object
+    nkRecWhen,            # when section of object
+    nkRefTy,              # ``ref T``
+    nkPtrTy,              # ``ptr T``
+    nkVarTy,              # ``var T``
+    nkConstTy,            # ``const T``
+    nkOutTy,              # ``out T``
+    nkDistinctTy,         # distinct type
+    nkProcTy,             # proc type
+    nkIteratorTy,         # iterator type
+    nkSinkAsgn,           # '=sink(x, y)'
+    nkEnumTy,             # enum body
+    nkEnumFieldDef,       # `ident = expr` in an enumeration
+    nkArgList,            # argument list
+    nkPattern,            # a special pattern; used for matching
+    nkHiddenTryStmt,      # a hidden try statement
+    nkClosure,            # (prc, env)-pair (internally used for code gen)
+    nkGotoState,          # used for the state machine (for iterators)
+    nkState,              # give a label to a code section (for iterators)
+    nkBreakState,         # special break statement for easier code generation
+    nkFuncDef,            # a func
+    nkTupleConstr         # a tuple constructor
+    nkError               # erroneous AST node
+    nkModuleRef           # for .rod file support: A (moduleId, itemId) pair
+    nkReplayAction        # for .rod file support: A replay action
+    nkNilRodNode          # for .rod file support: a 'nil' PNode
+    nkOpenSym             # container for captured sym that can be overriden by local symbols
+
+const
+  nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
+                  nkCommand, nkCallStrLit, nkHiddenCallConv}
diff --git a/compiler/nversion.nim b/compiler/nversion.nim
index 2da39b138..811008989 100644
--- a/compiler/nversion.nim
+++ b/compiler/nversion.nim
@@ -12,6 +12,8 @@
 
 const
   MaxSetElements* = 1 shl 16  # (2^16) to support unicode character sets?
+  DefaultSetElements* = 1 shl 8
+    ## assumed set element count when using int literals
   VersionAsString* = system.NimVersion
   RodFileVersion* = "1223"       # modify this if the rod-format changes!
 
diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim
index 10b092e11..34e8ec80f 100644
--- a/compiler/optimizer.nim
+++ b/compiler/optimizer.nim
@@ -12,10 +12,12 @@
 ## - recognize "all paths lead to 'wasMoved(x)'"
 
 import
-  ast, renderer, idents, intsets
+  ast, renderer, idents
 
 from trees import exprStructuralEquivalent
 
+import std/[strutils, intsets]
+
 const
   nfMarkForDeletion = nfNone # faster than a lookup table
 
@@ -64,7 +66,7 @@ proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} =
 proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool =
   result = false
   for i in 0..<branch.len:
-    if exprStructuralEquivalent(branch[i][1].skipAddr, moveTarget,
+    if exprStructuralEquivalent(branch[i][1].skipHiddenAddr, moveTarget,
                                 strictSymEquality = true):
       result = true
       matches.incl i
@@ -74,7 +76,7 @@ proc intersect(summary: var seq[PNode]; branch: seq[PNode]) =
   var i = 0
   var matches = initIntSet()
   while i < summary.len:
-    if wasMovedTarget(matches, branch, summary[i][1].skipAddr):
+    if wasMovedTarget(matches, branch, summary[i][1].skipHiddenAddr):
       inc i
     else:
       summary.del i
@@ -85,7 +87,7 @@ proc intersect(summary: var seq[PNode]; branch: seq[PNode]) =
 proc invalidateWasMoved(c: var BasicBlock; x: PNode) =
   var i = 0
   while i < c.wasMovedLocs.len:
-    if exprStructuralEquivalent(c.wasMovedLocs[i][1].skipAddr, x,
+    if exprStructuralEquivalent(c.wasMovedLocs[i][1].skipHiddenAddr, x,
                                 strictSymEquality = true):
       c.wasMovedLocs.del i
     else:
@@ -94,7 +96,7 @@ proc invalidateWasMoved(c: var BasicBlock; x: PNode) =
 proc wasMovedDestroyPair(c: var Con; b: var BasicBlock; d: PNode) =
   var i = 0
   while i < b.wasMovedLocs.len:
-    if exprStructuralEquivalent(b.wasMovedLocs[i][1].skipAddr, d[1].skipAddr,
+    if exprStructuralEquivalent(b.wasMovedLocs[i][1].skipHiddenAddr, d[1].skipHiddenAddr,
                                 strictSymEquality = true):
       b.wasMovedLocs[i].flags.incl nfMarkForDeletion
       c.somethingTodo = true
@@ -110,16 +112,17 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
     var reverse = false
     if n[0].kind == nkSym:
       let s = n[0].sym
-      if s.magic == mWasMoved:
+      let name = s.name.s.normalize
+      if name == "=wasmoved":
         b.wasMovedLocs.add n
         special = true
-      elif s.name.s == "=destroy":
+      elif name == "=destroy":
         if c.inFinally > 0 and (b.hasReturn or b.hasBreak):
           discard "cannot optimize away the destructor"
         else:
           c.wasMovedDestroyPair b, n
         special = true
-      elif s.name.s == "=sink":
+      elif name == "=sink":
         reverse = true
 
     if not special:
@@ -154,7 +157,7 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) =
       nkTypeOfExpr, nkMixinStmt, nkBindStmt:
     discard "do not follow the construct"
 
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     # reverse order, see remark for `=sink`:
     analyse(c, b, n[1])
     analyse(c, b, n[0])
@@ -276,8 +279,8 @@ proc optimize*(n: PNode): PNode =
 
     Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)'
   ]#
-  var c: Con
-  var b: BasicBlock
+  var c: Con = Con()
+  var b: BasicBlock = default(BasicBlock)
   analyse(c, b, n)
   if c.somethingTodo:
     result = shallowCopy(n)
diff --git a/compiler/options.nim b/compiler/options.nim
index a0077b46b..b77bdd2a3 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,11 +8,12 @@
 #
 
 import
-  os, strutils, strtabs, sets, lineinfos, platform,
-  prefixmatches, pathutils, nimpaths, tables
+  lineinfos, platform,
+  prefixmatches, pathutils, nimpaths
 
-from terminal import isatty
-from times import utc, fromUnix, local, getTime, format, DateTime
+import std/[tables, os, strutils, strtabs, sets]
+from std/terminal import isatty
+from std/times import utc, fromUnix, local, getTime, format, DateTime
 from std/private/globs import nativeToUnixPath
 
 when defined(nimPreviewSlimSystem):
@@ -24,7 +25,7 @@ const
   useEffectSystem* = true
   useWriteTracking* = false
   hasFFI* = defined(nimHasLibFFI)
-  copyrightYear* = "2022"
+  copyrightYear* = "2024"
 
   nimEnableCovariance* = defined(nimEnableCovariance)
 
@@ -49,6 +50,7 @@ type                          # please make sure we have under 32 options
     optSinkInference          # 'sink T' inference
     optCursorInference
     optImportHidden
+    optQuirky
 
   TOptions* = set[TOption]
   TGlobalOption* = enum
@@ -60,6 +62,7 @@ type                          # please make sure we have under 32 options
     optGenStaticLib,          # generate a static library
     optGenGuiApp,             # generate a GUI application
     optGenScript,             # generate a script file to compile the *.c files
+    optGenCDeps,              # generate a list of *.c files to be read by CMake
     optGenMapping,            # generate a mapping file
     optRun,                   # run the compiled project
     optUseNimcache,           # save artifacts (including binary) in $nimcache
@@ -77,10 +80,13 @@ type                          # please make sure we have under 32 options
     optThreadAnalysis,        # thread analysis pass
     optTlsEmulation,          # thread var emulation turned on
     optGenIndex               # generate index file for documentation;
+    optGenIndexOnly           # generate only index file for documentation
+    optNoImportdoc            # disable loading external documentation files
     optEmbedOrigSrc           # embed the original source in the generated code
                               # also: generate header file
     optIdeDebug               # idetools: debug mode
     optIdeTerse               # idetools: use terse descriptions
+    optIdeExceptionInlayHints
     optExcessiveStackTrace    # fully qualified module filenames
     optShowAllMismatches      # show all overloading resolution candidates
     optWholeProject           # for 'doc': output any dependency
@@ -99,12 +105,11 @@ type                          # please make sure we have under 32 options
     optBenchmarkVM            # Enables cpuTime() in the VM
     optProduceAsm             # produce assembler code
     optPanics                 # turn panics (sysFatal) into a process termination
-    optNimV1Emulation         # emulate Nim v1.0
-    optNimV12Emulation        # emulate Nim v1.2
-    optNimV16Emulation        # emulate Nim v1.6
     optSourcemap
     optProfileVM              # enable VM profiler
     optEnableDeepCopy         # ORC specific: enable 'deepcopy' for all types.
+    optShowNonExportedFields  # for documentation: show fields that are not exported
+    optJsBigInt64             # use bigints for 64-bit integers in JS
 
   TGlobalOptions* = set[TGlobalOption]
 
@@ -140,10 +145,11 @@ type
   Command* = enum  ## Nim's commands
     cmdNone # not yet processed command
     cmdUnknown # command unmapped
-    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS
+    cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS,
     cmdCrun # compile and run in nimache
     cmdTcc # run the project via TCC backend
     cmdCheck # semantic checking for whole project
+    cmdM     # only compile a single
     cmdParse # parse a single file (for debugging)
     cmdRod # .rod to some text representation (for debugging)
     cmdIdeTools # ide tools (e.g. nimsuggest)
@@ -164,16 +170,15 @@ type
     cmdInteractive # start interactive session
     cmdNop
     cmdJsonscript # compile a .json build file
-    cmdNimfix
     # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs)
 
 const
-  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun}
+  cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
+                  cmdCompileToJS, cmdCrun}
   cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc,
                  cmdCtags, cmdBuildindex}
 
 type
-  NimVer* = tuple[major: int, minor: int, patch: int]
   TStringSeq* = seq[string]
   TGCMode* = enum             # the selected GC
     gcUnselected = "unselected"
@@ -182,6 +187,7 @@ type
     gcRegions = "regions"
     gcArc = "arc"
     gcOrc = "orc"
+    gcAtomicArc = "atomicArc"
     gcMarkAndSweep = "markAndSweep"
     gcHooks = "hooks"
     gcRefc = "refc"
@@ -192,10 +198,9 @@ type
   IdeCmd* = enum
     ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod,
     ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols,
-    ideRecompile, ideChanged, ideType
+    ideRecompile, ideChanged, ideType, ideDeclaration, ideExpand, ideInlayHints
 
   Feature* = enum  ## experimental features; DO NOT RENAME THESE!
-    implicitDeref,
     dotOperators,
     callOperator,
     parallel,
@@ -207,16 +212,23 @@ type
     codeReordering,
     compiletimeFFI,
       ## This requires building nim with `-d:nimHasLibFFI`
-      ## which itself requires `nimble install libffi`, see #10150
+      ## which itself requires `koch installdeps libffi`, see #10150
       ## Note: this feature can't be localized with {.push.}
     vmopsDanger,
     strictFuncs,
     views,
     strictNotNil,
-    overloadableEnums, # not experimental anymore
+    overloadableEnums, # deadcode
     strictEffects,
-    unicodeOperators,
-    flexibleOptionalParams
+    unicodeOperators, # deadcode
+    flexibleOptionalParams,
+    strictDefs,
+    strictCaseObjects,
+    inferGenericTypes,
+    openSym, # remove nfDisabledOpenSym when this is default
+    # alternative to above:
+    genericsOpenSym
+    vtables
 
   LegacyFeature* = enum
     allowSemcheckedAstModification,
@@ -227,13 +239,21 @@ type
       ## Historically and especially in version 1.0.0 of the language
       ## conversions to unsigned numbers were checked. In 1.0.4 they
       ## are not anymore.
+    laxEffects
+      ## Lax effects system prior to Nim 2.0.
+    verboseTypeMismatch
+    emitGenerics
+      ## generics are emitted in the module that contains them.
+      ## Useful for libraries that rely on local passC
+    jsNoLambdaLifting
+      ## Old transformation for closures in JS backend
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest
 
   TSystemCC* = enum
     ccNone, ccGcc, ccNintendoSwitch, ccLLVM_Gcc, ccCLang, ccBcc, ccVcc,
-    ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl
+    ccTcc, ccEnv, ccIcl, ccIcc, ccClangCl, ccHipcc, ccNvcc
 
   ExceptionSystem* = enum
     excNone,   # no exception system selected yet
@@ -271,8 +291,27 @@ type
     scope*, localUsages*, globalUsages*: int # more usages is better
     tokenLen*: int
     version*: int
+    endLine*: uint16
+    endCol*: int
+    inlayHintInfo*: SuggestInlayHint
+
   Suggestions* = seq[Suggest]
 
+  SuggestInlayHintKind* = enum
+    sihkType = "Type",
+    sihkParameter = "Parameter"
+    sihkException = "Exception"
+
+  SuggestInlayHint* = ref object
+    kind*: SuggestInlayHintKind
+    line*: int                   # Starts at 1
+    column*: int                 # Starts at 0
+    label*: string
+    paddingLeft*: bool
+    paddingRight*: bool
+    allowInsert*: bool
+    tooltip*: string
+
   ProfileInfo* = object
     time*: float
     count*: int
@@ -324,12 +363,12 @@ type
 
     cppDefines*: HashSet[string] # (*)
     headerFile*: string
+    nimbasePattern*: string # pattern to find nimbase.h
     features*: set[Feature]
     legacyFeatures*: set[LegacyFeature]
     arguments*: string ## the arguments to be passed to the program that
                        ## should be run
     ideCmd*: IdeCmd
-    oldNewlines*: bool
     cCompiler*: TSystemCC # the used compiler
     modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs
     cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline
@@ -356,8 +395,7 @@ type
     outDir*: AbsoluteDir
     jsonBuildFile*: AbsoluteFile
     prefixDir*, libpath*, nimcacheDir*: AbsoluteDir
-    nimStdlibVersion*: NimVer
-    dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef
+    dllOverrides*, moduleOverrides*, cfileSpecificOptions*: StringTableRef
     projectName*: string # holds a name like 'nim'
     projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/
     projectFull*: AbsoluteFile # projectPath/projectName
@@ -369,7 +407,6 @@ type
     commandArgs*: seq[string] # any arguments after the main command
     commandLine*: string
     extraCmds*: seq[string] # for writeJsonBuildInstructions
-    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. \
@@ -394,22 +431,22 @@ type
     suggestVersion*: int
     suggestMaxResults*: int
     lastLineInfo*: TLineInfo
-    writelnHook*: proc (output: string) {.closure.} # cannot make this gcsafe yet because of Nimble
+    writelnHook*: proc (output: string) {.closure, gcsafe.}
     structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string;
                                 severity: Severity) {.closure, gcsafe.}
     cppCustomNamespace*: string
     nimMainPrefix*: string
     vmProfileData*: ProfileData
 
-proc parseNimVersion*(a: string): NimVer =
-  # could be moved somewhere reusable
-  if a.len > 0:
-    let b = a.split(".")
-    assert b.len == 3, a
-    template fn(i) = result[i] = b[i].parseInt # could be optimized if needed
-    fn(0)
-    fn(1)
-    fn(2)
+    expandProgress*: bool
+    expandLevels*: int
+    expandNodeResult*: string
+    expandPosition*: TLineInfo
+
+    currentConfigDir*: string # used for passPP only; absolute dir
+    clientProcessId*: int
+
+
 
 proc assignIfDefault*[T](result: var T, val: T, def = default(T)) =
   ## if `result` was already assigned to a value (that wasn't `def`), this is a noop.
@@ -452,7 +489,7 @@ when false:
     fn(globalOptions)
     fn(selectedGC)
 
-const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel}
+const oldExperimentalFeatures* = {dotOperators, callOperator, parallel}
 
 const
   ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
@@ -463,7 +500,8 @@ const
     optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,
     optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs`
     optTrMacros, optStyleCheck, optCursorInference}
-  DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace}
+  DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace,
+    optJsBigInt64}
 
 proc getSrcTimestamp(): DateTime =
   try:
@@ -542,7 +580,6 @@ proc newConfigRef*(): ConfigRef =
     command: "", # the main command (e.g. cc, check, scan, etc)
     commandArgs: @[], # any arguments after the main command
     commandLine: "",
-    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: "",
@@ -562,6 +599,7 @@ proc newConfigRef*(): ConfigRef =
     maxLoopIterationsVM: 10_000_000,
     vmProfileData: newProfileData(),
     spellSuggestMax: spellSuggestSecretSauce,
+    currentConfigDir: ""
   )
   initConfigRefCommon(result)
   setTargetFromSystem(result.target)
@@ -582,12 +620,6 @@ proc newPartialConfigRef*(): ConfigRef =
 proc cppDefine*(c: ConfigRef; define: string) =
   c.cppDefines.incl define
 
-proc getStdlibVersion*(conf: ConfigRef): NimVer =
-  if conf.nimStdlibVersion == (0,0,0):
-    let s = conf.symbols.getOrDefault("nimVersion", "")
-    conf.nimStdlibVersion = s.parseNimVersion
-  result = conf.nimStdlibVersion
-
 proc isDefined*(conf: ConfigRef; symbol: string): bool =
   if conf.symbols.hasKey(symbol):
     result = true
@@ -605,7 +637,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
                             osQnx, osAtari, osAix,
                             osHaiku, osVxWorks, osSolaris, osNetbsd,
                             osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos,
-                            osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr}
+                            osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr, osNuttX}
     of "linux":
       result = conf.target.targetOS in {osLinux, osAndroid}
     of "bsd":
@@ -627,6 +659,8 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
       result = conf.target.targetOS == osFreeRTOS
     of "zephyr":
       result = conf.target.targetOS == osZephyr
+    of "nuttx":
+      result = conf.target.targetOS == osNuttX
     of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian
     of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian
     of "cpu8": result = CPU[conf.target.targetCPU].bit == 8
@@ -636,12 +670,12 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool =
     of "nimrawsetjmp":
       result = conf.target.targetOS in {osSolaris, osNetbsd, osFreebsd, osOpenbsd,
                             osDragonfly, osMacosx}
-    else: discard
+    else: result = false
 
 template quitOrRaise*(conf: ConfigRef, msg = "") =
   # xxx in future work, consider whether to also intercept `msgQuit` calls
   if conf.isDefined("nimDebug"):
-    doAssert false, msg
+    raiseAssert msg
   else:
     quit(msg) # quits with QuitFailure
 
@@ -701,22 +735,24 @@ proc getPrefixDir*(conf: ConfigRef): AbsoluteDir =
   ##  clone or using installed nim, so that these exist: `result/doc/advopt.txt`
   ## and `result/lib/system.nim`
   if not conf.prefixDir.isEmpty: result = conf.prefixDir
-  else: result = AbsoluteDir splitPath(getAppDir()).head
+  else:
+    let binParent = AbsoluteDir splitPath(getAppDir()).head
+    when defined(posix):
+      if binParent == AbsoluteDir"/usr":
+        result = AbsoluteDir"/usr/lib/nim"
+      elif binParent == AbsoluteDir"/usr/local":
+        result = AbsoluteDir"/usr/local/lib/nim"
+      else:
+        result = binParent
+    else:
+      result = binParent
 
 proc setDefaultLibpath*(conf: ConfigRef) =
   # set default value (can be overwritten):
   if conf.libpath.isEmpty:
     # choose default libpath:
     var prefix = getPrefixDir(conf)
-    when defined(posix):
-      if prefix == AbsoluteDir"/usr":
-        conf.libpath = AbsoluteDir"/usr/lib/nim"
-      elif prefix == AbsoluteDir"/usr/local":
-        conf.libpath = AbsoluteDir"/usr/local/lib/nim"
-      else:
-        conf.libpath = prefix / RelativeDir"lib"
-    else:
-      conf.libpath = prefix / RelativeDir"lib"
+    conf.libpath = prefix / RelativeDir"lib"
 
     # Special rule to support other tools (nimble) which import the compiler
     # modules and make use of them.
@@ -737,7 +773,10 @@ proc setFromProjectName*(conf: ConfigRef; projectName: string) =
     conf.projectFull = AbsoluteFile projectName
   let p = splitFile(conf.projectFull)
   let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir
-  conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
+  try:
+    conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir)
+  except OSError:
+    conf.projectPath = dir
   conf.projectName = p.name
 
 proc removeTrailingDirSep*(path: string): string =
@@ -770,16 +809,17 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir =
     else: "_d"
 
   # XXX projectName should always be without a file extension!
-  result = if not conf.nimcacheDir.isEmpty:
-             conf.nimcacheDir
-           elif conf.backend == backendJs:
-             if conf.outDir.isEmpty:
-               conf.projectPath / genSubDir
-             else:
-               conf.outDir / genSubDir
-           else:
-            AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
-               nimcacheSuffix(conf))
+  result =
+    if not conf.nimcacheDir.isEmpty:
+      conf.nimcacheDir
+    elif conf.backend == backendJs:
+      if conf.outDir.isEmpty:
+        conf.projectPath / genSubDir
+      else:
+        conf.outDir / genSubDir
+    else:
+      AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name &
+        nimcacheSuffix(conf))
 
 proc pathSubs*(conf: ConfigRef; p, config: string): string =
   let home = removeTrailingDirSep(os.getHomeDir())
@@ -846,27 +886,21 @@ template patchModule(conf: ConfigRef) {.dirty.} =
       let ov = conf.moduleOverrides[key]
       if ov.len > 0: result = AbsoluteFile(ov)
 
-when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
-  proc isRelativeTo(path, base: string): bool =
-    # pending #13212 use os.isRelativeTo
-    let path = path.normalizedPath
-    let base = base.normalizedPath
-    let ret = relativePath(path, base)
-    result = path.len > 0 and not ret.startsWith ".."
-
 const stdlibDirs* = [
   "pure", "core", "arch",
   "pure/collections",
   "pure/concurrency",
   "pure/unidecode", "impure",
   "wrappers", "wrappers/linenoise",
-  "windows", "posix", "js"]
+  "windows", "posix", "js",
+  "deprecated/pure"]
 
 const
   pkgPrefix = "pkg/"
-  stdPrefix = "std/"
+  stdPrefix* = "std/"
 
 proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile =
+  result = RelativeFile("")
   let f = $f
   if isTitle:
     for dir in stdlibDirs:
@@ -897,10 +931,12 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile
 proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile =
   # returns path to module
   var m = addFileExt(modulename, NimExt)
+  var hasRelativeDot = false
   if m.startsWith(pkgPrefix):
     result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true)
   else:
     if m.startsWith(stdPrefix):
+      result = AbsoluteFile("")
       let stripped = m.substr(stdPrefix.len)
       for candidate in stdlibDirs:
         let path = (conf.libpath.string / candidate / stripped)
@@ -910,7 +946,11 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi
     else: # If prefixed with std/ why would we add the current module path!
       let currentPath = currentModule.splitFile.dir
       result = AbsoluteFile currentPath / m
-    if not fileExists(result):
+      if m.startsWith('.') and not fileExists(result):
+        result = AbsoluteFile ""
+        hasRelativeDot = true
+
+    if not fileExists(result) and not hasRelativeDot:
       result = findFile(conf, m)
   patchModule(conf)
 
@@ -988,6 +1028,12 @@ proc isDynlibOverride*(conf: ConfigRef; lib: string): bool =
   result = optDynlibOverrideAll in conf.globalOptions or
      conf.dllOverrides.hasKey(lib.canonDynlibName)
 
+proc showNonExportedFields*(conf: ConfigRef) =
+  incl(conf.globalOptions, optShowNonExportedFields)
+
+proc expandDone*(conf: ConfigRef): bool =
+  result = conf.ideCmd == ideExpand and conf.expandLevels == 0 and conf.expandProgress
+
 proc parseIdeCmd*(s: string): IdeCmd =
   case s:
   of "sug": ideSug
@@ -1026,9 +1072,12 @@ proc `$`*(c: IdeCmd): string =
   of ideMsg: "msg"
   of ideProject: "project"
   of ideGlobalSymbols: "globalSymbols"
+  of ideDeclaration: "declaration"
+  of ideExpand: "expand"
   of ideRecompile: "recompile"
   of ideChanged: "changed"
   of ideType: "type"
+  of ideInlayHints: "inlayHints"
 
 proc floatInt64Align*(conf: ConfigRef): int16 =
   ## Returns either 4 or 8 depending on reasons.
diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim
index 8cf209779..30f407792 100644
--- a/compiler/packagehandling.nim
+++ b/compiler/packagehandling.nim
@@ -17,6 +17,7 @@ iterator myParentDirs(p: string): string =
 
 proc getNimbleFile*(conf: ConfigRef; path: string): string =
   ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble
+  result = ""
   var parents = 0
   block packageSearch:
     for d in myParentDirs(path):
diff --git a/compiler/packages.nim b/compiler/packages.nim
index d8b97e374..bb54d6154 100644
--- a/compiler/packages.nim
+++ b/compiler/packages.nim
@@ -8,7 +8,7 @@
 #
 
 ## Package related procs.
-## 
+##
 ## See Also:
 ## * `packagehandling` for package path handling
 ## * `modulegraphs.getPackage`
@@ -22,7 +22,7 @@ when defined(nimPreviewSlimSystem):
 
 proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
   ## Return a new package symbol.
-  ## 
+  ##
   ## See Also:
   ## * `modulegraphs.getPackage`
   let
@@ -31,7 +31,7 @@ proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym =
     info = newLineInfo(fileIdx, 1, 1)
     pkgName = getPackageName(conf, filename.string)
     pkgIdent = getIdent(cache, pkgName)
-  newSym(skPackage, pkgIdent, ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info)
+  newSym(skPackage, pkgIdent, idGeneratorForPackage(int32(fileIdx)), nil, info)
 
 func getPackageSymbol*(sym: PSym): PSym =
   ## Return the owning package symbol.
@@ -47,7 +47,7 @@ func getPackageId*(sym: PSym): int =
 
 func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool =
   ## Return whether the symbol belongs to the project's package.
-  ## 
+  ##
   ## See Also:
   ## * `modulegraphs.belongsToStdlib`
   conf.mainPackageId == sym.getPackageId
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index cceb236ae..e8ec22fe1 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,9 +10,11 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import strutils, ast, types, msgs, idents, renderer, wordrecg, trees,
+import ast, types, msgs, idents, renderer, wordrecg, trees,
   options
 
+import std/strutils
+
 # 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
 # actually improves performance.
@@ -183,7 +185,8 @@ type
     arLentValue,              # lent value
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
-proc exprRoot*(n: PNode): PSym =
+proc exprRoot*(n: PNode; allowCalls = true): PSym =
+  result = nil
   var it = n
   while true:
     case it.kind
@@ -204,7 +207,7 @@ proc exprRoot*(n: PNode): PSym =
       if it.len > 0 and it.typ != nil: it = it.lastSon
       else: break
     of nkCallKinds:
-      if it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1:
+      if allowCalls and it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1:
         # See RFC #7373, calls returning 'var T' are assumed to
         # return a view into the first argument (if there is one):
         it = it[1]
@@ -224,7 +227,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
     const kinds = {skVar, skResult, skTemp, skParam, skLet, skForVar}
     if n.sym.kind == skParam:
       result = if n.sym.typ.kind in {tyVar, tySink}: arLValue else: arAddressableConst
-    elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast):
+    elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.astdef):
       result = arAddressableConst
     elif n.sym.kind in kinds:
       if n.sym.kind in {skParam, skLet, skForVar}:
@@ -263,7 +266,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
       result = isAssignable(owner, n[1])
-    elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct):
+    elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct, {IgnoreRangeShallow}):
       # types that are equal modulo distinction preserve l-value:
       result = isAssignable(owner, n[1])
   of nkHiddenDeref:
@@ -280,8 +283,15 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
     result = isAssignable(owner, n[0])
   of nkCallKinds:
-    # builtin slice keeps lvalue-ness:
-    if getMagic(n) in {mArrGet, mSlice}:
+    let m = getMagic(n)
+    if m == mSlice:
+      # builtin slice keeps l-value-ness
+      # except for pointers because slice dereferences
+      if n[1].typ.kind == tyPtr:
+        result = arLValue
+      else:
+        result = isAssignable(owner, n[1])
+    elif m == mArrGet:
       result = isAssignable(owner, n[1])
     elif n.typ != nil:
       case n.typ.kind
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 8b0a9d1c6..747505097 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -16,25 +16,49 @@
 
 
 # In fact the grammar is generated from this file:
-when isMainModule:
+when isMainModule or defined(nimTestGrammar):
   # Leave a note in grammar.txt that it is generated:
   #| # This file is generated by compiler/parser.nim.
-  import pegs
+  import std/pegs
   when defined(nimPreviewSlimSystem):
     import std/syncio
-  var outp = open("doc/grammar.txt", fmWrite)
-  for line in lines("compiler/parser.nim"):
-    if line =~ peg" \s* '#| ' {.*}":
-      outp.write matches[0], "\L"
-  outp.close
 
-  import ".." / tools / grammar_nanny
-  checkGrammarFile()
+  proc writeGrammarFile(x: string) =
+    var outp = open(x, fmWrite)
+    for line in lines("compiler/parser.nim"):
+      if line =~ peg" \s* '#| ' {.*}":
+        outp.write matches[0], "\L"
+    outp.close
+
+  when defined(nimTestGrammar):
+    import std/os
+    from ../testament/lib/stdtest/specialpaths import buildDir
+    const newGrammarText = buildDir / "grammar.txt"
+
+    if not dirExists(buildDir):
+      createDir(buildDir)
+
+    writeGrammarFile(newGrammarText)
+
+    proc checkSameGrammar*() =
+      doAssert sameFileContent(newGrammarText, "doc/grammar.txt"),
+              "execute 'nim r compiler/parser.nim' to keep grammar.txt up-to-date"
+  else:
+    writeGrammarFile("doc/grammar.txt")
+    import ".." / tools / grammar_nanny
+    checkGrammarFile()
 
 import
-  llstream, lexer, idents, strutils, ast, msgs, options, lineinfos,
+  llstream, lexer, idents, msgs, options, lineinfos,
   pathutils
 
+when not defined(nimCustomAst):
+  import ast
+else:
+  import plugins / customast
+
+import std/strutils
+
 when defined(nimpretty):
   import layouter
 
@@ -54,7 +78,8 @@ type
     bufposPrevious*: int
     inPragma*: int            # Pragma level
     inSemiStmtList*: int
-    emptyNode: PNode
+    when not defined(nimCustomAst):
+      emptyNode: PNode
     when defined(nimpretty):
       em*: Emitter
 
@@ -62,11 +87,11 @@ type
     smNormal, smAllowNil, smAfterDot
 
   PrimaryMode = enum
-    pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix
+    pmNormal, pmTypeDesc, pmTypeDef, pmTrySimple
 
-proc parseAll*(p: var Parser): PNode
-proc closeParser*(p: var Parser)
-proc parseTopLevelStmt*(p: var Parser): PNode
+when defined(nimCustomAst):
+  # For the `customast` version we cannot share nodes, not even empty nodes:
+  template emptyNode(p: Parser): PNode = newNode(nkEmpty)
 
 # helpers for the other parsers
 proc isOperator*(tok: Token): bool
@@ -76,7 +101,7 @@ proc skipComment*(p: var Parser, node: PNode)
 proc newNodeP*(kind: TNodeKind, p: Parser): PNode
 proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode
 proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode
-proc newStrNodeP*(kind: TNodeKind, strVal: string, p: Parser): PNode
+proc newStrNodeP*(kind: TNodeKind, strVal: sink string, p: Parser): PNode
 proc newIdentNodeP*(ident: PIdent, p: Parser): PNode
 proc expectIdentOrKeyw*(p: Parser)
 proc expectIdent*(p: Parser)
@@ -125,23 +150,22 @@ proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef) =
   ## Open a parser, using the given arguments to set up its internal state.
   ##
-  initToken(p.tok)
+  reset(p.tok)
   openLexer(p.lex, fileIdx, inputStream, cache, config)
   when defined(nimpretty):
     openEmitter(p.em, cache, config, fileIdx)
   getTok(p)                   # read the first token
   p.firstTok = true
-  p.emptyNode = newNode(nkEmpty)
+  when not defined(nimCustomAst):
+    p.emptyNode = newNode(nkEmpty)
 
 proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream,
                  cache: IdentCache; config: ConfigRef) =
   openParser(p, fileInfoIdx(config, filename), inputStream, cache, config)
 
-proc closeParser(p: var Parser) =
+proc closeParser*(p: var Parser) =
   ## Close a parser, freeing up its resources.
   closeLexer(p.lex)
-  when defined(nimpretty):
-    closeEmitter(p.em)
 
 proc parMessage(p: Parser, msg: TMsgKind, arg = "") =
   ## Produce and emit the parser message `arg` to output.
@@ -248,28 +272,25 @@ proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) =
     skipComment(p, n)
 
 proc newNodeP(kind: TNodeKind, p: Parser): PNode =
-  result = newNodeI(kind, parLineInfo(p))
+  result = newNode(kind, parLineInfo(p))
 
 proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode =
-  result = newNodeP(kind, p)
-  result.intVal = intVal
+  result = newAtom(kind, intVal, parLineInfo(p))
 
 proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat,
                    p: Parser): PNode =
-  result = newNodeP(kind, p)
-  result.floatVal = floatVal
+  result = newAtom(kind, floatVal, parLineInfo(p))
 
-proc newStrNodeP(kind: TNodeKind, strVal: string, p: Parser): PNode =
-  result = newNodeP(kind, p)
-  result.strVal = strVal
+proc newStrNodeP(kind: TNodeKind, strVal: sink string, p: Parser): PNode =
+  result = newAtom(kind, strVal, parLineInfo(p))
 
 proc newIdentNodeP(ident: PIdent, p: Parser): PNode =
-  result = newNodeP(nkIdent, p)
-  result.ident = ident
+  result = newAtom(ident, parLineInfo(p))
 
 proc parseExpr(p: var Parser): PNode
 proc parseStmt(p: var Parser): PNode
-proc parseTypeDesc(p: var Parser): PNode
+proc parseTypeDesc(p: var Parser, fullExpr = false): PNode
+proc parseTypeDefValue(p: var Parser): PNode
 proc parseParamList(p: var Parser, retColon = true): PNode
 
 proc isSigilLike(tok: Token): bool {.inline.} =
@@ -283,17 +304,16 @@ proc isRightAssociative(tok: Token): bool {.inline.} =
 proc isUnary(tok: Token): bool =
   ## Check if the given token is a unary operator
   tok.tokType in {tkOpr, tkDotDot} and
-  tok.strongSpaceB == 0 and
-  tok.strongSpaceA > 0
+  tok.spacing == {tsLeading}
 
 proc checkBinary(p: Parser) {.inline.} =
   ## Check if the current parser token is a binary operator.
   # we don't check '..' here as that's too annoying
   if p.tok.tokType == tkOpr:
-    if p.tok.strongSpaceB > 0 and p.tok.strongSpaceA == 0:
+    if p.tok.spacing == {tsTrailing}:
       parMessage(p, warnInconsistentSpacing, prettyTok(p.tok))
 
-#| module = stmt ^* (';' / IND{=})
+#| module = complexOrSimpleStmt ^* (';' / IND{=})
 #|
 #| comma = ',' COMMENT?
 #| semicolon = ';' COMMENT?
@@ -303,7 +323,7 @@ proc checkBinary(p: Parser) {.inline.} =
 #| operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
 #|          | 'or' | 'xor' | 'and'
 #|          | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
-#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
+#|          | 'div' | 'mod' | 'shl' | 'shr' | 'not' | '..'
 #|
 #| prefixOperator = operator
 #|
@@ -336,9 +356,16 @@ proc colcom(p: var Parser, n: PNode) =
 
 const tkBuiltInMagics = {tkType, tkStatic, tkAddr}
 
+template setEndInfo() =
+  when defined(nimsuggest):
+    result.endInfo = TLineInfo(fileIndex: p.lex.fileIdx,
+                     line: p.lex.previousTokenEnd.line,
+                     col: p.lex.previousTokenEnd.col)
+
 proc parseSymbol(p: var Parser, mode = smNormal): PNode =
   #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
-  #|        | IDENT | KEYW
+  #|        | IDENT | 'addr' | 'type' | 'static'
+  #| symbolOrKeyword = symbol | KEYW
   case p.tok.tokType
   of tkSymbol:
     result = newIdentNodeP(p.tok.ident, p)
@@ -361,7 +388,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
     while true:
       case p.tok.tokType
       of tkAccent:
-        if result.len == 0:
+        if not result.hasSon:
           parMessage(p, errIdentifierExpected, p.tok)
         break
       of tkOpr, tkDot, tkDotDot, tkEquals, tkParLe..tkParDotRi:
@@ -371,8 +398,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
                                 tkParLe..tkParDotRi}:
           accm.add($p.tok)
           getTok(p)
-        let node = newNodeI(nkIdent, lineinfo)
-        node.ident = p.lex.cache.getIdent(accm)
+        let node = newAtom(p.lex.cache.getIdent(accm), lineinfo)
         result.add(node)
       of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit:
         result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p))
@@ -388,32 +414,47 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode =
     # if it is a keyword:
     #if not isKeyword(p.tok.tokType): getTok(p)
     result = p.emptyNode
+  setEndInfo()
 
-proc colonOrEquals(p: var Parser, a: PNode): PNode =
-  if p.tok.tokType == tkColon:
-    result = newNodeP(nkExprColonExpr, p)
+proc equals(p: var Parser, a: PNode): PNode =
+  if p.tok.tokType == tkEquals:
+    result = newNodeP(nkExprEqExpr, p)
     getTok(p)
-    newlineWasSplitting(p)
     #optInd(p, result)
     result.add(a)
     result.add(parseExpr(p))
-  elif p.tok.tokType == tkEquals:
-    result = newNodeP(nkExprEqExpr, p)
+  else:
+    result = a
+
+proc colonOrEquals(p: var Parser, a: PNode): PNode =
+  if p.tok.tokType == tkColon:
+    result = newNodeP(nkExprColonExpr, p)
     getTok(p)
+    newlineWasSplitting(p)
     #optInd(p, result)
     result.add(a)
     result.add(parseExpr(p))
   else:
-    result = a
+    result = equals(p, a)
 
 proc exprColonEqExpr(p: var Parser): PNode =
-  #| exprColonEqExpr = expr (':'|'=' expr)?
+  #| exprColonEqExpr = expr ((':'|'=') expr
+  #|                        / doBlock extraPostExprBlock*)?
   var a = parseExpr(p)
   if p.tok.tokType == tkDo:
     result = postExprBlocks(p, a)
   else:
     result = colonOrEquals(p, a)
 
+proc exprEqExpr(p: var Parser): PNode =
+  #| exprEqExpr = expr ('=' expr
+  #|                   / doBlock extraPostExprBlock*)?
+  var a = parseExpr(p)
+  if p.tok.tokType == tkDo:
+    result = postExprBlocks(p, a)
+  else:
+    result = equals(p, a)
+
 proc exprList(p: var Parser, endTok: TokType, result: PNode) =
   #| exprList = expr ^+ comma
   when defined(nimpretty):
@@ -421,6 +462,24 @@ proc exprList(p: var Parser, endTok: TokType, result: PNode) =
   getTok(p)
   optInd(p, result)
   # progress guaranteed
+  var a = parseExpr(p)
+  result.add(a)
+  while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
+    if p.tok.tokType != tkComma: break
+    getTok(p)
+    optInd(p, a)
+    var a = parseExpr(p)
+    result.add(a)
+  when defined(nimpretty):
+    dec p.em.doIndentMore
+
+proc optionalExprList(p: var Parser, endTok: TokType, result: PNode) =
+  #| optionalExprList = expr ^* comma
+  when defined(nimpretty):
+    inc p.em.doIndentMore
+  getTok(p)
+  optInd(p, result)
+  # progress guaranteed
   while (p.tok.tokType != endTok) and (p.tok.tokType != tkEof):
     var a = parseExpr(p)
     result.add(a)
@@ -456,26 +515,26 @@ proc exprColonEqExprList(p: var Parser, kind: TNodeKind,
 proc dotExpr(p: var Parser, a: PNode): PNode =
   var info = p.parLineInfo
   getTok(p)
-  result = newNodeI(nkDotExpr, info)
+  result = newNode(nkDotExpr, info)
   optInd(p, result)
   result.add(a)
   result.add(parseSymbol(p, smAfterDot))
-  if p.tok.tokType == tkBracketLeColon and p.tok.strongSpaceA <= 0:
-    var x = newNodeI(nkBracketExpr, p.parLineInfo)
+  if p.tok.tokType == tkBracketLeColon and tsLeading notin p.tok.spacing:
+    var x = newNode(nkBracketExpr, p.parLineInfo)
     # rewrite 'x.y[:z]()' to 'y[z](x)'
-    x.add result[1]
+    x.add result.secondSon
     exprList(p, tkBracketRi, x)
     eat(p, tkBracketRi)
-    var y = newNodeI(nkCall, p.parLineInfo)
+    var y = newNode(nkCall, p.parLineInfo)
     y.add x
-    y.add result[0]
-    if p.tok.tokType == tkParLe and p.tok.strongSpaceA <= 0:
+    y.add result.firstSon
+    if p.tok.tokType == tkParLe and tsLeading notin p.tok.spacing:
       exprColonEqExprListAux(p, tkParRi, y)
     result = y
 
 proc dotLikeExpr(p: var Parser, a: PNode): PNode =
   var info = p.parLineInfo
-  result = newNodeI(nkInfix, info)
+  result = newNode(nkInfix, info)
   optInd(p, result)
   var opNode = newIdentNodeP(p.tok.ident, p)
   getTok(p)
@@ -484,7 +543,7 @@ proc dotLikeExpr(p: var Parser, a: PNode): PNode =
   result.add(parseSymbol(p, smAfterDot))
 
 proc qualifiedIdent(p: var Parser): PNode =
-  #| qualifiedIdent = symbol ('.' optInd symbol)?
+  #| qualifiedIdent = symbol ('.' optInd symbolOrKeyword)?
   result = parseSymbol(p)
   if p.tok.tokType == tkDot: result = dotExpr(p, result)
 
@@ -529,13 +588,20 @@ proc parseCast(p: var Parser): PNode =
     result.add(exprColonEqExpr(p))
   optPar(p)
   eat(p, tkParRi)
+  setEndInfo()
+
+template setNodeFlag(n: PNode; f: untyped) =
+  when defined(nimCustomAst):
+    discard
+  else:
+    incl n.flags, f
 
 proc setBaseFlags(n: PNode, base: NumericalBase) =
   case base
   of base10: discard
-  of base2: incl(n.flags, nfBase2)
-  of base8: incl(n.flags, nfBase8)
-  of base16: incl(n.flags, nfBase16)
+  of base2: setNodeFlag(n, nfBase2)
+  of base8: setNodeFlag(n, nfBase8)
+  of base16: setNodeFlag(n, nfBase16)
 
 proc parseGStrLit(p: var Parser, a: PNode): PNode =
   case p.tok.tokType
@@ -551,6 +617,7 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode =
     getTok(p)
   else:
     result = a
+  setEndInfo()
 
 proc complexOrSimpleStmt(p: var Parser): PNode
 proc simpleExpr(p: var Parser, mode = pmNormal): PNode
@@ -590,7 +657,8 @@ proc parsePar(p: var Parser): PNode =
   #|           ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
   #|           | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
   #|           | pragmaStmt
-  #|           | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
+  #|           | simpleExpr ( (doBlock extraPostExprBlock*)
+  #|                        | ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
   #|                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
   #|           optPar ')'
   #
@@ -655,6 +723,7 @@ proc parsePar(p: var Parser): PNode =
           skipComment(p, a)
   optPar(p)
   eat(p, tkParRi)
+  setEndInfo()
 
 proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode =
   #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
@@ -785,25 +854,24 @@ proc namedParams(p: var Parser, callee: PNode,
 proc commandParam(p: var Parser, isFirstParam: var bool; mode: PrimaryMode): PNode =
   if mode == pmTypeDesc:
     result = simpleExpr(p, mode)
+  elif not isFirstParam:
+    result = exprEqExpr(p)
   else:
     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)
-    result.add(lhs)
-    result.add(parseExpr(p))
+    if p.tok.tokType == tkDo:
+      result = postExprBlocks(p, result)
   isFirstParam = false
 
 proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode =
-  result = newNodeP(nkCommand, p)
-  result.add(r)
-  var isFirstParam = true
-  # progress NOT guaranteed
-  p.hasProgress = false
-  result.add commandParam(p, isFirstParam, mode)
+  if mode == pmTrySimple:
+    result = r
+  else:
+    result = newNodeP(nkCommand, p)
+    result.add(r)
+    var isFirstParam = true
+    # progress NOT guaranteed
+    p.hasProgress = false
+    result.add commandParam(p, isFirstParam, mode)
 
 proc isDotLike(tok: Token): bool =
   result = tok.tokType == tkOpr and tok.ident.s.len > 1 and
@@ -812,11 +880,11 @@ proc isDotLike(tok: Token): bool =
 proc primarySuffix(p: var Parser, r: PNode,
                    baseIndent: int, mode: PrimaryMode): PNode =
   #| primarySuffix = '(' (exprColonEqExpr comma?)* ')'
-  #|       | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
-  #|       | DOTLIKEOP optInd symbol generalizedLit?
+  #|       | '.' optInd symbolOrKeyword ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
+  #|       | DOTLIKEOP optInd symbolOrKeyword generalizedLit?
   #|       | '[' optInd exprColonEqExprList optPar ']'
   #|       | '{' optInd exprColonEqExprList optPar '}'
-  #|       | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax
+  # XXX strong spaces need to be reflected above
   result = r
 
   # progress guaranteed
@@ -825,18 +893,11 @@ proc primarySuffix(p: var Parser, r: PNode,
     case p.tok.tokType
     of tkParLe:
       # progress guaranteed
-      if p.tok.strongSpaceA > 0:
+      if tsLeading in p.tok.spacing:
         result = commandExpr(p, result, mode)
-        # type sections allow full command syntax
-        if mode == pmTypeDef:
-          var isFirstParam = false
-          while p.tok.tokType == tkComma:
-            getTok(p)
-            optInd(p, result)
-            result.add(commandParam(p, isFirstParam, mode))
         break
       result = namedParams(p, result, nkCall, tkParRi)
-      if result.len > 1 and result[1].kind == nkExprColonExpr:
+      if result.has2Sons and result.secondSon.kind == nkExprColonExpr:
         result.transitionSonsKind(nkObjConstr)
     of tkDot:
       # progress guaranteed
@@ -844,13 +905,13 @@ proc primarySuffix(p: var Parser, r: PNode,
       result = parseGStrLit(p, result)
     of tkBracketLe:
       # progress guaranteed
-      if p.tok.strongSpaceA > 0:
+      if tsLeading in p.tok.spacing:
         result = commandExpr(p, result, mode)
         break
       result = namedParams(p, result, nkBracketExpr, tkBracketRi)
     of tkCurlyLe:
       # progress guaranteed
-      if p.tok.strongSpaceA > 0:
+      if tsLeading in p.tok.spacing:
         result = commandExpr(p, result, mode)
         break
       result = namedParams(p, result, nkCurlyExpr, tkCurlyRi)
@@ -874,18 +935,9 @@ proc primarySuffix(p: var Parser, r: PNode,
           # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet
           # solution, but pragmas.nim can't handle that
           result = commandExpr(p, result, mode)
-          if mode == pmTypeDef:
-            var isFirstParam = false
-            while p.tok.tokType == tkComma:
-              getTok(p)
-              optInd(p, result)
-              result.add(commandParam(p, isFirstParam, mode))
         break
     else:
       break
-  # type sections allow post-expr blocks
-  if mode == pmTypeDef:
-    result = postExprBlocks(p, result)
 
 proc parseOperators(p: var Parser, headNode: PNode,
                     limit: int, mode: PrimaryMode): PNode =
@@ -910,9 +962,13 @@ proc parseOperators(p: var Parser, headNode: PNode,
     a.add(b)
     result = a
     opPrec = getPrecedence(p.tok)
+  setEndInfo()
 
 proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode =
+  var mode = mode
   result = primary(p, mode)
+  if mode == pmTrySimple:
+    mode = pmNormal
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)) and
      mode == pmNormal:
     var pragmaExp = newNodeP(nkPragmaExpr, p)
@@ -956,10 +1012,11 @@ proc parsePragma(p: var Parser): PNode =
   when defined(nimpretty):
     dec p.em.doIndentMore
     dec p.em.keepIndents
+  setEndInfo()
 
 proc identVis(p: var Parser; allowDot=false): PNode =
   #| identVis = symbol OPR?  # postfix position
-  #| identVisDot = symbol '.' optInd symbol OPR?
+  #| identVisDot = symbol '.' optInd symbolOrKeyword OPR?
   var a = parseSymbol(p)
   if p.tok.tokType == tkOpr:
     when defined(nimpretty):
@@ -993,9 +1050,9 @@ type
 
 proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
   #| declColonEquals = identWithPragma (comma identWithPragma)* comma?
-  #|                   (':' optInd typeDesc)? ('=' optInd expr)?
+  #|                   (':' optInd typeDescExpr)? ('=' optInd expr)?
   #| identColonEquals = IDENT (comma IDENT)* comma?
-  #|      (':' optInd typeDesc)? ('=' optInd expr)?)
+  #|      (':' optInd typeDescExpr)? ('=' optInd expr)?)
   var a: PNode
   result = newNodeP(nkIdentDefs, p)
   # progress guaranteed
@@ -1013,7 +1070,7 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
   if p.tok.tokType == tkColon:
     getTok(p)
     optInd(p, result)
-    result.add(parseTypeDesc(p))
+    result.add(parseTypeDesc(p, fullExpr = true))
   else:
     result.add(newNodeP(nkEmpty, p))
     if p.tok.tokType != tkEquals and withBothOptional notin flags:
@@ -1024,11 +1081,13 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode =
     result.add(parseExpr(p))
   else:
     result.add(newNodeP(nkEmpty, p))
+  setEndInfo()
 
 proc parseTuple(p: var Parser, indentAllowed = false): PNode =
-  #| tupleDecl = 'tuple'
-  #|     '[' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']' |
-  #|     COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
+  #| tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
+  #| tupleType = 'tuple' tupleTypeBracket
+  #| tupleDecl = 'tuple' (tupleTypeBracket /
+  #|     COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?)
   result = newNodeP(nkTupleTy, p)
   getTok(p)
   if p.tok.tokType == tkBracketLe:
@@ -1067,6 +1126,7 @@ proc parseTuple(p: var Parser, indentAllowed = false): PNode =
     parMessage(p, errGenerated, "the syntax for tuple types is 'tuple[...]', not 'tuple(...)'")
   else:
     result = newNodeP(nkTupleClassTy, p)
+  setEndInfo()
 
 proc parseParamList(p: var Parser, retColon = true): PNode =
   #| paramList = '(' declColonEquals ^* (comma/semicolon) ')'
@@ -1108,13 +1168,14 @@ proc parseParamList(p: var Parser, retColon = true): PNode =
   if hasRet and p.tok.indent < 0:
     getTok(p)
     optInd(p, result)
-    result[0] = parseTypeDesc(p)
+    result.replaceFirstSon parseTypeDesc(p)
   elif not retColon and not hasParLe:
     # Mark as "not there" in order to mark for deprecation in the semantic pass:
     result = p.emptyNode
   when defined(nimpretty):
     dec p.em.doIndentMore
     dec p.em.keepIndents
+  setEndInfo()
 
 proc optPragmas(p: var Parser): PNode =
   if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)):
@@ -1124,6 +1185,7 @@ proc optPragmas(p: var Parser): PNode =
 
 proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
   #| doBlock = 'do' paramListArrow pragma? colcom stmt
+  result = nil
   var params = parseParamList(p, retColon=false)
   let pragmas = optPragmas(p)
   colcom(p, result)
@@ -1135,35 +1197,41 @@ proc parseDoBlock(p: var Parser; info: TLineInfo): PNode =
     result = newProcNode(nkDo, info,
       body = result, params = params, name = p.emptyNode, pattern = p.emptyNode,
       genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
+  setEndInfo()
 
 proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode =
   #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
+  #| routineType = ('proc' | 'iterator') paramListColon pragma?
   # 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 == tkEquals and isExpr:
     getTok(p)
-    skipComment(p, result)
-    result = newProcNode(kind, info, body = parseStmt(p),
+    result = newProcNode(kind, info, body = p.emptyNode,
       params = params, name = p.emptyNode, pattern = p.emptyNode,
       genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode)
+    skipComment(p, result)
+    result.replaceSon bodyPos, parseStmt(p)
   else:
-    result = newNodeI(nkProcTy, info)
-    if hasSignature:
-      result.add(params)
+    result = newNode(if kind == nkIteratorDef: nkIteratorTy else: nkProcTy, info)
+    if hasSignature or pragmas.kind != nkEmpty:
+      if hasSignature:
+        result.add(params)
+      else: # pragmas but no param list, implies typeclass with pragmas
+        result.add(p.emptyNode)
       if kind == nkFuncDef:
         parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead")
       result.add(pragmas)
+  setEndInfo()
 
 proc isExprStart(p: Parser): bool =
   case p.tok.tokType
   of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor,
      tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics,
      tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr,
-     tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock:
+     tkEnum, tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock:
     result = true
   else: result = false
 
@@ -1176,6 +1244,7 @@ proc parseSymbolList(p: var Parser, result: PNode) =
     if p.tok.tokType != tkComma: break
     getTok(p)
     optInd(p, s)
+  setEndInfo()
 
 proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
                        mode: PrimaryMode): PNode =
@@ -1183,8 +1252,12 @@ proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
   getTok(p)
   if p.tok.indent != -1 and p.tok.indent <= p.currInd: return
   optInd(p, result)
+  let isTypedef = mode == pmTypeDef and p.tok.tokType in {tkObject, tkTuple}
   if not isOperator(p.tok) and isExprStart(p):
-    result.add(primary(p, mode))
+    if isTypedef:
+      result.add(parseTypeDefValue(p))
+    else:
+      result.add(primary(p, mode))
   if kind == nkDistinctTy and p.tok.tokType == tkSymbol:
     # XXX document this feature!
     var nodeKind: TNodeKind
@@ -1198,11 +1271,14 @@ proc parseTypeDescKAux(p: var Parser, kind: TNodeKind,
     let list = newNodeP(nodeKind, p)
     result.add list
     parseSymbolList(p, list)
+  if mode == pmTypeDef and not isTypedef:
+    result = parseOperators(p, result, -1, mode)
+  setEndInfo()
 
 proc parseVarTuple(p: var Parser): PNode
 
 proc parseFor(p: var Parser): PNode =
-  #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
+  #| forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
   #| forExpr = forStmt
   getTokNoInd(p)
   result = newNodeP(nkForStmt, p)
@@ -1223,6 +1299,7 @@ proc parseFor(p: var Parser): PNode =
   result.add(parseExpr(p))
   colcom(p, result)
   result.add(parseStmt(p))
+  setEndInfo()
 
 template nimprettyDontTouch(body) =
   when defined(nimpretty):
@@ -1251,7 +1328,7 @@ proc parseExpr(p: var Parser): PNode =
       result = parseFor(p)
   of tkWhen:
     nimprettyDontTouch:
-      result = parseIfOrWhenExpr(p, nkWhenExpr)
+      result = parseIfOrWhenExpr(p, nkWhenStmt)
   of tkCase:
     # Currently we think nimpretty is good enough with case expressions,
     # so it is allowed to touch them:
@@ -1261,17 +1338,25 @@ proc parseExpr(p: var Parser): PNode =
     nimprettyDontTouch:
       result = parseTry(p, isExpr=true)
   else: result = simpleExpr(p)
+  setEndInfo()
 
 proc parseEnum(p: var Parser): PNode
 proc parseObject(p: var Parser): PNode
 proc parseTypeClass(p: var Parser): PNode
 
 proc primary(p: var Parser, mode: PrimaryMode): PNode =
-  #| primary = operatorB primary primarySuffix* |
-  #|           tupleDecl | routineExpr | enumDecl
-  #|           objectDecl | conceptDecl | ('bind' primary)
-  #|           ('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary
-  #|         /  prefixOperator* identOrLiteral primarySuffix*
+  #| simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
+  #| commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
+  #|                  'static'|'enum'|'tuple'|'object'|'proc')
+  #| primary = simplePrimary (commandStart expr (doBlock extraPostExprBlock*)?)?
+  #|         / operatorB primary
+  #|         / routineExpr
+  #|         / rawTypeDesc
+  #|         / prefixOperator primary
+  # XXX strong spaces need to be reflected in commandStart
+  # command part is handled in the primarySuffix proc
+
+  # prefix operators:
   if isOperator(p.tok):
     # Note 'sigil like' operators are currently not reflected in the grammar
     # and should be removed for Nim 2.0, I don't think anybody uses them.
@@ -1281,67 +1366,46 @@ proc primary(p: var Parser, mode: PrimaryMode): PNode =
     result.add(a)
     getTok(p)
     optInd(p, a)
-    if isSigil:
-      #XXX prefix operators
+    const identOrLiteralKinds = tkBuiltInMagics + {tkSymbol, tkAccent, tkNil,
+      tkIntLit..tkCustomLit, tkCast, tkOut, tkParLe, tkBracketLe, tkCurlyLe}
+    if isSigil and p.tok.tokType in identOrLiteralKinds:
       let baseInd = p.lex.currLineIndent
-      result.add(primary(p, pmSkipSuffix))
+      result.add(identOrLiteral(p, mode))
       result = primarySuffix(p, result, baseInd, mode)
     else:
       result.add(primary(p, pmNormal))
     return
 
   case p.tok.tokType
-  of tkTuple: result = parseTuple(p, mode == pmTypeDef)
-  of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
-  of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef)
+  of tkProc:
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkLambda)
+  of tkFunc:
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkFuncDef)
   of tkIterator:
-    result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda)
-    if result.kind == nkLambda: result.transitionSonsKind(nkIteratorDef)
-    else: result.transitionSonsKind(nkIteratorTy)
-  of tkEnum:
-    if mode == pmTypeDef:
-      prettySection:
-        result = parseEnum(p)
-    else:
-      result = newNodeP(nkEnumTy, p)
-      getTok(p)
-  of tkObject:
-    if mode == pmTypeDef:
-      prettySection:
-        result = parseObject(p)
-    else:
-      result = newNodeP(nkObjectTy, p)
-      getTok(p)
-  of tkConcept:
-    if mode == pmTypeDef:
-      result = parseTypeClass(p)
-    else:
-      parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
+    getTok(p)
+    result = parseProcExpr(p, mode != pmTypeDesc, nkIteratorDef)
   of tkBind:
+    # legacy syntax, no-op in current nim
     result = newNodeP(nkBind, p)
     getTok(p)
     optInd(p, result)
     result.add(primary(p, pmNormal))
-  of tkVar: result = parseTypeDescKAux(p, nkVarTy, mode)
-  of tkOut:
-    # I like this parser extension to be in 1.4 as it still might turn out
-    # useful in the long run.
-    result = parseTypeDescKAux(p, nkMutableTy, mode)
-  of tkRef: result = parseTypeDescKAux(p, nkRefTy, mode)
-  of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, mode)
-  of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, mode)
+  of tkTuple, tkEnum, tkObject, tkConcept,
+    tkVar, tkOut, tkRef, tkPtr, tkDistinct:
+    result = parseTypeDesc(p)
   else:
     let baseInd = p.lex.currLineIndent
     result = identOrLiteral(p, mode)
-    if mode != pmSkipSuffix:
-      result = primarySuffix(p, result, baseInd, mode)
+    result = primarySuffix(p, result, baseInd, mode)
 
 proc binaryNot(p: var Parser; a: PNode): PNode =
-  if p.tok.tokType == tkNot:
+  if p.tok.tokType == tkNot and p.tok.indent < 0:
     let notOpr = newIdentNodeP(p.tok.ident, p)
     getTok(p)
     optInd(p, notOpr)
-    let b = parseExpr(p)
+    let b = primary(p, pmTypeDesc)
     result = newNodeP(nkInfix, p)
     result.add notOpr
     result.add a
@@ -1349,33 +1413,91 @@ proc binaryNot(p: var Parser; a: PNode): PNode =
   else:
     result = a
 
-proc parseTypeDesc(p: var Parser): PNode =
-  #| typeDesc = simpleExpr ('not' expr)?
+proc parseTypeDesc(p: var Parser, fullExpr = false): PNode =
+  #| rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
+  #|                 ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
+  #|                 ('not' primary)?
+  #| typeDescExpr = (routineType / simpleExpr) ('not' primary)?
+  #| typeDesc = rawTypeDesc / typeDescExpr
   newlineWasSplitting(p)
-  result = simpleExpr(p, pmTypeDesc)
+  if fullExpr:
+    result = simpleExpr(p, pmTypeDesc)
+  else:
+    case p.tok.tokType
+    of tkTuple:
+      result = parseTuple(p, false)
+    of tkProc:
+      getTok(p)
+      result = parseProcExpr(p, false, nkLambda)
+    of tkIterator:
+      getTok(p)
+      result = parseProcExpr(p, false, nkIteratorDef)
+    of tkEnum:
+      result = newNodeP(nkEnumTy, p)
+      getTok(p)
+    of tkObject:
+      result = newNodeP(nkObjectTy, p)
+      getTok(p)
+    of tkConcept:
+      result = p.emptyNode
+      parMessage(p, "the 'concept' keyword is only valid in 'type' sections")
+    of tkVar: result = parseTypeDescKAux(p, nkVarTy, pmTypeDesc)
+    of tkOut: result = parseTypeDescKAux(p, nkOutTy, pmTypeDesc)
+    of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDesc)
+    of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDesc)
+    of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDesc)
+    else:
+      result = simpleExpr(p, pmTypeDesc)
   result = binaryNot(p, result)
+  setEndInfo()
 
-proc parseTypeDefAux(p: var Parser): PNode =
-  #| typeDefAux = simpleExpr ('not' expr
-  #|                         | postExprBlocks)?
-  result = simpleExpr(p, pmTypeDef)
+proc parseTypeDefValue(p: var Parser): PNode =
+  #| typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
+  #|                  ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
+  #|                / (simpleExpr (exprEqExpr ^+ comma postExprBlocks?)?))
+  #|                ('not' primary)?
+  case p.tok.tokType
+  of tkTuple: result = parseTuple(p, true)
+  of tkRef: result = parseTypeDescKAux(p, nkRefTy, pmTypeDef)
+  of tkPtr: result = parseTypeDescKAux(p, nkPtrTy, pmTypeDef)
+  of tkDistinct: result = parseTypeDescKAux(p, nkDistinctTy, pmTypeDef)
+  of tkEnum:
+    prettySection:
+      result = parseEnum(p)
+  of tkObject:
+    prettySection:
+      result = parseObject(p)
+  of tkConcept:
+    result = parseTypeClass(p)
+  else:
+    result = simpleExpr(p, pmTypeDef)
+    if p.tok.tokType != tkNot:
+      if result.kind == nkCommand:
+        var isFirstParam = false
+        while p.tok.tokType == tkComma:
+          getTok(p)
+          optInd(p, result)
+          result.add(commandParam(p, isFirstParam, pmTypeDef))
+      result = postExprBlocks(p, result)
   result = binaryNot(p, result)
+  setEndInfo()
 
 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 = newNode(nkCall, n.info)
     result.add n
 
 proc postExprBlocks(p: var Parser, x: PNode): PNode =
-  #| postExprBlocks = ':' stmt? ( IND{=} doBlock
-  #|                            | IND{=} 'of' exprList ':' stmt
-  #|                            | IND{=} 'elif' expr ':' stmt
-  #|                            | IND{=} 'except' exprList ':' stmt
-  #|                            | IND{=} 'finally' ':' stmt
-  #|                            | IND{=} 'else' ':' stmt )*
+  #| extraPostExprBlock = ( IND{=} doBlock
+  #|                      | IND{=} 'of' exprList ':' stmt
+  #|                      | IND{=} 'elif' expr ':' stmt
+  #|                      | IND{=} 'except' optionalExprList ':' stmt
+  #|                      | IND{=} 'finally' ':' stmt
+  #|                      | IND{=} 'else' ':' stmt )
+  #| postExprBlocks = (doBlock / ':' (extraPostExprBlock / stmt)) extraPostExprBlock*
   result = x
   if p.tok.indent >= 0: return
 
@@ -1392,13 +1514,13 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
     result = makeCall(result)
     getTok(p)
     skipComment(p, result)
-    if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
+    if not (p.tok.tokType in {tkOf, tkElif, tkElse, tkExcept, tkFinally} and sameInd(p)):
       var stmtList = newNodeP(nkStmtList, p)
       stmtList.add parseStmt(p)
       # to keep backwards compatibility (see tests/vm/tstringnil)
-      if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]
+      if stmtList.firstSon.kind == nkStmtList: stmtList = stmtList.firstSon
 
-      stmtList.flags.incl nfBlockArg
+      setNodeFlag stmtList, nfBlockArg
       if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty:
         if openingParams.kind == nkEmpty:
           openingParams = newNodeP(nkFormalParams, p)
@@ -1431,7 +1553,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
           nextBlock.add parseExpr(p)
         of tkExcept:
           nextBlock = newNodeP(nkExceptBranch, p)
-          exprList(p, tkColon, nextBlock)
+          optionalExprList(p, tkColon, nextBlock)
         of tkFinally:
           nextBlock = newNodeP(nkFinally, p)
           getTok(p)
@@ -1442,7 +1564,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
         eat(p, tkColon)
         nextBlock.add parseStmt(p)
 
-      nextBlock.flags.incl nfBlockArg
+      setNodeFlag nextBlock, nfBlockArg
       result.add nextBlock
 
       if nextBlock.kind in {nkElse, nkFinally}: break
@@ -1451,12 +1573,10 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode =
       parMessage(p, "expected ':'")
 
 proc parseExprStmt(p: var Parser): PNode =
-  #| exprStmt = simpleExpr
-  #|          (( '=' optInd expr colonBody? )
-  #|          / ( expr ^+ comma
-  #|              postExprBlocks
-  #|            ))?
-  var a = simpleExpr(p)
+  #| exprStmt = simpleExpr postExprBlocks?
+  #|          / simplePrimary (exprEqExpr ^+ comma) postExprBlocks?
+  #|          / simpleExpr '=' optInd (expr postExprBlocks?)
+  var a = simpleExpr(p, pmTrySimple)
   if p.tok.tokType == tkEquals:
     result = newNodeP(nkAsgn, p)
     getTok(p)
@@ -1466,25 +1586,23 @@ proc parseExprStmt(p: var Parser): PNode =
     result.add(a)
     result.add(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)
-        result.add(commandParam(p, isFirstParam, pmNormal))
-        if p.tok.tokType != tkComma: break
-    elif p.tok.indent < 0 and isExprStart(p):
-      result = newTreeI(nkCommand, a.info, a)
+    # if an expression is starting here, a simplePrimary was parsed and
+    # this is the start of a command
+    if p.tok.indent < 0 and isExprStart(p):
+      result = newTree(nkCommand, a.info, a)
+      let baseIndent = p.currInd
       while true:
         result.add(commandParam(p, isFirstParam, pmNormal))
-        if p.tok.tokType != tkComma: break
+        if p.tok.tokType != tkComma or
+          (p.tok.indent >= 0 and p.tok.indent < baseIndent):
+          break
         getTok(p)
         optInd(p, result)
     else:
       result = a
     result = postExprBlocks(p, result)
+  setEndInfo()
 
 proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
   result = parseExpr(p)
@@ -1496,6 +1614,7 @@ proc parseModuleName(p: var Parser, kind: TNodeKind): PNode =
       getTok(p)
       result.add(a)
       result.add(parseExpr(p))
+  setEndInfo()
 
 proc parseImport(p: var Parser, kind: TNodeKind): PNode =
   #| importStmt = 'import' optInd expr
@@ -1524,6 +1643,7 @@ proc parseImport(p: var Parser, kind: TNodeKind): PNode =
       getTok(p)
       optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
 proc parseIncludeStmt(p: var Parser): PNode =
   #| includeStmt = 'include' optInd expr ^+ comma
@@ -1540,6 +1660,7 @@ proc parseIncludeStmt(p: var Parser): PNode =
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
 proc parseFromStmt(p: var Parser): PNode =
   #| fromStmt = 'from' expr 'import' optInd expr (comma expr)*
@@ -1560,6 +1681,7 @@ proc parseFromStmt(p: var Parser): PNode =
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
 proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
   #| returnStmt = 'return' optInd expr?
@@ -1581,6 +1703,7 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode =
     var e = parseExpr(p)
     e = postExprBlocks(p, e)
     result.add(e)
+  setEndInfo()
 
 proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
   #| condStmt = expr colcom stmt COMMENT?
@@ -1605,11 +1728,12 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode =
     colcom(p, branch)
     branch.add(parseStmt(p))
     result.add(branch)
+  setEndInfo()
 
 proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
-  #| condExpr = expr colcom expr optInd
-  #|         ('elif' expr colcom expr optInd)*
-  #|          'else' colcom expr
+  #| condExpr = expr colcom stmt optInd
+  #|         ('elif' expr colcom stmt optInd)*
+  #|          'else' colcom stmt
   #| ifExpr = 'if' condExpr
   #| whenExpr = 'when' condExpr
   result = newNodeP(kind, p)
@@ -1629,6 +1753,7 @@ proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode =
     colcom(p, branch)
     branch.add(parseStmt(p))
     result.add(branch)
+  setEndInfo()
 
 proc parseWhile(p: var Parser): PNode =
   #| whileStmt = 'while' expr colcom stmt
@@ -1638,6 +1763,7 @@ proc parseWhile(p: var Parser): PNode =
   result.add(parseExpr(p))
   colcom(p, result)
   result.add(parseStmt(p))
+  setEndInfo()
 
 proc parseCase(p: var Parser): PNode =
   #| ofBranch = 'of' exprList colcom stmt
@@ -1685,24 +1811,27 @@ proc parseCase(p: var Parser): PNode =
 
   if wasIndented:
     p.currInd = oldInd
+  setEndInfo()
 
 proc parseTry(p: var Parser; isExpr: bool): PNode =
   #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
-  #|            (IND{=}? 'except' exprList colcom stmt)*
+  #|            (IND{=}? 'except' optionalExprList colcom stmt)*
   #|            (IND{=}? 'finally' colcom stmt)?
   #| tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
-  #|            (optInd 'except' exprList colcom stmt)*
+  #|            (optInd 'except' optionalExprList colcom stmt)*
   #|            (optInd 'finally' colcom stmt)?
   result = newNodeP(nkTryStmt, p)
+  let parentIndent = p.currInd # isExpr
   getTok(p)
   colcom(p, result)
   result.add(parseStmt(p))
   var b: PNode = nil
-  while sameOrNoInd(p) or isExpr:
+
+  while sameOrNoInd(p) or (isExpr and parentIndent <= p.tok.indent):
     case p.tok.tokType
     of tkExcept:
       b = newNodeP(nkExceptBranch, p)
-      exprList(p, tkColon, b)
+      optionalExprList(p, tkColon, b)
     of tkFinally:
       b = newNodeP(nkFinally, p)
       getTok(p)
@@ -1711,12 +1840,14 @@ proc parseTry(p: var Parser; isExpr: bool): PNode =
     b.add(parseStmt(p))
     result.add(b)
   if b == nil: parMessage(p, "expected 'except'")
+  setEndInfo()
 
 proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   colcom(p, result)
   result.add(parseStmt(p))
+  setEndInfo()
 
 proc parseBlock(p: var Parser): PNode =
   #| blockStmt = 'block' symbol? colcom stmt
@@ -1727,6 +1858,7 @@ proc parseBlock(p: var Parser): PNode =
   else: result.add(parseSymbol(p))
   colcom(p, result)
   result.add(parseStmt(p))
+  setEndInfo()
 
 proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
   #| staticStmt = 'static' colcom stmt
@@ -1735,6 +1867,7 @@ proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode =
   getTok(p)
   colcom(p, result)
   result.add(parseStmt(p))
+  setEndInfo()
 
 proc parseAsm(p: var Parser): PNode =
   #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT)
@@ -1751,6 +1884,7 @@ proc parseAsm(p: var Parser): PNode =
     result.add(p.emptyNode)
     return
   getTok(p)
+  setEndInfo()
 
 proc parseGenericParam(p: var Parser): PNode =
   #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)?
@@ -1786,6 +1920,7 @@ proc parseGenericParam(p: var Parser): PNode =
     result.add(parseExpr(p))
   else:
     result.add(p.emptyNode)
+  setEndInfo()
 
 proc parseGenericParamList(p: var Parser): PNode =
   #| genericParamList = '[' optInd
@@ -1804,12 +1939,14 @@ proc parseGenericParamList(p: var Parser): PNode =
     skipComment(p, a)
   optPar(p)
   eat(p, tkBracketRi)
+  setEndInfo()
 
 proc parsePattern(p: var Parser): PNode =
   #| pattern = '{' stmt '}'
   eat(p, tkCurlyLe)
   result = parseStmt(p)
   eat(p, tkCurlyRi)
+  setEndInfo()
 
 proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
   #| indAndComment = (IND{>} COMMENT)? | COMMENT?
@@ -1818,6 +1955,12 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
   result = newNodeP(kind, p)
   getTok(p)
   optInd(p, result)
+  if kind in {nkProcDef, nkLambda, nkIteratorDef, nkFuncDef} and
+      p.tok.tokType notin {tkSymbol, tokKeywordLow..tokKeywordHigh, tkAccent}:
+    # no name; lambda or proc type
+    # in every context that we can parse a routine, we can also parse these
+    result = parseProcExpr(p, true, if kind == nkProcDef: nkLambda else: kind)
+    return
   result.add(identVis(p))
   if p.tok.tokType == tkCurlyLe and p.validInd: result.add(p.parsePattern)
   else: result.add(p.emptyNode)
@@ -1838,16 +1981,17 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode =
   else:
     result.add(p.emptyNode)
   indAndComment(p, result, maybeMissEquals)
-  let body = result[^1]
-  if body.kind == nkStmtList and body.len > 0 and body[0].comment.len > 0 and body[0].kind != nkCommentStmt:
+  let body = result.lastSon
+  if body.kind == nkStmtList and body.hasSon and body.firstSon.comment.len > 0 and body.firstSon.kind != nkCommentStmt:
     if result.comment.len == 0:
       # proc fn*(a: int): int = a ## foo
       # => moves comment `foo` to `fn`
-      result.comment = body[0].comment
-      body[0].comment = ""
+      result.comment = body.firstSon.comment
+      body.firstSon.comment = ""
     #else:
     #  assert false, p.lex.config$body.info # avoids hard to track bugs, fail early.
     # Yeah, that worked so well. There IS a bug in this logic, now what?
+  setEndInfo()
 
 proc newCommentStmt(p: var Parser): PNode =
   #| commentStmt = COMMENT
@@ -1877,12 +2021,13 @@ proc parseSection(p: var Parser, kind: TNodeKind,
         else:
           parMessage(p, errIdentifierExpected, p.tok)
           break
-    if result.len == 0: parMessage(p, errIdentifierExpected, p.tok)
+    if not result.hasSon: parMessage(p, errIdentifierExpected, p.tok)
   elif p.tok.tokType in {tkSymbol, tkAccent, tkParLe} and p.tok.indent < 0:
     # tkParLe is allowed for ``var (x, y) = ...`` tuple parsing
     result.add(defparser(p))
   else:
     parMessage(p, errIdentifierExpected, p.tok)
+  setEndInfo()
 
 proc parseEnum(p: var Parser): PNode =
   #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+
@@ -1927,8 +2072,9 @@ proc parseEnum(p: var Parser): PNode =
     if p.tok.indent >= 0 and p.tok.indent <= p.currInd or
         p.tok.tokType == tkEof:
       break
-  if result.len <= 1:
+  if not result.has2Sons:
     parMessage(p, errIdentifierExpected, p.tok)
+  setEndInfo()
 
 proc parseObjectPart(p: var Parser): PNode
 proc parseObjectWhen(p: var Parser): PNode =
@@ -1954,22 +2100,19 @@ proc parseObjectWhen(p: var Parser): PNode =
     branch.add(parseObjectPart(p))
     flexComment(p, branch)
     result.add(branch)
+  setEndInfo()
 
 proc parseObjectCase(p: var Parser): PNode =
   #| objectBranch = 'of' exprList colcom objectPart
   #| objectBranches = objectBranch (IND{=} objectBranch)*
   #|                       (IND{=} 'elif' expr colcom objectPart)*
   #|                       (IND{=} 'else' colcom objectPart)?
-  #| objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
+  #| objectCase = 'case' declColonEquals ':'? COMMENT?
   #|             (IND{>} objectBranches DED
   #|             | IND{=} objectBranches)
   result = newNodeP(nkRecCase, p)
   getTokNoInd(p)
-  var a = newNodeP(nkIdentDefs, p)
-  a.add(identWithPragma(p))
-  eat(p, tkColon)
-  a.add(parseTypeDesc(p))
-  a.add(p.emptyNode)
+  var a = parseIdentColonEquals(p, {withPragma})
   result.add(a)
   if p.tok.tokType == tkColon: getTok(p)
   flexComment(p, result)
@@ -1999,6 +2142,7 @@ proc parseObjectCase(p: var Parser): PNode =
     if b.kind == nkElse: break
   if wasIndented:
     p.currInd = oldInd
+  setEndInfo()
 
 proc parseObjectPart(p: var Parser): PNode =
   #| objectPart = IND{>} objectPart^+IND{=} DED
@@ -2031,6 +2175,7 @@ proc parseObjectPart(p: var Parser): PNode =
       result = p.emptyNode
   else:
     result = p.emptyNode
+  setEndInfo()
 
 proc parseObject(p: var Parser): PNode =
   #| objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
@@ -2051,11 +2196,13 @@ proc parseObject(p: var Parser): PNode =
     result.add(p.emptyNode)
   else:
     result.add(parseObjectPart(p))
+  setEndInfo()
 
 proc parseTypeClassParam(p: var Parser): PNode =
   let modifier =
     case p.tok.tokType
-    of tkOut, tkVar: nkVarTy
+    of tkVar: nkVarTy
+    of tkOut: nkOutTy
     of tkPtr: nkPtrTy
     of tkRef: nkRefTy
     of tkStatic: nkStaticTy
@@ -2068,9 +2215,10 @@ proc parseTypeClassParam(p: var Parser): PNode =
     result.add(p.parseSymbol)
   else:
     result = p.parseSymbol
+  setEndInfo()
 
 proc parseTypeClass(p: var Parser): PNode =
-  #| conceptParam = ('var' | 'out')? symbol
+  #| conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
   #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
   #|               &IND{>} stmt
   result = newNodeP(nkTypeClassTy, p)
@@ -2111,10 +2259,11 @@ proc parseTypeClass(p: var Parser): PNode =
     result.add(p.emptyNode)
   else:
     result.add(parseStmt(p))
+  setEndInfo()
 
 proc parseTypeDef(p: var Parser): PNode =
   #|
-  #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefAux
+  #| typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
   #|             indAndComment?
   result = newNodeP(nkTypeDef, p)
   var identifier = identVis(p, allowDot=true)
@@ -2140,26 +2289,39 @@ proc parseTypeDef(p: var Parser): PNode =
     result.info = parLineInfo(p)
     getTok(p)
     optInd(p, result)
-    result.add(parseTypeDefAux(p))
+    result.add(parseTypeDefValue(p))
   else:
     result.add(p.emptyNode)
   indAndComment(p, result)    # special extension!
+  setEndInfo()
 
 proc parseVarTuple(p: var Parser): PNode =
-  #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
+  #| varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
+  #| varTuple = varTupleLhs '=' optInd expr
   result = newNodeP(nkVarTuple, p)
   getTok(p)                   # skip '('
   optInd(p, result)
   # progress guaranteed
-  while p.tok.tokType in {tkSymbol, tkAccent}:
-    var a = identWithPragma(p, allowDot=true)
+  while p.tok.tokType in {tkSymbol, tkAccent, tkParLe}:
+    var a: PNode
+    if p.tok.tokType == tkParLe:
+      a = parseVarTuple(p)
+      a.add(p.emptyNode)
+    else:
+      a = identWithPragma(p, allowDot=true)
     result.add(a)
     if p.tok.tokType != tkComma: break
     getTok(p)
     skipComment(p, a)
-  result.add(p.emptyNode)         # no type desc
   optPar(p)
   eat(p, tkParRi)
+  if p.tok.tokType == tkColon:
+    getTok(p)
+    optInd(p, result)
+    result.add(parseTypeDesc(p, fullExpr = true))
+  else:
+    result.add(p.emptyNode)         # no type desc
+  setEndInfo()
 
 proc parseVariable(p: var Parser): PNode =
   #| colonBody = colcom stmt postExprBlocks?
@@ -2170,8 +2332,9 @@ proc parseVariable(p: var Parser): PNode =
     optInd(p, result)
     result.add(parseExpr(p))
   else: result = parseIdentColonEquals(p, {withPragma, withDot})
-  result[^1] = postExprBlocks(p, result[^1])
+  result.setLastSon postExprBlocks(p, result.lastSon)
   indAndComment(p, result)
+  setEndInfo()
 
 proc parseConstant(p: var Parser): PNode =
   #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
@@ -2188,9 +2351,10 @@ proc parseConstant(p: var Parser): PNode =
   eat(p, tkEquals)
   optInd(p, result)
   #add(result, parseStmtListExpr(p))
-  result.add(parseExpr(p))
-  result[^1] = postExprBlocks(p, result[^1])
+  let a = parseExpr(p)
+  result.add postExprBlocks(p, a)
   indAndComment(p, result)
+  setEndInfo()
 
 proc parseBind(p: var Parser, k: TNodeKind): PNode =
   #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma
@@ -2206,17 +2370,19 @@ proc parseBind(p: var Parser, k: TNodeKind): PNode =
     getTok(p)
     optInd(p, a)
   #expectNl(p)
+  setEndInfo()
 
 proc parseStmtPragma(p: var Parser): PNode =
   #| pragmaStmt = pragma (':' COMMENT? stmt)?
   result = parsePragma(p)
   if p.tok.tokType == tkColon and p.tok.indent < 0:
     let a = result
-    result = newNodeI(nkPragmaBlock, a.info)
+    result = newNode(nkPragmaBlock, a.info)
     getTok(p)
     skipComment(p, result)
     result.add a
     result.add parseStmt(p)
+  setEndInfo()
 
 proc simpleStmt(p: var Parser): PNode =
   #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt
@@ -2359,27 +2525,13 @@ proc parseStmt(p: var Parser): PNode =
           if p.tok.tokType != tkSemiColon: break
           getTok(p)
           if err and p.tok.tokType == tkEof: break
-
-proc parseAll(p: var Parser): PNode =
-  ## Parses the rest of the input stream held by the parser into a PNode.
-  result = newNodeP(nkStmtList, p)
-  while p.tok.tokType != tkEof:
-    p.hasProgress = false
-    var a = complexOrSimpleStmt(p)
-    if a.kind != nkEmpty and p.hasProgress:
-      result.add(a)
-    else:
-      parMessage(p, errExprExpected, p.tok)
-      # bugfix: consume a token here to prevent an endless loop:
-      getTok(p)
-    if p.tok.indent != 0:
-      parMessage(p, errInvalidIndentation)
+  setEndInfo()
 
 proc checkFirstLineIndentation*(p: var Parser) =
-  if p.tok.indent != 0 and p.tok.strongSpaceA > 0:
+  if p.tok.indent != 0 and tsLeading in p.tok.spacing:
     parMessage(p, errInvalidIndentation)
 
-proc parseTopLevelStmt(p: var Parser): PNode =
+proc parseTopLevelStmt*(p: var Parser): PNode =
   ## Implements an iterator which, when called repeatedly, returns the next
   ## top-level statement or emptyNode if end of stream.
   result = p.emptyNode
@@ -2407,6 +2559,17 @@ proc parseTopLevelStmt(p: var Parser): PNode =
       result = complexOrSimpleStmt(p)
       if result.kind == nkEmpty: parMessage(p, errExprExpected, p.tok)
       break
+  setEndInfo()
+
+proc parseAll*(p: var Parser): PNode =
+  ## Parses the rest of the input stream held by the parser into a PNode.
+  result = newNodeP(nkStmtList, p)
+  while true:
+    let nextStmt = p.parseTopLevelStmt()
+    if nextStmt.kind == nkEmpty:
+      break
+    result &= nextStmt
+  setEndInfo()
 
 proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
                   filename: string = ""; line: int = 0;
@@ -2418,9 +2581,10 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef;
   var stream = llStreamOpen(s)
   stream.lineOffset = line
 
-  var parser: Parser
-  parser.lex.errorHandler = errorHandler
-  openParser(parser, AbsoluteFile filename, stream, cache, config)
+  var p = Parser()
+  p.lex.errorHandler = errorHandler
+  openParser(p, AbsoluteFile filename, stream, cache, config)
 
-  result = parser.parseAll
-  closeParser(parser)
+  result = p.parseAll
+  closeParser(p)
+  setEndInfo()
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 46c36f9d1..d6b141078 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -14,10 +14,22 @@ import
   options, ast, llstream, msgs,
   idents,
   syntaxes, modulegraphs, reorder,
-  lineinfos, pathutils, std/sha1, packages
+  lineinfos,
+  pipelineutils,
+  modules, pathutils, packages,
+  sem, semdata
+
+import ic/replayer
+
+export skipCodegen, resolveMod, prepareConfigNotes
+
+when defined(nimsuggest):
+  import ../dist/checksums/src/checksums/sha1
 
 when defined(nimPreviewSlimSystem):
-  import std/syncio
+  import std/[syncio, assertions]
+
+import std/tables
 
 type
   TPassData* = tuple[input: PNode, closeOutput: PNode]
@@ -35,12 +47,6 @@ proc makePass*(open: TPassOpen = nil,
   result.process = process
   result.isFrontend = isFrontend
 
-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 = config.errorCounter > 0
-
 const
   maxPasses = 10
 
@@ -77,13 +83,6 @@ proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray)
       if isNil(m): return false
   result = true
 
-proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex =
-  let fullPath = findModule(conf, module, relativeTo)
-  if fullPath.isEmpty:
-    result = InvalidFileIdx
-  else:
-    result = fileInfoIdx(conf, fullPath)
-
 proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
                       a: var TPassContextArray; m: PSym) =
   # XXX fixme this should actually be relative to the config file!
@@ -97,23 +96,6 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod
       importStmt.add str
       if not processTopLevelStmt(graph, importStmt, a): break
 
-const
-  imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef,
-    nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma,
-    nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt}
-
-proc prepareConfigNotes(graph: ModuleGraph; module: PSym) =
-  # don't be verbose unless the module belongs to the main package:
-  if graph.config.belongsToProjectPackage(module):
-    graph.config.notes = graph.config.mainPackageNotes
-  else:
-    if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
-    graph.config.notes = graph.config.foreignPackageNotes
-
-proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
-  result = true
-  #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
-
 proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
                     stream: PLLStream): bool {.discardable.} =
   if graph.stopCompile(): return true
@@ -140,7 +122,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
   while true:
     openParser(p, fileIdx, s, graph.cache, graph.config)
 
-    if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
+    if (not belongsToStdlib(graph, module)) or module.name.s == "distros":
       # 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
@@ -150,43 +132,22 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
         processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module
 
     checkFirstLineIndentation(p)
-    while true:
-      if graph.stopCompile(): break
+    block processCode:
+      if graph.stopCompile(): break processCode
       var n = parseTopLevelStmt(p)
-      if n.kind == nkEmpty: break
-      if (sfSystemModule notin module.flags and
-          ({sfNoForward, sfReorder} * module.flags != {} or
-          codeReordering in graph.config.features)):
-        # read everything, no streaming possible
-        var sl = newNodeI(nkStmtList, n.info)
-        sl.add n
-        while true:
-          var n = parseTopLevelStmt(p)
-          if n.kind == nkEmpty: break
-          sl.add n
-        if sfReorder in module.flags or codeReordering in graph.config.features:
-          sl = reorder(graph, sl, module)
-        discard processTopLevelStmt(graph, sl, a)
-        break
-      elif n.kind in imperativeCode:
-        # read everything until the next proc declaration etc.
-        var sl = newNodeI(nkStmtList, n.info)
+      if n.kind == nkEmpty: break processCode
+
+      # read everything, no streaming possible
+      var sl = newNodeI(nkStmtList, n.info)
+      sl.add n
+      while true:
+        var n = parseTopLevelStmt(p)
+        if n.kind == nkEmpty: break
         sl.add n
-        var rest: PNode = nil
-        while true:
-          var n = parseTopLevelStmt(p)
-          if n.kind == nkEmpty or n.kind notin imperativeCode:
-            rest = n
-            break
-          sl.add n
-        #echo "-----\n", sl
-        if not processTopLevelStmt(graph, sl, a): break
-        if rest != nil:
-          #echo "-----\n", rest
-          if not processTopLevelStmt(graph, rest, a): break
-      else:
-        #echo "----- single\n", n
-        if not processTopLevelStmt(graph, n, a): break
+      if sfReorder in module.flags or codeReordering in graph.config.features:
+        sl = reorder(graph, sl, module)
+      discard processTopLevelStmt(graph, sl, a)
+
     closeParser(p)
     if s.kind != llsStdIn: break
   closePasses(graph, a)
@@ -196,3 +157,99 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
     # They are responsible for closing the rod files. See `cbackend.nim`.
     closeRodFile(graph, module)
   result = true
+
+proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym =
+  var flags = flags
+  if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
+  result = graph.getModule(fileIdx)
+
+  template processModuleAux(moduleStatus) =
+    onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
+    var s: PLLStream = nil
+    if sfMainModule in flags:
+      if graph.config.projectIsStdin: s = stdin.llStreamOpen
+      elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
+    discard processModule(graph, result, idGeneratorFromModule(result), s)
+  if result == nil:
+    var cachedModules: seq[FileIndex] = @[]
+    result = moduleFromRodFile(graph, fileIdx, cachedModules)
+    let filename = AbsoluteFile toFullPath(graph.config, fileIdx)
+    if result == nil:
+      result = newModule(graph, fileIdx)
+      result.flags.incl flags
+      registerModule(graph, result)
+      processModuleAux("import")
+    else:
+      if sfSystemModule in flags:
+        graph.systemModule = result
+      partialInitModule(result, graph, fileIdx, filename)
+    for m in cachedModules:
+      registerModuleById(graph, m)
+      replayStateChanges(graph.packed.pm[m.int].module, graph)
+      replayGenericCacheInformation(graph, m.int)
+  elif graph.isDirty(result):
+    result.flags.excl sfDirty
+    # reset module fields:
+    initStrTables(graph, result)
+    result.ast = nil
+    processModuleAux("import(dirty)")
+    graph.markClientsDirty(fileIdx)
+
+proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
+  # this is called by the semantic checking phase
+  assert graph.config != nil
+  result = compileModule(graph, fileIdx, {}, s)
+  graph.addDep(s, fileIdx)
+  # keep track of import relationships
+  if graph.config.hcrOn:
+    graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
+  #if sfSystemModule in result.flags:
+  #  localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  graph.config.notes =
+    if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
+
+proc connectCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = modules.includeModule
+  graph.importModuleCallback = importModule
+
+proc compileSystemModule*(graph: ModuleGraph) =
+  if graph.systemModule == nil:
+    connectCallbacks(graph)
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
+        graph.config.libpath / RelativeFile"system.nim")
+    discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule})
+
+proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
+  connectCallbacks(graph)
+  let conf = graph.config
+  wantMainModule(conf)
+  configComplete(graph)
+
+  let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
+  let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
+  conf.projectMainIdx2 = projectFile
+
+  let packSym = getPackage(graph, projectFile)
+  graph.config.mainPackageId = packSym.getPackageId
+  graph.importStack.add projectFile
+
+  if projectFile == systemFileIdx:
+    discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule})
+  else:
+    graph.compileSystemModule()
+    discard graph.compileModule(projectFile, {sfMainModule})
+
+proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
+  result = preparePContext(graph, module, idgen)
+
+proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
+  var c = PContext(context)
+  closePContext(graph, c, n)
+
+proc mySemProcess(context: PPassContext, n: PNode): PNode =
+  result = semWithPContext(PContext(context), n)
+
+const semPass* = makePass(mySemOpen, mySemProcess, mySemClose,
+                          isFrontend = true)
diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim
index 1ef0143d5..5f6212bb2 100644
--- a/compiler/pathutils.nim
+++ b/compiler/pathutils.nim
@@ -10,7 +10,7 @@
 ## Path handling utilities for Nim. Strictly typed code in order
 ## to avoid the never ending time sink in getting path handling right.
 
-import os, pathnorm
+import std/[os, pathnorm, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
@@ -102,3 +102,52 @@ when true:
   proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.}
 
   proc writeFile*(x: AbsoluteFile; content: string) {.borrow.}
+
+proc skipHomeDir(x: string): int =
+  when defined(windows):
+    if x.continuesWith("Users/", len("C:/")):
+      result = 3
+    else:
+      result = 0
+  else:
+    if x.startsWith("/home/") or x.startsWith("/Users/"):
+      result = 3
+    elif x.startsWith("/mnt/") and x.continuesWith("/Users/", len("/mnt/c")):
+      result = 5
+    else:
+      result = 0
+
+proc relevantPart(s: string; afterSlashX: int): string =
+  result = newStringOfCap(s.len - 8)
+  var slashes = afterSlashX
+  for i in 0..<s.len:
+    if slashes == 0:
+      result.add s[i]
+    elif s[i] == '/':
+      dec slashes
+
+template canonSlashes(x: string): string =
+  when defined(windows):
+    x.replace('\\', '/')
+  else:
+    x
+
+proc customPathImpl(x: string): string =
+  # Idea: Encode a "protocol" via "//protocol/path" which is not ambiguous
+  # as path canonicalization would have removed the double slashes.
+  # /mnt/X/Users/Y
+  # X:\\Users\Y
+  # /home/Y
+  # -->
+  # //user/
+  if not isAbsolute(x):
+    result = customPathImpl(canonSlashes(getCurrentDir() / x))
+  else:
+    let slashes = skipHomeDir(x)
+    if slashes > 0:
+      result = "//user/" & relevantPart(x, slashes)
+    else:
+      result = x
+
+proc customPath*(x: string): string =
+  customPathImpl canonSlashes x
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 87e9c825c..32ec7fb53 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -29,6 +29,8 @@ type
 proc getLazy(c: PPatternContext, sym: PSym): PNode =
   if c.mappingIsFull:
     result = c.mapping[sym.position]
+  else:
+    result = nil
 
 proc putLazy(c: PPatternContext, sym: PSym, n: PNode) =
   if not c.mappingIsFull:
@@ -44,7 +46,7 @@ proc canonKind(n: PNode): TNodeKind =
   case result
   of nkCallKinds: result = nkCall
   of nkStrLit..nkTripleStrLit: result = nkStrLit
-  of nkFastAsgn: result = nkAsgn
+  of nkFastAsgn, nkSinkAsgn: result = nkAsgn
   else: discard
 
 proc sameKinds(a, b: PNode): bool {.inline.} =
@@ -65,14 +67,21 @@ proc sameTrees*(a, b: PNode): bool =
         for i in 0..<a.len:
           if not sameTrees(a[i], b[i]): return
         result = true
+      else:
+        result = false
+  else:
+    result = false
 
 proc inSymChoice(sc, x: PNode): bool =
   if sc.kind == nkClosedSymChoice:
+    result = false
     for i in 0..<sc.len:
       if sc[i].sym == x.sym: return true
   elif sc.kind == nkOpenSymChoice:
     # same name suffices for open sym choices!
     result = sc[0].sym.name.id == x.sym.name.id
+  else:
+    result = false
 
 proc checkTypes(c: PPatternContext, p: PSym, n: PNode): bool =
   # check param constraints first here as this is quite optimized:
@@ -88,6 +97,7 @@ proc isPatternParam(c: PPatternContext, p: PNode): bool {.inline.} =
   result = p.kind == nkSym and p.sym.kind == skParam and p.sym.owner == c.owner
 
 proc matchChoice(c: PPatternContext, p, n: PNode): bool =
+  result = false
   for i in 1..<p.len:
     if matches(c, p[i], n): return true
 
@@ -99,6 +109,8 @@ proc bindOrCheck(c: PPatternContext, param: PSym, n: PNode): bool =
   elif n.kind == nkArgList or checkTypes(c, param, n):
     putLazy(c, param, n)
     result = true
+  else:
+    result = false
 
 proc gather(c: PPatternContext, param: PSym, n: PNode) =
   var pp = getLazy(c, param)
@@ -132,6 +144,10 @@ proc matchNested(c: PPatternContext, p, n: PNode, rpn: bool): bool =
     var arglist = newNodeI(nkArgList, n.info)
     if matchStarAux(c, p, n, arglist, rpn):
       result = bindOrCheck(c, p[2].sym, arglist)
+    else:
+      result = false
+  else:
+    result = false
 
 proc matches(c: PPatternContext, p, n: PNode): bool =
   let n = skipHidden(n)
@@ -146,7 +162,8 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
   elif n.kind == nkSym and n.sym.kind == skConst:
     # try both:
     if p.kind == nkSym: result = p.sym == n.sym
-    elif matches(c, p, n.sym.ast): result = true
+    elif matches(c, p, n.sym.astdef): result = true
+    else: result = false
   elif p.kind == nkPattern:
     # pattern operators: | *
     let opr = p[0].ident.s
@@ -155,7 +172,9 @@ 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[1], n)
-    else: doAssert(false, "invalid pattern")
+    else:
+      result = false
+      doAssert(false, "invalid pattern")
     # template {add(a, `&` * b)}(a: string{noalias}, b: varargs[string]) =
     #   a.add(b)
   elif p.kind == nkCurlyExpr:
@@ -163,10 +182,14 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
       if matches(c, p[0], n):
         gather(c, p[1][1].sym, n)
         result = true
+      else:
+        result = false
     else:
       assert isPatternParam(c, p[1])
       if matches(c, p[0], n):
         result = bindOrCheck(c, p[1].sym, n)
+      else:
+        result = false
   elif sameKinds(p, n):
     case p.kind
     of nkSym: result = p.sym == n.sym
@@ -179,6 +202,7 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
     else:
       # special rule for p(X) ~ f(...); this also works for stuff like
       # partial case statements, etc! - Not really ... :-/
+      result = false
       let v = lastSon(p)
       if isPatternParam(c, v) and v.sym.typ.kind == tyVarargs:
         var arglist: PNode
@@ -207,6 +231,8 @@ proc matches(c: PPatternContext, p, n: PNode): bool =
         for i in 0..<p.len:
           if not matches(c, p[i], n[i]): return
         result = true
+  else:
+    result = false
 
 proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
   proc matchRange(c: PPatternContext, p, n: PNode, i: int): bool =
@@ -219,6 +245,7 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
     result = true
 
   if p.kind == nkStmtList and n.kind == p.kind and p.len < n.len:
+    result = nil
     let n = flattenStmts(n)
     # no need to flatten 'p' here as that has already been done
     for i in 0..n.len - p.len:
@@ -231,8 +258,11 @@ proc matchStmtList(c: PPatternContext, p, n: PNode): PNode =
         break
   elif matches(c, p, n):
     result = n
+  else:
+    result = nil
 
 proc aliasAnalysisRequested(params: PNode): bool =
+  result = false
   if params.len >= 2:
     for i in 1..<params.len:
       let param = params[i].sym
@@ -246,10 +276,7 @@ proc addToArgList(result, n: PNode) =
 
 proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   ## returns a tree to semcheck if the rule triggered; nil otherwise
-  var ctx: TPatternContext
-  ctx.owner = s
-  ctx.c = c
-  ctx.formals = s.typ.len-1
+  var ctx = TPatternContext(owner: s, c: c, formals: s.typ.paramsLen)
   var m = matchStmtList(ctx, s.ast[patternPos], n)
   if isNil(m): return nil
   # each parameter should have been bound; we simply setup a call and
@@ -258,9 +285,11 @@ proc applyRule*(c: PContext, s: PSym, n: PNode): PNode =
   result.add(newSymNode(s, n.info))
   let params = s.typ.n
   let requiresAA = aliasAnalysisRequested(params)
-  var args: PNode
-  if requiresAA:
-    args = newNodeI(nkArgList, n.info)
+  var args: PNode =
+    if requiresAA:
+      newNodeI(nkArgList, n.info)
+    else:
+      nil
   for i in 1..<params.len:
     let param = params[i].sym
     let x = getLazy(ctx, param)
diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim
new file mode 100644
index 000000000..55e7fe892
--- /dev/null
+++ b/compiler/pipelines.nim
@@ -0,0 +1,312 @@
+import sem, cgen, modulegraphs, ast, llstream, parser, msgs,
+       lineinfos, reorder, options, semdata, cgendata, modules, pathutils,
+       packages, syntaxes, depends, vm, pragmas, idents, lookups, wordrecg,
+       liftdestructors
+
+import pipelineutils
+
+import ../dist/checksums/src/checksums/sha1
+
+when not defined(leanCompiler):
+  import jsgen, docgen2
+
+import std/[syncio, objectdollar, assertions, tables, strutils, strtabs]
+import renderer
+import ic/replayer
+
+proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) =
+  graph.pipelinePass = pass
+
+proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode =
+  case graph.pipelinePass
+  of CgenPass:
+    result = semNode
+    if bModule != nil:
+      genTopLevelStmt(BModule(bModule), result)
+  of JSgenPass:
+    when not defined(leanCompiler):
+      result = processJSCodeGen(bModule, semNode)
+    else:
+      result = nil
+  of GenDependPass:
+    result = addDotDependency(bModule, semNode)
+  of SemPass:
+    result = graph.emptyNode
+  of Docgen2Pass, Docgen2TexPass:
+    when not defined(leanCompiler):
+      result = processNode(bModule, semNode)
+    else:
+      result = nil
+  of Docgen2JsonPass:
+    when not defined(leanCompiler):
+      result = processNodeJson(bModule, semNode)
+    else:
+      result = nil
+  of EvalPass, InterpreterPass:
+    result = interpreterCode(bModule, semNode)
+  of NonePass:
+    raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind,
+                      m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator,
+                      ) =
+  # XXX fixme this should actually be relative to the config file!
+  let relativeTo = toFullPath(graph.config, m.info)
+  for module in items(implicits):
+    # implicit imports should not lead to a module importing itself
+    if m.position != resolveMod(graph.config, module, relativeTo).int32:
+      var importStmt = newNodeI(nodeKind, m.info)
+      var str = newStrNode(nkStrLit, module)
+      str.info = m.info
+      importStmt.add str
+      message(graph.config, importStmt.info, hintProcessingStmt, $idgen[])
+      let semNode = semWithPContext(ctx, importStmt)
+      if semNode == nil or processPipeline(graph, semNode, bModule) == nil:
+        break
+
+proc prePass(c: PContext; n: PNode) =
+  for son in n:
+    if son.kind == nkPragma:
+      for s in son:
+        var key = if s.kind in nkPragmaCallKinds and s.len > 1: s[0] else: s
+        if key.kind in {nkBracketExpr, nkCast} or key.kind notin nkIdentKinds:
+          continue
+        let ident = whichKeyword(considerQuotedIdent(c, key))
+        case ident
+        of wReorder:
+          pragmaNoForward(c, s, flag = sfReorder)
+        of wExperimental:
+          if isTopLevel(c) and s.kind in nkPragmaCallKinds and s.len == 2:
+            let name = c.semConstExpr(c, s[1])
+            case name.kind
+            of nkStrLit, nkRStrLit, nkTripleStrLit:
+              try:
+                let feature = parseEnum[Feature](name.strVal)
+                if feature == codeReordering:
+                  c.features.incl feature
+                  c.module.flags.incl sfReorder
+              except ValueError:
+                discard
+            else:
+              discard
+        else:
+          discard
+
+proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator;
+                    stream: PLLStream): bool =
+  if graph.stopCompile(): return true
+  var
+    p: Parser = default(Parser)
+    s: PLLStream
+    fileIdx = module.fileIdx
+
+  prepareConfigNotes(graph, module)
+  let ctx = preparePContext(graph, module, idgen)
+  let bModule: PPassContext =
+    case graph.pipelinePass
+    of CgenPass:
+      setupCgen(graph, module, idgen)
+    of JSgenPass:
+      when not defined(leanCompiler):
+        setupJSgen(graph, module, idgen)
+      else:
+        nil
+    of EvalPass, InterpreterPass:
+      setupEvalGen(graph, module, idgen)
+    of GenDependPass:
+      setupDependPass(graph, module, idgen)
+    of Docgen2Pass:
+      when not defined(leanCompiler):
+        openHtml(graph, module, idgen)
+      else:
+        nil
+    of Docgen2TexPass:
+      when not defined(leanCompiler):
+        openTex(graph, module, idgen)
+      else:
+        nil
+    of Docgen2JsonPass:
+      when not defined(leanCompiler):
+        openJson(graph, module, idgen)
+      else:
+        nil
+    of SemPass:
+      nil
+    of NonePass:
+      raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+  if stream == nil:
+    let filename = toFullPathConsiderDirty(graph.config, fileIdx)
+    s = llStreamOpen(filename, fmRead)
+    if s == nil:
+      rawMessage(graph.config, errCannotOpenFile, filename.string)
+      return false
+    graph.interactive = false
+  else:
+    s = stream
+    graph.interactive = stream.kind == llsStdIn
+  while true:
+    syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config)
+
+    if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"):
+      # 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.
+      if module.name.s != "nimscriptapi":
+        processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen
+        processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen
+
+    checkFirstLineIndentation(p)
+    block processCode:
+      if graph.stopCompile(): break processCode
+      var n = parseTopLevelStmt(p)
+      if n.kind == nkEmpty: break processCode
+      # read everything, no streaming possible
+      var sl = newNodeI(nkStmtList, n.info)
+      sl.add n
+      while true:
+        var n = parseTopLevelStmt(p)
+        if n.kind == nkEmpty: break
+        sl.add n
+
+      prePass(ctx, sl)
+      if sfReorder in module.flags or codeReordering in graph.config.features:
+        sl = reorder(graph, sl, module)
+      if graph.pipelinePass != EvalPass:
+        message(graph.config, sl.info, hintProcessingStmt, $idgen[])
+      var semNode = semWithPContext(ctx, sl)
+      discard processPipeline(graph, semNode, bModule)
+
+    closeParser(p)
+    if s.kind != llsStdIn: break
+  let finalNode = closePContext(graph, ctx, nil)
+  case graph.pipelinePass
+  of CgenPass:
+    if bModule != nil:
+      let m = BModule(bModule)
+      finalCodegenActions(graph, m, finalNode)
+      if graph.dispatchers.len > 0:
+        let ctx = preparePContext(graph, module, idgen)
+        for disp in getDispatchers(graph):
+          let retTyp = disp.typ.returnType
+          if retTyp != nil:
+            # TODO: properly semcheck the code of dispatcher?
+            createTypeBoundOps(graph, ctx, retTyp, disp.ast.info, idgen)
+          genProcAux(m, disp)
+        discard closePContext(graph, ctx, nil)
+  of JSgenPass:
+    when not defined(leanCompiler):
+      discard finalJSCodeGen(graph, bModule, finalNode)
+  of EvalPass, InterpreterPass:
+    discard interpreterCode(bModule, finalNode)
+  of SemPass, GenDependPass:
+    discard
+  of Docgen2Pass, Docgen2TexPass:
+    when not defined(leanCompiler):
+      discard closeDoc(graph, bModule, finalNode)
+  of Docgen2JsonPass:
+    when not defined(leanCompiler):
+      discard closeJson(graph, bModule, finalNode)
+  of NonePass:
+    raiseAssert "use setPipeLinePass to set a proper PipelinePass"
+
+  if graph.config.backend notin {backendC, backendCpp, backendObjc}:
+    # We only write rod files here if no C-like backend is active.
+    # The C-like backends have been patched to support the IC mechanism.
+    # They are responsible for closing the rod files. See `cbackend.nim`.
+    closeRodFile(graph, module)
+  result = true
+
+proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags; fromModule: PSym = nil): PSym =
+  var flags = flags
+  if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule
+  result = graph.getModule(fileIdx)
+
+  template processModuleAux(moduleStatus) =
+    onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule)
+    var s: PLLStream = nil
+    if sfMainModule in flags:
+      if graph.config.projectIsStdin: s = stdin.llStreamOpen
+      elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput)
+    discard processPipelineModule(graph, result, idGeneratorFromModule(result), s)
+  if result == nil:
+    var cachedModules: seq[FileIndex] = @[]
+    result = moduleFromRodFile(graph, fileIdx, cachedModules)
+    let path = toFullPath(graph.config, fileIdx)
+    let filename = AbsoluteFile path
+    if fileExists(filename): # it could be a stdinfile
+      graph.cachedFiles[path] = $secureHashFile(path)
+    if result == nil:
+      result = newModule(graph, fileIdx)
+      result.flags.incl flags
+      registerModule(graph, result)
+      processModuleAux("import")
+    else:
+      if sfSystemModule in flags:
+        graph.systemModule = result
+      if sfMainModule in flags and graph.config.cmd == cmdM:
+        result.flags.incl flags
+        registerModule(graph, result)
+        processModuleAux("import")
+      partialInitModule(result, graph, fileIdx, filename)
+    for m in cachedModules:
+      registerModuleById(graph, m)
+      if sfMainModule in flags and graph.config.cmd == cmdM:
+        discard
+      else:
+        replayStateChanges(graph.packed.pm[m.int].module, graph)
+        replayGenericCacheInformation(graph, m.int)
+  elif graph.isDirty(result):
+    result.flags.excl sfDirty
+    # reset module fields:
+    initStrTables(graph, result)
+    result.ast = nil
+    processModuleAux("import(dirty)")
+    graph.markClientsDirty(fileIdx)
+
+proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym =
+  # this is called by the semantic checking phase
+  assert graph.config != nil
+  result = compilePipelineModule(graph, fileIdx, {}, s)
+  graph.addDep(s, fileIdx)
+  # keep track of import relationships
+  if graph.config.hcrOn:
+    graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx)
+  #if sfSystemModule in result.flags:
+  #  localError(result.info, errAttemptToRedefine, result.name.s)
+  # restore the notes for outer module:
+  graph.config.notes =
+    if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes
+    else: graph.config.foreignPackageNotes
+
+proc connectPipelineCallbacks*(graph: ModuleGraph) =
+  graph.includeFileCallback = modules.includeModule
+  graph.importModuleCallback = importPipelineModule
+
+proc compilePipelineSystemModule*(graph: ModuleGraph) =
+  if graph.systemModule == nil:
+    connectPipelineCallbacks(graph)
+    graph.config.m.systemFileIdx = fileInfoIdx(graph.config,
+        graph.config.libpath / RelativeFile"system.nim")
+    discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule})
+
+proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) =
+  connectPipelineCallbacks(graph)
+  let conf = graph.config
+  wantMainModule(conf)
+  configComplete(graph)
+
+  let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim")
+  let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx
+  conf.projectMainIdx2 = projectFile
+
+  let packSym = getPackage(graph, projectFile)
+  graph.config.mainPackageId = packSym.getPackageId
+  graph.importStack.add projectFile
+
+  if projectFile == systemFileIdx:
+    discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule})
+  else:
+    graph.compilePipelineSystemModule()
+    discard graph.compilePipelineModule(projectFile, {sfMainModule})
diff --git a/compiler/pipelineutils.nim b/compiler/pipelineutils.nim
new file mode 100644
index 000000000..75ba33f14
--- /dev/null
+++ b/compiler/pipelineutils.nim
@@ -0,0 +1,26 @@
+import ast, options, lineinfos, pathutils, msgs, modulegraphs, packages
+
+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 = config.errorCounter > 0
+
+proc resolveMod*(conf: ConfigRef; module, relativeTo: string): FileIndex =
+  let fullPath = findModule(conf, module, relativeTo)
+  if fullPath.isEmpty:
+    result = InvalidFileIdx
+  else:
+    result = fileInfoIdx(conf, fullPath)
+
+proc prepareConfigNotes*(graph: ModuleGraph; module: PSym) =
+  # don't be verbose unless the module belongs to the main package:
+  if graph.config.belongsToProjectPackage(module):
+    graph.config.notes = graph.config.mainPackageNotes
+  else:
+    if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes
+    graph.config.notes = graph.config.foreignPackageNotes
+
+proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} =
+  result = true
+  #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange")
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 4b4316bc2..03d0cc461 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -14,7 +14,7 @@
 # Feel free to test for your excentric platform!
 
 import
-  strutils
+  std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -26,7 +26,8 @@ type
     osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
     osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx,
     osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks
-    osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr, osAny
+    osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr,
+    osNuttX, osAny
 
 type
   TInfoOSProp* = enum
@@ -193,6 +194,10 @@ const
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
       props: {ospPosix}),
+     (name: "NuttX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
+      objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
+      scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
+      props: {ospPosix}),
      (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
       objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
       scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
@@ -272,6 +277,7 @@ proc nameToOS*(name: string): TSystemOS =
   result = osNone
 
 proc listOSnames*(): seq[string] =
+  result = @[]
   for i in succ(osNone)..high(TSystemOS):
     result.add OS[i].name
 
@@ -282,6 +288,7 @@ proc nameToCPU*(name: string): TSystemCPU =
   result = cpuNone
 
 proc listCPUnames*(): seq[string] =
+  result = @[]
   for i in succ(cpuNone)..high(TSystemCPU):
     result.add CPU[i].name
 
diff --git a/compiler/plugins/customast.nim b/compiler/plugins/customast.nim
new file mode 100644
index 000000000..87461ae39
--- /dev/null
+++ b/compiler/plugins/customast.nim
@@ -0,0 +1,136 @@
+# This file exists to make it overridable via
+# patchFile("plugins", "customast.nim", "customast.nim")
+
+## This also serves as a blueprint for a possible implementation.
+
+import "$nim" / compiler / [lineinfos, idents]
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+import "$nim" / compiler / nodekinds
+export nodekinds
+
+type
+  PNode* = ref TNode
+  TNode*{.final, acyclic.} = object
+    case kind*: TNodeKind
+    of nkCharLit..nkUInt64Lit:
+      intVal: BiggestInt
+    of nkFloatLit..nkFloat128Lit:
+      floatVal: BiggestFloat
+    of nkStrLit..nkTripleStrLit:
+      strVal: string
+    of nkSym:
+      discard
+    of nkIdent:
+      ident: PIdent
+    else:
+      son, next, last: PNode # linked structure instead of a `seq`
+    info*: TLineInfo
+
+const
+  bodyPos* = 6
+  paramsPos* = 3
+
+proc comment*(n: PNode): string =
+  result = ""
+
+proc `comment=`*(n: PNode, a: string) =
+  discard "XXX implement me"
+
+proc add*(father, son: PNode) =
+  assert son != nil
+  if father.son == nil:
+    father.son = son
+    father.last = son
+  else:
+    father.last.next = son
+    father.last = son
+
+template firstSon*(n: PNode): PNode = n.son
+template secondSon*(n: PNode): PNode = n.son.next
+
+proc replaceFirstSon*(n, newson: PNode) {.inline.} =
+  let old = n.son
+  n.son = newson
+  newson.next = old
+
+proc replaceSon*(n: PNode; i: int; newson: PNode) =
+  assert i > 0
+  assert newson.next == nil
+  var i = i
+  var it = n.son
+  while i > 0:
+    it = it.next
+    dec i
+  let old = it.next
+  it.next = newson
+  newson.next = old
+
+template newNodeImpl(info2) =
+  result = PNode(kind: kind, info: info2)
+
+proc newNode*(kind: TNodeKind): PNode =
+  ## new node with unknown line info, no type, and no children
+  newNodeImpl(unknownLineInfo)
+
+proc newNode*(kind: TNodeKind, info: TLineInfo): PNode =
+  ## new node with line info, no type, and no children
+  newNodeImpl(info)
+
+proc newTree*(kind: TNodeKind; info: TLineInfo; child: PNode): PNode =
+  result = newNode(kind, info)
+  result.son = child
+
+proc newAtom*(ident: PIdent, info: TLineInfo): PNode =
+  result = newNode(nkIdent)
+  result.ident = ident
+  result.info = info
+
+proc newAtom*(kind: TNodeKind, intVal: BiggestInt, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.intVal = intVal
+
+proc newAtom*(kind: TNodeKind, floatVal: BiggestFloat, info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.floatVal = floatVal
+
+proc newAtom*(kind: TNodeKind; strVal: sink string; info: TLineInfo): PNode =
+  result = newNode(kind, info)
+  result.strVal = strVal
+
+proc lastSon*(n: PNode): PNode {.inline.} = n.last
+proc setLastSon*(n: PNode, s: PNode) =
+  assert s.next == nil
+  n.last = s
+  if n.son == nil: n.son = s
+
+proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode,
+                 params,
+                 name, pattern, genericParams,
+                 pragmas, exceptions: PNode): PNode =
+  result = newNode(kind, info)
+  result.add name
+  result.add pattern
+  result.add genericParams
+  result.add params
+  result.add pragmas
+  result.add exceptions
+  result.add body
+
+template transitionNodeKindCommon(k: TNodeKind) =
+  let obj {.inject.} = n[]
+  n[] = TNode(kind: k, info: obj.info)
+  # n.comment = obj.comment # shouldn't be needed, the address doesnt' change
+
+proc transitionSonsKind*(n: PNode, kind: range[nkComesFrom..nkTupleConstr]) =
+  transitionNodeKindCommon(kind)
+  n.son = obj.son
+
+template hasSon*(n: PNode): bool = n.son != nil
+template has2Sons*(n: PNode): bool = n.son != nil and n.son.next != nil
+
+proc isNewStyleConcept*(n: PNode): bool {.inline.} =
+  assert n.kind == nkTypeClassTy
+  result = n.firstSon.kind == nkEmpty
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 24e26b2b7..e2c97bdc5 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -25,14 +25,14 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode =
     return
 
   let t = n[2].typ.skipTypes({tyTypeDesc, tyGenericInst})
-  if t.kind notin {tyRef, tyPtr} or t.lastSon.kind != tyObject:
+  if t.kind notin {tyRef, tyPtr} or t.elementType.kind != tyObject:
     localError(c.config, n[2].info,
         "type must be a non-generic ref|ptr to object with state field")
     return
   let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen)
 
-  let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info)
-  prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc)
+  let prc = newSym(skProc, n[3].ident, c.idgen, iter.sym.owner, iter.sym.info)
+  prc.typ = copyType(iter.sym.typ, c.idgen, prc)
   excl prc.typ.flags, tfCapturesEnv
   prc.typ.n.add newSymNode(getEnvParam(iter.sym))
   prc.typ.rawAddSon t
diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim
index 384780f7b..d3046cd65 100644
--- a/compiler/plugins/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -26,7 +26,7 @@ proc semLocals*(c: PContext, n: PNode): PNode =
             {tyVarargs, tyOpenArray, tyTypeDesc, tyStatic, tyUntyped, tyTyped, tyEmpty}:
 
         if it.owner == owner:
-          var field = newSym(skField, it.name, nextSymId c.idgen, owner, n.info)
+          var field = newSym(skField, it.name, c.idgen, owner, n.info)
           field.typ = it.typ.skipTypes({tyVar})
           field.position = counter
           inc(counter)
diff --git a/compiler/plugins/plugins.nimble b/compiler/plugins/plugins.nimble
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/compiler/plugins/plugins.nimble
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index fdf4dd91f..9a298cd90 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -10,9 +10,13 @@
 # This module implements semantic checking for pragmas
 
 import
-  os, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
-  wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
-  types, lookups, lineinfos, pathutils, linter
+  condsyms, ast, astalgo, idents, semdata, msgs, renderer,
+  wordrecg, ropes, options, extccomp, magicsys, trees,
+  types, lookups, lineinfos, pathutils, linter, modulepaths
+
+from sigmatch import trySuggestPragmas
+
+import std/[os, math, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -29,16 +33,16 @@ const
     ## common pragmas for declarations, to a good approximation
   procPragmas* = declPragmas + {FirstCallConv..LastCallConv,
     wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader,
-    wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge,
+    wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime,
     wBorrow, wImportCompilerProc, wThread,
     wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl,
     wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe,
     wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy,
-    wRequires, wEnsures, wEnforceNoRaises}
+    wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky, wMember}
   converterPragmas* = procPragmas
   methodPragmas* = procPragmas+{wBase}-{wImportCpp}
   templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty,
-    wDelegator, wExportNims, wUsed, wPragma, wRedefine}
+    wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite}
   macroPragmas* = declPragmas + {FirstCallConv..LastCallConv,
     wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore,
     wDiscardable, wGensym, wInject, wDelegator}
@@ -71,18 +75,20 @@ const
     wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow,
     wIncompleteStruct, wCompleteStruct, wByCopy, wByRef,
     wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
-    wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage}
+    wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl,
+    wSendable, wNoInit}
   fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
-    wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these?
+    wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these?
   varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar,
     wMagic, wHeader, wCompilerProc, wCore, wDynlib,
-    wNoInit, wCompileTime, wGlobal,
+    wNoInit, wCompileTime, wGlobal, wLiftLocals,
     wGensym, wInject, wCodegenDecl,
     wGuard, wGoto, wCursor, wNoalias, wAlign}
   constPragmas* = declPragmas + {wHeader, wMagic,
     wGensym, wInject,
-    wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore}
-  paramPragmas* = {wNoalias, wInject, wGensym}
+    wIntDefine, wStrDefine, wBoolDefine, wDefine,
+    wCompilerProc, wCore}
+  paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp}
   letPragmas* = varPragmas
   procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect,
                       wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe,
@@ -92,6 +98,7 @@ const
   enumFieldPragmas* = {wDeprecated}
 
 proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode =
+  result = nil
   let p = procAst[pragmasPos]
   if p.kind == nkEmpty: return nil
   for it in p:
@@ -114,8 +121,17 @@ const
 
 proc invalidPragma*(c: PContext; n: PNode) =
   localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments}))
+
 proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) =
-  localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'")
+  var msg = "cannot attach a custom pragma to '" & s.name.s & "'"
+  if s != nil:
+    msg.add("; custom pragmas are not supported for ")
+    case s.kind
+    of skForVar: msg.add("`for` loop variables")
+    of skEnumField: msg.add("enum fields")
+    of skModule: msg.add("modules")
+    else: msg.add("symbol kind " & $s.kind)
+  localError(c.config, n.info, msg)
 
 proc pragmaProposition(c: PContext, n: PNode) =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -129,44 +145,28 @@ proc pragmaEnsures(c: PContext, n: PNode) =
   else:
     openScope(c)
     let o = getCurrOwner(c)
-    if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil:
-      var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info)
-      s.typ = o.typ.sons[0]
+    if o.kind in routineKinds and o.typ != nil and o.typ.returnType != nil:
+      var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info)
+      s.typ = o.typ.returnType
       incl(s.flags, sfUsed)
       addDecl(c, s)
     n[1] = c.semExpr(c, n[1])
     closeScope(c)
 
-proc pragmaAsm*(c: PContext, n: PNode): char =
-  result = '\0'
-  if n != nil:
-    for i in 0..<n.len:
-      let it = n[i]
-      if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
-        case whichKeyword(it[0].ident)
-        of wSubsChar:
-          if it[1].kind == nkCharLit: result = chr(int(it[1].intVal))
-          else: invalidPragma(c, it)
-        else: invalidPragma(c, it)
-      else:
-        invalidPragma(c, it)
-
 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)
+    s.loc.snippet = rope(s.name.s)
   elif '$' notin extname:
-    s.loc.r = rope(extname)
+    s.loc.snippet = rope(extname)
   else:
     try:
-      s.loc.r = rope(extname % s.name.s)
+      s.loc.snippet = rope(extname % s.name.s)
     except ValueError:
       localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
   when hasFFI:
-    s.cname = $s.loc.r
-  if c.config.cmd == cmdNimfix and '$' notin extname:
-    # note that '{.importc.}' is transformed into '{.importc: "$1".}'
-    s.loc.flags.incl(lfFullExternalName)
+    s.cname = $s.loc.snippet
+
 
 proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   setExternName(c, s, extname, info)
@@ -201,9 +201,9 @@ proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) =
   let m = s.getModule()
   incl(m.flags, sfCompileToObjc)
 
-proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} =
+proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} =
   result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
-  result.strVal = ""
+  result.strVal = strVal
 
 proc getStrLitNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -223,6 +223,7 @@ proc expectStrLit(c: PContext, n: PNode): string =
   result = getStrLitNode(c, n).strVal
 
 proc expectIntLit(c: PContext, n: PNode): int =
+  result = 0
   if n.kind notin nkPragmaCallKinds or n.len != 2:
     localError(c.config, n.info, errIntLiteralExpected)
   else:
@@ -235,8 +236,17 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
   if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n)
   else: result = defaultStr
 
+proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) =
+  s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1"))
+  s.constraint.strVal = s.constraint.strVal % s.name.s
+  s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp}
+
+  s.typ.callConv = ccMember
+  incl c.config.globalOptions, optMixedMode
+
 proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
   sym.constraint = getStrLitNode(c, n)
+  sym.flags.incl sfCodegenDecl
 
 proc processMagic(c: PContext, n: PNode, s: PSym) =
   #if sfSystemModule notin c.module.flags:
@@ -259,6 +269,7 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention =
   TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall))
 
 proc isTurnedOn(c: PContext, n: PNode): bool =
+  result = false
   if n.kind in nkPragmaCallKinds and n.len == 2:
     let x = c.semConstBoolExpr(c, n[1])
     n[1] = x
@@ -269,7 +280,7 @@ proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
   if isTurnedOn(c, n): resOptions.incl op
   else: resOptions.excl op
 
-proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
+proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) =
   if isTurnedOn(c, n):
     incl(c.module.flags, flag)
     c.features.incl codeReordering
@@ -282,6 +293,24 @@ proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
           "use {.experimental: \"codeReordering\".} instead; " &
           (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated")
 
+proc pragmaAsm*(c: PContext, n: PNode): char =
+  ## Checks asm pragmas and get's the asm subschar (default: '`').
+  result = '\0'
+  if n != nil:
+    for i in 0..<n.len:
+      let it = n[i]
+      if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
+        case whichKeyword(it[0].ident)
+        of wSubsChar:
+          if it[1].kind == nkCharLit: result = chr(int(it[1].intVal))
+          else: invalidPragma(c, it)
+        of wAsmSyntax:
+          let s = expectStrLit(c, it)
+          if s notin ["gcc", "vcc"]: invalidPragma(c, it)
+        else: invalidPragma(c, it)
+      else:
+        invalidPragma(c, it)
+
 proc processCallConv(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent:
     let sw = whichKeyword(n[1].ident)
@@ -301,7 +330,7 @@ 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(c.config, path.strVal)
+    result.isOverridden = options.isDynlibOverride(c.config, path.strVal)
 
 proc expectDynlibNode(c: PContext, n: PNode): PNode =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -313,7 +342,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
     # {.dynlib: myGetProcAddr(...).}
     result = c.semExpr(c, n[1])
     if result.kind == nkSym and result.sym.kind == skConst:
-      result = result.sym.ast # look it up
+      result = c.semConstExpr(c, result) # fold const
     if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
       localError(c.config, n.info, errStringLiteralExpected)
       result = newEmptyStrNode(c, n)
@@ -321,12 +350,12 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode =
 proc processDynLib(c: PContext, n: PNode, sym: PSym) =
   if (sym == nil) or (sym.kind == skModule):
     let lib = getLib(c, libDynamic, expectDynlibNode(c, n))
-    if not lib.isOverriden:
+    if not lib.isOverridden:
       c.optionStack[^1].dynlib = lib
   else:
     if n.kind in nkPragmaCallKinds:
       var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
-      if not lib.isOverriden:
+      if not lib.isOverridden:
         addToLib(lib, sym)
         incl(sym.loc.flags, lfDynamicLib)
     else:
@@ -341,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) =
 proc processNote(c: PContext, n: PNode) =
   template handleNote(enumVals, notes) =
     let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
-    if x !=  errUnknown:
+    if x != errUnknown:
       nk = TNoteKind(x)
       let x = c.semConstBoolExpr(c, n[1])
       n[1] = x
@@ -363,7 +392,7 @@ proc processNote(c: PContext, n: PNode) =
     else: invalidPragma(c, n)
   else: invalidPragma(c, n)
 
-proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
+proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} =
   case w
   of wChecks: ChecksOptions
   of wObjChecks: {optObjCheck}
@@ -389,6 +418,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
   of wImplicitStatic: {optImplicitStatic}
   of wPatterns, wTrMacros: {optTrMacros}
   of wSinkInference: {optSinkInference}
+  of wQuirky: {optQuirky}
   else: {}
 
 proc processExperimental(c: PContext; n: PNode) =
@@ -450,6 +480,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) =
     # calling conventions (boring...):
     localError(c.config, n.info, "option expected")
 
+proc checkPushedPragma(c: PContext, n: PNode) =
+  let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1
+  var key = if keyDeep: n[0] else: n
+  if key.kind in nkIdentKinds:
+    let ident = considerQuotedIdent(c, key)
+    var userPragma = strTableGet(c.userPragmas, ident)
+    if userPragma == nil:
+      let k = whichKeyword(ident)
+      # TODO: might as well make a list which is not accepted by `push`: emit, cast etc.
+      if k == wEmit:
+        localError(c.config, n.info, "an 'emit' pragma cannot be pushed")
+
 proc processPush(c: PContext, n: PNode, start: int) =
   if n[start-1].kind in nkPragmaCallKinds:
     localError(c.config, n.info, "'push' cannot have arguments")
@@ -457,6 +499,7 @@ proc processPush(c: PContext, n: PNode, start: int) =
   for i in start..<n.len:
     if not tryProcessOption(c, n[i], c.config.options):
       # simply store it somewhere:
+      checkPushedPragma(c, n[i])
       if x.otherPragmas.isNil:
         x.otherPragmas = newNodeI(nkPragma, n.info)
       x.otherPragmas.add n[i]
@@ -476,8 +519,16 @@ proc processPop(c: PContext, n: PNode) =
   when defined(debugOptions):
     echo c.config $ n.info, " POP config is now ", c.config.options
 
-proc processDefine(c: PContext, n: PNode) =
-  if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
+proc processDefineConst(c: PContext, n: PNode, sym: PSym, kind: TMagic) =
+  sym.magic = kind
+  if n.kind in nkPragmaCallKinds and n.len == 2:
+    # could also use TLib
+    n[1] = getStrLitNode(c, n)
+
+proc processDefine(c: PContext, n: PNode, sym: PSym) =
+  if sym != nil and sym.kind == skConst:
+    processDefineConst(c, n, sym, mGenericDefine)
+  elif (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
     defineSymbol(c.config.symbols, n[1].ident.s)
   else:
     invalidPragma(c, n)
@@ -500,18 +551,25 @@ proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile =
       if result.isEmpty: result = AbsoluteFile s
 
 proc processCompile(c: PContext, n: PNode) =
+  ## This pragma can take two forms. The first is a simple file input:
+  ##     {.compile: "file.c".}
+  ## The second is a tuple where the second arg is the output name strutils formatter:
+  ##     {.compile: ("file.c", "$1.o").}
   proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) =
     var cf = Cfile(nimname: splitFile(src).name,
                    cname: src, obj: dest, flags: {CfileFlag.External},
                    customArgs: customArgs)
-    extccomp.addExternalFileToCompile(c.config, cf)
-    recordPragma(c, it, "compile", src.string, dest.string, customArgs)
+    if not fileExists(src):
+      localError(c.config, n.info, "cannot find: " & src.string)
+    else:
+      extccomp.addExternalFileToCompile(c.config, cf)
+      recordPragma(c, it, "compile", src.string, dest.string, customArgs)
 
   proc getStrLit(c: PContext, n: PNode; i: int): string =
     n[i] = c.semConstExpr(c, n[i])
     case n[i].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit:
-      when defined(gcArc) or defined(gcOrc):
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
         result = n[i].strVal
       else:
         shallowCopy(result, n[i].strVal)
@@ -545,7 +603,8 @@ proc processCompile(c: PContext, n: PNode) =
       else:
         found = findFile(c.config, s)
         if found.isEmpty: found = AbsoluteFile s
-    let obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
+    let mangled = completeCfilePath(c.config, mangleModuleName(c.config, found).AbsoluteFile)
+    let obj = toObjFile(c.config, mangled)
     docompile(c, it, found, obj, customArgs)
 
 proc processLink(c: PContext, n: PNode) =
@@ -557,6 +616,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
   case n[1].kind
   of nkStrLit, nkRStrLit, nkTripleStrLit:
     result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
+    if n.kind == nkAsmStmt: result.add n[0] # save asm pragmas for NIR
     var str = n[1].strVal
     if str == "":
       localError(con.config, n.info, "empty 'asm' statement")
@@ -588,6 +648,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
   else:
     illFormedAstLocal(n, con.config)
     result = newNodeI(nkAsmStmt, n.info)
+    if n.kind == nkAsmStmt: result.add n[0]
 
 proc pragmaEmit(c: PContext, n: PNode) =
   if n.kind notin nkPragmaCallKinds or n.len != 2:
@@ -597,7 +658,7 @@ proc pragmaEmit(c: PContext, n: PNode) =
     if n1.kind == nkBracket:
       var b = newNodeI(nkBracket, n1.info, n1.len)
       for i in 0..<n1.len:
-        b[i] = c.semExpr(c, n1[i])
+        b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed})
       n[1] = b
     else:
       n[1] = c.semConstExpr(c, n1)
@@ -646,11 +707,15 @@ proc pragmaLine(c: PContext, n: PNode) =
 proc processPragma(c: PContext, n: PNode, i: int) =
   ## Create and add a new custom pragma `{.pragma: name.}` node to the module's context.
   let it = n[i]
-  if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n)
+  if it.kind notin nkPragmaCallKinds and it.safeLen == 2:
+    invalidPragma(c, n)
+    return
   elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent:
     invalidPragma(c, n)
+    return
 
-  var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), c.module, it.info, c.config.options)
+  var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options)
+  styleCheckDef(c, userPragma)
   userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1])
   strTableAdd(c.userPragmas, userPragma)
 
@@ -684,23 +749,6 @@ proc pragmaLockStmt(c: PContext; it: PNode) =
       for i in 0..<n.len:
         n[i] = c.semExpr(c, n[i])
 
-proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
-  if it.kind notin nkPragmaCallKinds or it.len != 2:
-    invalidPragma(c, it)
-  else:
-    case it[1].kind
-    of nkStrLit, nkRStrLit, nkTripleStrLit:
-      if it[1].strVal == "unknown":
-        result = UnknownLockLevel
-      else:
-        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(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel)
-      else:
-        result = TLockLevel(x)
-
 proc typeBorrow(c: PContext; sym: PSym, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2:
     let it = n[1]
@@ -727,19 +775,8 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) =
     return
   if pragma.kind != nkBracket:
     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(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
-      let src = considerQuotedIdent(c, n[0])
-      let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options)
-      incl(alias.flags, sfExported)
-      if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
-      addInterfaceDecl(c, alias)
-      n[1] = newSymNode(dest)
-    else:
-      localError(c.config, n.info, "key:value pair expected")
+  message(c.config, pragma.info, warnDeprecated,
+    "deprecated statement is now a no-op, use regular deprecated pragma")
 
 proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
   if it.kind notin nkPragmaCallKinds or it.len != 2:
@@ -755,31 +792,39 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
       # We return a dummy symbol; later passes over the type will repair it.
       # Generic instantiation needs to know about this too. But we're lazy
       # and perform the lookup on demand instead.
-      result = newSym(skUnknown, considerQuotedIdent(c, n), nextSymId(c.idgen), nil, n.info,
+      result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info,
         c.config.options)
   else:
     result = qualifiedLookUp(c, n, {checkUndeclared})
 
-proc semCustomPragma(c: PContext, n: PNode): PNode =
+proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
   var callNode: PNode
 
-  if n.kind in {nkIdent, nkSym}:
+  case n.kind
+  of nkIdentKinds:
     # pragma -> pragma()
     callNode = newTree(nkCall, n)
-  elif n.kind == nkExprColonExpr:
+  of nkExprColonExpr:
     # pragma: arg -> pragma(arg)
     callNode = newTree(nkCall, n[0], n[1])
-  elif n.kind in nkPragmaCallKinds:
+  of nkPragmaCallKinds - {nkExprColonExpr}:
     callNode = n
   else:
     invalidPragma(c, n)
     return n
 
+  trySuggestPragmas(c, callNode[0])
+
   let r = c.semOverloadedCall(c, callNode, n, {skTemplate}, {efNoUndeclared})
   if r.isNil or sfCustomPragma notin r[0].sym.flags:
     invalidPragma(c, n)
     return n
 
+  # we have a valid custom pragma
+  if sym != nil and sym.kind in {skEnumField, skForVar, skModule}:
+    illegalCustomPragma(c, n, sym)
+    return n
+
   result = r
   # Transform the nkCall node back to its original form if possible
   if n.kind == nkIdent and r.len == 1:
@@ -812,8 +857,10 @@ proc processEffectsOf(c: PContext, n: PNode; owner: PSym) =
 proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
                   validPragmas: TSpecialWords,
                   comesFromPush, isStatement: bool): bool =
+  result = false
   var it = n[i]
-  var key = if it.kind in nkPragmaCallKinds and it.len > 1: it[0] else: it
+  let keyDeep = it.kind in nkPragmaCallKinds and it.len > 1
+  var key = if keyDeep: it[0] else: it
   if key.kind == nkBracketExpr:
     processNote(c, it)
     return
@@ -827,7 +874,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
     else: discard
     return
   elif key.kind notin nkIdentKinds:
-    n[i] = semCustomPragma(c, it)
+    n[i] = semCustomPragma(c, it, sym)
     return
   let ident = considerQuotedIdent(c, key)
   var userPragma = strTableGet(c.userPragmas, ident)
@@ -836,17 +883,20 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
 
     # number of pragmas increase/decrease with user pragma expansion
     inc c.instCounter
+    defer: dec c.instCounter
     if c.instCounter > 100:
       globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s)
 
+    if keyDeep:
+      localError(c.config, it.info, "user pragma cannot have arguments")
+
     pragma(c, sym, userPragma.ast, validPragmas, isStatement)
     n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
     i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
-    dec c.instCounter
   else:
     let k = whichKeyword(ident)
     if k in validPragmas:
-      checkPragmaUse(c.config, key.info, k, ident.s)
+      checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module))
       case k
       of wExportc, wExportCpp:
         makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
@@ -873,6 +923,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wRedefine:
         if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition)
         else: invalidPragma(c, it)
+      of wCallsite:
+        if sym.kind == skTemplate: incl(sym.flags, sfCallsite)
+        else: invalidPragma(c, it)
       of wImportCpp:
         processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
       of wCppNonPod:
@@ -931,7 +984,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wThreadVar:
         noVal(c, it)
         incl(sym.flags, {sfThread, sfGlobal})
-      of wDeadCodeElimUnused: discard  # deprecated, dead code elim always on
+      of wDeadCodeElimUnused:
+        warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop")  # deprecated, dead code elim always on
       of wNoForward: pragmaNoForward(c, it)
       of wReorder: pragmaNoForward(c, it, flag = sfReorder)
       of wMagic: processMagic(c, it, sym)
@@ -947,12 +1001,13 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         incl(sym.flags, sfGlobal)
         incl(sym.flags, sfPure)
-      of wMerge:
-        # only supported for backwards compat, doesn't do anything anymore
-        noVal(c, it)
       of wConstructor:
-        noVal(c, it)
         incl(sym.flags, sfConstructor)
+        if sfImportc notin sym.flags:
+          sym.constraint = newEmptyStrNode(c, it, getOptionalStr(c, it, ""))
+          sym.constraint.strVal = sym.constraint.strVal
+          sym.flags.incl {sfExportc, sfMangleCpp}
+          sym.typ.callConv = ccNoConvention
       of wHeader:
         var lib = getLib(c, libHeader, getStrLitNode(c, it))
         addToLib(lib, sym)
@@ -960,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         incl(sym.loc.flags, lfHeader)
         incl(sym.loc.flags, lfNoDecl)
         # implies nodecl, because otherwise header would not make sense
-        if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
+        if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
       of wNoSideEffect:
         noVal(c, it)
         if sym != nil:
@@ -974,7 +1029,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode!
         if c.config.exc != excQuirky:
           incl(sym.flags, sfNoReturn)
-        if sym.typ[0] != nil:
+        if sym.typ.returnType != nil:
           localError(c.config, sym.ast[paramsPos][0].info,
             ".noreturn with return type not allowed")
       of wNoDestroy:
@@ -998,7 +1053,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wExplain:
         sym.flags.incl sfExplain
       of wDeprecated:
-        if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet}:
+        if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}:
           if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
           incl(sym.flags, sfDeprecated)
         elif sym != nil and sym.kind != skModule:
@@ -1046,6 +1101,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         if sym.typ != nil:
           incl(sym.typ.flags, tfThread)
           if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall
+      of wSendable:
+        noVal(c, it)
+        if sym != nil and sym.typ != nil:
+          incl(sym.typ.flags, tfSendable)
+        else:
+          invalidPragma(c, it)
       of wGcSafe:
         noVal(c, it)
         if sym != nil:
@@ -1080,21 +1141,29 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
           recordPragma(c, it, "error", s)
           localError(c.config, it.info, errUser, s)
       of wFatal: fatal(c.config, it.info, expectStrLit(c, it))
-      of wDefine: processDefine(c, it)
+      of wDefine: processDefine(c, it, sym)
       of wUndef: processUndef(c, it)
-      of wCompile: processCompile(c, it)
+      of wCompile:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
+        processCompile(c, it)
       of wLink: processLink(c, it)
       of wPassl:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
         let s = expectStrLit(c, it)
         extccomp.addLinkOption(c.config, s)
         recordPragma(c, it, "passl", s)
       of wPassc:
+        let m = sym.getModule()
+        incl(m.flags, sfUsed)
         let s = expectStrLit(c, it)
         extccomp.addCompileOption(c.config, s)
         recordPragma(c, it, "passc", s)
       of wLocalPassc:
         assert sym != nil and sym.kind == skModule
         let s = expectStrLit(c, it)
+        appendToModule(sym, n)
         extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex))
         recordPragma(c, it, "localpassl", s)
       of wPush:
@@ -1170,13 +1239,17 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
           invalidPragma(c, it)
       of wByRef:
         noVal(c, it)
-        if sym == nil or sym.typ == nil:
+        if sym != nil and sym.kind == skParam:
+          sym.options.incl optByRef
+        elif sym == nil or sym.typ == nil:
           processOption(c, it, c.config.options)
         else:
           incl(sym.typ.flags, tfByRef)
       of wByCopy:
         noVal(c, it)
-        if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
+        if sym.kind == skParam:
+          incl(sym.flags, sfByCopy)
+        elif sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
         else: incl(sym.typ.flags, tfByCopy)
       of wPartial:
         noVal(c, it)
@@ -1193,7 +1266,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
       of wLocks:
         if sym == nil: pragmaLockStmt(c, it)
         elif sym.typ == nil: invalidPragma(c, it)
-        else: sym.typ.lockLevel = pragmaLocks(c, it)
+        else: warningDeprecated(c.config, n.info, "'Lock levels' are deprecated, now a noop")
       of wBitsize:
         if sym == nil or sym.kind != skField:
           invalidPragma(c, it)
@@ -1227,33 +1300,36 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         noVal(c, it)
         sym.flags.incl sfBase
       of wIntDefine:
-        sym.magic = mIntDefine
+        processDefineConst(c, n, sym, mIntDefine)
       of wStrDefine:
-        sym.magic = mStrDefine
+        processDefineConst(c, n, sym, mStrDefine)
       of wBoolDefine:
-        sym.magic = mBoolDefine
+        processDefineConst(c, n, sym, mBoolDefine)
       of wUsed:
         noVal(c, it)
         if sym == nil: invalidPragma(c, it)
         else: sym.flags.incl sfUsed
-      of wLiftLocals: discard
+      of wLiftLocals:
+        sym.flags.incl(sfForceLift)
       of wRequires, wInvariant, wAssume, wAssert:
         pragmaProposition(c, it)
       of wEnsures:
         pragmaEnsures(c, it)
-      of wEnforceNoRaises:
+      of wEnforceNoRaises, wQuirky:
         sym.flags.incl sfNeverRaises
+      of wSystemRaisesDefect:
+        sym.flags.incl sfSystemRaisesDefect
+      of wVirtual:
+        processVirtual(c, it, sym, sfVirtual)
+      of wMember:
+        processVirtual(c, it, sym, sfMember)
+
       else: invalidPragma(c, it)
     elif comesFromPush and whichKeyword(ident) != wInvalid:
       discard "ignore the .push pragma; it doesn't apply"
     else:
-      if sym == nil or (sym.kind in {skVar, skLet, skParam, skIterator,
-                        skField, skProc, skFunc, skConverter, skMethod, skType}):
-        n[i] = semCustomPragma(c, it)
-      elif sym != nil:
-        illegalCustomPragma(c, it, sym)
-      else:
-        invalidPragma(c, it)
+      # semCustomPragma gives appropriate error for invalid pragmas
+      n[i] = semCustomPragma(c, it, sym)
 
 proc overwriteLineInfo(n: PNode; info: TLineInfo) =
   n.info = info
@@ -1268,12 +1344,22 @@ proc mergePragmas(n, pragmas: PNode) =
   else:
     for p in pragmas: n[pragmasPos].add p
 
+proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) =
+  if n[pragmasPos].kind == nkEmpty:
+    n[pragmasPos] = newNodeI(nkPragma, n.info)
+  for p in pragmas:
+    let prag = whichPragma(p)
+    if prag in validPragmas:
+      let copy = copyTree(p)
+      overwriteLineInfo copy, n.info
+      n[pragmasPos].add copy
+
 proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
                       validPragmas: TSpecialWords) =
   if sym != nil and sym.kind != skModule:
     for it in c.optionStack:
       let o = it.otherPragmas
-      if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985
+      if not o.isNil and sfFromGeneric notin sym.flags: # bug #23019
         pushInfoContext(c.config, info)
         var i = 0
         while i < o.len:
@@ -1281,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
             internalError(c.config, info, "implicitPragmas")
           inc i
         popInfoContext(c.config)
-        if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o)
+        if sym.kind in routineKinds and sym.ast != nil:
+          mergeValidPragmas(sym.ast, o, validPragmas)
 
     if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
       localError(c.config, info, ".dynlib requires .exportc")
@@ -1290,7 +1377,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo,
         sfImportc in sym.flags and lib != nil:
       incl(sym.loc.flags, lfDynamicLib)
       addToLib(lib, sym)
-      if sym.loc.r == "": sym.loc.r = rope(sym.name.s)
+      if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s)
 
 proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
   if n == nil: return false
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
index 71ae9a844..bfbe3d888 100644
--- a/compiler/prefixmatches.nim
+++ b/compiler/prefixmatches.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-from strutils import toLowerAscii
+from std/strutils import toLowerAscii
 
 type
   PrefixMatch* {.pure.} = enum
diff --git a/compiler/procfind.nim b/compiler/procfind.nim
index 0bdb3dae6..c2cc6e71f 100644
--- a/compiler/procfind.nim
+++ b/compiler/procfind.nim
@@ -11,7 +11,9 @@
 # This is needed for proper handling of forward declarations.
 
 import
-  ast, astalgo, msgs, semdata, types, trees, strutils, lookups
+  ast, astalgo, msgs, semdata, types, trees, lookups
+
+import std/strutils
 
 proc equalGenericParams(procA, procB: PNode): bool =
   if procA.len != procB.len: return false
@@ -31,7 +33,7 @@ proc equalGenericParams(procA, procB: PNode): bool =
 proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
   const flags = {ExactGenericParams, ExactTypeDescValues,
                  ExactConstraints, IgnoreCC}
-  var it: TIdentIter
+  var it: TIdentIter = default(TIdentIter)
   result = initIdentIter(it, scope.symbols, fn.name)
   while result != nil:
     if result.kind == fn.kind: #and sameType(result.typ, fn.typ, flags):
@@ -52,7 +54,7 @@ proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym =
 
 proc searchForProc*(c: PContext, scope: PScope, fn: PSym): tuple[proto: PSym, comesFromShadowScope: bool] =
   var scope = scope
-  result.proto = searchForProcAux(c, scope, fn)
+  result = (searchForProcAux(c, scope, fn), false)
   while result.proto == nil and scope.isShadowScope:
     scope = scope.parent
     result.proto = searchForProcAux(c, scope, fn)
@@ -74,7 +76,7 @@ when false:
   proc searchForBorrowProc*(c: PContext, startScope: PScope, fn: PSym): PSym =
     # Searches for the fn in the symbol table. If the parameter lists are suitable
     # for borrowing the sym in the symbol table is returned, else nil.
-    var it: TIdentIter
+    var it: TIdentIter = default(TIdentIter)
     for scope in walkScopes(startScope):
       result = initIdentIter(it, scope.symbols, fn.Name)
       while result != nil:
diff --git a/compiler/pushpoppragmas.nim b/compiler/pushpoppragmas.nim
new file mode 100644
index 000000000..773e7013b
--- /dev/null
+++ b/compiler/pushpoppragmas.nim
@@ -0,0 +1,54 @@
+import pragmas, options, ast, trees, lineinfos, idents, wordrecg
+import std/assertions
+
+import renderer
+
+
+proc processNote(config: ConfigRef, n: PNode) =
+  template handleNote(enumVals, notes) =
+    let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
+    assert x != errUnknown
+    assert n[1].kind == nkIntLit
+
+    nk = TNoteKind(x)
+    if n[1].intVal != 0: incl(notes, nk)
+    else: excl(notes, nk)
+
+  var nk: TNoteKind
+  case whichKeyword(n[0][0].ident)
+  of wHint: handleNote(hintMin .. hintMax, config.notes)
+  of wWarning: handleNote(warnMin .. warnMax, config.notes)
+  of wWarningAsError: handleNote(warnMin .. warnMax, config.warningAsErrors)
+  of wHintAsError: handleNote(hintMin .. hintMax, config.warningAsErrors)
+  else: discard
+
+proc pushBackendOption(optionsStack: var seq[(TOptions, TNoteKinds)], options: TOptions, notes: TNoteKinds) =
+  optionsStack.add (options, notes)
+
+proc popBackendOption(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  let entry = optionsStack[^1]
+  options = entry[0]
+  config.notes = entry[1]
+  optionsStack.setLen(optionsStack.len-1)
+
+proc processPushBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions,
+                           n: PNode, start: int) =
+  pushBackendOption(optionsStack, options, config.notes)
+  for i in start..<n.len:
+    let it = n[i]
+    if it.kind in nkPragmaCallKinds and it.len == 2:
+      if it[0].kind == nkBracketExpr and
+          it[0].len == 2 and
+          it[0][1].kind == nkIdent and it[0][0].kind == nkIdent:
+        processNote(config, it)
+      elif it[1].kind == nkIntLit:
+        let sw = whichPragma(it[0])
+        let opts = pragmaToOptions(sw)
+        if opts != {}:
+          if it[1].intVal != 0:
+            options.incl opts
+          else:
+            options.excl opts
+
+template processPopBackendOption*(config: ConfigRef, optionsStack: var seq[(TOptions, TNoteKinds)], options: var TOptions) =
+  popBackendOption(config, optionsStack, options)
diff --git a/compiler/readme.md b/compiler/readme.md
index ac47508dc..4a197991c 100644
--- a/compiler/readme.md
+++ b/compiler/readme.md
@@ -4,4 +4,4 @@
 - Note that this code has been translated from a bootstrapping version written in Pascal.
 - So the code is **not** a poster child of good Nim code.
 
-See [Internals of the Nim Compiler](https://nim-lang.org/docs/intern.html) for more information.
+See [Internals of the Nim Compiler](https://nim-lang.github.io/Nim/intern.html) for more information.
diff --git a/compiler/renderer.nim b/compiler/renderer.nim
index d8c46fc92..cc07c0c2d 100644
--- a/compiler/renderer.nim
+++ b/compiler/renderer.nim
@@ -9,13 +9,14 @@
 
 # This module implements the renderer of the standard Nim representation.
 
-when defined(nimHasUsed):
-  # 'import renderer' is so useful for debugging
-  # that Nim shouldn't produce a warning for that:
-  {.used.}
+# 'import renderer' is so useful for debugging
+# that Nim shouldn't produce a warning for that:
+{.used.}
 
 import
-  lexer, options, idents, strutils, ast, msgs, lineinfos
+  lexer, options, idents, ast, msgs, lineinfos, wordrecg
+
+import std/[strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions, formatfloat]
@@ -24,13 +25,18 @@ type
   TRenderFlag* = enum
     renderNone, renderNoBody, renderNoComments, renderDocComments,
     renderNoPragmas, renderIds, renderNoProcDefs, renderSyms, renderRunnableExamples,
-    renderIr
+    renderIr, renderNonExportedFields, renderExpandUsing, renderNoPostfix
+
   TRenderFlags* = set[TRenderFlag]
   TRenderTok* = object
     kind*: TokType
     length*: int16
     sym*: PSym
 
+  Section = enum
+    GenericParams
+    ObjectDef
+
   TRenderTokSeq* = seq[TRenderTok]
   TSrcGen* = object
     indent*: int
@@ -45,7 +51,7 @@ type
     pendingWhitespace: int
     comStack*: seq[PNode]  # comment stack
     flags*: TRenderFlags
-    inGenericParams: bool
+    inside: set[Section] # Keeps track of contexts we are in
     checkAnon: bool        # we're in a context that can contain sfAnon
     inPragma: int
     when defined(nimpretty):
@@ -72,6 +78,18 @@ proc isKeyword*(i: PIdent): bool =
   if (i.id >= ord(tokKeywordLow) - ord(tkSymbol)) and
       (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)):
     result = true
+  else:
+    result = false
+
+proc isExported(n: PNode): bool =
+  ## Checks if an ident is exported.
+  ## This is meant to be used with idents in nkIdentDefs.
+  case n.kind
+  of nkPostfix:
+    n[0].ident.s == "*" and n[1].kind == nkIdent
+  of nkPragmaExpr:
+    n[0].isExported()
+  else: false
 
 proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   ## Returns the definition name of the symbol.
@@ -85,9 +103,30 @@ proc renderDefinitionName*(s: PSym, noQuotes = false): string =
   else:
     result = '`' & x & '`'
 
+template inside(g: var TSrcGen, section: Section, body: untyped) =
+  ## Runs `body` with `section` included in `g.inside`.
+  ## Removes it at the end of the body if `g` wasn't inside it
+  ## before the template.
+  let wasntInSection = section notin g.inside
+  g.inside.incl section
+  body
+  if wasntInSection:
+    g.inside.excl section
+
+template outside(g: var TSrcGen, section: Section, body: untyped) =
+  ## Temporarily removes `section` from `g.inside`. Adds it back
+  ## at the end of the body if `g` was inside it before the template
+  let wasInSection = section in g.inside
+  g.inside.excl section
+  body
+  if wasInSection:
+    g.inside.incl section
+
 const
   IndentWidth = 2
   longIndentWid = IndentWidth * 2
+  MaxLineLen = 80
+  LineCommentColumn = 30
 
 when defined(nimpretty):
   proc minmaxLine(n: PNode): (int, int) =
@@ -106,23 +145,13 @@ when defined(nimpretty):
   proc lineDiff(a, b: PNode): int =
     result = minmaxLine(b)[0] - minmaxLine(a)[1]
 
-const
-  MaxLineLen = 80
-  LineCommentColumn = 30
-
-proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) =
-  g.comStack = @[]
-  g.tokens = @[]
-  g.indent = 0
-  g.lineLen = 0
-  g.pos = 0
-  g.idx = 0
-  g.buf = ""
-  g.flags = renderFlags
-  g.pendingNL = -1
-  g.pendingWhitespace = -1
-  g.inGenericParams = false
-  g.config = config
+proc initSrcGen(renderFlags: TRenderFlags; config: ConfigRef): TSrcGen =
+  result = TSrcGen(comStack: @[], tokens: @[], indent: 0,
+                   lineLen: 0, pos: 0, idx: 0, buf: "",
+                   flags: renderFlags, pendingNL: -1,
+                   pendingWhitespace: -1, inside: {},
+                   config: config
+                   )
 
 proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
   g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym)
@@ -241,6 +270,7 @@ proc putComment(g: var TSrcGen, s: string) =
   optNL(g)
 
 proc maxLineLength(s: string): int =
+  result = 0
   if s.len == 0: return 0
   var i = 0
   let hi = s.len - 1
@@ -336,8 +366,9 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string =
     result = t
     while result != nil and result.kind in {tyGenericInst, tyRange, tyVar,
                           tyLent, tyDistinct, tyOrdinal, tyAlias, tySink}:
-      result = lastSon(result)
+      result = skipModifier(result)
 
+  result = ""
   let typ = n.typ.skip
   if typ != nil and typ.kind in {tyBool, tyEnum}:
     if sfPure in typ.sym.flags:
@@ -379,6 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string =
   of nkEmpty: result = ""
   of nkIdent: result = n.ident.s
   of nkSym: result = n.sym.name.s
+  of nkClosedSymChoice, nkOpenSymChoice: result = n[0].sym.name.s
   of nkStrLit: result = ""; result.addQuoted(n.strVal)
   of nkRStrLit: result = "r\"" & replace(n.strVal, "\"", "\"\"") & '\"'
   of nkTripleStrLit: result = "\"\"\"" & n.strVal & "\"\"\""
@@ -398,18 +430,23 @@ proc atom(g: TSrcGen; n: PNode): string =
   of nkUInt64Lit: result = ulitAux(g, n, n.intVal, 8) & "\'u64"
   of nkFloatLit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}: result = $(n.floatVal)
-    else: result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[] , 8)
+    else: result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[] , 8)
   of nkFloat32Lit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f32"
     else:
       f = n.floatVal.float32
-      result = litAux(g, n, (cast[PInt32](addr(f)))[], 4) & "\'f32"
+      result = litAux(g, n, (cast[ptr int32](addr(f)))[], 4) & "\'f32"
   of nkFloat64Lit:
     if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
       result = $n.floatVal & "\'f64"
     else:
-      result = litAux(g, n, (cast[PInt64](addr(n.floatVal)))[], 8) & "\'f64"
+      result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f64"
+  of nkFloat128Lit:
+    if n.flags * {nfBase2, nfBase8, nfBase16} == {}:
+      result = $n.floatVal & "\'f128"
+    else:
+      result = litAux(g, n, (cast[ptr int64](addr(n.floatVal)))[], 8) & "\'f128"
   of nkNilLit: result = "nil"
   of nkType:
     if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s
@@ -434,8 +471,27 @@ proc lsons(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int =
   result = 0
   for i in start..n.len + theEnd: inc(result, lsub(g, n[i]))
 
+proc origUsingType(n: PNode): PSym {.inline.} =
+  ## Returns the type that a parameter references. Check with referencesUsing first
+  ## to check `n` is actually referencing a using node
+  # If the node is untyped the typ field will be nil
+  if n[0].sym.typ != nil:
+    n[0].sym.typ.sym
+  else: nil
+
+proc referencesUsing(n: PNode): bool =
+  ## Returns true if n references a using statement.
+  ## e.g. proc foo(x) # x doesn't have type or def value so it references a using
+  result = n.kind == nkIdentDefs and
+           # Sometimes the node might not have been semmed (e.g. doc0) and will be nkIdent instead
+           n[0].kind == nkSym and
+           # Templates/macros can have parameters with no type (But their orig type will be nil)
+           n.origUsingType != nil and
+           n[1].kind == nkEmpty and n[2].kind == nkEmpty
+
 proc lsub(g: TSrcGen; n: PNode): int =
   # computes the length of a tree
+  result = 0
   if isNil(n): return 0
   if shouldRenderComment(g, n): return MaxLineLen + 1
   case n.kind
@@ -463,6 +519,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
     result = if n.len > 0: lcomma(g, n) + 2 else: len("{:}")
   of nkClosedSymChoice, nkOpenSymChoice:
     if n.len > 0: result += lsub(g, n[0])
+  of nkOpenSym: result = lsub(g, n[0])
   of nkTupleTy: result = lcomma(g, n) + len("tuple[]")
   of nkTupleClassTy: result = len("tuple")
   of nkDotExpr: result = lsons(g, n) + 1
@@ -474,8 +531,11 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkDo: result = lsons(g, n) + len("do__:_")
   of nkConstDef, nkIdentDefs:
     result = lcomma(g, n, 0, - 3)
-    if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2
-    if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3
+    if n.referencesUsing:
+      result += lsub(g, newSymNode(n.origUsingType)) + 2
+    else:
+      if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2
+      if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3
   of nkVarTuple:
     if n[^1].kind == nkEmpty:
       result = lcomma(g, n, 0, - 2) + len("()")
@@ -492,7 +552,11 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkInfix: result = lsons(g, n) + 2
   of nkPrefix:
     result = lsons(g, n)+1+(if n.len > 0 and n[1].kind == nkInfix: 2 else: 0)
-  of nkPostfix: result = lsons(g, n)
+  of nkPostfix:
+    if renderNoPostfix notin g.flags:
+      result = lsons(g, n)
+    else:
+      result = lsub(g, n[1])
   of nkCallStrLit: result = lsons(g, n)
   of nkPragmaExpr: result = lsub(g, n[0]) + lcomma(g, n, 1)
   of nkRange: result = lsons(g, n) + 2
@@ -506,7 +570,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkTypeOfExpr: result = (if n.len > 0: lsub(g, n[0]) else: 0)+len("typeof()")
   of nkRefTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ref")
   of nkPtrTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("ptr")
-  of nkVarTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
+  of nkVarTy, nkOutTy: result = (if n.len > 0: lsub(g, n[0])+1 else: 0) + len("var")
   of nkDistinctTy:
     result = len("distinct") + (if n.len > 0: lsub(g, n[0])+1 else: 0)
     if n.len > 1:
@@ -518,7 +582,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
   of nkOfInherit: result = lsub(g, n[0]) + len("of_")
   of nkProcTy: result = lsons(g, n) + len("proc_")
   of nkIteratorTy: result = lsons(g, n) + len("iterator_")
-  of nkSharedTy: result = lsons(g, n) + len("shared_")
+  of nkSinkAsgn: result = lsons(g, n) + len("`=sink`(, )")
   of nkEnumTy:
     if n.len > 0:
       result = lsub(g, n[0]) + lcomma(g, n, 1) + len("enum_")
@@ -532,7 +596,7 @@ proc lsub(g: TSrcGen; n: PNode): int =
     if n.len > 1: result = MaxLineLen + 1
     else: result = lsons(g, n) + len("using_")
   of nkReturnStmt:
-    if n.len > 0 and n[0].kind == nkAsgn:
+    if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
       result = len("return_") + lsub(g, n[0][1])
     else:
       result = len("return_") + lsub(g, n[0])
@@ -570,14 +634,12 @@ type
 const
   emptyContext: TContext = (spacing: 0, flags: {})
 
-proc initContext(c: var TContext) =
-  c.spacing = 0
-  c.flags = {}
+proc initContext(): TContext =
+  result = (spacing: 0, flags: {})
 
 proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false)
 proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) =
-  var c: TContext
-  initContext(c)
+  var c: TContext = initContext()
   gsub(g, n, c, fromStmtList = fromStmtList)
 
 proc hasCom(n: PNode): bool =
@@ -598,7 +660,9 @@ proc isHideable(config: ConfigRef, n: PNode): bool =
   # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but
   # this requires a `cache`.
   case n.kind
-  of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern", "deprecated"]
+  of nkExprColonExpr:
+    result = n[0].kind == nkIdent and
+             n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"]
   of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"]
   else: result = false
 
@@ -607,8 +671,8 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
   let inPragma = g.inPragma == 1 # just the top-level
   var inHideable = false
   for i in start..n.len + theEnd:
-    var c = i < n.len + theEnd
-    var sublen = lsub(g, n[i]) + ord(c)
+    let c = i < n.len + theEnd
+    let sublen = lsub(g, n[i]) + ord(c)
     if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
     let oldLen = g.tokens.len
     if inPragma:
@@ -630,7 +694,7 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
     inHideable = false
 
 proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
-            theEnd: int = - 1) =
+            theEnd: int = -1) =
   var ind: int
   if rfInConstExpr in c.flags:
     ind = g.indent + IndentWidth
@@ -705,9 +769,8 @@ proc gcond(g: var TSrcGen, n: PNode) =
     put(g, tkParRi, ")")
 
 proc gif(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   gcond(g, n[0][0])
-  initContext(c)
   putWithSpace(g, tkColon, ":")
   if longMode(g, n) or (lsub(g, n[0][1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
@@ -718,20 +781,18 @@ proc gif(g: var TSrcGen, n: PNode) =
     gsub(g, n[i], c)
 
 proc gwhile(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   putWithSpace(g, tkWhile, "while")
   gcond(g, n[0])
   putWithSpace(g, tkColon, ":")
-  initContext(c)
   if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n[1], c)
 
 proc gpattern(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   put(g, tkCurlyLe, "{")
-  initContext(c)
   if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
@@ -739,20 +800,18 @@ proc gpattern(g: var TSrcGen, n: PNode) =
   put(g, tkCurlyRi, "}")
 
 proc gpragmaBlock(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   gsub(g, n[0])
   putWithSpace(g, tkColon, ":")
-  initContext(c)
   if longMode(g, n) or (lsub(g, n[1]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
   gstmts(g, n[1], c)
 
 proc gtry(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   put(g, tkTry, "try")
   putWithSpace(g, tkColon, ":")
-  initContext(c)
   if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
@@ -760,9 +819,8 @@ proc gtry(g: var TSrcGen, n: PNode) =
   gsons(g, n, c, 1)
 
 proc gfor(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   putWithSpace(g, tkFor, "for")
-  initContext(c)
   if longMode(g, n) or
       (lsub(g, n[^1]) + lsub(g, n[^2]) + 6 + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
@@ -775,8 +833,7 @@ proc gfor(g: var TSrcGen, n: PNode) =
   gstmts(g, n[^1], c)
 
 proc gcase(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
+  var c: TContext = initContext()
   if n.len == 0: return
   var last = if n[^1].kind == nkElse: -2 else: -1
   if longMode(g, n, 0, last): incl(c.flags, rfLongMode)
@@ -786,17 +843,17 @@ proc gcase(g: var TSrcGen, n: PNode) =
   optNL(g)
   gsons(g, n, c, 1, last)
   if last == - 2:
-    initContext(c)
+    c = initContext()
     if longMode(g, n[^1]): incl(c.flags, rfLongMode)
     gsub(g, n[^1], c)
 
 proc genSymSuffix(result: var string, s: PSym) {.inline.} =
-  if sfGenSym in s.flags:
+  if sfGenSym in s.flags and s.name.id != ord(wUnderscore):
     result.add '_'
     result.addInt s.id
 
 proc gproc(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   if n[namePos].kind == nkSym:
     let s = n[namePos].sym
     var ret = renderDefinitionName(s)
@@ -807,25 +864,23 @@ proc gproc(g: var TSrcGen, n: PNode) =
 
   if n[patternPos].kind != nkEmpty:
     gpattern(g, n[patternPos])
-  let oldInGenericParams = g.inGenericParams
-  g.inGenericParams = true
-  if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
-      n[miscPos][1].kind != nkEmpty:
-    gsub(g, n[miscPos][1])
-  else:
-    gsub(g, n[genericParamsPos])
-  g.inGenericParams = oldInGenericParams
+  g.inside(GenericParams):
+    if renderNoBody in g.flags and n[miscPos].kind != nkEmpty and
+        n[miscPos][1].kind != nkEmpty:
+      gsub(g, n[miscPos][1])
+    else:
+      gsub(g, n[genericParamsPos])
   gsub(g, n[paramsPos])
   if renderNoPragmas notin g.flags:
     gsub(g, n[pragmasPos])
   if renderNoBody notin g.flags:
-    if n[bodyPos].kind != nkEmpty:
+    if n.len > bodyPos and n[bodyPos].kind != nkEmpty:
       put(g, tkSpaces, Space)
       putWithSpace(g, tkEquals, "=")
       indentNL(g)
       gcoms(g)
       dedent(g)
-      initContext(c)
+      c = initContext()
       gstmts(g, n[bodyPos], c)
       putNL(g)
     else:
@@ -834,8 +889,7 @@ proc gproc(g: var TSrcGen, n: PNode) =
       dedent(g)
 
 proc gTypeClassTy(g: var TSrcGen, n: PNode) =
-  var c: TContext
-  initContext(c)
+  var c: TContext = initContext()
   putWithSpace(g, tkConcept, "concept")
   gsons(g, n[0], c) # arglist
   gsub(g, n[1]) # pragmas
@@ -854,8 +908,7 @@ proc gblock(g: var TSrcGen, n: PNode) =
   if n.len == 0:
     return
 
-  var c: TContext
-  initContext(c)
+  var c: TContext = initContext()
 
   if n[0].kind != nkEmpty:
     putWithSpace(g, tkBlock, "block")
@@ -875,10 +928,9 @@ proc gblock(g: var TSrcGen, n: PNode) =
   gstmts(g, n[1], c)
 
 proc gstaticStmt(g: var TSrcGen, n: PNode) =
-  var c: TContext
+  var c: TContext = initContext()
   putWithSpace(g, tkStatic, "static")
   putWithSpace(g, tkColon, ":")
-  initContext(c)
   if longMode(g, n) or (lsub(g, n[0]) + g.lineLen > MaxLineLen):
     incl(c.flags, rfLongMode)
   gcoms(g)                    # a good place for comments
@@ -892,7 +944,7 @@ proc gasm(g: var TSrcGen, n: PNode) =
     gsub(g, n[1])
 
 proc gident(g: var TSrcGen, n: PNode) =
-  if g.inGenericParams and n.kind == nkSym:
+  if GenericParams in g.inside and n.kind == nkSym:
     if sfAnon in n.sym.flags or
       (n.typ != nil and tfImplicitTypeParam in n.typ.flags): return
 
@@ -916,7 +968,9 @@ proc gident(g: var TSrcGen, n: PNode) =
       s.addInt localId
     if sfCursor in n.sym.flags:
       s.add "_cursor"
-  elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp):
+  elif n.kind == nkSym and (renderIds in g.flags or
+      (sfGenSym in n.sym.flags and n.sym.name.id != ord(wUnderscore)) or
+      n.sym.kind == skTemp):
     s.add '_'
     s.addInt n.sym.id
     when defined(debugMagics):
@@ -931,6 +985,7 @@ proc doParamsAux(g: var TSrcGen, params: PNode) =
     put(g, tkParRi, ")")
 
   if params.len > 0 and params[0].kind != nkEmpty:
+    put(g, tkSpaces, Space)
     putWithSpace(g, tkOpr, "->")
     gsub(g, params[0])
 
@@ -949,6 +1004,7 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
     case n.kind
     of nkClosedSymChoice, nkOpenSymChoice:
       if n.len > 0: result = bracketKind(g, n[0])
+      else: result = bkNone
     of nkSym:
       result = case n.sym.name.s
         of "[]": bkBracket
@@ -957,11 +1013,13 @@ proc bracketKind*(g: TSrcGen, n: PNode): BracketKind =
         of "{}=": bkCurlyAsgn
         else: bkNone
     else: result = bkNone
+  else:
+    result = bkNone
 
 proc skipHiddenNodes(n: PNode): PNode =
   result = n
   while result != nil:
-    if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv} and result.len > 1:
+    if result.kind in {nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv, nkOpenSym} and result.len > 1:
       result = result[1]
     elif result.kind in {nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString} and
         result.len > 0:
@@ -982,7 +1040,7 @@ proc accentedName(g: var TSrcGen, n: PNode) =
     gsub(g, n)
 
 proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
-  if i < 1 and i > 2: return
+  if i < 1 or i > 2: return
   var needsParenthesis = false
   let nNext = n[i].skipHiddenNodes
   if nNext.kind == nkInfix:
@@ -1001,15 +1059,41 @@ proc infixArgument(g: var TSrcGen, n: PNode, i: int) =
   if needsParenthesis:
     put(g, tkParRi, ")")
 
+const postExprBlocks = {nkStmtList, nkStmtListExpr,
+  nkOfBranch, nkElifBranch, nkElse,
+  nkExceptBranch, nkFinally, nkDo}
+
+proc postStatements(g: var TSrcGen, n: PNode, i: int, fromStmtList: bool) =
+  var i = i
+  if n[i].kind in {nkStmtList, nkStmtListExpr}:
+    if fromStmtList:
+      put(g, tkColon, ":")
+    else:
+      put(g, tkSpaces, Space)
+      put(g, tkDo, "do")
+      put(g, tkColon, ":")
+  gsub(g, n, i)
+  i.inc
+  for j in i ..< n.len:
+    if n[j].kind == nkDo:
+      optNL(g)
+    elif n[j].kind in {nkStmtList, nkStmtListExpr}:
+      optNL(g)
+      put(g, tkDo, "do")
+      put(g, tkColon, ":")
+    gsub(g, n, j)
+
 proc isCustomLit(n: PNode): bool =
   if n.len == 2 and n[0].kind == nkRStrLit:
     let ident = n[1].getPIdent
     result = ident != nil and ident.s.startsWith('\'')
+  else:
+    result = false
 
 proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   if isNil(n): return
   var
-    a: TContext
+    a: TContext = default(TContext)
   if shouldRenderComment(g, n): pushCom(g, n)
   case n.kind                 # atoms:
   of nkTripleStrLit: put(g, tkTripleStrLit, atom(g, n))
@@ -1035,27 +1119,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   of nkCharLit: put(g, tkCharLit, atom(g, n))
   of nkNilLit: put(g, tkNil, atom(g, n))    # complex expressions
   of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr:
-    if n.len > 1 and n.lastSon.kind in {nkStmtList, nkStmtListExpr}:
+    if n.len > 1 and n.lastSon.kind in postExprBlocks:
       accentedName(g, n[0])
       var i = 1
-      while i < n.len and n[i].kind notin {nkStmtList, nkStmtListExpr}: i.inc
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
       if i > 1:
         put(g, tkParLe, "(")
         gcomma(g, n, 1, i - 1 - n.len)
         put(g, tkParRi, ")")
-      if fromStmtList:
-        put(g, tkColon, ":")
-      else:
-        put(g, tkSpaces, Space)
-        put(g, tkDo, "do")
-        put(g, tkColon, ":")
-      gsub(g, n, i)
-      i.inc
-      for j in i ..< n.len:
-        optNL(g)
-        put(g, tkDo, "do")
-        put(g, tkColon, ":")
-        gsub(g, n, j)
+      postStatements(g, n, i, fromStmtList)
     elif n.len >= 1:
       case bracketKind(g, n[0])
       of bkBracket:
@@ -1155,14 +1227,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   of nkCommand:
     accentedName(g, n[0])
     put(g, tkSpaces, Space)
-    if n[^1].kind == nkStmtList:
-      for i, child in n:
-        if i > 1 and i < n.len - 1:
-          put(g, tkComma, ",")
-        elif i == n.len - 1:
-          put(g, tkColon, ":")
-        if i > 0:
-          gsub(g, child)
+    if n.len > 1 and n.lastSon.kind in postExprBlocks:
+      var i = 1
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      if i > 1:
+        gcomma(g, n, 1, i - 1 - n.len)
+      postStatements(g, n, i, fromStmtList)
     else:
       gcomma(g, n, 1)
   of nkExprEqExpr, nkAsgn, nkFastAsgn:
@@ -1170,6 +1240,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     put(g, tkSpaces, Space)
     putWithSpace(g, tkEquals, "=")
     gsub(g, n, 1)
+  of nkSinkAsgn:
+    put(g, tkSymbol, "`=sink`")
+    put(g, tkParLe, "(")
+    gcomma(g, n)
+    put(g, tkParRi, ")")
   of nkChckRangeF:
     put(g, tkSymbol, "chckRangeF")
     put(g, tkParLe, "(")
@@ -1206,6 +1281,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
       put(g, tkParRi, if n.kind == nkOpenSymChoice: "|...)" else: ")")
     else:
       gsub(g, n, 0)
+  of nkOpenSym: gsub(g, n, 0)
   of nkPar, nkClosure:
     put(g, tkParLe, "(")
     gcomma(g, n, c)
@@ -1244,8 +1320,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     gsub(g, n, 0)
   of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString:
     if renderIds in g.flags:
-      putWithSpace(g, tkAddr, $n.kind)
+      put(g, tkAddr, $n.kind)
+      put(g, tkParLe, "(")
     gsub(g, n, 0)
+    if renderIds in g.flags:
+      put(g, tkParRi, ")")
+
   of nkLambda:
     putWithSpace(g, tkProc, "proc")
     gsub(g, n, paramsPos)
@@ -1260,11 +1340,38 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     gsub(g, n, pragmasPos)
     put(g, tkColon, ":")
     gsub(g, n, bodyPos)
-  of nkConstDef, nkIdentDefs:
+  of nkIdentDefs:
+    var exclFlags: TRenderFlags = {}
+    if ObjectDef in g.inside:
+      if not n[0].isExported() and renderNonExportedFields notin g.flags:
+        # Skip if this is a property in a type and its not exported
+        # (While also not allowing rendering of non exported fields)
+        return
+      # render postfix for object fields:
+      exclFlags = g.flags * {renderNoPostfix}
+    # We render the identDef without being inside the section incase we render something like
+    # y: proc (x: string) # (We wouldn't want to check if x is exported)
+    g.outside(ObjectDef):
+      g.flags.excl(exclFlags)
+      gcomma(g, n, 0, -3)
+      g.flags.incl(exclFlags)
+      if n.len >= 2 and n[^2].kind != nkEmpty:
+        putWithSpace(g, tkColon, ":")
+        gsub(g, n[^2], c)
+      elif n.referencesUsing and renderExpandUsing in g.flags:
+        putWithSpace(g, tkColon, ":")
+        gsub(g, newSymNode(n.origUsingType), c)
+
+      if n.len >= 1 and n[^1].kind != nkEmpty:
+        put(g, tkSpaces, Space)
+        putWithSpace(g, tkEquals, "=")
+        gsub(g, n[^1], c)
+  of nkConstDef:
     gcomma(g, n, 0, -3)
     if n.len >= 2 and n[^2].kind != nkEmpty:
       putWithSpace(g, tkColon, ":")
-      gsub(g, n, n.len - 2)
+      gsub(g, n[^2], c)
+
     if n.len >= 1 and n[^1].kind != nkEmpty:
       put(g, tkSpaces, Space)
       putWithSpace(g, tkEquals, "=")
@@ -1286,6 +1393,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     putWithSpace(g, tkColon, ":")
     gsub(g, n, 1)
   of nkInfix:
+    if n.len < 3:
+      var i = 0
+      put(g, tkOpr, "Too few children for nkInfix")
+      return
     let oldLineLen = g.lineLen # we cache this because lineLen gets updated below
     infixArgument(g, n, 1)
     put(g, tkSpaces, Space)
@@ -1296,6 +1407,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     else:
       put(g, tkSpaces, Space)
     infixArgument(g, n, 2)
+    if n.len > 3 and n.lastSon.kind in postExprBlocks:
+      var i = 3
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      postStatements(g, n, i, fromStmtList)
   of nkPrefix:
     gsub(g, n, 0)
     if n.len > 1:
@@ -1312,9 +1427,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
         put(g, tkParRi, ")")
       else:
         gsub(g, n[1])
+    if n.len > 2 and n.lastSon.kind in postExprBlocks:
+      var i = 2
+      while i < n.len and n[i].kind notin postExprBlocks: i.inc
+      postStatements(g, n, i, fromStmtList)
   of nkPostfix:
     gsub(g, n, 1)
-    gsub(g, n, 0)
+    if renderNoPostfix notin g.flags:
+      gsub(g, n, 0)
   of nkRange:
     gsub(g, n, 0)
     put(g, tkDotDot, "..")
@@ -1325,16 +1445,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   of nkAccQuoted:
     put(g, tkAccent, "`")
     for i in 0..<n.len:
-      proc getStrVal(n: PNode): string =
-        # pending https://github.com/nim-lang/Nim/pull/17540, use `getStrVal`
-        case n.kind
-        of nkIdent: n.ident.s
-        of nkSym: n.sym.name.s
-        else: ""
       proc isAlpha(n: PNode): bool =
         if n.kind in {nkIdent, nkSym}:
-          let tmp = n.getStrVal
+          let tmp = n.getPIdent.s
           result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'}
+        else:
+          result = false
       var useSpace = false
       if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]:
         if not n[1].isAlpha: # handle `=destroy`, `'big'
@@ -1384,6 +1500,12 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
       gsub(g, n[0])
     else:
       put(g, tkVar, "var")
+  of nkOutTy:
+    if n.len > 0:
+      putWithSpace(g, tkOut, "out")
+      gsub(g, n[0])
+    else:
+      put(g, tkOut, "out")
   of nkDistinctTy:
     if n.len > 0:
       putWithSpace(g, tkDistinct, "distinct")
@@ -1412,20 +1534,20 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   of nkObjectTy:
     if n.len > 0:
       putWithSpace(g, tkObject, "object")
-      gsub(g, n[0])
-      gsub(g, n[1])
-      gcoms(g)
-      gsub(g, n[2])
+      g.inside(ObjectDef):
+        gsub(g, n[0])
+        gsub(g, n[1])
+        gcoms(g)
+        indentNL(g)
+        gsub(g, n[2])
+        dedent(g)
     else:
       put(g, tkObject, "object")
   of nkRecList:
-    indentNL(g)
     for i in 0..<n.len:
       optNL(g)
       gsub(g, n[i], c)
       gcoms(g)
-    dedent(g)
-    putNL(g)
   of nkOfInherit:
     putWithSpace(g, tkOf, "of")
     gsub(g, n, 0)
@@ -1510,7 +1632,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
   of nkTypeSection:
     gsection(g, n, emptyContext, tkType, "type")
   of nkConstSection:
-    initContext(a)
+    a = initContext()
     incl(a.flags, rfInConstExpr)
     gsection(g, n, a, tkConst, "const")
   of nkVarSection, nkLetSection, nkUsingStmt:
@@ -1530,7 +1652,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
       gsub(g, n[0])
   of nkReturnStmt:
     putWithSpace(g, tkReturn, "return")
-    if n.len > 0 and n[0].kind == nkAsgn:
+    if n.len > 0 and n[0].kind == nkAsgn and renderIr notin g.flags:
       gsub(g, n[0], 1)
     else:
       gsub(g, n, 0)
@@ -1678,13 +1800,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
     gsub(g, n, 0)
     put(g, tkParRi, ")")
   of nkGotoState:
-    var c: TContext
-    initContext c
+    var c: TContext = initContext()
     putWithSpace g, tkSymbol, "goto"
     gsons(g, n, c)
   of nkState:
-    var c: TContext
-    initContext c
+    var c: TContext = initContext()
     putWithSpace g, tkSymbol, "state"
     gsub(g, n[0], c)
     putWithSpace(g, tkColon, ":")
@@ -1708,8 +1828,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) =
 
 proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string =
   if n == nil: return "<nil tree>"
-  var g: TSrcGen
-  initSrcGen(g, renderFlags, newPartialConfigRef())
+  var g: TSrcGen = initSrcGen(renderFlags, newPartialConfigRef())
   # do not indent the initial statement list so that
   # writeFile("file.nim", repr n)
   # produces working Nim code:
@@ -1726,9 +1845,8 @@ proc renderModule*(n: PNode, outfile: string,
                    fid = FileIndex(-1);
                    conf: ConfigRef = nil) =
   var
-    f: File
-    g: TSrcGen
-  initSrcGen(g, renderFlags, conf)
+    f: File = default(File)
+    g: TSrcGen = initSrcGen(renderFlags, conf)
   g.fid = fid
   for i in 0..<n.len:
     gsub(g, n[i])
@@ -1744,9 +1862,9 @@ proc renderModule*(n: PNode, outfile: string,
   else:
     rawMessage(g.config, errGenerated, "cannot open file: " & outfile)
 
-proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) =
-  initSrcGen(r, renderFlags, newPartialConfigRef())
-  gsub(r, n)
+proc initTokRender*(n: PNode, renderFlags: TRenderFlags = {}): TSrcGen =
+  result = initSrcGen(renderFlags, newPartialConfigRef())
+  gsub(result, n)
 
 proc getNextTok*(r: var TSrcGen, kind: var TokType, literal: var string) =
   if r.idx < r.tokens.len:
diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim
index 792079b3f..c12595156 100644
--- a/compiler/renderverbatim.nim
+++ b/compiler/renderverbatim.nim
@@ -1,4 +1,4 @@
-import strutils
+import std/strutils
 
 import ast, options, msgs
 
@@ -40,6 +40,7 @@ type LineData = object
 
 proc tripleStrLitStartsAtNextLine(conf: ConfigRef, n: PNode): bool =
   # enabling TLineInfo.offsetA,offsetB would probably make this easier
+  result = false
   const tripleQuote = "\"\"\""
   let src = sourceLine(conf, n.info)
   let col = n.info.col
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index daeb48248..2f7c04af1 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -1,12 +1,17 @@
 
 import
-  intsets, ast, idents, algorithm, renderer, strutils,
+  ast, idents, renderer,
   msgs, modulegraphs, syntaxes, options, modulepaths,
   lineinfos
 
+import std/[algorithm, strutils, intsets]
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
+when defined(nimDebugReorder):
+  import std/tables
+
 type
   DepN = ref object
     pnode: PNode
@@ -14,26 +19,20 @@ type
     onStack: bool
     kids: seq[DepN]
     hAQ, hIS, hB, hCmd: int
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       expls: seq[string]
   DepG = seq[DepN]
 
-when defined(debugReorder):
+when defined(nimDebugReorder):
   var idNames = newTable[int, string]()
 
 proc newDepN(id: int, pnode: PNode): DepN =
-  new(result)
-  result.id = id
-  result.pnode = pnode
-  result.idx = -1
-  result.lowLink = -1
-  result.onStack = false
-  result.kids = @[]
-  result.hAQ = -1
-  result.hIS = -1
-  result.hB = -1
-  result.hCmd = -1
-  when defined(debugReorder):
+  result = DepN(id: id, pnode: pnode, idx: -1,
+                lowLink: -1, onStack: false,
+                kids: @[], hAQ: -1, hIS: -1,
+                hB: -1, hCmd: -1
+  )
+  when defined(nimDebugReorder):
     result.expls = @[]
 
 proc accQuoted(cache: IdentCache; n: PNode): PIdent =
@@ -49,16 +48,16 @@ proc addDecl(cache: IdentCache; n: PNode; declares: var IntSet) =
   of nkPragmaExpr: addDecl(cache, n[0], declares)
   of nkIdent:
     declares.incl n.ident.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[n.ident.id] = n.ident.s
   of nkSym:
     declares.incl n.sym.name.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[n.sym.name.id] = n.sym.name.s
   of nkAccQuoted:
     let a = accQuoted(cache, n)
     declares.incl a.id
-    when defined(debugReorder):
+    when defined(nimDebugReorder):
       idNames[a.id] = a.s
   of nkEnumFieldDef:
     addDecl(cache, n[0], declares)
@@ -103,6 +102,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
     if a.kind == nkExprColonExpr and a[0].kind == nkIdent and a[0].ident.s == "pragma":
       # user defined pragma
       decl(a[1])
+      for i in 1..<n.safeLen: deps(n[i])
     else:
       for i in 0..<n.safeLen: deps(n[i])
   of nkMixinStmt, nkBindStmt: discard
@@ -110,7 +110,8 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev
     # XXX: for callables, this technically adds the return type dep before args
     for i in 0..<n.safeLen: deps(n[i])
 
-proc hasIncludes(n:PNode): bool =
+proc hasIncludes(n: PNode): bool =
+  result = false
   for a in n:
     if a.kind == nkIncludeStmt:
       return true
@@ -193,7 +194,7 @@ proc mergeSections(conf: ConfigRef; comps: seq[seq[DepN]], res: PNode) =
         # consecutive type and const sections
         var wmsg = "Circular dependency detected. `codeReordering` pragma may not be able to" &
           " reorder some nodes properly"
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           wmsg &= ":\n"
           for i in 0..<cs.len-1:
             for j in i..<cs.len:
@@ -230,8 +231,9 @@ proc hasImportStmt(n: PNode): bool =
   # i it contains one
   case n.kind
   of nkImportStmt, nkFromStmt, nkImportExceptStmt:
-    return true
+    result = true
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
       if a.hasImportStmt:
         return true
@@ -252,6 +254,7 @@ proc hasCommand(n: PNode): bool =
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse,
       nkStaticStmt, nkLetSection, nkConstSection, nkVarSection,
       nkIdentDefs:
+    result = false
     for a in n:
       if a.hasCommand:
         return true
@@ -264,6 +267,7 @@ proc hasCommand(n: DepN): bool =
   result = bool(n.hCmd)
 
 proc hasAccQuoted(n: PNode): bool =
+  result = false
   if n.kind == nkAccQuoted:
     return true
   for a in n:
@@ -279,6 +283,7 @@ proc hasAccQuotedDef(n: PNode): bool =
   of extendedProcDefs:
     result = n[0].hasAccQuoted
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
       if hasAccQuotedDef(a):
         return true
@@ -299,6 +304,7 @@ proc hasBody(n: PNode): bool =
   of extendedProcDefs:
     result = n[^1].kind == nkStmtList
   of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt:
+    result = false
     for a in n:
       if a.hasBody:
         return true
@@ -311,10 +317,19 @@ proc hasBody(n: DepN): bool =
   result = bool(n.hB)
 
 proc intersects(s1, s2: IntSet): bool =
+  result = false
   for a in s1:
     if s2.contains(a):
       return true
 
+proc hasPushOrPopPragma(n: DepN): bool =
+  # Checks if the tree node has some pragmas that do not
+  # play well with reordering, like the push/pop pragma
+  # no crossing for push/pop barrier
+  let a = n.pnode
+  result = a.kind == nkPragma and a[0].kind == nkIdent and
+      (a[0].ident.s == "push" or a[0].ident.s == "pop")
+
 proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
   # Build a dependency graph
   result = newSeqOfCap[DepN](deps.len)
@@ -332,13 +347,13 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
       if j < i and nj.hasCommand and niHasCmd:
         # Preserve order for commands and calls
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "both have commands and one comes after the other"
       elif j < i and nj.hasImportStmt:
         # Every node that comes after an import statement must
         # depend on that import
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "parent is, or contains, an import statement and child comes after it"
       elif j < i and niHasBody and nj.hasAccQuotedDef:
         # Every function, macro, template... with a body depends
@@ -346,21 +361,28 @@ proc buildGraph(n: PNode, deps: seq[(IntSet, IntSet)]): DepG =
         # That's because it is hard to detect the use of functions
         # like "[]=", "[]", "or" ... in their bodies.
         ni.kids.add nj
-        when defined(debugReorder):
+        when defined(nimDebugReorder):
           ni.expls.add "one declares a quoted identifier and the other has a body and comes after it"
       elif j < i and niHasBody and not nj.hasBody and
         intersects(deps[i][0], declares):
           # Keep function declaration before function definition
           ni.kids.add nj
-          when defined(debugReorder):
+          when defined(nimDebugReorder):
             for dep in deps[i][0]:
               if dep in declares:
                 ni.expls.add "one declares \"" & idNames[dep] & "\" and the other defines it"
+      elif hasPushOrPopPragma(nj):
+        # Every node that comes after a push/pop pragma must
+        # depend on it; vice versa
+        if j < i:
+          ni.kids.add nj
+        else:
+          nj.kids.add ni
       else:
         for d in declares:
           if uses.contains(d):
             ni.kids.add nj
-            when defined(debugReorder):
+            when defined(nimDebugReorder):
               ni.expls.add "one declares \"" & idNames[d] & "\" and the other uses it"
 
 proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
@@ -389,23 +411,14 @@ proc strongConnect(v: var DepN, idx: var int, s: var seq[DepN],
 proc getStrongComponents(g: var DepG): seq[seq[DepN]] =
   ## Tarjan's algorithm. Performs a topological sort
   ## and detects strongly connected components.
-  var s: seq[DepN]
+  result = @[]
+  var s: seq[DepN] = @[]
   var idx = 0
   for v in g.mitems:
     if v.idx < 0:
       strongConnect(v, idx, s, result)
 
-proc hasForbiddenPragma(n: PNode): bool =
-  # Checks if the tree node has some pragmas that do not
-  # play well with reordering, like the push/pop pragma
-  for a in n:
-    if a.kind == nkPragma and a[0].kind == nkIdent and
-        a[0].ident.s == "push":
-      return true
-
 proc reorder*(graph: ModuleGraph, n: PNode, module: PSym): PNode =
-  if n.hasForbiddenPragma:
-    return n
   var includedFiles = initIntSet()
   let mpath = toFullPath(graph.config, module.fileIdx)
   let n = expandIncludes(graph, module, n, mpath,
diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim
index e2e9e1eb2..5355829c1 100644
--- a/compiler/rodutils.nim
+++ b/compiler/rodutils.nim
@@ -8,7 +8,7 @@
 #
 
 ## Serialization utilities for the compiler.
-import strutils, math
+import std/[strutils, math]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -34,7 +34,7 @@ when defined(windows) and defined(bcc):
   #endif
   """.}
 
-proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
+proc c_snprintf(s: cstring; n: uint; frmt: cstring): cint {.importc: "snprintf", header: "<stdio.h>", nodecl, varargs.}
 
 
 when not declared(signbit):
@@ -61,6 +61,7 @@ proc toStrMaxPrecision*(f: BiggestFloat | float32): string =
   of fcNegInf:
     result = "-INF"
   else:
+    result = ""
     result.addFloatRoundtrip(f)
     result.add literalPostfix
 
diff --git a/compiler/ropes.nim b/compiler/ropes.nim
index 12eac733e..e0d5aa0d3 100644
--- a/compiler/ropes.nim
+++ b/compiler/ropes.nim
@@ -9,8 +9,6 @@
 
 # Ropes for the C code generator. Ropes are mapped to `string` directly nowadays.
 
-import hashes
-
 from pathutils import AbsoluteFile
 
 when defined(nimPreviewSlimSystem):
@@ -23,8 +21,8 @@ type
                        # though it is not necessary)
   Rope* = string
 
-proc newRopeAppender*(): string {.inline.} =
-  result = newString(0)
+proc newRopeAppender*(cap = 80): string {.inline.} =
+  result = newStringOfCap(cap)
 
 proc freeze*(r: Rope) {.inline.} = discard
 
@@ -45,7 +43,7 @@ proc writeRope*(f: File, r: Rope) =
   write(f, r)
 
 proc writeRope*(head: Rope, filename: AbsoluteFile): bool =
-  var f: File
+  var f: File = default(File)
   if open(f, filename.string, fmWrite):
     writeRope(f, head)
     close(f)
@@ -78,7 +76,7 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
           if i >= frmt.len or frmt[i] notin {'0'..'9'}: break
         num = j
         if j > high(args) + 1:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
         else:
           result.add(args[j-1])
       of '{':
@@ -90,10 +88,10 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
         num = j
         if frmt[i] == '}': inc(i)
         else:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
 
         if j > high(args) + 1:
-          doAssert false, "invalid format string: " & frmt
+          raiseAssert "invalid format string: " & frmt
         else:
           result.add(args[j-1])
       of 'n':
@@ -103,13 +101,10 @@ proc runtimeFormat*(frmt: FormatStr, args: openArray[Rope]): Rope =
         result.add("\n")
         inc(i)
       else:
-        doAssert false, "invalid format string: " & frmt
-    var start = i
-    while i < frmt.len:
-      if frmt[i] != '$': inc(i)
-      else: break
-    if i - 1 >= start:
-      result.add(substr(frmt, start, i - 1))
+        raiseAssert "invalid format string: " & frmt
+    else:
+      result.add(frmt[i])
+      inc(i)
 
 proc `%`*(frmt: static[FormatStr], args: openArray[Rope]): Rope =
   runtimeFormat(frmt, args)
@@ -124,7 +119,7 @@ const
 proc equalsFile*(s: Rope, f: File): bool =
   ## returns true if the contents of the file `f` equal `r`.
   var
-    buf: array[bufSize, char]
+    buf: array[bufSize, char] = default(array[bufSize, char])
     bpos = buf.len
     blen = buf.len
     btotal = 0
@@ -156,7 +151,7 @@ proc equalsFile*(s: Rope, f: File): bool =
 proc equalsFile*(r: Rope, filename: AbsoluteFile): bool =
   ## returns true if the contents of the file `f` equal `r`. If `f` does not
   ## exist, false is returned.
-  var f: File
+  var f: File = default(File)
   result = open(f, filename.string)
   if result:
     result = equalsFile(r, f)
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index adc228d1e..e3d2bcd45 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -11,16 +11,18 @@
 ## language.
 
 import
-  ast, modules, idents, passes, condsyms,
-  options, sem, llstream, vm, vmdef, commands,
-  os, times, osproc, wordrecg, strtabs, modulegraphs,
-  pathutils
+  ast, modules, idents, condsyms,
+  options, llstream, vm, vmdef, commands,
+  wordrecg, modulegraphs,
+  pathutils, pipelines
 
 when defined(nimPreviewSlimSystem):
-  import std/syncio
+  import std/[syncio, assertions]
+
+import std/[strtabs, os, times, osproc]
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
-from strutils import cmpIgnoreStyle, contains
+from std/strutils import cmpIgnoreStyle, contains
 
 proc listDirs(a: VmArgs, filter: set[PathComponent]) =
   let dir = getString(a, 0)
@@ -160,6 +162,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
   cbconf getCommand:
     setResult(a, conf.command)
   cbconf switch:
+    conf.currentConfigDir = vthisDir
     processSwitch(a.getString 0, a.getString 1, passPP, module.info, conf)
   cbconf hintImpl:
     processSpecificNote(a.getString 0, wHint, passPP, module.info,
@@ -181,15 +184,13 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string;
     options.cppDefine(conf, a.getString(0))
   cbexc stdinReadLine, EOFError:
     if defined(nimsuggest) or graph.config.cmd == cmdCheck:
-      discard
-    else:
       setResult(a, "")
+    else:
       setResult(a, stdin.readLine())
   cbexc stdinReadAll, EOFError:
     if defined(nimsuggest) or graph.config.cmd == cmdCheck:
-      discard
-    else:
       setResult(a, "")
+    else:
       setResult(a, stdin.readAll())
 
 proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
@@ -199,20 +200,18 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
   conf.symbolFiles = disabledSf
 
   let graph = newModuleGraph(cache, conf)
-  connectCallbacks(graph)
+  connectPipelineCallbacks(graph)
   if freshDefines: initDefines(conf.symbols)
 
   defineSymbol(conf.symbols, "nimscript")
   defineSymbol(conf.symbols, "nimconfig")
-  registerPass(graph, semPass)
-  registerPass(graph, evalPass)
 
   conf.searchPaths.add(conf.libpath)
 
   let oldGlobalOptions = conf.globalOptions
   let oldSelectedGC = conf.selectedGC
-  undefSymbol(conf.symbols, "nimv2")
-  conf.globalOptions.excl {optTinyRtti, optOwnedRefs, optSeqDestructors}
+  unregisterArcOrc(conf)
+  conf.globalOptions.excl optOwnedRefs
   conf.selectedGC = gcUnselected
 
   var m = graph.makeModule(scriptName)
@@ -220,8 +219,9 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
   var vm = setupVM(m, cache, scriptName.string, graph, idgen)
   graph.vm = vm
 
-  graph.compileSystemModule()
-  discard graph.processModule(m, vm.idgen, stream)
+  graph.setPipeLinePass(EvalPass)
+  graph.compilePipelineSystemModule()
+  discard graph.processPipelineModule(m, vm.idgen, stream)
 
   # watch out, "newruntime" can be set within NimScript itself and then we need
   # to remember this:
@@ -230,9 +230,20 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile;
   if optOwnedRefs in oldGlobalOptions:
     conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors}
     defineSymbol(conf.symbols, "nimv2")
-  if conf.selectedGC in {gcArc, gcOrc}:
+  if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
     conf.globalOptions.incl {optTinyRtti, optSeqDestructors}
     defineSymbol(conf.symbols, "nimv2")
+    defineSymbol(conf.symbols, "gcdestructors")
+    defineSymbol(conf.symbols, "nimSeqsV2")
+    case conf.selectedGC
+    of gcArc:
+      defineSymbol(conf.symbols, "gcarc")
+    of gcOrc:
+      defineSymbol(conf.symbols, "gcorc")
+    of gcAtomicArc:
+      defineSymbol(conf.symbols, "gcatomicarc")
+    else:
+      raiseAssert "unreachable"
 
   # ensure we load 'system.nim' again for the real non-config stuff!
   resetSystemArtifacts(graph)
diff --git a/compiler/sem.nim b/compiler/sem.nim
index b6ee76cc9..2cf93d365 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -10,23 +10,27 @@
 # This module implements the semantic checking pass.
 
 import
-  ast, strutils, options, astalgo, trees,
-  wordrecg, ropes, msgs, idents, renderer, types, platform, math,
+  ast, options, astalgo, trees,
+  wordrecg, ropes, msgs, idents, renderer, types, platform,
   magicsys, nversion, nimsets, semfold, modulepaths, importer,
-  procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
-  intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting,
+  procfind, lookups, pragmas, semdata, semtypinst, sigmatch,
+  transf, vmdef, vm, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
-  lowerings, plugins/active, lineinfos, strtabs, int128,
-  isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs
+  lowerings, plugins/active, lineinfos, int128,
+  isolation_check, typeallowed, modulegraphs, enumtostr, concepts, astmsgs,
+  extccomp
 
-when defined(nimfix):
-  import nimfix/prettybase
+import vtables
+import std/[strtabs, math, tables, intsets, strutils, packedsets]
 
 when not defined(leanCompiler):
   import spawn
 
 when defined(nimPreviewSlimSystem):
-  import std/formatfloat
+  import std/[
+    formatfloat,
+    assertions,
+  ]
 
 # implementation
 
@@ -81,7 +85,7 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode;
 proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode =
   let x = arg.skipConv
   if (x.kind == nkCurly and formal.kind == tySet and formal.base.kind != tyGenericParam) or
-    (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass}:
+    (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass, tyAnything}:
     changeType(c, x, formal, check=true)
   result = arg
   result = skipHiddenSubConv(result, c.graph, c.idgen)
@@ -96,9 +100,10 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode =
     result.typ = formal
   elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum:
     # Pick the right 'sym' from the sym choice by looking at 'formal' type:
+    result = nil
     for ch in arg:
       if sameType(ch.typ, formal):
-        return getConstExpr(c.module, ch, c.idgen, c.graph)
+        return ch
     typeMismatch(c.config, info, formal, arg.typ, arg)
   else:
     result = indexTypesMatch(c, formal, arg.typ, arg)
@@ -139,26 +144,26 @@ proc commonType*(c: PContext; x, y: PType): PType =
   elif b.kind == tyTyped: result = b
   elif a.kind == tyTypeDesc:
     # turn any concrete typedesc into the abstract typedesc type
-    if a.len == 0: result = a
+    if not a.hasElementType: result = a
     else:
-      result = newType(tyTypeDesc, nextTypeId(c.idgen), a.owner)
-      rawAddSon(result, newType(tyNone, nextTypeId(c.idgen), a.owner))
+      result = newType(tyTypeDesc, c.idgen, a.owner)
+      rawAddSon(result, newType(tyNone, c.idgen, a.owner))
   elif b.kind in {tyArray, tySet, tySequence} and
       a.kind == b.kind:
     # check for seq[empty] vs. seq[int]
     let idx = ord(b.kind == tyArray)
     if a[idx].kind == tyEmpty: return y
-  elif a.kind == tyTuple and b.kind == tyTuple and a.len == b.len:
-    var nt: PType
-    for i in 0..<a.len:
-      let aEmpty = isEmptyContainer(a[i])
-      let bEmpty = isEmptyContainer(b[i])
+  elif a.kind == tyTuple and b.kind == tyTuple and sameTupleLengths(a, b):
+    var nt: PType = nil
+    for i, aa, bb in tupleTypePairs(a, b):
+      let aEmpty = isEmptyContainer(aa)
+      let bEmpty = isEmptyContainer(bb)
       if aEmpty != bEmpty:
         if nt.isNil:
-          nt = copyType(a, nextTypeId(c.idgen), a.owner)
+          nt = copyType(a, c.idgen, a.owner)
           copyTypeProps(c.graph, c.idgen.module, nt, a)
 
-        nt[i] = if aEmpty: b[i] else: a[i]
+        nt[i] = if aEmpty: bb else: aa
     if not nt.isNil: result = nt
     #elif b[idx].kind == tyEmpty: return x
   elif a.kind == tyRange and b.kind == tyRange:
@@ -191,8 +196,8 @@ proc commonType*(c: PContext; x, y: PType): PType =
       k = a.kind
       if b.kind != a.kind: return x
       # bug #7601, array construction of ptr generic
-      a = a.lastSon.skipTypes({tyGenericInst})
-      b = b.lastSon.skipTypes({tyGenericInst})
+      a = a.elementType.skipTypes({tyGenericInst})
+      b = b.elementType.skipTypes({tyGenericInst})
     if a.kind == tyObject and b.kind == tyObject:
       result = commonSuperclass(a, b)
       # this will trigger an error later:
@@ -202,16 +207,22 @@ proc commonType*(c: PContext; x, y: PType): PType =
       # ill-formed AST, no need for additional tyRef/tyPtr
       if k != tyNone and x.kind != tyGenericInst:
         let r = result
-        result = newType(k, nextTypeId(c.idgen), r.owner)
+        result = newType(k, c.idgen, r.owner)
         result.addSonSkipIntLit(r, c.idgen)
 
-proc endsInNoReturn(n: PNode): bool =
-  # check if expr ends in raise exception or call of noreturn proc
-  var it = n
-  while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
-    it = it.lastSon
-  result = it.kind in nkLastBlockStmts or
-    it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
+proc shouldCheckCaseCovered(caseTyp: PType): bool =
+  result = false
+  case caseTyp.kind
+  of shouldChckCovered:
+    result = true
+  of tyRange:
+    if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
+      result = true
+  else:
+    discard
+
+proc endsInNoReturn(n: PNode): bool
 
 proc commonType*(c: PContext; x: PType, y: PNode): PType =
   # ignore exception raising branches in case/if expressions
@@ -219,7 +230,7 @@ proc commonType*(c: PContext; x: PType, y: PNode): PType =
   commonType(c, x, y.typ)
 
 proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
-  result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
+  result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
   when defined(nimsuggest):
     suggestDecl(c, n, result)
 
@@ -242,7 +253,9 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
     # template; we must fix it here: see #909
     result.owner = getCurrOwner(c)
   else:
-    result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info)
+    result = newSym(kind, considerQuotedIdent(c, n), c.idgen, getCurrOwner(c), n.info)
+    if find(result.name.s, '`') >= 0:
+      result.flags.incl sfWasGenSym
   #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule:
   #  incl(result.flags, sfGlobal)
   when defined(nimsuggest):
@@ -252,7 +265,7 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
                  allowed: TSymFlags): PSym
   # identifier with visibility
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym
+                        allowed: TSymFlags, fromTopLevel = false): PSym
 
 proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind;
                       flags: TTypeAllowedFlags = {}) =
@@ -279,16 +292,6 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}; expectedType: PType = nil): PNode
 
-proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym =
-  if t.sym != nil: return t.sym
-  result = newSym(skType, getIdent(c.cache, "AnonType"), nextSymId c.idgen, t.owner, info)
-  result.flags.incl sfAnon
-  result.typ = t
-
-proc symNodeFromType(c: PContext, t: PType, info: TLineInfo): PNode =
-  result = newSymNode(symFromType(c, t, info), info)
-  result.typ = makeTypeDesc(c, t)
-
 when false:
   proc createEvalContext(c: PContext, mode: TEvalMode): PEvalContext =
     result = newEvalContext(c.module, mode)
@@ -305,6 +308,7 @@ when false:
       result = isOpImpl(c, n)
 
 proc hasCycle(n: PNode): bool =
+  result = false
   incl n.flags, nfNone
   for i in 0..<n.safeLen:
     if nfNone in n[i].flags or hasCycle(n[i]):
@@ -351,6 +355,11 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   c.config.m.errorOutputs = {}
   c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here
 
+  when defined(nimsuggest):
+    # Remove the error hook so nimsuggest doesn't report errors there
+    let tempHook = c.graph.config.structuredErrorHook
+    c.graph.config.structuredErrorHook = nil
+
   try:
     result = evalConstExpr(c.module, c.idgen, c.graph, e)
     if result == nil or result.kind == nkEmpty:
@@ -361,6 +370,10 @@ proc tryConstExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   except ERecoverableError:
     result = nil
 
+  when defined(nimsuggest):
+    # Restore the error hook
+    c.graph.config.structuredErrorHook = tempHook
+
   c.config.errorCounter = oldErrorCount
   c.config.errorMax = oldErrorMax
   c.config.m.errorOutputs = oldErrorOutputs
@@ -405,8 +418,7 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedTyp
       evaluated = evalAtCompileTime(c, result)
       if evaluated != nil: return evaluated
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
+proc semGenericStmt(c: PContext, n: PNode): PNode
 
 include hlo, seminst, semcall
 
@@ -429,17 +441,17 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
   c.friendModules.add(s.owner.getModule)
   result = macroResult
   resetSemFlag result
-  if s.typ[0] == nil:
+  if s.typ.returnType == nil:
     result = semStmt(c, result, flags)
   else:
-    var retType = s.typ[0]
+    var retType = s.typ.returnType
     if retType.kind == tyTypeDesc and tfUnresolved in retType.flags and
-        retType.len == 1:
+        retType.hasElementType:
       # bug #11941: template fails(T: type X, v: auto): T
       # does not mean we expect a tyTypeDesc.
-      retType = retType[0]
+      retType = retType.skipModifier
     case retType.kind
-    of tyUntyped:
+    of tyUntyped, tyAnything:
       # Not expecting a type here allows templates like in ``tmodulealias.in``.
       result = semExpr(c, result, flags, expectedType)
     of tyTyped:
@@ -461,22 +473,30 @@ proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
         # e.g. template foo(T: typedesc): seq[T]
         # We will instantiate the return type here, because
         # we now know the supplied arguments
-        var paramTypes = newIdTable()
+        var paramTypes = initTypeMapping()
         for param, value in genericParamsInMacroCall(s, call):
-          idTablePut(paramTypes, param.typ, value.typ)
+          var givenType = value.typ
+          # the sym nodes used for the supplied generic arguments for
+          # templates and macros leave type nil so regular sem can handle it
+          # in this case, get the type directly from the sym
+          if givenType == nil and value.kind == nkSym and value.sym.typ != nil:
+            givenType = value.sym.typ
+          idTablePut(paramTypes, param.typ, givenType)
 
         retType = generateTypeInstance(c, paramTypes,
                                        macroResult.info, retType)
 
-      result = semExpr(c, result, flags, expectedType)
-      result = fitNode(c, retType, result, result.info)
-      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ[0]))
+      if retType.kind == tyVoid:
+        result = semStmt(c, result, flags)
+      else:
+        result = semExpr(c, result, flags, expectedType)
+        result = fitNode(c, retType, result, result.info)
+      #globalError(s.info, errInvalidParamKindX, typeToString(s.typ.returnType))
   dec(c.config.evalTemplateCounter)
   discard c.friendModules.pop()
 
 const
   errMissingGenericParamsForTemplate = "'$1' has unspecified generic parameters"
-  errFloatToString = "cannot convert '$1' to '$2'"
 
 proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
                   flags: TExprFlags = {}; expectedType: PType = nil): PNode =
@@ -513,8 +533,6 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode =
   result = forceBool(c, semConstExpr(c, n, getSysType(c.graph, n.info, tyBool)))
   if result.kind != nkIntLit:
     localError(c.config, n.info, errConstExprExpected)
-
-proc semGenericStmt(c: PContext, n: PNode): PNode
 proc semConceptBody(c: PContext, n: PNode): PNode
 
 include semtypes
@@ -538,6 +556,147 @@ proc setGenericParamsMisc(c: PContext; n: PNode) =
   else:
     n[miscPos][1] = orig
 
+proc caseBranchMatchesExpr(branch, matched: PNode): bool =
+  result = false
+  for i in 0 ..< branch.len-1:
+    if branch[i].kind == nkRange:
+      if overlap(branch[i], matched): return true
+    elif exprStructuralEquivalent(branch[i], matched):
+      return true
+
+proc pickCaseBranchIndex(caseExpr, matched: PNode): int =
+  result = 0
+  let endsWithElse = caseExpr[^1].kind == nkElse
+  for i in 1..<caseExpr.len - endsWithElse.int:
+    if caseExpr[i].caseBranchMatchesExpr(matched):
+      return i
+  if endsWithElse:
+    return caseExpr.len - 1
+
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode]
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode
+
+const defaultFieldsSkipTypes = {tyGenericInst, tyAlias, tySink}
+
+proc defaultFieldsForTuple(c: PContext, recNode: PNode, hasDefault: var bool, checkDefault: bool): seq[PNode] =
+  result = @[]
+  case recNode.kind
+  of nkRecList:
+    for field in recNode:
+      result.add defaultFieldsForTuple(c, field, hasDefault, checkDefault)
+  of nkSym:
+    let field = recNode.sym
+    let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
+    if field.ast != nil: #Try to use default value
+      hasDefault = true
+      result.add newTree(nkExprColonExpr, recNode, field.ast)
+    else:
+      if recType.kind in {tyObject, tyArray, tyTuple}:
+        let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
+        if asgnExpr != nil:
+          hasDefault = true
+          asgnExpr.flags.incl nfSkipFieldChecking
+          result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+          return
+
+      let asgnType = newType(tyTypeDesc, c.idgen, recNode.typ.owner)
+      rawAddSon(asgnType, recNode.typ)
+      let asgnExpr = newTree(nkCall,
+                      newSymNode(getSysMagic(c.graph, recNode.info, "zeroDefault", mZeroDefault)),
+                      newNodeIT(nkType, recNode.info, asgnType)
+                    )
+      asgnExpr.flags.incl nfSkipFieldChecking
+      asgnExpr.typ = recNode.typ
+      result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+  else:
+    raiseAssert "unreachable"
+
+proc defaultFieldsForTheUninitialized(c: PContext, recNode: PNode, checkDefault: bool): seq[PNode] =
+  result = @[]
+  case recNode.kind
+  of nkRecList:
+    for field in recNode:
+      result.add defaultFieldsForTheUninitialized(c, field, checkDefault)
+  of nkRecCase:
+    let discriminator = recNode[0]
+    var selectedBranch: int
+    var defaultValue = discriminator.sym.ast
+    if defaultValue == nil:
+      # None of the branches were explicitly selected by the user and no 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:
+      if checkDefault: # don't add defaults when checking whether a case branch has default fields
+        return
+      defaultValue = newIntNode(nkIntLit#[c.graph]#, 0)
+      defaultValue.typ = discriminator.typ
+    selectedBranch = recNode.pickCaseBranchIndex defaultValue
+    defaultValue.flags.incl nfSkipFieldChecking
+    result.add newTree(nkExprColonExpr, discriminator, defaultValue)
+    result.add defaultFieldsForTheUninitialized(c, recNode[selectedBranch][^1], checkDefault)
+  of nkSym:
+    let field = recNode.sym
+    let recType = recNode.typ.skipTypes(defaultFieldsSkipTypes)
+    if field.ast != nil: #Try to use default value
+      result.add newTree(nkExprColonExpr, recNode, field.ast)
+    elif recType.kind in {tyObject, tyArray, tyTuple}:
+      let asgnExpr = defaultNodeField(c, recNode, recNode.typ, checkDefault)
+      if asgnExpr != nil:
+        asgnExpr.typ = recNode.typ
+        asgnExpr.flags.incl nfSkipFieldChecking
+        result.add newTree(nkExprColonExpr, recNode, asgnExpr)
+  else:
+    raiseAssert "unreachable"
+
+proc defaultNodeField(c: PContext, a: PNode, aTyp: PType, checkDefault: bool): PNode =
+  let aTypSkip = aTyp.skipTypes(defaultFieldsSkipTypes)
+  case aTypSkip.kind
+  of tyObject:
+    let child = defaultFieldsForTheUninitialized(c, aTypSkip.n, checkDefault)
+    if child.len > 0:
+      var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, a.info, aTyp))
+      asgnExpr.typ = aTyp
+      asgnExpr.sons.add child
+      result = semExpr(c, asgnExpr)
+    else:
+      result = nil
+  of tyArray:
+    let child = defaultNodeField(c, a, aTypSkip[1], checkDefault)
+
+    if child != nil:
+      let node = newNode(nkIntLit)
+      node.intVal = toInt64(lengthOrd(c.graph.config, aTypSkip))
+      result = semExpr(c, newTree(nkCall, newSymNode(getSysSym(c.graph, a.info, "arrayWith"), a.info),
+              semExprWithType(c, child),
+              node
+                ))
+      result.typ = aTyp
+    else:
+      result = nil
+  of tyTuple:
+    var hasDefault = false
+    if aTypSkip.n != nil:
+      let children = defaultFieldsForTuple(c, aTypSkip.n, hasDefault, checkDefault)
+      if hasDefault and children.len > 0:
+        result = newNodeI(nkTupleConstr, a.info)
+        result.typ = aTyp
+        result.sons.add children
+        result = semExpr(c, result)
+      else:
+        result = nil
+    else:
+      result = nil
+  of tyRange:
+    if c.graph.config.isDefined("nimPreviewRangeDefault"):
+      result = firstRange(c.config, aTypSkip)
+    else:
+      result = nil
+  else:
+    result = nil
+
+proc defaultNodeField(c: PContext, a: PNode, checkDefault: bool): PNode =
+  result = defaultNodeField(c, a, a.typ, checkDefault)
+
 include semtempl, semgnrc, semstmts, semexprs
 
 proc addCodeForGenerics(c: PContext, n: PNode) =
@@ -550,54 +709,63 @@ proc addCodeForGenerics(c: PContext, n: PNode) =
         n.add prc.ast
   c.lastGenericIdx = c.generics.len
 
-proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
-  var c = newContext(graph, module)
-  c.idgen = idgen
-  c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil)
-  c.voidType = newType(tyVoid, nextTypeId(idgen), nil)
-
-  if c.p != nil: internalError(graph.config, module.info, "sem.myOpen")
-  c.semConstExpr = semConstExpr
-  c.semExpr = semExpr
-  c.semTryExpr = tryExpr
-  c.semTryConstExpr = tryConstExpr
-  c.computeRequiresInit = computeRequiresInit
-  c.semOperand = semOperand
-  c.semConstBoolExpr = semConstBoolExpr
-  c.semOverloadedCall = semOverloadedCall
-  c.semInferredLambda = semInferredLambda
-  c.semGenerateInstance = generateInstance
-  c.semTypeNode = semTypeNode
-  c.instTypeBoundOp = sigmatch.instTypeBoundOp
-  c.hasUnresolvedArgs = hasUnresolvedArgs
-  c.templInstCounter = new int
-
-  pushProcCon(c, module)
-  pushOwner(c, c.module)
-
-  c.moduleScope = openScope(c)
-  c.moduleScope.addSym(module) # a module knows itself
+proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext =
+  result = newContext(graph, module)
+  result.idgen = idgen
+  result.enforceVoidContext = newType(tyTyped, idgen, nil)
+  result.voidType = newType(tyVoid, idgen, nil)
+
+  if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext")
+  result.semConstExpr = semConstExpr
+  result.semExpr = semExpr
+  result.semExprWithType = semExprWithType
+  result.semTryExpr = tryExpr
+  result.semTryConstExpr = tryConstExpr
+  result.computeRequiresInit = computeRequiresInit
+  result.semOperand = semOperand
+  result.semConstBoolExpr = semConstBoolExpr
+  result.semOverloadedCall = semOverloadedCall
+  result.semInferredLambda = semInferredLambda
+  result.semGenerateInstance = generateInstance
+  result.instantiateOnlyProcType = instantiateOnlyProcType
+  result.semTypeNode = semTypeNode
+  result.instTypeBoundOp = sigmatch.instTypeBoundOp
+  result.hasUnresolvedArgs = hasUnresolvedArgs
+  result.templInstCounter = new int
+
+  pushProcCon(result, module)
+  pushOwner(result, result.module)
+
+  result.moduleScope = openScope(result)
+  result.moduleScope.addSym(module) # a module knows itself
 
   if sfSystemModule in module.flags:
     graph.systemModule = module
-  c.topLevelScope = openScope(c)
-  result = c
+  result.topLevelScope = openScope(result)
 
 proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool =
   if g.systemModule == nil: return false
+  var n = n
+  if n.kind == nkStmtList:
+    for i in 0..<n.len-1:
+      if n[i].kind notin {nkCommentStmt, nkEmpty}:
+        n = n[i]
+        break
   case n.kind
   of nkImportStmt:
+    result = false
     for x in n:
       if x.kind == nkIdent:
         let f = checkModuleName(g.config, x, false)
         if f == g.systemModule.info.fileIndex:
           return true
   of nkImportExceptStmt, nkFromStmt:
+    result = false
     if n[0].kind == nkIdent:
       let f = checkModuleName(g.config, n[0], false)
       if f == g.systemModule.info.fileIndex:
         return true
-  else: discard
+  else: result = false
 
 proc isEmptyTree(n: PNode): bool =
   case n.kind
@@ -640,6 +808,13 @@ proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode =
   if c.config.cmd == cmdIdeTools:
     appendToModule(c.module, result)
   trackStmt(c, c.module, result, isTopLevel = true)
+  if optMultiMethods notin c.config.globalOptions and
+      c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
+      Feature.vtables in c.config.features:
+    sortVTableDispatchers(c.graph)
+
+    if sfMainModule in c.module.flags:
+      collectVTableDispatchers(c.graph)
 
 proc recoverContext(c: PContext) =
   # clean up in case of a semantic error: We clean up the stacks, etc. This is
@@ -649,8 +824,7 @@ proc recoverContext(c: PContext) =
   while getCurrOwner(c).kind != skModule: popOwner(c)
   while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next
 
-proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} =
-  var c = PContext(context)
+proc semWithPContext*(c: PContext, n: PNode): PNode =
   # no need for an expensive 'try' if we stop after the first error anyway:
   if c.config.errorMax <= 1:
     result = semStmtAndGenerateGenerics(c, n)
@@ -671,13 +845,14 @@ proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} =
       #if c.config.cmd == cmdIdeTools: findSuggest(c, n)
   storeRodNode(c, result)
 
+
 proc reportUnusedModules(c: PContext) =
+  if c.config.cmd == cmdM: return
   for i in 0..high(c.unusedImports):
     if sfUsed notin c.unusedImports[i][0].flags:
       message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s)
 
-proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
-  var c = PContext(context)
+proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode =
   if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
     suggestSentinel(c)
   closeScope(c)         # close module's scope
@@ -692,6 +867,3 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
   popOwner(c)
   popProcCon(c)
   sealRodFile(c)
-
-const semPass* = makePass(myOpen, myProcess, myClose,
-                          isFrontend = true)
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 21e519a59..13f2273a9 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -10,7 +10,8 @@
 ## This module implements semantic checking for calls.
 # included from sem.nim
 
-from algorithm import sort
+from std/algorithm import sort
+
 
 proc sameMethodDispatcher(a, b: PSym): bool =
   result = false
@@ -42,16 +43,29 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode,
                           best, alt: var TCandidate,
                           o: var TOverloadIter,
                           diagnostics: bool): seq[tuple[s: PSym, scope: int]] =
+  ## puts all overloads into a seq and prepares best+alt
   result = @[]
   var symx = initOverloadIter(o, c, headSymbol)
   while symx != nil:
     if symx.kind in filter:
       result.add((symx, o.lastOverloadScope))
+    elif symx.kind == skGenericParam:
+      #[
+        This code handles looking up a generic parameter when it's a static callable.
+        For instance:
+          proc name[T: static proc()]() = T()
+          name[proc() = echo"hello"]()
+      ]#
+      for paramSym in searchInScopesAllCandidatesFilterBy(c, symx.name, {skConst}):
+        let paramTyp = paramSym.typ
+        if paramTyp.n.kind == nkSym and paramTyp.n.sym.kind in filter:
+          result.add((paramTyp.n.sym, o.lastOverloadScope))
+
     symx = nextOverloadIter(o, c, headSymbol)
   if result.len > 0:
-    initCandidate(c, best, result[0].s, initialBinding,
+    best = initCandidate(c, result[0].s, initialBinding,
                   result[0].scope, diagnostics)
-    initCandidate(c, alt, result[0].s, initialBinding,
+    alt = initCandidate(c, result[0].s, initialBinding,
                   result[0].scope, diagnostics)
     best.state = csNoMatch
 
@@ -63,36 +77,35 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
                        errors: var CandidateErrors,
                        diagnosticsFlag: bool,
                        errorsEnabled: bool, flags: TExprFlags) =
-  var o: TOverloadIter
-  var sym = initOverloadIter(o, c, headSymbol)
-  var scope = o.lastOverloadScope
-  # Thanks to the lazy semchecking for operands, we need to check whether
-  # 'initCandidate' modifies the symbol table (via semExpr).
-  # 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:
-      # Initialise 'best' and 'alt' with the first available symbol
-      initCandidate(c, best, sym, initialBinding, scope, diagnosticsFlag)
-      initCandidate(c, alt, sym, initialBinding, scope, diagnosticsFlag)
-      best.state = csNoMatch
-      break
-    else:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-  var z: TCandidate
-  while sym != nil:
-    if sym.kind notin filter:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-      continue
+  # `matches` may find new symbols, so keep track of count
+  var symCount = c.currentScope.symbols.counter
+
+  var o: TOverloadIter = default(TOverloadIter)
+  # https://github.com/nim-lang/Nim/issues/21272
+  # prevent mutation during iteration by storing them in a seq
+  # luckily `initCandidateSymbols` does just that
+  var syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
+                                  best, alt, o, diagnosticsFlag)
+  if len(syms) == 0:
+    return
+  # current overload being considered
+  var sym = syms[0].s
+  var scope = syms[0].scope
+
+  # starts at 1 because 0 is already done with setup, only needs checking
+  var nextSymIndex = 1
+  var z: TCandidate # current candidate
+  while true:
     determineType(c, sym)
-    initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag)
-    if c.currentScope.symbols.counter == counterInitial or syms.len != 0:
+    z = initCandidate(c, sym, initialBinding, scope, diagnosticsFlag)
+
+    # this is kinda backwards as without a check here the described
+    # problems in recalc would not happen, but instead it 100%
+    # does check forever in some cases
+    if c.currentScope.symbols.counter == symCount:
+      # may introduce new symbols with caveats described in recalc branch
       matches(c, n, orig, z)
+
       if z.state == csMatch:
         # little hack so that iterators are preferred over everything else:
         if sym.kind == skIterator:
@@ -112,22 +125,36 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
           firstMismatch: z.firstMismatch,
           diagnostics: z.diagnostics))
     else:
+      # this branch feels like a ticking timebomb
+      # one of two bad things could happen
+      # 1) new symbols are discovered but the loop ends before we recalc
+      # 2) new symbols are discovered and resemmed forever
+      # not 100% sure if these are possible though as they would rely
+      #  on somehow introducing a new overload during overload resolution
+
       # Symbol table has been modified. Restart and pre-calculate all syms
       # before any further candidate init and compare. SLOW, but rare case.
       syms = initCandidateSymbols(c, headSymbol, initialBinding, filter,
                                   best, alt, o, diagnosticsFlag)
-      noSyms = false
-    if noSyms:
-      sym = nextOverloadIter(o, c, headSymbol)
-      scope = o.lastOverloadScope
-    elif nextSymIndex < syms.len:
-      # rare case: retrieve the next pre-calculated symbol
-      sym = syms[nextSymIndex].s
-      scope = syms[nextSymIndex].scope
-      nextSymIndex += 1
-    else:
+
+      # reset counter because syms may be in a new order
+      symCount = c.currentScope.symbols.counter
+      nextSymIndex = 0
+
+      # just in case, should be impossible though
+      if syms.len == 0:
+        break
+
+    if nextSymIndex > high(syms):
+      # we have reached the end
       break
 
+    # advance to next sym
+    sym = syms[nextSymIndex].s
+    scope = syms[nextSymIndex].scope
+    inc(nextSymIndex)
+
+
 proc effectProblem(f, a: PType; result: var string; c: PContext) =
   if f.kind == tyProc and a.kind == tyProc:
     if tfThread in f.flags and tfThread notin a.flags:
@@ -149,9 +176,6 @@ proc effectProblem(f, a: PType; result: var string; c: PContext) =
       of efTagsUnknown:
         result.add "\n  The `.tags` requirements differ. Annotate the " &
             "proc with {.tags: [].} to get extended error information."
-      of efLockLevelsDiffer:
-        result.add "\n  The `.locks` requirements differ. Annotate the " &
-            "proc with {.locks: 0.} to get extended error information."
       of efEffectsDelayed:
         result.add "\n  The `.effectsOf` annotations differ."
       of efTagsIllegal:
@@ -195,7 +219,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
   # argument in order to remove plenty of candidates. This is
   # comparable to what C# does and C# is doing fine.
   var filterOnlyFirst = false
-  if optShowAllMismatches notin c.config.globalOptions:
+  if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures:
     for err in errors:
       if err.firstMismatch.arg > 1:
         filterOnlyFirst = true
@@ -203,7 +227,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
 
   var maybeWrongSpace = false
 
-  var candidatesAll: seq[string]
+  var candidatesAll: seq[string] = @[]
   var candidates = ""
   var skipped = 0
   for err in errors:
@@ -211,6 +235,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
     if filterOnlyFirst and err.firstMismatch.arg == 1:
       inc skipped
       continue
+
+    if verboseTypeMismatch notin c.config.legacyFeatures:
+      candidates.add "[" & $err.firstMismatch.arg & "] "
+
     if err.sym.kind in routineKinds and err.sym.ast != nil:
       candidates.add(renderTree(err.sym.ast,
             {renderNoBody, renderNoComments, renderNoPragmas}))
@@ -218,48 +246,151 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
       candidates.add(getProcHeader(c.config, err.sym, prefer))
     candidates.addDeclaredLocMaybe(c.config, err.sym)
     candidates.add("\n")
-    let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil
+    const genericParamMismatches = {kGenericParamTypeMismatch, kExtraGenericParam, kMissingGenericParam}
+    let isGenericMismatch = err.firstMismatch.kind in genericParamMismatches
+    var argList = n
+    if isGenericMismatch and n[0].kind == nkBracketExpr:
+      argList = n[0]
+    let nArg =
+      if err.firstMismatch.arg < argList.len:
+        argList[err.firstMismatch.arg]
+      else:
+        nil
     let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: ""
     if n.len > 1:
-      candidates.add("  first type mismatch at position: " & $err.firstMismatch.arg)
-      # candidates.add "\n  reason: " & $err.firstMismatch.kind # for debugging
-      case err.firstMismatch.kind
-      of kUnknownNamedParam:
-        if nArg == nil:
-          candidates.add("\n  unknown named parameter")
-        else:
-          candidates.add("\n  unknown named parameter: " & $nArg[0])
-      of kAlreadyGiven: candidates.add("\n  named param already provided: " & $nArg[0])
-      of kPositionalAlreadyGiven: candidates.add("\n  positional param was already given as named param")
-      of kExtraArg: candidates.add("\n  extra argument given")
-      of kMissingParam: candidates.add("\n  missing parameter: " & nameParam)
-      of kTypeMismatch, kVarNeeded:
-        doAssert nArg != nil
-        let wanted = err.firstMismatch.formal.typ
-        doAssert err.firstMismatch.formal != nil
-        candidates.add("\n  required type for " & nameParam &  ": ")
-        candidates.addTypeDeclVerboseMaybe(c.config, wanted)
-        candidates.add "\n  but expression '"
-        if err.firstMismatch.kind == kVarNeeded:
+      if verboseTypeMismatch notin c.config.legacyFeatures:
+        case err.firstMismatch.kind
+        of kUnknownNamedParam:
+          if nArg == nil:
+            candidates.add("  unknown named parameter")
+          else:
+            candidates.add("  unknown named parameter: " & $nArg[0])
+          candidates.add "\n"
+        of kAlreadyGiven:
+          candidates.add("  named param already provided: " & $nArg[0])
+          candidates.add "\n"
+        of kPositionalAlreadyGiven:
+          candidates.add("  positional param was already given as named param")
+          candidates.add "\n"
+        of kExtraArg:
+          candidates.add("  extra argument given")
+          candidates.add "\n"
+        of kMissingParam:
+          candidates.add("  missing parameter: " & nameParam)
+          candidates.add "\n"
+        of kExtraGenericParam:
+          candidates.add("  extra generic param given")
+          candidates.add "\n"
+        of kMissingGenericParam:
+          candidates.add("  missing generic parameter: " & nameParam)
+          candidates.add "\n"
+        of kVarNeeded:
+          doAssert nArg != nil
+          doAssert err.firstMismatch.formal != nil
+          candidates.add "  expression '"
           candidates.add renderNotLValue(nArg)
           candidates.add "' is immutable, not 'var'"
-        else:
-          candidates.add renderTree(nArg)
-          candidates.add "' is of type: "
+          candidates.add "\n"
+        of kTypeMismatch:
+          doAssert nArg != nil
+          if nArg.kind in nkSymChoices:
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+          let wanted = err.firstMismatch.formal.typ
+          doAssert err.firstMismatch.formal != nil
+          doAssert wanted != nil
           let got = nArg.typ
-          candidates.addTypeDeclVerboseMaybe(c.config, got)
+          if got != nil and got.kind == tyProc and wanted.kind == tyProc:
+            # These are proc mismatches so,
+            # add the extra explict detail of the mismatch
+            candidates.add "  expression '"
+            candidates.add renderTree(nArg)
+            candidates.add "' is of type: "
+            candidates.addTypeDeclVerboseMaybe(c.config, got)
+            candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+            effectProblem(wanted, got, candidates, c)
+            candidates.add "\n"
+        of kGenericParamTypeMismatch:
+          let pos = err.firstMismatch.arg
+          doAssert n[0].kind == nkBracketExpr and pos < n[0].len
+          let arg = n[0][pos]
+          doAssert arg != nil
+          var wanted = err.firstMismatch.formal.typ
+          if wanted.kind == tyGenericParam and wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
+          let got = arg.typ.skipTypes({tyTypeDesc})
+          doAssert err.firstMismatch.formal != nil
           doAssert wanted != nil
+          doAssert got != nil
+          candidates.add "  generic parameter mismatch, expected "
+          candidates.addTypeDeclVerboseMaybe(c.config, wanted)
+          candidates.add " but got '"
+          candidates.add renderTree(arg)
+          candidates.add "' of type: "
+          candidates.addTypeDeclVerboseMaybe(c.config, got)
+          if nArg.kind in nkSymChoices:
+            candidates.add "\n"
+            candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+          if got != nil and got.kind == tyProc and wanted.kind == tyProc:
+            # These are proc mismatches so,
+            # add the extra explict detail of the mismatch
+            candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
           if got != nil:
-            if got.kind == tyProc and wanted.kind == tyProc:
-              # These are proc mismatches so,
-              # add the extra explict detail of the mismatch
-              candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
             effectProblem(wanted, got, candidates, c)
-
-      of kUnknown: discard "do not break 'nim check'"
-      candidates.add "\n"
-      if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and
-          n.kind == nkCommand:
+          candidates.add "\n"
+        of kUnknown: discard "do not break 'nim check'"
+      else:
+        candidates.add("  first type mismatch at position: " & $err.firstMismatch.arg)
+        if err.firstMismatch.kind in genericParamMismatches:
+          candidates.add(" in generic parameters")
+        # candidates.add "\n  reason: " & $err.firstMismatch.kind # for debugging
+        case err.firstMismatch.kind
+        of kUnknownNamedParam:
+          if nArg == nil:
+            candidates.add("\n  unknown named parameter")
+          else:
+            candidates.add("\n  unknown named parameter: " & $nArg[0])
+        of kAlreadyGiven: candidates.add("\n  named param already provided: " & $nArg[0])
+        of kPositionalAlreadyGiven: candidates.add("\n  positional param was already given as named param")
+        of kExtraArg: candidates.add("\n  extra argument given")
+        of kMissingParam: candidates.add("\n  missing parameter: " & nameParam)
+        of kExtraGenericParam:
+          candidates.add("\n  extra generic param given")
+        of kMissingGenericParam:
+          candidates.add("\n  missing generic parameter: " & nameParam)
+        of kTypeMismatch, kGenericParamTypeMismatch, kVarNeeded:
+          doAssert nArg != nil
+          var wanted = err.firstMismatch.formal.typ
+          if isGenericMismatch and wanted.kind == tyGenericParam and
+              wanted.genericParamHasConstraints:
+            wanted = wanted.genericConstraint
+          doAssert err.firstMismatch.formal != nil
+          candidates.add("\n  required type for " & nameParam &  ": ")
+          candidates.addTypeDeclVerboseMaybe(c.config, wanted)
+          candidates.add "\n  but expression '"
+          if err.firstMismatch.kind == kVarNeeded:
+            candidates.add renderNotLValue(nArg)
+            candidates.add "' is immutable, not 'var'"
+          else:
+            candidates.add renderTree(nArg)
+            candidates.add "' is of type: "
+            var got = nArg.typ
+            if isGenericMismatch: got = got.skipTypes({tyTypeDesc})
+            candidates.addTypeDeclVerboseMaybe(c.config, got)
+            if nArg.kind in nkSymChoices:
+              candidates.add "\n"
+              candidates.add ambiguousIdentifierMsg(nArg, indent = 2)
+            doAssert wanted != nil
+            if got != nil:
+              if got.kind == tyProc and wanted.kind == tyProc:
+                # These are proc mismatches so,
+                # add the extra explict detail of the mismatch
+                candidates.addPragmaAndCallConvMismatch(wanted, got, c.config)
+              effectProblem(wanted, got, candidates, c)
+
+        of kUnknown: discard "do not break 'nim check'"
+        candidates.add "\n"
+      if err.firstMismatch.arg == 1 and nArg != nil and
+          nArg.kind == nkTupleConstr and n.kind == nkCommand:
         maybeWrongSpace = true
     for diag in err.diagnostics:
       candidates.add(diag & "\n")
@@ -277,11 +408,28 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
 const
   errTypeMismatch = "type mismatch: got <"
   errButExpected = "but expected one of:"
+  errExpectedPosition = "Expected one of (first mismatch at [position]):"
   errUndeclaredField = "undeclared field: '$1'"
   errUndeclaredRoutine = "attempting to call undeclared routine: '$1'"
   errBadRoutine = "attempting to call routine: '$1'$2"
   errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3"
 
+proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
+  result = "Expression: " & $n
+  for i in startIdx..<n.len:
+    result.add "\n  [" & $i & "] " & renderTree(n[i]) & ": "
+    result.add describeArg(c, n, i, startIdx, prefer)
+  result.add "\n"
+
+template legacynotFoundError(c: PContext, n: PNode, errors: CandidateErrors) =
+  let (prefer, candidates) = presentFailedCandidates(c, n, errors)
+  var result = errTypeMismatch
+  result.add(describeArgs(c, n, 1, prefer))
+  result.add('>')
+  if candidates != "":
+    result.add("\n" & errButExpected & "\n" & candidates)
+  localError(c.config, n.info, result & "\nexpression: " & $n)
+
 proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
   # Gives a detailed error message; this is separated from semOverloadedCall,
   # as semOverloadedCall is already pretty slow (and we need this information
@@ -290,50 +438,51 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) =
     # fail fast:
     globalError(c.config, n.info, "type mismatch")
     return
+  # see getMsgDiagnostic:
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
+    let ident = considerQuotedIdent(c, n[0], n).s
+    let sym = n[1].typ.typSym
+    var typeHint = ""
+    if sym == nil:
+      discard
+    else:
+      typeHint = " for type " & getProcHeader(c.config, sym)
+    localError(c.config, n.info, errUndeclaredField % ident & typeHint)
+    return
   if errors.len == 0:
-    localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
+    if n[0].kind in nkIdentKinds:
+      let ident = considerQuotedIdent(c, n[0], n).s
+      localError(c.config, n.info, errUndeclaredRoutine % ident)
+    else:
+      localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree)
     return
 
-  let (prefer, candidates) = presentFailedCandidates(c, n, errors)
-  var result = errTypeMismatch
-  result.add(describeArgs(c, n, 1, prefer))
-  result.add('>')
-  if candidates != "":
-    result.add("\n" & errButExpected & "\n" & candidates)
-  localError(c.config, n.info, result & "\nexpression: " & $n)
-
-proc bracketNotFoundError(c: PContext; n: PNode) =
-  var errors: CandidateErrors = @[]
-  var o: TOverloadIter
-  let headSymbol = n[0]
-  var symx = initOverloadIter(o, c, headSymbol)
-  while symx != nil:
-    if symx.kind in routineKinds:
-      errors.add(CandidateError(sym: symx,
-                                firstMismatch: MismatchInfo(),
-                                diagnostics: @[],
-                                enabled: false))
-    symx = nextOverloadIter(o, c, headSymbol)
-  if errors.len == 0:
-    localError(c.config, n.info, "could not resolve: " & $n)
+  if verboseTypeMismatch in c.config.legacyFeatures:
+    legacynotFoundError(c, n, errors)
   else:
-    notFoundError(c, n, errors)
+    let (prefer, candidates) = presentFailedCandidates(c, n, errors)
+    var result = "type mismatch\n"
+    result.add describeParamList(c, n, 1, prefer)
+    if candidates != "":
+      result.add("\n" & errExpectedPosition & "\n" & candidates)
+    localError(c.config, n.info, result)
 
 proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string =
+  result = ""
   if c.compilesContextId > 0:
     # we avoid running more diagnostic when inside a `compiles(expr)`, to
     # errors while running diagnostic (see test D20180828T234921), and
     # also avoid slowdowns in evaluating `compiles(expr)`.
     discard
   else:
-    var o: TOverloadIter
+    var o: TOverloadIter = default(TOverloadIter)
     var sym = initOverloadIter(o, c, f)
     while sym != nil:
       result &= "\n  found $1" % [getSymRepr(c.config, sym)]
       sym = nextOverloadIter(o, c, f)
 
   let ident = considerQuotedIdent(c, f, n).s
-  if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}:
+  if nfExplicitCall notin n.flags and {nfDotField, nfDotSetter} * n.flags != {}:
     let sym = n[1].typ.typSym
     var typeHint = ""
     if sym == nil:
@@ -355,8 +504,9 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
                       filter: TSymKinds, flags: TExprFlags,
                       errors: var CandidateErrors,
                       errorsEnabled: bool): TCandidate =
+  result = default(TCandidate)
   var initialBinding: PNode
-  var alt: TCandidate
+  var alt: TCandidate = default(TCandidate)
   var f = n[0]
   if f.kind == nkBracketExpr:
     # fill in the bindings:
@@ -366,11 +516,15 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
   else:
     initialBinding = nil
 
-  template pickBest(headSymbol) =
+  pickBestCandidate(c, f, n, orig, initialBinding,
+                    filter, result, alt, errors, efExplain in flags,
+                    errorsEnabled, flags)
+
+  var dummyErrors: CandidateErrors = @[]
+  template pickSpecialOp(headSymbol) =
     pickBestCandidate(c, headSymbol, n, orig, initialBinding,
-                      filter, result, alt, errors, efExplain in flags,
-                      errorsEnabled, flags)
-  pickBest(f)
+                      filter, result, alt, dummyErrors, efExplain in flags,
+                      false, flags)
 
   let overloadsState = result.state
   if overloadsState != csMatch:
@@ -386,7 +540,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         let op = newIdentNode(getIdent(c.cache, x), n.info)
         n[0] = op
         orig[0] = op
-        pickBest(op)
+        pickSpecialOp(op)
 
       if nfExplicitCall in n.flags:
         tryOp ".()"
@@ -400,23 +554,16 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
       let callOp = newIdentNode(getIdent(c.cache, ".="), n.info)
       n.sons[0..1] = [callOp, n[1], calleeName]
       orig.sons[0..1] = [callOp, orig[1], calleeName]
-      pickBest(callOp)
+      pickSpecialOp(callOp)
 
     if overloadsState == csEmpty and result.state == csEmpty:
       if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim
-        template impl() =
-          # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
-          localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
-        if n[0].kind == nkIdent and n[0].ident.s == ".=" and n[2].kind == nkIdent:
-          let sym = n[1].typ.sym
-          if sym == nil:
-            impl()
-          else:
-            let field = n[2].ident.s
-            let msg = errUndeclaredField % field & " for type " & getProcHeader(c.config, sym)
-            localError(c.config, orig[2].info, msg)
-        else:
-          impl()
+        result.state = csNoMatch
+        if c.inGenericContext > 0 and nfExprCall in n.flags:
+          # untyped expression calls end up here, see #24099
+          return
+        # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident)
+        localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f))
       return
     elif result.state != csMatch:
       if nfExprCall in n.flags:
@@ -449,6 +596,39 @@ proc resolveOverloads(c: PContext, n, orig: PNode,
         getProcHeader(c.config, alt.calleeSym),
         args])
 
+proc bracketNotFoundError(c: PContext; n: PNode; flags: TExprFlags) =
+  var errors: CandidateErrors = @[]
+  let headSymbol = n[0]
+  block:
+    # we build a closed symchoice of all `[]` overloads for their errors,
+    # except add a custom error for the magics which always match
+    var choice = newNodeIT(nkClosedSymChoice, headSymbol.info, newTypeS(tyNone, c))
+    var o: TOverloadIter = default(TOverloadIter)
+    var symx = initOverloadIter(o, c, headSymbol)
+    while symx != nil:
+      if symx.kind in routineKinds:
+        if symx.magic in {mArrGet, mArrPut}:
+          errors.add(CandidateError(sym: symx,
+                                    firstMismatch: MismatchInfo(),
+                                    diagnostics: @[],
+                                    enabled: false))
+        else:
+          choice.add newSymNode(symx, headSymbol.info)
+      symx = nextOverloadIter(o, c, headSymbol)
+    n[0] = choice
+  # copied from semOverloadedCallAnalyzeEffects, might be overkill:
+  const baseFilter = {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}
+  let filter =
+    if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
+      baseFilter + {skIterator}
+    else: baseFilter
+  # this will add the errors:
+  var r = resolveOverloads(c, n, n, filter, flags, errors, true)
+  if errors.len == 0:
+    localError(c.config, n.info, "could not resolve: " & $n)
+  else:
+    notFoundError(c, n, errors)
+
 proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
   let a = if a.kind == nkHiddenDeref: a[0] else: a
   if a.kind == nkHiddenCallConv and a[0].kind == nkSym:
@@ -457,7 +637,7 @@ proc instGenericConvertersArg*(c: PContext, a: PNode, x: TCandidate) =
       let finalCallee = generateInstance(c, s, x.bindings, a.info)
       a[0].sym = finalCallee
       a[0].typ = finalCallee.typ
-      #a.typ = finalCallee.typ[0]
+      #a.typ = finalCallee.typ.returnType
 
 proc instGenericConvertersSons*(c: PContext, n: PNode, x: TCandidate) =
   assert n.kind in nkCallKinds
@@ -490,7 +670,7 @@ proc inferWithMetatype(c: PContext, formal: PType,
     result = copyTree(arg)
     result.typ = formal
 
-proc updateDefaultParams(call: PNode) =
+proc updateDefaultParams(c: PContext, call: PNode) =
   # In generic procs, the default parameter may be unique for each
   # instantiation (see tlateboundgenericparams).
   # After a call is resolved, we need to re-assign any default value
@@ -500,8 +680,18 @@ proc updateDefaultParams(call: PNode) =
   let calleeParams = call[0].sym.typ.n
   for i in 1..<call.len:
     if nfDefaultParam in call[i].flags:
-      let def = calleeParams[i].sym.ast
+      let formal = calleeParams[i].sym
+      let def = formal.ast
       if nfDefaultRefsParam in def.flags: call.flags.incl nfDefaultRefsParam
+      # mirrored with sigmatch:
+      if def.kind == nkEmpty:
+        # The default param value is set to empty in `instantiateProcType`
+        # when the type of the default expression doesn't match the type
+        # of the instantiated proc param:
+        pushInfoContext(c.config, call.info, call[0].sym.detailedInfo)
+        typeMismatch(c.config, def.info, formal.typ, def.typ, formal.ast)
+        popInfoContext(c.config)
+        def.typ = errorType(c)
       call[i] = def
 
 proc getCallLineInfo(n: PNode): TLineInfo =
@@ -516,20 +706,77 @@ proc getCallLineInfo(n: PNode): TLineInfo =
     discard
   result = n.info
 
-proc semResolvedCall(c: PContext, x: TCandidate,
-                     n: PNode, flags: TExprFlags): PNode =
+proc inheritBindings(c: PContext, x: var TCandidate, expectedType: PType) =
+  ## Helper proc to inherit bound generic parameters from expectedType into x.
+  ## Does nothing if 'inferGenericTypes' isn't in c.features.
+  if inferGenericTypes notin c.features: return
+  if expectedType == nil or x.callee.returnType == nil: return # required for inference
+
+  var
+    flatUnbound: seq[PType] = @[]
+    flatBound: seq[PType] = @[]
+  # seq[(result type, expected type)]
+  var typeStack = newSeq[(PType, PType)]()
+
+  template stackPut(a, b) =
+    ## skips types and puts the skipped version on stack
+    # It might make sense to skip here one by one. It's not part of the main
+    #  type reduction because the right side normally won't be skipped
+    const toSkip = {tyVar, tyLent, tyStatic, tyCompositeTypeClass, tySink}
+    let
+      x = a.skipTypes(toSkip)
+      y = if a.kind notin toSkip: b
+          else: b.skipTypes(toSkip)
+    typeStack.add((x, y))
+
+  stackPut(x.callee.returnType, expectedType)
+
+  while typeStack.len() > 0:
+    let (t, u) = typeStack.pop()
+    if t == u or t == nil or u == nil or t.kind == tyAnything or u.kind == tyAnything:
+      continue
+    case t.kind
+    of ConcreteTypes, tyGenericInvocation, tyUncheckedArray:
+      # XXX This logic makes no sense for `tyUncheckedArray`
+      # nested, add all the types to stack
+      let
+        startIdx = if u.kind in ConcreteTypes: 0 else: 1
+        endIdx = min(u.kidsLen() - startIdx, t.kidsLen())
+
+      for i in startIdx ..< endIdx:
+        # early exit with current impl
+        if t[i] == nil or u[i] == nil: return
+        stackPut(t[i], u[i])
+    of tyGenericParam:
+      let prebound = x.bindings.idTableGet(t)
+      if prebound != nil:
+        continue # Skip param, already bound
+
+      # fully reduced generic param, bind it
+      if t notin flatUnbound:
+        flatUnbound.add(t)
+        flatBound.add(u)
+    else:
+      discard
+  # update bindings
+  for i in 0 ..< flatUnbound.len():
+    x.bindings.idTablePut(flatUnbound[i], flatBound[i])
+
+proc semResolvedCall(c: PContext, x: var TCandidate,
+                     n: PNode, flags: TExprFlags;
+                     expectedType: PType = nil): PNode =
   assert x.state == csMatch
   var finalCallee = x.calleeSym
   let info = getCallLineInfo(n)
   markUsed(c, info, finalCallee)
   onUse(info, finalCallee)
   assert finalCallee.ast != nil
-  if x.hasFauxMatch:
+  if x.matchedErrorType:
     result = x.call
     result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
-    if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
-      result.typ = newTypeS(x.fauxMatch, c)
-      if result.typ.kind == tyError: incl result.typ.flags, tfCheckedForDestructor
+    if containsGenericType(result.typ):
+      result.typ = newTypeS(tyError, c)
+      incl result.typ.flags, tfCheckedForDestructor
     return
   let gp = finalCallee.ast[genericParamsPos]
   if gp.isGenericParams:
@@ -537,27 +784,35 @@ proc semResolvedCall(c: PContext, x: TCandidate,
       if x.calleeSym.magic in {mArrGet, mArrPut}:
         finalCallee = x.calleeSym
       else:
+        c.inheritBindings(x, expectedType)
         finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
     else:
       # For macros and templates, the resolved generic params
       # are added as normal params.
+      c.inheritBindings(x, expectedType)
       for s in instantiateGenericParamList(c, gp, x.bindings):
         case s.kind
         of skConst:
-          if not s.ast.isNil:
-            x.call.add s.ast
+          if not s.astdef.isNil:
+            x.call.add s.astdef
           else:
             x.call.add c.graph.emptyNode
         of skType:
-          x.call.add newSymNode(s, n.info)
+          var tn = newSymNode(s, n.info)
+          # this node will be used in template substitution,
+          # pretend this is an untyped node and let regular sem handle the type
+          # to prevent problems where a generic parameter is treated as a value
+          tn.typ = nil
+          x.call.add tn
         else:
           internalAssert c.config, false
 
   result = x.call
   instGenericConvertersSons(c, result, x)
   result[0] = newSymNode(finalCallee, getCallLineInfo(result[0]))
-  result.typ = finalCallee.typ[0]
-  updateDefaultParams(result)
+  if finalCallee.magic notin {mArrGet, mArrPut}:
+    result.typ = finalCallee.typ.returnType
+  updateDefaultParams(c, result)
 
 proc canDeref(n: PNode): bool {.inline.} =
   result = n.len >= 2 and (let t = n[1].typ;
@@ -569,7 +824,8 @@ proc tryDeref(n: PNode): PNode =
   result.add n
 
 proc semOverloadedCall(c: PContext, n, nOrig: PNode,
-                       filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} =
+                       filter: TSymKinds, flags: TExprFlags;
+                       expectedType: PType = nil): PNode =
   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:
@@ -579,57 +835,36 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
       message(c.config, n.info, hintUserRaw,
               "Non-matching candidates for " & renderTree(n) & "\n" &
               candidates)
-    result = semResolvedCall(c, r, n, flags)
-  elif implicitDeref in c.features and canDeref(n):
-    # try to deref the first argument and then try overloading resolution again:
-    #
-    # XXX: why is this here?
-    # it could be added to the long list of alternatives tried
-    # inside `resolveOverloads` or it could be moved all the way
-    # into sigmatch with hidden conversion produced there
-    #
-    n[1] = n[1].tryDeref
-    var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags)
-    if r.state == csMatch: result = semResolvedCall(c, r, n, flags)
-    else:
-      # get rid of the deref again for a better error message:
-      n[1] = n[1][0]
-      #notFoundError(c, n, errors)
-      if efExplain notin flags:
-        # repeat the overload resolution,
-        # this time enabling all the diagnostic output (this should fail again)
-        discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
-      elif efNoUndeclared notin flags:
-        notFoundError(c, n, errors)
+    result = semResolvedCall(c, r, n, flags, expectedType)
   else:
-    if efExplain notin flags:
+    if c.inGenericContext > 0 and c.matchedConcept == nil:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, result.copyTree)
+    elif efExplain notin flags:
       # repeat the overload resolution,
       # this time enabling all the diagnostic output (this should fail again)
-      discard semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
+      result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
     elif efNoUndeclared notin flags:
+      result = nil
       notFoundError(c, n, errors)
+    else:
+      result = nil
 
 proc explicitGenericInstError(c: PContext; n: PNode): PNode =
   localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))
   result = n
 
 proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
+  if s.kind in {skTemplate, skMacro}:
+    internalError c.config, n.info, "cannot get explicitly instantiated symbol of " &
+      (if s.kind == skTemplate: "template" else: "macro")
   # binding has to stay 'nil' for this to work!
   var m = newCandidate(c, s, nil)
-
-  for i in 1..<n.len:
-    let formal = s.ast[genericParamsPos][i-1].typ
-    var arg = n[i].typ
-    # try transforming the argument into a static one before feeding it into
-    # typeRel
-    if formal.kind == tyStatic and arg.kind != tyStatic:
-      let evaluated = c.semTryConstExpr(c, n[i])
-      if evaluated != nil:
-        arg = newTypeS(tyStatic, c)
-        arg.sons = @[evaluated.typ]
-        arg.n = evaluated
-    let tm = typeRel(m, formal, arg)
-    if tm in {isNone, isConvertible}: return nil
+  matchGenericParams(m, n, s)
+  if m.state != csMatch:
+    # state is csMatch only if *all* generic params were matched,
+    # including implicit parameters
+    return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   newInst.typ.flags.excl tfUnresolved
   let info = getCallLineInfo(n)
@@ -637,14 +872,24 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
   onUse(info, s)
   result = newSymNode(newInst, info)
 
-proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
-  assert n.kind == nkBracketExpr
+proc setGenericParams(c: PContext, n, expectedParams: PNode) =
+  ## sems generic params in subscript expression
   for i in 1..<n.len:
-    let e = semExpr(c, n[i])
+    let
+      constraint =
+        if expectedParams != nil and i <= expectedParams.len:
+          expectedParams[i - 1].typ
+        else:
+          nil
+      e = semExprWithType(c, n[i], expectedType = constraint)
     if e.typ == nil:
       n[i].typ = errorType(c)
     else:
       n[i].typ = e.typ.skipTypes({tyTypeDesc})
+
+proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
+  assert n.kind == nkBracketExpr
+  setGenericParams(c, n, s.ast[genericParamsPos])
   var s = s
   var a = n[0]
   if a.kind == nkSym:
@@ -679,17 +924,25 @@ proc explicitGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   else:
     result = explicitGenericInstError(c, n)
 
-proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
+proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): tuple[s: PSym, state: TBorrowState] =
   # Searches for the fn in the symbol table. If the parameter lists are suitable
   # for borrowing the sym in the symbol table is returned, else nil.
   # New approach: generate fn(x, y, z) where x, y, z have the proper types
   # and use the overloading resolution mechanism:
+  const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
+
+  template getType(isDistinct: bool; t: PType):untyped =
+    if isDistinct: t.baseOfDistinct(c.graph, c.idgen) else: t
+
+  result = default(tuple[s: PSym, state: TBorrowState])
   var call = newNodeI(nkCall, fn.info)
   var hasDistinct = false
+  var isDistinct: bool
+  var x: PType
+  var t: PType
   call.add(newIdentNode(fn.name, fn.info))
   for i in 1..<fn.typ.n.len:
     let param = fn.typ.n[i]
-    const desiredTypes = abstractVar + {tyCompositeTypeClass} - {tyTypeDesc, tyDistinct}
     #[.
       # We only want the type not any modifiers such as `ptr`, `var`, `ref` ...
       # tyCompositeTypeClass is here for
@@ -698,22 +951,31 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym =
       proc `$`(f: Foo): string {.borrow.}
       # We want to skip the `Foo` to get `int`
     ]#
-    let t = skipTypes(param.typ, desiredTypes)
-    if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true
-    var x: PType
+    t = skipTypes(param.typ, desiredTypes)
+    isDistinct = t.kind == tyDistinct or param.typ.kind == tyDistinct
+    if t.kind == tyGenericInvocation and t.genericHead.last.kind == tyDistinct:
+      result.state = bsGeneric
+      return
+    if isDistinct: hasDistinct = true
     if param.typ.kind == tyVar:
       x = newTypeS(param.typ.kind, c)
-      x.addSonSkipIntLit(t.baseOfDistinct(c.graph, c.idgen), c.idgen)
+      x.addSonSkipIntLit(getType(isDistinct, t), c.idgen)
     else:
-      x = t.baseOfDistinct(c.graph, c.idgen)
-    call.add(newNodeIT(nkEmpty, fn.info, x))
+      x = getType(isDistinct, t)
+    var s = copySym(param.sym, c.idgen)
+    s.typ = x
+    s.info = param.info
+    call.add(newSymNode(s))
   if hasDistinct:
     let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind}
     var resolved = semOverloadedCall(c, call, call, filter, {})
     if resolved != nil:
-      result = resolved[0].sym
-      if not compareTypes(result.typ[0], fn.typ[0], dcEqIgnoreDistinct):
-        result = nil
-      elif result.magic in {mArrPut, mArrGet}:
+      result.s = resolved[0].sym
+      result.state = bsMatch
+      if not compareTypes(result.s.typ.returnType, fn.typ.returnType, dcEqIgnoreDistinct, {IgnoreFlags}):
+        result.state = bsReturnNotMatch
+      elif result.s.magic in {mArrPut, mArrGet}:
         # cannot borrow these magics for now
-        result = nil
+        result.state = bsNotSupported
+  else:
+    result.state = bsNoDistinct
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 363b7e5a4..ca35ddc53 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -9,14 +9,14 @@
 
 ## This module contains the data structures for the semantic checking phase.
 
-import tables
+import std/[tables, intsets, sets]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import
-  intsets, options, ast, astalgo, msgs, idents, renderer,
-  magicsys, vmdef, modulegraphs, lineinfos, sets, pathutils
+  options, ast, astalgo, msgs, idents, renderer,
+  magicsys, vmdef, modulegraphs, lineinfos, pathutils
 
 import ic / ic
 
@@ -38,9 +38,10 @@ type
     resultSym*: PSym          # the result symbol (if we are in a proc)
     nestedLoopCounter*: int   # whether we are in a loop or not
     nestedBlockCounter*: int  # whether we are in a block or not
+    breakInLoop*: bool        # whether we are in a loop without block
     next*: PProcCon           # used for stacking procedure contexts
     mappingExists*: bool
-    mapping*: TIdTable
+    mapping*: Table[ItemId, PSym]
     caseContext*: seq[tuple[n: PNode, idx: int]]
     localBindStmts*: seq[PNode]
 
@@ -69,9 +70,13 @@ type
     efWantStmt, efAllowStmt, efDetermineType, efExplain,
     efWantValue, efOperand, efNoSemCheck,
     efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check,
-    efNoUndeclared
+    efNoUndeclared, efIsDotCall, efCannotBeDotCall,
       # Use this if undeclared identifiers should not raise an error during
       # overload resolution.
+    efTypeAllowed # typeAllowed will be called after
+    efWantNoDefaults
+    efIgnoreDefaults # var statements without initialization
+    efAllowSymChoice # symchoice node should not be resolved
 
   TExprFlags* = set[TExprFlag]
 
@@ -117,11 +122,10 @@ type
     converters*: seq[PSym]
     patterns*: seq[PSym]       # sequence of pattern matchers
     optionStack*: seq[POptionEntry]
-    symMapping*: TIdTable      # every gensym'ed symbol needs to be mapped
-                               # to some new symbol in a generic instantiation
     libs*: seq[PLib]           # all libs used by this module
     semConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.} # for the pragmas
     semExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
+    semExprWithType*: proc (c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode {.nimcall.}
     semTryExpr*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semTryConstExpr*: proc (c: PContext, n: PNode; expectedType: PType = nil): PNode {.nimcall.}
     computeRequiresInit*: proc (c: PContext, t: PType): bool {.nimcall.}
@@ -130,11 +134,14 @@ type
     semOperand*: proc (c: PContext, n: PNode, flags: TExprFlags = {}): PNode {.nimcall.}
     semConstBoolExpr*: proc (c: PContext, n: PNode): PNode {.nimcall.} # XXX bite the bullet
     semOverloadedCall*: proc (c: PContext, n, nOrig: PNode,
-                              filter: TSymKinds, flags: TExprFlags): PNode {.nimcall.}
+                              filter: TSymKinds, flags: TExprFlags, expectedType: PType = nil): PNode {.nimcall.}
     semTypeNode*: proc(c: PContext, n: PNode, prev: PType): PType {.nimcall.}
-    semInferredLambda*: proc(c: PContext, pt: TIdTable, n: PNode): PNode
-    semGenerateInstance*: proc (c: PContext, fn: PSym, pt: TIdTable,
+    semInferredLambda*: proc(c: PContext, pt: Table[ItemId, PType], n: PNode): PNode
+    semGenerateInstance*: proc (c: PContext, fn: PSym, pt: Table[ItemId, PType],
                                 info: TLineInfo): PSym
+    instantiateOnlyProcType*: proc (c: PContext, pt: TypeMapping,
+                                    prc: PSym, info: TLineInfo): PType
+      # used by sigmatch for explicit generic instantiations
     includedFiles*: IntSet    # used to detect recursive include files
     pureEnumFields*: TStrTable   # pure enum fields that can be used unambiguously
     userPragmas*: TStrTable
@@ -162,6 +169,11 @@ type
     lastTLineInfo*: TLineInfo
     sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index
     inUncheckedAssignSection*: int
+    importModuleLookup*: Table[int, seq[int]] # (module.ident.id, [module.id])
+    skipTypes*: seq[PNode] # used to skip types between passes in type section. So far only used for inheritance, sets and generic bodies.
+    inTypeofContext*: int
+  TBorrowState* = enum
+    bsNone, bsReturnNotMatch, bsNoDistinct, bsGeneric, bsNotSupported, bsMatch
 
 template config*(c: PContext): ConfigRef = c.graph.config
 
@@ -172,12 +184,12 @@ proc getIntLitType*(c: PContext; literal: PNode): PType =
     result = c.intTypeCache[value.int]
     if result == nil:
       let ti = getSysType(c.graph, literal.info, tyInt)
-      result = copyType(ti, nextTypeId(c.idgen), ti.owner)
+      result = copyType(ti, c.idgen, ti.owner)
       result.n = literal
       c.intTypeCache[value.int] = result
   else:
     let ti = getSysType(c.graph, literal.info, tyInt)
-    result = copyType(ti, nextTypeId(c.idgen), ti.owner)
+    result = copyType(ti, c.idgen, ti.owner)
     result.n = literal
 
 proc setIntLitType*(c: PContext; result: PNode) =
@@ -210,12 +222,11 @@ proc setIntLitType*(c: PContext; result: PNode) =
     internalError(c.config, result.info, "invalid int size")
 
 proc makeInstPair*(s: PSym, inst: PInstantiation): TInstantiationPair =
-  result.genericSym = s
-  result.inst = inst
+  result = TInstantiationPair(genericSym: s, inst: inst)
 
 proc filename*(c: PContext): string =
   # the module's filename
-  return toFilename(c.config, FileIndex c.module.position)
+  result = toFilename(c.config, FileIndex c.module.position)
 
 proc scopeDepth*(c: PContext): int {.inline.} =
   result = if c.currentScope != nil: c.currentScope.depthLevel
@@ -242,14 +253,14 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
 
 proc put*(p: PProcCon; key, val: PSym) =
   if not p.mappingExists:
-    initIdTable(p.mapping)
+    p.mapping = initTable[ItemId, PSym]()
     p.mappingExists = true
   #echo "put into table ", key.info
-  p.mapping.idTablePut(key, val)
+  p.mapping[key.itemId] = val
 
 proc get*(p: PProcCon; key: PSym): PSym =
   if not p.mappingExists: return nil
-  result = PSym(p.mapping.idTableGet(key))
+  result = p.mapping.getOrDefault(key.itemId)
 
 proc getGenSym*(c: PContext; s: PSym): PSym =
   if sfGenSym notin s.flags: return s
@@ -308,17 +319,18 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext =
   result.converters = @[]
   result.patterns = @[]
   result.includedFiles = initIntSet()
-  initStrTable(result.pureEnumFields)
-  initStrTable(result.userPragmas)
+  result.pureEnumFields = initStrTable()
+  result.userPragmas = initStrTable()
   result.generics = @[]
   result.unknownIdents = initIntSet()
   result.cache = graph.cache
   result.graph = graph
-  initStrTable(result.signatures)
+  result.signatures = initStrTable()
   result.features = graph.config.features
   if graph.config.symbolFiles != disabledSf:
     let id = module.position
-    assert graph.packed[id].status in {undefined, outdated}
+    if graph.config.cmd != cmdM:
+      assert graph.packed[id].status in {undefined, outdated}
     graph.packed[id].status = storing
     graph.packed[id].module = module
     initEncoder graph, module
@@ -379,19 +391,18 @@ proc reexportSym*(c: PContext; s: PSym) =
 
 proc newLib*(kind: TLibKind): PLib =
   new(result)
-  result.kind = kind          #initObjectSet(result.syms)
+  result.kind = kind          #result.syms = initObjectSet()
 
 proc addToLib*(lib: PLib, sym: PSym) =
   #if sym.annex != nil and not isGenericRoutine(sym):
   #  LocalError(sym.info, errInvalidPragma)
   sym.annex = lib
 
-proc newTypeS*(kind: TTypeKind, c: PContext): PType =
-  result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c))
+proc newTypeS*(kind: TTypeKind; c: PContext; son: sink PType = nil): PType =
+  result = newType(kind, c.idgen, getCurrOwner(c), son = son)
 
 proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType =
-  result = newType(tyPtr, nextTypeId(idgen), owner)
-  addSonSkipIntLit(result, baseType, idgen)
+  result = newType(tyPtr, idgen, owner, skipIntLit(baseType, idgen))
 
 proc makePtrType*(c: PContext, baseType: PType): PType =
   makePtrType(getCurrOwner(c), baseType, c.idgen)
@@ -404,29 +415,20 @@ proc makeTypeWithModifier*(c: PContext,
   if modifier in {tyVar, tyLent, tyTypeDesc} and baseType.kind == modifier:
     result = baseType
   else:
-    result = newTypeS(modifier, c)
-    addSonSkipIntLit(result, baseType, c.idgen)
+    result = newTypeS(modifier, c, skipIntLit(baseType, c.idgen))
 
 proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType =
   if baseType.kind == kind:
     result = baseType
   else:
-    result = newTypeS(kind, c)
-    addSonSkipIntLit(result, baseType, c.idgen)
-
-proc makeVarType*(owner: PSym, baseType: PType; idgen: IdGenerator; kind = tyVar): PType =
-  if baseType.kind == kind:
-    result = baseType
-  else:
-    result = newType(kind, nextTypeId(idgen), owner)
-    addSonSkipIntLit(result, baseType, idgen)
+    result = newTypeS(kind, c, skipIntLit(baseType, c.idgen))
 
 proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode =
   let typedesc = newTypeS(tyTypeDesc, c)
   incl typedesc.flags, tfCheckedForDestructor
   internalAssert(c.config, typ != nil)
   typedesc.addSonSkipIntLit(typ, c.idgen)
-  let sym = newSym(skType, c.cache.idAnon, nextSymId(c.idgen), getCurrOwner(c), info,
+  let sym = newSym(skType, c.cache.idAnon, c.idgen, getCurrOwner(c), info,
                    c.config.options).linkTo(typedesc)
   result = newSymNode(sym, info)
 
@@ -435,38 +437,40 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
   assert n != nil
   result.n = n
 
-proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
-                      idgen: IdGenerator): PType =
-  result = newType(kind, nextTypeId(idgen), owner)
-  result.sons = sons
+when false:
+  proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType];
+                        idgen: IdGenerator): PType =
+    result = newType(kind, idgen, owner, sons = sons)
 
-proc newTypeWithSons*(c: PContext, kind: TTypeKind,
-                      sons: seq[PType]): PType =
-  result = newType(kind, nextTypeId(c.idgen), getCurrOwner(c))
-  result.sons = sons
+  proc newTypeWithSons*(c: PContext, kind: TTypeKind,
+                        sons: seq[PType]): PType =
+    result = newType(kind, c.idgen, getCurrOwner(c), sons = sons)
 
 proc makeStaticExpr*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStaticExpr, n.info)
   result.sons = @[n]
   result.typ = if n.typ != nil and n.typ.kind == tyStatic: n.typ
-               else: newTypeWithSons(c, tyStatic, @[n.typ])
+               else: newTypeS(tyStatic, c, n.typ)
 
 proc makeAndType*(c: PContext, t1, t2: PType): PType =
   result = newTypeS(tyAnd, c)
-  result.sons = @[t1, t2]
+  result.rawAddSon t1
+  result.rawAddSon t2
   propagateToOwner(result, t1)
   propagateToOwner(result, t2)
   result.flags.incl((t1.flags + t2.flags) * {tfHasStatic})
   result.flags.incl tfHasMeta
 
 proc makeOrType*(c: PContext, t1, t2: PType): PType =
-  result = newTypeS(tyOr, c)
   if t1.kind != tyOr and t2.kind != tyOr:
-    result.sons = @[t1, t2]
+    result = newTypeS(tyOr, c)
+    result.rawAddSon t1
+    result.rawAddSon t2
   else:
+    result = newTypeS(tyOr, c)
     template addOr(t1) =
       if t1.kind == tyOr:
-        for x in t1.sons: result.rawAddSon x
+        for x in t1.kids: result.rawAddSon x
       else:
         result.rawAddSon t1
     addOr(t1)
@@ -477,8 +481,7 @@ proc makeOrType*(c: PContext, t1, t2: PType): PType =
   result.flags.incl tfHasMeta
 
 proc makeNotType*(c: PContext, t1: PType): PType =
-  result = newTypeS(tyNot, c)
-  result.sons = @[t1]
+  result = newTypeS(tyNot, c, son = t1)
   propagateToOwner(result, t1)
   result.flags.incl(t1.flags * {tfHasStatic})
   result.flags.incl tfHasMeta
@@ -489,8 +492,7 @@ proc nMinusOne(c: PContext; n: PNode): PNode =
 # Remember to fix the procs below this one when you make changes!
 proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
   let intType = getSysType(c.graph, n.info, tyInt)
-  result = newTypeS(tyRange, c)
-  result.sons = @[intType]
+  result = newTypeS(tyRange, c, son = intType)
   if n.typ != nil and n.typ.n == nil:
     result.flags.incl tfUnresolved
   result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType),
@@ -542,6 +544,27 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt;
   result.n = n
   addSonSkipIntLit(result, intType, c.idgen) # basetype of range
 
+proc isSelf*(t: PType): bool {.inline.} =
+  ## Is this the magical 'Self' type from concepts?
+  t.kind == tyTypeDesc and tfPacked in t.flags
+
+proc makeTypeDesc*(c: PContext, typ: PType): PType =
+  if typ.kind == tyTypeDesc and not isSelf(typ):
+    result = typ
+  else:
+    result = newTypeS(tyTypeDesc, c, skipIntLit(typ, c.idgen))
+    incl result.flags, tfCheckedForDestructor
+
+proc symFromType*(c: PContext; t: PType, info: TLineInfo): PSym =
+  if t.sym != nil: return t.sym
+  result = newSym(skType, getIdent(c.cache, "AnonType"), c.idgen, t.owner, info)
+  result.flags.incl sfAnon
+  result.typ = t
+
+proc symNodeFromType*(c: PContext, t: PType, info: TLineInfo): PNode =
+  result = newSymNode(symFromType(c, t, info), info)
+  result.typ = makeTypeDesc(c, t)
+
 proc markIndirect*(c: PContext, s: PSym) {.inline.} =
   if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}:
     incl(s.flags, sfAddrTaken)
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 32bb4c331..2885142a7 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -52,12 +52,8 @@ template rejectEmptyNode(n: PNode) =
 proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   rejectEmptyNode(n)
   # same as 'semExprWithType' but doesn't check for proc vars
-  result = semExpr(c, n, flags + {efOperand})
+  result = semExpr(c, n, flags + {efOperand, efAllowSymChoice})
   if result.typ != nil:
-    # XXX tyGenericInst here?
-    if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
-      #and tfUnresolved in result.typ.flags:
-      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)
@@ -84,7 +80,7 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType
     result = errorNode(c, n)
 
 proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
-  result = semExprCheck(c, n, flags, expectedType)
+  result = semExprCheck(c, n, flags-{efTypeAllowed}, expectedType)
   if result.typ == nil and efInTypeof in flags:
     result.typ = c.voidType
   elif result.typ == nil or result.typ == c.enforceVoidContext:
@@ -94,6 +90,18 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType
   elif result.typ.kind == tyError:
     # associates the type error to the current owner
     result.typ = errorType(c)
+  elif efTypeAllowed in flags and result.typ.kind == tyProc and
+      hasUnresolvedParams(result, {}):
+    # mirrored with semOperand but only on efTypeAllowed
+    let owner = result.typ.owner
+    let err =
+      # consistent error message with evaltempl/semMacroExpr
+      if owner != nil and owner.kind in {skTemplate, skMacro}:
+        errMissingGenericParamsForTemplate % n.renderTree
+      else:
+        errProcHasNoConcreteType % n.renderTree
+    localError(c.config, n.info, err)
+    result.typ = errorType(c)
   else:
     if result.typ.kind in {tyVar, tyLent}: result = newDeref(result)
 
@@ -107,8 +115,114 @@ proc semExprNoDeref(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
 proc semSymGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
   result = symChoice(c, n, s, scClosed)
 
+proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode
+
+proc isSymChoice(n: PNode): bool {.inline.} =
+  result = n.kind in nkSymChoices
+
+proc resolveSymChoice(c: PContext, n: var PNode, flags: TExprFlags = {}, expectedType: PType = nil) =
+  ## Attempts to resolve a symchoice `n`, `n` remains a symchoice if
+  ## it cannot be resolved (this is the case even when `n.len == 1`).
+  if expectedType != nil:
+    # resolve from type inference, see paramTypesMatch
+    n = fitNode(c, expectedType, n, n.info)
+  if isSymChoice(n) and efAllowSymChoice notin flags:
+    # some contexts might want sym choices preserved for later disambiguation
+    # in general though they are ambiguous
+    let first = n[0].sym
+    var foundSym: PSym = nil
+    if first.kind == skEnumField and
+        not isAmbiguous(c, first.name, {skEnumField}, foundSym) and
+        foundSym == first:
+      # choose the first resolved enum field, i.e. the latest in scope
+      # to mirror behavior before overloadable enums
+      n = n[0]
+
+proc semOpenSym(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType,
+                warnDisabled = false): PNode =
+  ## sem the child of an `nkOpenSym` node, that is, captured symbols that can be
+  ## replaced by newly injected symbols in generics. `s` must be the captured
+  ## symbol if the original node is an `nkSym` node; and `nil` if it is an
+  ## `nkOpenSymChoice`, in which case only non-overloadable injected symbols
+  ## will be considered.
+  let isSym = n.kind == nkSym
+  let ident = n.getPIdent
+  assert ident != nil
+  let id = newIdentNode(ident, n.info)
+  c.isAmbiguous = false
+  let s2 = qualifiedLookUp(c, id, {})
+  # for `nkSym`, the first found symbol being different and unambiguous is
+  # enough to replace the original
+  # for `nkOpenSymChoice`, the first found symbol must be non-overloadable,
+  # since otherwise we have to use regular `nkOpenSymChoice` functionality
+  # but of the overloadable sym kinds, semExpr does not handle skModule, skMacro, skTemplate
+  # as overloaded in the case where `nkIdent` finds them first
+  if s2 != nil and not c.isAmbiguous and
+      ((isSym and s2 != n.sym) or
+        (not isSym and s2.kind notin OverloadableSyms-{skModule, skMacro, skTemplate})):
+    # only consider symbols defined under current proc:
+    var o = s2.owner
+    while o != nil:
+      if o == c.p.owner:
+        if not warnDisabled:
+          result = semExpr(c, id, flags, expectedType)
+          return
+        else:
+          var msg =
+            "a new symbol '" & ident.s & "' has been injected during " &
+            # msgContext should show what is being instantiated:
+            "template or generic instantiation, however "
+          if isSym:
+            msg.add(
+              getSymRepr(c.config, n.sym) & " captured at " &
+              "the proc declaration will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this captured symbol explicitly")
+          else:
+            msg.add(
+              "overloads of " & ident.s & " will be used instead; " &
+              "either enable --experimental:openSym to use the injected symbol, " &
+              "or `bind` this symbol explicitly")
+          message(c.config, n.info, warnIgnoredSymbolInjection, msg)
+          break
+      o = o.owner
+  # nothing found
+  n.flags.excl nfDisabledOpenSym
+  if not warnDisabled and isSym:
+    result = semExpr(c, n, flags, expectedType)
+  else:
+    result = nil
+    if not isSym:
+      # set symchoice node type back to None
+      n.typ = newTypeS(tyNone, c)
+
+proc semSymChoice(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
+  if n.kind == nkOpenSymChoice:
+    result = semOpenSym(c, n, flags, expectedType,
+      warnDisabled = nfDisabledOpenSym in n.flags and
+        genericsOpenSym notin c.features)
+    if result != nil:
+      return
+  result = n
+  resolveSymChoice(c, result, flags, expectedType)
+  if isSymChoice(result) and result.len == 1:
+    # resolveSymChoice can leave 1 sym
+    result = result[0]
+  if isSymChoice(result) and efAllowSymChoice notin flags:
+    var err = "ambiguous identifier: '" & result[0].sym.name.s &
+      "' -- use one of the following:\n"
+    for child in n:
+      let candidate = child.sym
+      err.add "  " & candidate.owner.name.s & "." & candidate.name.s
+      err.add ": " & typeToString(candidate.typ) & "\n"
+    localError(c.config, n.info, err)
+    n.typ = errorType(c)
+    result = n
+  if result.kind == nkSym:
+    result = semSym(c, result, result.sym, flags)
+
 proc inlineConst(c: PContext, n: PNode, s: PSym): PNode {.inline.} =
-  result = copyTree(s.ast)
+  result = copyTree(s.astdef)
   if result.isNil:
     localError(c.config, n.info, "constant of type '" & typeToString(s.typ) & "' has no value")
     result = newSymNode(s)
@@ -144,19 +258,19 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
   var d = skipTypes(targetTyp, abstractVar)
   var s = srcTyp
   if s.kind in tyUserTypeClasses and s.isResolvedUserTypeClass:
-    s = s.lastSon
+    s = s.last
   s = skipTypes(s, abstractVar-{tyTypeDesc, tyOwned})
   if s.kind == tyOwned and d.kind != tyOwned:
-    s = s.lastSon
+    s = s.skipModifier
   var pointers = 0
   while (d != nil) and (d.kind in {tyPtr, tyRef, tyOwned}):
     if s.kind == tyOwned and d.kind != tyOwned:
-      s = s.lastSon
+      s = s.skipModifier
     elif d.kind != s.kind:
       break
     else:
-      d = d.lastSon
-      s = s.lastSon
+      d = d.elementType
+      s = s.elementType
     inc pointers
 
   let targetBaseTyp = skipTypes(targetTyp, abstractVarRange)
@@ -168,14 +282,16 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
     result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers)
   elif (targetBaseTyp.kind in IntegralTypes) and
       (srcBaseTyp.kind in IntegralTypes):
-    if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum:
+    if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum and
+        not sameType(targetTyp, srcBaseTyp):
       message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion")
     # `elif` would be incorrect here
     if targetTyp.kind == tyBool:
       discard "convOk"
     elif targetTyp.isOrdinalType:
       if src.kind in nkCharLit..nkUInt64Lit and
-          src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp):
+          src.getInt notin firstOrd(c.config, targetTyp)..lastOrd(c.config, targetTyp) and
+          targetTyp.kind notin {tyUInt..tyUInt64}:
         result = convNotInRange
       elif src.kind in nkFloatLit..nkFloat64Lit and
           (classify(src.floatVal) in {fcNan, fcNegInf, fcInf} or
@@ -190,6 +306,9 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus =
         result = convNotInRange
   else:
     # we use d, s here to speed up that operation a bit:
+    if d.kind == tyFromExpr:
+      result = convNotLegal
+      return
     case cmpTypes(c, d, s)
     of isNone, isGeneric:
       if not compareTypes(targetTyp.skipTypes(abstractVar), srcTyp.skipTypes({tyOwned}), dcEqIgnoreDistinct):
@@ -213,7 +332,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
   if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass:
     return false
   let conf = c.config
-  if conf.selectedGC in {gcArc, gcOrc}:
+  if conf.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
     let d = skipTypes(dst, abstractInst)
     let s = skipTypes(src, abstractInst)
     if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal:
@@ -232,7 +351,7 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
     return false
   elif srcSize < 0:
     return false
-  elif typeAllowed(dst, skParam, c) != nil:
+  elif typeAllowed(dst, skParam, c, {taIsCastable}) != nil:
     return false
   elif dst.kind == tyProc and dst.callConv == ccClosure:
     return src.kind == tyProc and src.callConv == ccClosure
@@ -240,17 +359,9 @@ proc isCastable(c: PContext; dst, src: PType, info: TLineInfo): bool =
     result = (dstSize >= srcSize) or
         (skipTypes(dst, abstractInst).kind in IntegralTypes) or
         (skipTypes(src, abstractInst-{tyTypeDesc}).kind in IntegralTypes)
-    if result and (dstSize > srcSize):
-      var warnMsg = "target type is larger than source type"
-      warnMsg.add("\n  target type: '$1' ($2)" % [$dst, if dstSize == 1: "1 byte" else: $dstSize & " bytes"])
-      warnMsg.add("\n  source type: '$1' ($2)" % [$src, if srcSize == 1: "1 byte" else: $srcSize & " bytes"])
-      message(conf, info, warnCastSizes, warnMsg)
   if result and src.kind == tyNil:
     return dst.size <= conf.target.ptrSize
 
-proc isSymChoice(n: PNode): bool {.inline.} =
-  result = n.kind in nkSymChoices
-
 proc maybeLiftType(t: var PType, c: PContext, info: TLineInfo) =
   # XXX: liftParamType started to perform addDecl
   # we could do that instead in semTypeNode by snooping for added
@@ -265,7 +376,7 @@ proc isOwnedSym(c: PContext; n: PNode): bool =
   let s = qualifiedLookUp(c, n, {})
   result = s != nil and sfSystemModule in s.owner.flags and s.name.s == "owned"
 
-proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+proc semConv(c: PContext, n: PNode; flags: TExprFlags = {}, expectedType: PType = nil): PNode =
   if n.len != 2:
     localError(c.config, n.info, "a type conversion takes exactly one argument")
     return n
@@ -273,7 +384,7 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   result = newNodeI(nkConv, n.info)
 
   var targetType = semTypeNode(c, n[0], nil)
-  case targetType.kind
+  case targetType.skipTypes({tyDistinct}).kind
   of tyTypeDesc:
     internalAssert c.config, targetType.len > 0
     if targetType.base.kind == tyNone:
@@ -290,16 +401,17 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
       return evaluated
     else:
       targetType = targetType.base
+  of tyAnything, tyUntyped, tyTyped:
+    localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType))
   else: discard
 
   maybeLiftType(targetType, c, n[0].info)
 
   if targetType.kind in {tySink, tyLent} or isOwnedSym(c, n[0]):
     let baseType = semTypeNode(c, n[1], nil).skipTypes({tyTypeDesc})
-    let t = newTypeS(targetType.kind, c)
+    let t = newTypeS(targetType.kind, c, baseType)
     if targetType.kind == tyOwned:
       t.flags.incl tfHasOwned
-    t.rawAddSonNoPropagationOfTypeFlags baseType
     result = newNodeI(nkType, n.info)
     result.typ = makeTypeDesc(c, t)
     return
@@ -310,7 +422,10 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   if n[1].kind == nkExprEqExpr and
       targetType.skipTypes(abstractPtrs).kind == tyObject:
     localError(c.config, n.info, "object construction uses ':', not '='")
-  var op = semExprWithType(c, n[1])
+  var op = semExprWithType(c, n[1], flags * {efDetermineType} + {efAllowSymChoice})
+  if isSymChoice(op) and op[0].sym.kind notin routineKinds:
+    # T(foo) disambiguation syntax only allowed for routines
+    op = semSymChoice(c, op)
   if targetType.kind != tyGenericParam and targetType.isMetaType:
     let final = inferWithMetatype(c, targetType, op, true)
     result.add final
@@ -322,7 +437,9 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   # here or needs to be overwritten too then.
   result.add op
 
-  if targetType.kind == tyGenericParam:
+  if targetType.kind == tyGenericParam or
+      (op.typ != nil and op.typ.kind == tyFromExpr and c.inGenericContext > 0):
+    # expression is compiled early in a generic body
     result.typ = makeTypeFromExpr(c, copyTree(result))
     return result
 
@@ -336,7 +453,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode =
       elif op.kind in {nkPar, nkTupleConstr} and targetType.kind == tyTuple:
         op = fitNode(c, targetType, op, result.info)
     of convNotNeedeed:
-      message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
+      if efNoSem2Check notin flags:
+        message(c.config, n.info, hintConvFromXtoItselfNotNeeded, result.typ.typeToString)
     of convNotLegal:
       result = fitNode(c, result.typ, result[1], result.info)
       if result == nil:
@@ -363,6 +481,10 @@ proc semCast(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 2, c.config)
   let targetType = semTypeNode(c, n[0], nil)
   let castedExpr = semExprWithType(c, n[1])
+  if castedExpr.kind == nkClosedSymChoice:
+    errorUseQualifier(c, n[1].info, castedExpr)
+  if targetType == nil:
+    localError(c.config, n.info, "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d).")
   if tfHasMeta in targetType.flags:
     localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType)
   if not isCastable(c, targetType, castedExpr.typ, n.info):
@@ -384,7 +506,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode =
     of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs:
       n.typ = getSysType(c.graph, n.info, tyInt)
     of tyArray:
-      n.typ = typ[0] # indextype
+      n.typ = typ.indexType
       if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range
         n.typ = getSysType(c.graph, n.info, tyInt)
     of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64:
@@ -409,7 +531,7 @@ proc fixupStaticType(c: PContext, n: PNode) =
   # apply this measure only in code that is enlightened to work
   # with static types.
   if n.typ.kind != tyStatic:
-    n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ], c.idgen)
+    n.typ = newTypeS(tyStatic, c, n.typ)
     n.typ.n = n # XXX: cycles like the one here look dangerous.
                 # Consider using `n.copyTree`
 
@@ -434,6 +556,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
       res = t.kind == tyProc and
             t.callConv == ccClosure
     of "iterator":
+      # holdover from when `is iterator` didn't work
       let t = skipTypes(t1, abstractRange)
       res = t.kind == tyProc and
             t.callConv == ccClosure and
@@ -461,8 +584,9 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode =
   result.typ = n.typ
 
 proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  if n.len != 3:
+  if n.len != 3 or n[2].kind == nkEmpty:
     localError(c.config, n.info, "'is' operator takes 2 arguments")
+    return errorNode(c, n)
 
   let boolType = getSysType(c.graph, n.info, tyBool)
   result = n
@@ -496,15 +620,14 @@ proc semIs(c: PContext, n: PNode, flags: TExprFlags): PNode =
       n[1] = makeTypeSymNode(c, lhsType, n[1].info)
       lhsType = n[1].typ
   else:
-    if lhsType.base.kind == tyNone or
-        (c.inGenericContext > 0 and lhsType.base.containsGenericType):
+    if c.inGenericContext > 0 and lhsType.base.containsUnresolvedType:
       # BUGFIX: don't evaluate this too early: ``T is void``
       return
 
   result = isOpImpl(c, n, flags)
 
 proc semOpAux(c: PContext, n: PNode) =
-  const flags = {efDetermineType}
+  const flags = {efDetermineType, efAllowSymChoice}
   for i in 1..<n.len:
     var a = n[i]
     if a.kind == nkExprEqExpr and a.len == 2:
@@ -525,11 +648,18 @@ proc overloadedCallOpr(c: PContext, n: PNode): PNode =
     result = newNodeI(nkCall, n.info)
     result.add newIdentNode(par, n.info)
     for i in 0..<n.len: result.add n[i]
-    result = semExpr(c, result)
+    result = semExpr(c, result, flags = {efNoUndeclared})
 
 proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
   case n.kind
-  of nkCurly, nkBracket:
+  of nkCurly:
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        changeType(c, n[i][0], elemType(newType), check)
+        changeType(c, n[i][1], elemType(newType), check)
+      else:
+        changeType(c, n[i], elemType(newType), check)
+  of nkBracket:
     for i in 0..<n.len:
       changeType(c, n[i], elemType(newType), check)
   of nkPar, nkTupleConstr:
@@ -562,14 +692,20 @@ proc changeType(c: PContext; n: PNode, newType: PType, check: bool) =
           a.add m
           changeType(m, tup[i], check)
   of nkCharLit..nkUInt64Lit:
-    if check and n.kind != nkUInt64Lit and not sameType(n.typ, newType):
+    if check and n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType):
       let value = n.intVal
       if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
         localError(c.config, n.info, "cannot convert " & $value &
-                                         " to " & typeToString(newType))
+                                         " to " & typeNameAndDesc(newType))
   of nkFloatLit..nkFloat64Lit:
     if check and not floatRangeCheck(n.floatVal, newType):
-      localError(c.config, n.info, errFloatToString % [$n.floatVal, typeToString(newType)])
+      localError(c.config, n.info, errFloatToString % [$n.floatVal, typeNameAndDesc(newType)])
+  of nkSym:
+    if check and n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        localError(c.config, n.info, "cannot convert '" & n.sym.name.s &
+                                         "' to '" & typeNameAndDesc(newType) & "'")
   else: discard
   n.typ = newType
 
@@ -581,34 +717,46 @@ proc arrayConstrType(c: PContext, n: PNode): PType =
   else:
     var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
     addSonSkipIntLit(typ, t, c.idgen)
-  typ[0] = makeRangeType(c, 0, n.len - 1, n.info)
+  typ.setIndexType makeRangeType(c, 0, n.len - 1, n.info)
   result = typ
 
 proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   result = newNodeI(nkBracket, n.info)
-  result.typ = newTypeS(tyArray, c)
+  # nkBracket nodes can also be produced by the VM as seq constant nodes
+  # in which case, we cannot produce a new array type for the node,
+  # as this might lose type info even when the node has array type
+  let constructType = n.typ.isNil
   var expectedElementType, expectedIndexType: PType = nil
-  if expectedType != nil:
-    let expected = expectedType.skipTypes(abstractRange-{tyDistinct})
-    case expected.kind
+  var expectedBase: PType = nil
+  if constructType:
+    result.typ = newTypeS(tyArray, c)
+    rawAddSon(result.typ, nil)     # index type
+    if expectedType != nil:
+      expectedBase = expectedType.skipTypes(abstractRange-{tyDistinct})
+  else:
+    result.typ = n.typ
+    expectedBase = n.typ.skipTypes(abstractRange) # include tyDistinct this time
+  if expectedBase != nil:
+    case expectedBase.kind
     of tyArray:
-      expectedIndexType = expected[0]
-      expectedElementType = expected[1]
-    of tyOpenArray:
-      expectedElementType = expected[0]
+      expectedIndexType = expectedBase[0]
+      expectedElementType = expectedBase[1]
+    of tyOpenArray, tySequence:
+      # typed bracket expressions can also have seq type
+      expectedElementType = expectedBase[0]
     else: discard
-  rawAddSon(result.typ, nil)     # index type
   var
-    firstIndex, lastIndex: Int128
+    firstIndex, lastIndex: Int128 = Zero
     indexType = getSysType(c.graph, n.info, tyInt)
     lastValidIndex = lastOrd(c.config, indexType)
   if n.len == 0:
-    rawAddSon(result.typ,
-      if expectedElementType != nil and
-          typeAllowed(expectedElementType, skLet, c) == nil:
-        expectedElementType
-      else:
-        newTypeS(tyEmpty, c)) # needs an empty basetype!
+    if constructType:
+      rawAddSon(result.typ,
+        if expectedElementType != nil and
+            typeAllowed(expectedElementType, skLet, c) == nil:
+          expectedElementType
+        else:
+          newTypeS(tyEmpty, c)) # needs an empty basetype!
     lastIndex = toInt128(-1)
   else:
     var x = n[0]
@@ -624,10 +772,14 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
         lastValidIndex = lastOrd(c.config, indexType)
         x = x[1]
 
-    let yy = semExprWithType(c, x, expectedType = expectedElementType)
-    var typ = yy.typ
-    if expectedElementType == nil:
-      expectedElementType = typ
+    let yy = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
+    var typ: PType
+    if constructType:
+      typ = yy.typ
+      if expectedElementType == nil:
+        expectedElementType = typ
+    else:
+      typ = expectedElementType
     result.add yy
     #var typ = skipTypes(result[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal})
     for i in 1..<n.len:
@@ -645,21 +797,29 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
           localError(c.config, x.info, "invalid order in array constructor")
         x = x[1]
 
-      let xx = semExprWithType(c, x, {}, expectedElementType)
+      let xx = semExprWithType(c, x, {efTypeAllowed}, expectedElementType)
       result.add xx
-      typ = commonType(c, typ, xx.typ)
+      if constructType:
+        typ = commonType(c, typ, xx.typ)
       #n[i] = semExprWithType(c, x, {})
       #result.add fitNode(c, typ, n[i])
       inc(lastIndex)
-    addSonSkipIntLit(result.typ, typ, c.idgen)
+    if constructType:
+      addSonSkipIntLit(result.typ, typ, c.idgen)
     for i in 0..<result.len:
       result[i] = fitNode(c, typ, result[i], result[i].info)
-  result.typ[0] = makeRangeType(c, toInt64(firstIndex), toInt64(lastIndex), n.info,
-                                     indexType)
+  if constructType:
+    result.typ.setIndexType(
+      makeRangeType(c,
+        toInt64(firstIndex), toInt64(lastIndex),
+        n.info, indexType))
 
 proc fixAbstractType(c: PContext, n: PNode) =
   for i in 1..<n.len:
     let it = n[i]
+    if it == nil:
+      localError(c.config, n.info, "'$1' has nil child at index $2" % [renderTree(n, {renderNoComments}), $i])
+      return
     # do not get rid of nkHiddenSubConv for OpenArrays, the codegen needs it:
     if it.kind == nkHiddenSubConv and
         skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}:
@@ -677,7 +837,7 @@ proc isUnresolvedSym(s: PSym): bool =
   result = s.kind == skGenericParam
   if not result and s.typ != nil:
     result = tfInferrableStatic in s.typ.flags or
-        (s.kind == skParam and s.typ.isMetaType) or
+        (s.kind == skParam and (s.typ.isMetaType or sfTemplateParam in s.flags)) or
         (s.kind == skType and
         s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} != {})
 
@@ -705,7 +865,7 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool =
       if hasUnresolvedArgs(c, n[i]): return true
     return false
 
-proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
+proc newHiddenAddrTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
   if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or
                                       sfCompileToCpp in c.module.flags):
     checkSonsLen(n, 1, c.config)
@@ -714,13 +874,17 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
     result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ))
     result.add n
     let aa = isAssignable(c, n)
+    let sym = getRoot(n)
     if aa notin {arLValue, arLocalLValue}:
       if aa == arDiscriminant and c.inUncheckedAssignSection > 0:
         discard "allow access within a cast(unsafeAssign) section"
+      elif strictDefs in c.features and aa == arAddressableConst and
+              sym != nil and sym.kind == skLet and isOutParam:
+        discard "allow let varaibles to be passed to out parameters"
       else:
         localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n))
 
-proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
+proc analyseIfAddressTaken(c: PContext, n: PNode, isOutParam: bool): PNode =
   result = n
   case n.kind
   of nkSym:
@@ -728,7 +892,7 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
     if n.sym.typ != nil and
         skipTypes(n.sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       incl(n.sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   of nkDotExpr:
     checkSonsLen(n, 2, c.config)
     if n[1].kind != nkSym:
@@ -736,27 +900,35 @@ proc analyseIfAddressTaken(c: PContext, n: PNode): PNode =
       return
     if skipTypes(n[1].sym.typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       incl(n[1].sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   of nkBracketExpr:
     checkMinSonsLen(n, 1, c.config)
     if skipTypes(n[0].typ, abstractInst-{tyTypeDesc}).kind notin {tyVar, tyLent}:
       if n[0].kind == nkSym: incl(n[0].sym.flags, sfAddrTaken)
-      result = newHiddenAddrTaken(c, n)
+      result = newHiddenAddrTaken(c, n, isOutParam)
   else:
-    result = newHiddenAddrTaken(c, n)
+    result = newHiddenAddrTaken(c, n, isOutParam)
 
-proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
+proc analyseIfAddressTakenInCall(c: PContext, n: PNode, isConverter = false) =
   checkMinSonsLen(n, 1, c.config)
+  if n[0].typ == nil:
+    # n[0] might be erroring node in nimsuggest
+    return
   const
     FakeVarParams = {mNew, mNewFinalize, mInc, ast.mDec, mIncl, mExcl,
       mSetLengthStr, mSetLengthSeq, mAppendStrCh, mAppendStrStr, mSwap,
-      mAppendSeqElem, mNewSeq, mReset, mShallowCopy, mDeepCopy, mMove,
+      mAppendSeqElem, mNewSeq, mShallowCopy, mDeepCopy, mMove,
       mWasMoved}
 
+  template checkIfConverterCalled(c: PContext, n: PNode) =
+    ## Checks if there is a converter call which wouldn't be checked otherwise
+    # Call can sometimes be wrapped in a deref
+    let node = if n.kind == nkHiddenDeref: n[0] else: n
+    if node.kind == nkHiddenCallConv:
+      analyseIfAddressTakenInCall(c, node, true)
   # get the real type of the callee
   # it may be a proc var with a generic alias type, so we skip over them
   var t = n[0].typ.skipTypes({tyGenericInst, tyAlias, tySink})
-
   if n[0].kind == nkSym and n[0].sym.magic in FakeVarParams:
     # BUGFIX: check for L-Value still needs to be done for the arguments!
     # note sometimes this is eval'ed twice so we check for nkHiddenAddr here:
@@ -771,6 +943,8 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
               discard "allow access within a cast(unsafeAssign) section"
             else:
               localError(c.config, it.info, errVarForOutParamNeededX % $it)
+        # Make sure to still check arguments for converters
+        c.checkIfConverterCalled(n[i])
     # 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
@@ -782,14 +956,13 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) =
     return
   for i in 1..<n.len:
     let n = if n.kind == nkHiddenDeref: n[0] else: n
-    if n[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[i])
+    c.checkIfConverterCalled(n[i])
     if i < t.len and
         skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}:
-      if n[i].kind != nkHiddenAddr:
-        n[i] = analyseIfAddressTaken(c, n[i])
+      # Converters wrap var parameters in nkHiddenAddr but they haven't been analysed yet.
+      # So we need to make sure we are checking them still when in a converter call
+      if n[i].kind != nkHiddenAddr or isConverter:
+        n[i] = analyseIfAddressTaken(c, n[i].skipAddr(), isOutParam(skipTypes(t[i], abstractInst-{tyTypeDesc})))
 
 include semmagic
 
@@ -829,7 +1002,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
         if n[i].typ.isNil or n[i].typ.kind != tyStatic or
             tfUnresolved notin n[i].typ.flags:
           break maybeLabelAsStatic
-      n.typ = newTypeWithSons(c, tyStatic, @[n.typ])
+      n.typ = newTypeS(tyStatic, c, n.typ)
       n.typ.flags.incl tfUnresolved
 
   # optimization pass: not necessary for correctness of the semantic pass
@@ -843,7 +1016,8 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode =
 
     if callee.magic notin ctfeWhitelist: return
 
-    if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine:
+    if callee.kind notin {skProc, skFunc, skConverter, skConst} or
+        callee.isGenericRoutineStrict:
       return
 
     if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return
@@ -886,21 +1060,22 @@ proc semStaticExpr(c: PContext, n: PNode; expectedType: PType = nil): PNode =
     result = fixupTypeAfterEval(c, result, a)
 
 proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
-                                     flags: TExprFlags): PNode =
+                                     flags: TExprFlags; expectedType: PType = nil): PNode =
   if flags*{efInTypeof, efWantIterator, efWantIterable} != {}:
     # consider: 'for x in pReturningArray()' --> we don't want the restriction
     # to 'skIterator' anymore; skIterator is preferred in sigmatch already
     # for typeof support.
     # for ``typeof(countup(1,3))``, see ``tests/ttoseq``.
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate, skIterator}, flags, expectedType)
   else:
     result = semOverloadedCall(c, n, nOrig,
-      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags)
+      {skProc, skFunc, skMethod, skConverter, skMacro, skTemplate}, flags, expectedType)
 
   if result != nil:
     if result[0].kind != nkSym:
-      internalError(c.config, "semOverloadedCallAnalyseEffects")
+      if not (c.inGenericContext > 0): # see generic context check in semOverloadedCall
+        internalError(c.config, "semOverloadedCallAnalyseEffects")
       return
     let callee = result[0].sym
     case callee.kind
@@ -918,51 +1093,72 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode,
           rawAddSon(typ, result.typ)
           result.typ = typ
 
-proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
-
 proc resolveIndirectCall(c: PContext; n, nOrig: PNode;
                          t: PType): TCandidate =
-  initCandidate(c, result, t)
+  result = initCandidate(c, t)
   matches(c, n, nOrig, result)
-  if result.state != csMatch:
-    # try to deref the first argument:
-    if implicitDeref in c.features and canDeref(n):
-      n[1] = n[1].tryDeref
-      initCandidate(c, result, t)
-      matches(c, n, nOrig, result)
-
-proc bracketedMacro(n: PNode): PSym =
-  if n.len >= 1 and n[0].kind == nkSym:
-    result = n[0].sym
-    if result.kind notin {skMacro, skTemplate}:
-      result = nil
 
-proc setGenericParams(c: PContext, n: PNode) =
-  for i in 1..<n.len:
-    n[i].typ = semTypeNode(c, n[i], nil)
+proc finishOperand(c: PContext, a: PNode): PNode =
+  if a.typ.isNil:
+    result = c.semOperand(c, a, {efDetermineType})
+  else:
+    result = a
+  # XXX tyGenericInst here?
+  if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}):
+    #and tfUnresolved in result.typ.flags:
+    let owner = result.typ.owner
+    let err =
+      # consistent error message with evaltempl/semMacroExpr
+      if owner != nil and owner.kind in {skTemplate, skMacro}:
+        errMissingGenericParamsForTemplate % a.renderTree
+      else:
+        errProcHasNoConcreteType % a.renderTree
+    localError(c.config, a.info, err)
+  considerGenSyms(c, result)
+
+proc semFinishOperands(c: PContext; n: PNode; isBracketExpr = false) =
+  # this needs to be called to ensure that after overloading resolution every
+  # argument has been sem'checked
+
+  # skip the first argument for operands of `[]` since it may be an unresolved
+  # generic proc, which is handled in semMagic
+  let start = 1 + ord(isBracketExpr)
+  for i in start..<n.len:
+    n[i] = finishOperand(c, n[i])
 
 proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   if efNoSemCheck notin flags and n.typ != nil and n.typ.kind == tyError:
     return errorNode(c, n)
+  if n.typ != nil and n.typ.kind == tyFromExpr and c.inGenericContext > 0:
+    return n
 
   result = n
+
+  when defined(nimsuggest):
+    if c.config.expandProgress:
+      if c.config.expandLevels == 0:
+        return n
+      else:
+        c.config.expandLevels -= 1
+
   let callee = result[0].sym
   case callee.kind
   of skMacro: result = semMacroExpr(c, result, orig, callee, flags, expectedType)
   of skTemplate: result = semTemplateExpr(c, result, callee, flags, expectedType)
   else:
-    semFinishOperands(c, result)
+    semFinishOperands(c, result, isBracketExpr = callee.magic in {mArrGet, mArrPut})
     activate(c, result)
     fixAbstractType(c, result)
     analyseIfAddressTakenInCall(c, result)
     if callee.magic != mNone:
-      result = magicsAfterOverloadResolution(c, result, flags)
+      result = magicsAfterOverloadResolution(c, result, flags, expectedType)
     when false:
       if result.typ != nil and
-          not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty):
+          not (result.typ.kind == tySequence and result.elementType.kind == tyEmpty):
         liftTypeBoundOps(c, result.typ, n.info)
     #result = patchResolvedTypeBoundOp(c, result)
-  if c.matchedConcept == nil:
+  if c.matchedConcept == nil and (c.inTypeofContext == 0 or callee.magic != mNone):
+    # don't fold calls in concepts and typeof
     result = evalAtCompileTime(c, result)
 
 proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
@@ -971,33 +1167,36 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
   var prc = n[0]
   if n[0].kind == nkDotExpr:
     checkSonsLen(n[0], 2, c.config)
-    let n0 = semFieldAccess(c, n[0])
+    let n0 = semFieldAccess(c, n[0], {efIsDotCall})
     if n0.kind == nkDotCall:
       # it is a static call!
       result = n0
       result.transitionSonsKind(nkCall)
       result.flags.incl nfExplicitCall
       for i in 1..<n.len: result.add n[i]
-      return semExpr(c, result, flags)
+      return semExpr(c, result, flags, expectedType)
+    elif n0.typ.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't make assumptions, entire expression needs to be tyFromExpr
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, result.copyTree)
+      return
     else:
       n[0] = n0
   else:
-    n[0] = semExpr(c, n[0], {efInCall})
+    n[0] = semExpr(c, n[0], {efInCall, efAllowSymChoice})
     let t = n[0].typ
     if t != nil and t.kind in {tyVar, tyLent}:
       n[0] = newDeref(n[0])
-    elif n[0].kind == nkBracketExpr:
-      let s = bracketedMacro(n[0])
-      if s != nil:
-        setGenericParams(c, n[0])
-        return semDirectOp(c, n, flags, expectedType)
+    elif isSymChoice(n[0]) and nfDotField notin n.flags:
+      # overloaded generic procs e.g. newSeq[int] can end up here
+      return semDirectOp(c, n, flags, expectedType)
 
   var t: PType = nil
   if n[0].typ != nil:
     t = skipTypes(n[0].typ, abstractInst+{tyOwned}-{tyTypeDesc, tyDistinct})
   if t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags, expectedType)
-    return semConv(c, n)
+    return semConv(c, n, flags)
 
   let nOrig = n.copyTree
   semOpAux(c, n)
@@ -1034,16 +1233,15 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
           msg.addDeclaredLocMaybe(c.config, typ)
           localError(c.config, n.info, msg)
         return errorNode(c, n)
-      result = nil
     else:
       result = m.call
       instGenericConvertersSons(c, result, m)
 
   else:
-    result = overloadedCallOpr(c, n)
+    result = overloadedCallOpr(c, n) # this uses efNoUndeclared
     # Now that nkSym does not imply an iteration over the proc/iterator space,
     # the old ``prc`` (which is likely an nkIdent) has to be restored:
-    if result == nil:
+    if result == nil or result.kind == nkEmpty:
       # XXX: hmm, what kind of symbols will end up here?
       # do we really need to try the overload resolution?
       n[0] = prc
@@ -1066,7 +1264,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType =
   # this seems to be a hotspot in the compiler!
   let nOrig = n.copyTree
   #semLazyOpAux(c, n)
-  result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags)
+  result = semOverloadedCallAnalyseEffects(c, n, nOrig, flags, expectedType)
   if result != nil: result = afterCallActions(c, result, nOrig, flags, expectedType)
   else: result = errorNode(c, n)
 
@@ -1177,7 +1375,7 @@ proc readTypeParameter(c: PContext, typ: PType,
             # This seems semantically correct and then we'll be able
             # to return the section symbol directly here
             let foundType = makeTypeDesc(c, def[2].typ)
-            return newSymNode(copySym(def[0].sym, nextSymId c.idgen).linkTo(foundType), info)
+            return newSymNode(copySym(def[0].sym, c.idgen).linkTo(foundType), info)
 
       of nkConstSection:
         for def in statement:
@@ -1188,7 +1386,7 @@ proc readTypeParameter(c: PContext, typ: PType,
         discard
 
   if typ.kind != tyUserTypeClass:
-    let ty = if typ.kind == tyCompositeTypeClass: typ[1].skipGenericAlias
+    let ty = if typ.kind == tyCompositeTypeClass: typ.firstGenericParam.skipGenericAlias
              else: typ.skipGenericAlias
     let tbody = ty[0]
     for s in 0..<tbody.len-1:
@@ -1202,11 +1400,13 @@ proc readTypeParameter(c: PContext, typ: PType,
             return c.graph.emptyNode
         else:
           let foundTyp = makeTypeDesc(c, rawTyp)
-          return newSymNode(copySym(tParam.sym, nextSymId c.idgen).linkTo(foundTyp), info)
+          return newSymNode(copySym(tParam.sym, c.idgen).linkTo(foundTyp), info)
 
   return nil
 
 proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
+  result = nil
+  assert n.kind in nkIdentKinds + {nkDotExpr}
   let s = getGenSym(c, sym)
   case s.kind
   of skConst:
@@ -1215,7 +1415,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
     onUse(n.info, s)
     let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc})
     case typ.kind
-    of  tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
+    of tyNil, tyChar, tyInt..tyInt64, tyFloat..tyFloat128,
         tyTuple, tySet, tyUInt..tyUInt64:
       if s.magic == mNone: result = inlineConst(c, n, s)
       else: result = newSymNode(s, n.info)
@@ -1230,7 +1430,7 @@ 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.safeLen == 0: result = inlineConst(c, n, s)
+      if s.astdef.safeLen == 0: result = inlineConst(c, n, s)
       else: result = newSymNode(s, n.info)
     of tyStatic:
       if typ.n != nil:
@@ -1240,24 +1440,18 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
         result = newSymNode(s, n.info)
     else:
       result = newSymNode(s, n.info)
-  of skMacro:
-    if efNoEvaluateGeneric in flags and s.ast[genericParamsPos].len > 0 or
-       (n.kind notin nkCallKinds and s.requiredParams > 0):
-      markUsed(c, n.info, s)
-      onUse(n.info, s)
-      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:
+  of skMacro, skTemplate:
+    # check if we cannot use alias syntax (no required args or generic params)
+    if sfNoalias in s.flags:
       let info = getCallLineInfo(n)
       markUsed(c, info, s)
       onUse(info, s)
       result = symChoice(c, n, s, scClosed)
     else:
-      result = semTemplateExpr(c, n, s, flags)
+      case s.kind
+      of skMacro: result = semMacroExpr(c, n, n, s, flags)
+      of skTemplate: result = semTemplateExpr(c, n, s, flags)
+      else: discard # unreachable
   of skParam:
     markUsed(c, n.info, s)
     onUse(n.info, s)
@@ -1306,6 +1500,12 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
     markUsed(c, n.info, s)
     onUse(n.info, s)
     result = newSymNode(s, n.info)
+  of skModule:
+    # make sure type is None and not nil for discard checking
+    if efWantStmt in flags: s.typ = newTypeS(tyNone, c)
+    markUsed(c, n.info, s)
+    onUse(n.info, s)
+    result = newSymNode(s, n.info)
   else:
     let info = getCallLineInfo(n)
     #if efInCall notin flags:
@@ -1315,24 +1515,39 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode =
 
 proc tryReadingGenericParam(c: PContext, n: PNode, i: PIdent, t: PType): PNode =
   case t.kind
-  of tyTypeParamsHolders:
+  of tyGenericInst:
     result = readTypeParameter(c, t, i, n.info)
     if result == c.graph.emptyNode:
-      result = n
-      n.typ = makeTypeFromExpr(c, n.copyTree)
+      if c.inGenericContext > 0:
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, result.copyTree)
+      else:
+        result = nil
   of tyUserTypeClasses:
     if t.isResolvedUserTypeClass:
       result = readTypeParameter(c, t, i, n.info)
+    elif c.inGenericContext > 0:
+      result = semGenericStmt(c, n)
+      result.typ = makeTypeFromExpr(c, copyTree(result))
     else:
-      n.typ = makeTypeFromExpr(c, copyTree(n))
-      result = n
-  of tyGenericParam, tyAnything:
-    n.typ = makeTypeFromExpr(c, copyTree(n))
-    result = n
+      result = nil
+  of tyGenericBody, tyCompositeTypeClass:
+    if c.inGenericContext > 0:
+      result = readTypeParameter(c, t, i, n.info)
+      if result != nil:
+        # generic parameter exists, stop here but delay until instantiation
+        result = semGenericStmt(c, n)
+        result.typ = makeTypeFromExpr(c, copyTree(result))
+    else:
+      result = nil
+  elif c.inGenericContext > 0 and t.containsUnresolvedType:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, copyTree(result))
   else:
-    discard
+    result = nil
 
 proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode =
+  result = nil
   var ty = ty.skipTypes(tyDotOpTransparent)
   case ty.kind
   of tyEnum:
@@ -1341,7 +1556,7 @@ proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode =
     while ty != nil:
       f = getSymFromList(ty.n, i)
       if f != nil: break
-      ty = ty.sons[0]         # enum inheritance
+      ty = ty[0]         # enum inheritance
     if f != nil:
       result = newSymNode(f)
       result.info = n.info
@@ -1355,13 +1570,13 @@ proc tryReadingTypeField(c: PContext, n: PNode, i: PIdent, ty: PType): PNode =
         n.typ = makeTypeDesc(c, field.typ)
         result = n
   of tyGenericInst:
-    result = tryReadingTypeField(c, n, i, ty.lastSon)
+    result = tryReadingTypeField(c, n, i, ty.skipModifier)
     if result == nil:
       result = tryReadingGenericParam(c, n, i, ty)
   else:
     result = tryReadingGenericParam(c, n, i, ty)
 
-proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
+proc builtinFieldAccess(c: PContext; n: PNode; flags: var TExprFlags): PNode =
   ## returns nil if it's not a built-in field access
   checkSonsLen(n, 2, c.config)
   # tests/bind/tbindoverload.nim wants an early exit here, but seems to
@@ -1384,7 +1599,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     onUse(n[1].info, s)
     return
 
-  n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable})
+  # extra flags since LHS may become a call operand:
+  n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable, efAllowSymChoice})
   #restoreOldStyleType(n[0])
   var i = considerQuotedIdent(c, n[1], n)
   var ty = n[0].typ
@@ -1399,19 +1615,22 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
       # field access and we leave the compiler to compile a normal call:
       if getCurrOwner(c).kind != skMacro:
         n.typ = makeTypeFromExpr(c, n.copyTree)
+        flags.incl efCannotBeDotCall
         return n
       else:
         return nil
     else:
+      flags.incl efCannotBeDotCall
       return tryReadingTypeField(c, n, i, ty.base)
   elif isTypeExpr(n.sons[0]):
+    flags.incl efCannotBeDotCall
     return tryReadingTypeField(c, n, i, ty)
   elif ty.kind == tyError:
     # a type error doesn't have any builtin fields
     return nil
 
   if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
-    ty = ty.lastSon
+    ty = ty.last
   ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic})
   while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias})
   var check: PNode = nil
@@ -1456,9 +1675,12 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   if result == nil:
     let t = n[0].typ.skipTypes(tyDotOpTransparent)
     result = tryReadingGenericParam(c, n, i, t)
+    flags.incl efCannotBeDotCall
 
 proc dotTransformation(c: PContext, n: PNode): PNode =
-  if isSymChoice(n[1]):
+  if isSymChoice(n[1]) or
+      # generics usually leave field names as symchoices, but not types
+      (n[1].kind == nkSym and n[1].sym.kind == skType):
     result = newNodeI(nkDotCall, n.info)
     result.add n[1]
     result.add copyTree(n[0])
@@ -1472,8 +1694,11 @@ proc dotTransformation(c: PContext, n: PNode): PNode =
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
   # this is difficult, because the '.' is used in many different contexts
   # in Nim. We first allow types in the semantic checking.
-  result = builtinFieldAccess(c, n, flags)
-  if result == nil:
+  var f = flags - {efIsDotCall}
+  result = builtinFieldAccess(c, n, f)
+  if result == nil or ((result.typ == nil or result.typ.skipTypes(abstractInst).kind != tyProc) and
+      efIsDotCall in flags and callOperator notin c.features and
+      efCannotBeDotCall notin f):
     result = dotTransformation(c, n)
 
 proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
@@ -1481,18 +1706,20 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode =
   result.add(newIdentNode(ident, n.info))
   for s in n: result.add s
 
-proc semDeref(c: PContext, n: PNode): PNode =
+proc semDeref(c: PContext, n: PNode, flags: TExprFlags): PNode =
   checkSonsLen(n, 1, c.config)
   n[0] = semExprWithType(c, n[0])
   let a = getConstExpr(c.module, n[0], c.idgen, c.graph)
   if a != nil:
-    if a.kind == nkNilLit:
+    if a.kind == nkNilLit and efInTypeof notin flags:
       localError(c.config, n.info, "nil dereference is not allowed")
     n[0] = a
   result = n
   var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
   case t.kind
-  of tyRef, tyPtr: n.typ = t.lastSon
+  of tyRef, tyPtr: n.typ = t.elementType
+  of tyMetaTypes, tyFromExpr:
+    n.typ = makeTypeFromExpr(c, n.copyTree)
   else: result = nil
   #GlobalError(n[0].info, errCircumNeedsPointer)
 
@@ -1515,22 +1742,23 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode =
     result = explicitGenericInstantiation(c, n, s)
     if result == n:
       n[0] = copyTree(result[0])
-    else:
-      n[0] = result
 
 proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
   ## returns nil if not a built-in subscript operator; also called for the
   ## checking of assignments
+  result = nil
   if n.len == 1:
-    let x = semDeref(c, n)
+    let x = semDeref(c, n, flags)
     if x == nil: return nil
+    if x.typ.kind == tyFromExpr:
+      # depends on generic type
+      return x
     result = newNodeIT(nkDerefExpr, x.info, x.typ)
     result.add(x[0])
     return
   checkMinSonsLen(n, 2, c.config)
-  # make sure we don't evaluate generic macros/templates
-  n[0] = semExprWithType(c, n[0],
-                              {efNoEvaluateGeneric})
+  # signal that generic parameters may be applied after
+  n[0] = semExprWithType(c, n[0], {efNoEvaluateGeneric, efAllowSymChoice})
   var arr = skipTypes(n[0].typ, {tyGenericInst, tyUserTypeClassInst, tyOwned,
                                       tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink})
   if arr.kind == tyStatic:
@@ -1580,13 +1808,16 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
         {tyInt..tyInt64}:
       let idx = getOrdValue(n[1])
       if idx >= 0 and idx < arr.len: n.typ = arr[toInt(idx)]
-      else: localError(c.config, n.info, "invalid index value for tuple subscript")
+      else:
+        localError(c.config, n.info,
+          "invalid index $1 in subscript for tuple of length $2" %
+            [$idx, $arr.len])
       result = n
     else:
       result = nil
   else:
     let s = if n[0].kind == nkSym: n[0].sym
-            elif n[0].kind in nkSymChoices: n[0][0].sym
+            elif n[0].kind in nkSymChoices + {nkOpenSym}: n[0][0].sym
             else: nil
     if s != nil:
       case s.kind
@@ -1626,7 +1857,7 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode =
   # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
   # nodes?
   let aOrig = nOrig[0]
-  result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1]))
+  result = newTreeI(nkCall, n.info, setterId, a[0], n[1])
   result.flags.incl nfDotSetter
   let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1])
   result = semOverloadedCallAnalyseEffects(c, result, orig, {})
@@ -1667,6 +1898,8 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode =
     else:
       localError(c.config, n.info, errExprHasNoAddress)
   result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ))
+  if n.typ.kind in {tyVar, tyLent}:
+    n.typ = n.typ.elementType
   result.add(n)
 
 proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} =
@@ -1733,6 +1966,39 @@ proc goodLineInfo(arg: PNode): TLineInfo =
   else:
     arg.info
 
+proc makeTupleAssignments(c: PContext; n: PNode): PNode =
+  ## expand tuple unpacking assignment into series of assignments
+  ##
+  ## mirrored with semstmts.makeVarTupleSection
+  let lhs = n[0]
+  let value = semExprWithType(c, n[1], {efTypeAllowed})
+  if value.typ.kind != tyTuple:
+    localError(c.config, n[1].info, errTupleUnpackingTupleExpected %
+      [typeToString(value.typ, preferDesc)])
+  elif lhs.len != value.typ.len:
+    localError(c.config, n.info, errTupleUnpackingDifferentLengths %
+      [$lhs.len, typeToString(value.typ, preferDesc), $value.typ.len])
+  result = newNodeI(nkStmtList, n.info)
+
+  let temp = newSym(skTemp, getIdent(c.cache, "tmpTupleAsgn"), c.idgen, getCurrOwner(c), n.info)
+  temp.typ = value.typ
+  temp.flags.incl(sfGenSym)
+  var v = newNodeI(nkLetSection, value.info)
+  let tempNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
+  var vpart = newNodeI(nkIdentDefs, v.info, 3)
+  vpart[0] = tempNode
+  vpart[1] = c.graph.emptyNode
+  vpart[2] = value
+  v.add vpart
+  result.add(v)
+
+  for i in 0..<lhs.len:
+    if lhs[i].kind == nkIdent and lhs[i].ident.id == ord(wUnderscore):
+      # skip _ assignments if we are using a temp as they are already evaluated
+      discard
+    else:
+      result.add newAsgnStmt(lhs[i], newTupleAccessRaw(tempNode, i))
+
 proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
   checkSonsLen(n, 2, c.config)
   var a = n[0]
@@ -1741,7 +2007,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
     # r.f = x
     # --> `f=` (r, x)
     let nOrig = n.copyTree
-    a = builtinFieldAccess(c, a, {efLValue})
+    var flags = {efLValue}
+    a = builtinFieldAccess(c, a, flags)
     if a == nil:
       a = propertyWriteAccess(c, n, nOrig, n[0])
       if a != nil: return a
@@ -1759,8 +2026,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
       result = buildOverloadedSubscripts(n[0], getIdent(c.cache, "[]="))
       result.add(n[1])
       if mode == noOverloadedSubscript:
-        bracketNotFoundError(c, result)
-        return n
+        bracketNotFoundError(c, result, {})
+        return errorNode(c, n)
       else:
         result = semExprNoType(c, result)
         return result
@@ -1770,11 +2037,11 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
     result.add(n[1])
     return semExprNoType(c, result)
   of nkPar, nkTupleConstr:
-    if a.len >= 2:
+    if a.len >= 2 or a.kind == nkTupleConstr:
       # unfortunately we need to rewrite ``(x, y) = foo()`` already here so
       # that overloading of the assignment operator still works. Usually we
       # prefer to do these rewritings in transf.nim:
-      return semStmt(c, lowerTupleUnpackingForAsgn(c.graph, n, c.idgen, c.p.owner), {})
+      return semStmt(c, makeTupleAssignments(c, n), {})
     else:
       a = semExprWithType(c, a, {efLValue})
   else:
@@ -1783,30 +2050,37 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode =
   # a = b # both are vars, means: a[] = b[]
   # a = b # b no 'var T' means: a = addr(b)
   var le = a.typ
+  let assignable = isAssignable(c, a)
+  let root = getRoot(a)
+  let useStrictDefLet = root != nil and root.kind == skLet and
+                       assignable == arAddressableConst and
+                       strictDefs in c.features and isLocalSym(root)
   if le == nil:
     localError(c.config, a.info, "expression has no type")
   elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and
-        isAssignable(c, a) in {arNone, arLentValue, arAddressableConst}) or (
-      skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features):
+        assignable in {arNone, arLentValue, arAddressableConst} and not useStrictDefLet
+        ) or (skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features):
     # Direct assignment to a discriminant is allowed!
     localError(c.config, a.info, errXCannotBeAssignedTo %
                renderTree(a, {renderNoComments}))
   else:
     let lhs = n[0]
-    let rhs = semExprWithType(c, n[1], {}, le)
+    let rhs = semExprWithType(c, n[1], {efTypeAllowed}, le)
     if lhs.kind == nkSym and lhs.sym.kind == skResult:
       n.typ = c.enforceVoidContext
       if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ):
         var rhsTyp = rhs.typ
         if rhsTyp.kind in tyUserTypeClasses and rhsTyp.isResolvedUserTypeClass:
-          rhsTyp = rhsTyp.lastSon
+          rhsTyp = rhsTyp.last
+        if lhs.sym.typ.kind == tyAnything:
+          rhsTyp = rhsTyp.skipTypes({tySink}).skipIntLit(c.idgen)
         if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}:
           internalAssert c.config, c.p.resultSym != nil
           # Make sure the type is valid for the result variable
           typeAllowedCheck(c, n.info, rhsTyp, skResult)
           lhs.typ = rhsTyp
           c.p.resultSym.typ = rhsTyp
-          c.p.owner.typ[0] = rhsTyp
+          c.p.owner.typ.setReturnType rhsTyp
         else:
           typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs)
     borrowCheck(c, n, lhs, rhs)
@@ -1843,6 +2117,9 @@ proc semReturn(c: PContext, n: PNode): PNode =
     localError(c.config, n.info, "'return' not allowed here")
 
 proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
+  when defined(nimsuggest):
+    if c.graph.config.expandDone():
+      return n
   openScope(c)
   result = semExpr(c, n, expectedType = expectedType)
   if c.p.resultSym != nil and not isEmptyType(result.typ):
@@ -1869,12 +2146,12 @@ proc semProcBody(c: PContext, n: PNode; expectedType: PType = nil): PNode =
     if isEmptyType(result.typ):
       # we inferred a 'void' return type:
       c.p.resultSym.typ = errorType(c)
-      c.p.owner.typ[0] = nil
+      c.p.owner.typ.setReturnType nil
     else:
       localError(c.config, c.p.resultSym.info, errCannotInferReturnType %
         c.p.owner.name.s)
-  if isInlineIterator(c.p.owner.typ) and c.p.owner.typ[0] != nil and
-      c.p.owner.typ[0].kind == tyUntyped:
+  if isIterator(c.p.owner.typ) and c.p.owner.typ.returnType != nil and
+      c.p.owner.typ.returnType.kind == tyAnything:
     localError(c.config, c.p.owner.info, errCannotInferReturnType %
       c.p.owner.name.s)
   closeScope(c)
@@ -1900,6 +2177,8 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) =
             tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent)
         else:
           localError(c.config, n[0].info, errXExpected, "tuple constructor")
+      elif e.kind == tyEmpty:
+        localError(c.config, n[0].info, errTypeExpected)
   else:
     when false:
       # XXX investigate what we really need here.
@@ -1912,12 +2191,10 @@ proc semYield(c: PContext, n: PNode): PNode =
   if c.p.owner == nil or c.p.owner.kind != skIterator:
     localError(c.config, n.info, errYieldNotAllowedHere)
   elif n[0].kind != nkEmpty:
-    n[0] = semExprWithType(c, n[0]) # check for type compatibility:
     var iterType = c.p.owner.typ
     let restype = iterType[0]
+    n[0] = semExprWithType(c, n[0], {}, restype) # check for type compatibility:
     if restype != nil:
-      if restype.kind != tyUntyped:
-        n[0] = fitNode(c, restype, n[0], n.info)
       if n[0].typ == nil: internalError(c.config, n.info, "semYield")
 
       if resultTypeIsInferrable(restype):
@@ -1925,11 +2202,13 @@ proc semYield(c: PContext, n: PNode): PNode =
         iterType[0] = inferred
         if c.p.resultSym != nil:
           c.p.resultSym.typ = inferred
+      else:
+        n[0] = fitNode(c, restype, n[0], n.info)
 
       semYieldVarResult(c, n, restype)
     else:
       localError(c.config, n.info, errCannotReturnExpr)
-  elif c.p.owner.typ[0] != nil:
+  elif c.p.owner.typ.returnType != nil:
     localError(c.config, n.info, errGenerated, "yield statement must yield a value")
 
 proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent =
@@ -1976,6 +2255,8 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym =
     result = n.sym
   of nkOpenSymChoice, nkClosedSymChoice:
     result = n[0].sym
+  of nkOpenSym:
+    result = lookUpForDeclared(c, n[0], onlyCurrentScope)
   else:
     localError(c.config, n.info, "identifier expected, but got: " & renderTree(n))
     result = nil
@@ -2011,10 +2292,11 @@ proc expectString(c: PContext, n: PNode): string =
   if n.kind in nkStrKinds:
     return n.strVal
   else:
+    result = ""
     localError(c.config, n.info, errStringLiteralExpected)
 
 proc newAnonSym(c: PContext; kind: TSymKind, info: TLineInfo): PSym =
-  result = newSym(kind, c.cache.idAnon, nextSymId c.idgen, getCurrOwner(c), info)
+  result = newSym(kind, c.cache.idAnon, c.idgen, getCurrOwner(c), info)
 
 proc semExpandToAst(c: PContext, n: PNode): PNode =
   let macroCall = n[1]
@@ -2035,7 +2317,7 @@ proc semExpandToAst(c: PContext, n: PNode): PNode =
     let headSymbol = macroCall[0]
     var cands = 0
     var cand: PSym = nil
-    var o: TOverloadIter
+    var o: TOverloadIter = default(TOverloadIter)
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro} and symx.typ.len == macroCall.len:
@@ -2143,10 +2425,14 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
   if ids.len > 0:
     dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info)
     dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type
-    ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type
-    ids.add c.graph.emptyNode # no default value
-    dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids)
-
+    dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
+    for i in 1..<ids.len:
+      let exp = semExprWithType(c, quotes[i+1], {})
+      let typ = exp.typ
+      if tfTriggersCompileTime notin typ.flags and typ.kind != tyStatic and exp.kind == nkSym and exp.sym.kind notin routineKinds + {skType}:
+        dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], newNodeIT(nkType, n.info, typ), c.graph.emptyNode)
+      else:
+        dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[i], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode)
   var tmpl = semTemplateDef(c, dummyTemplate)
   quotes[0] = tmpl[namePos]
   # This adds a call to newIdentNode("result") as the first argument to the template call
@@ -2202,7 +2488,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     if c.config.errorCounter != oldErrorCount:
       result = nil
   except ERecoverableError:
-    discard
+    result = nil
   # undo symbol table changes (as far as it's possible):
   c.compilesContextId = oldCompilesId
   c.generics = oldGenerics
@@ -2241,7 +2527,7 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
     result = semDirectOp(c, n, flags)
 
 proc createFlowVar(c: PContext; t: PType; info: TLineInfo): PType =
-  result = newType(tyGenericInvocation, nextTypeId c.idgen, c.module)
+  result = newType(tyGenericInvocation, c.idgen, c.module)
   addSonSkipIntLit(result, magicsys.getCompilerProc(c.graph, "FlowVar").typ, c.idgen)
   addSonSkipIntLit(result, t, c.idgen)
   result = instGenericContainer(c, info, result, allowMetaTypes = false)
@@ -2251,15 +2537,14 @@ proc instantiateCreateFlowVarCall(c: PContext; t: PType;
   let sym = magicsys.getCompilerProc(c.graph, "nimCreateFlowVar")
   if sym == nil:
     localError(c.config, info, "system needs: nimCreateFlowVar")
-  var bindings: TIdTable
-  initIdTable(bindings)
+  var bindings = initTypeMapping()
   bindings.idTablePut(sym.ast[genericParamsPos][0].typ, t)
   result = c.semGenerateInstance(c, sym, bindings, info)
   # since it's an instantiation, we unmark it as a compilerproc. Otherwise
   # codegen would fail:
   if sfCompilerProc in result.flags:
     result.flags.excl {sfCompilerProc, sfExportc, sfImportc}
-    result.loc.r = ""
+    result.loc.snippet = ""
 
 proc setMs(n: PNode, s: PSym): PNode =
   result = n
@@ -2282,9 +2567,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P
   of mAddr:
     markUsed(c, n.info, s)
     checkSonsLen(n, 2, c.config)
-    result[0] = newSymNode(s, n[0].info)
-    result[1] = semAddrArg(c, n[1])
-    result.typ = makePtrType(c, result[1].typ)
+    result = semAddr(c, n[1])
   of mTypeOf:
     markUsed(c, n.info, s)
     result = semTypeOf(c, n)
@@ -2392,12 +2675,16 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags; expectedType: P
     markUsed(c, n.info, s)
     result = semSizeof(c, setMs(n, s))
   of mArrToSeq, mOpenArrayToSeq:
-    if n.len == 2 and expectedType != nil and (
+    if expectedType != nil and (
         let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
         expected.kind in {tySequence, tyOpenArray}):
       # seq type inference
-      var arrayType = newType(tyOpenArray, nextTypeId(c.idgen), expected.owner)
+      var arrayType = newType(tyOpenArray, c.idgen, expected.owner)
       arrayType.rawAddSon(expected[0])
+      if n[0].kind == nkSym and sfFromGeneric in n[0].sym.flags:
+        # may have been resolved to `@`[empty] at some point,
+        # reset to `@` to deal with this
+        n[0] = newSymNode(n[0].sym.instantiatedFrom, n[0].info)
       n[1] = semExpr(c, n[1], flags, arrayType)
     result = semDirectOp(c, n, flags, expectedType)
   else:
@@ -2407,9 +2694,10 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   # If semCheck is set to false, ``when`` will return the verbatim AST of
   # the correct branch. Otherwise the AST will be passed through semStmt.
   result = nil
+  let flags = if semCheck: {efWantStmt} else: {}
 
   template setResult(e: untyped) =
-    if semCheck: result = semExpr(c, e) # do not open a new scope!
+    if semCheck: result = semExpr(c, e, flags) # do not open a new scope!
     else: result = e
 
   # Check if the node is "when nimvm"
@@ -2419,15 +2707,18 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
   #   ...
   var whenNimvm = false
   var typ = commonTypeBegin
-  if n.len == 2 and n[0].kind == nkElifBranch and
-      n[1].kind == nkElse:
-    let exprNode = n[0][0]
+  if n.len in 1..2 and n[0].kind == nkElifBranch and (
+      n.len == 1 or n[1].kind == nkElse):
+    var exprNode = n[0][0]
+    if exprNode.kind == nkOpenSym:
+      exprNode = exprNode[0]
     if exprNode.kind == nkIdent:
       whenNimvm = lookUp(c, exprNode).magic == mNimvm
     elif exprNode.kind == nkSym:
       whenNimvm = exprNode.sym.magic == mNimvm
     if whenNimvm: n.flags.incl nfLL
 
+  var cannotResolve = false
   for i in 0..<n.len:
     var it = n[i]
     case it.kind
@@ -2435,9 +2726,22 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
       checkSonsLen(it, 2, c.config)
       if whenNimvm:
         if semCheck:
-          it[1] = semExpr(c, it[1])
+          it[1] = semExpr(c, it[1], flags)
           typ = commonType(c, typ, it[1].typ)
         result = n # when nimvm is not elimited until codegen
+      elif c.inGenericContext > 0:
+        let e = semExprWithType(c, it[0])
+        if e.typ.kind == tyFromExpr:
+          it[0] = makeStaticExpr(c, e)
+          cannotResolve = true
+        else:
+          it[0] = forceBool(c, e)
+          let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+          if val == nil or val.kind != nkIntLit:
+            cannotResolve = true
+          elif not cannotResolve and val.intVal != 0 and result == nil:
+            setResult(it[1])
+            return # we're not in nimvm and we already have a result
       else:
         let e = forceBool(c, semConstExpr(c, it[0]))
         if e.kind != nkIntLit:
@@ -2449,16 +2753,27 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
           return # we're not in nimvm and we already have a result
     of nkElse, nkElseExpr:
       checkSonsLen(it, 1, c.config)
-      if result == nil or whenNimvm:
+      if cannotResolve:
+        discard
+      elif result == nil or whenNimvm:
         if semCheck:
-          it[0] = semExpr(c, it[0])
+          it[0] = semExpr(c, it[0], flags)
           typ = commonType(c, typ, it[0].typ)
+          if typ != nil and typ.kind != tyUntyped:
+            it[0] = fitNode(c, typ, it[0], it[0].info)
         if result == nil:
           result = it[0]
     else: illFormedAst(n, c.config)
+  if cannotResolve:
+    result = semGenericStmt(c, n)
+    result.typ = makeTypeFromExpr(c, result.copyTree)
+    return
   if result == nil:
     result = newNodeI(nkEmpty, n.info)
-  if whenNimvm: result.typ = typ
+  if whenNimvm:
+    result.typ = typ
+    if n.len == 1:
+      result.add(newTree(nkElse, newNode(nkStmtList)))
 
 proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
   result = newNodeI(nkCurly, n.info)
@@ -2480,34 +2795,39 @@ proc semSetConstr(c: PContext, n: PNode, expectedType: PType = nil): PNode =
     # only semantic checking for all elements, later type checking:
     var typ: PType = nil
     for i in 0..<n.len:
+      let doSetType = typ == nil
       if isRange(n[i]):
         checkSonsLen(n[i], 3, c.config)
-        n[i][1] = semExprWithType(c, n[i][1], {}, expectedElementType)
-        n[i][2] = semExprWithType(c, n[i][2], {}, expectedElementType)
-        if typ == nil:
+        n[i][1] = semExprWithType(c, n[i][1], {efTypeAllowed}, expectedElementType)
+        n[i][2] = semExprWithType(c, n[i][2], {efTypeAllowed}, expectedElementType)
+        if doSetType:
           typ = skipTypes(n[i][1].typ,
                           {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-          if expectedElementType == nil:
-            expectedElementType = typ
         n[i].typ = n[i][2].typ # range node needs type too
       elif n[i].kind == nkRange:
         # already semchecked
-        if typ == nil:
+        if doSetType:
           typ = skipTypes(n[i][0].typ,
                           {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-          if expectedElementType == nil:
-            expectedElementType = typ
       else:
-        n[i] = semExprWithType(c, n[i], {}, expectedElementType)
-        if typ == nil:
+        n[i] = semExprWithType(c, n[i], {efTypeAllowed}, expectedElementType)
+        if doSetType:
           typ = skipTypes(n[i].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink})
-          if expectedElementType == nil:
-            expectedElementType = typ
-    if not isOrdinalType(typ, allowEnumWithHoles=true):
-      localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
-      typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
-    elif lengthOrd(c.config, typ) > MaxSetElements:
-      typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
+      if doSetType:
+        if not isOrdinalType(typ, allowEnumWithHoles=true):
+          localError(c.config, n.info, errOrdinalTypeExpected % typeToString(typ, preferDesc))
+          typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
+        elif isIntLit(typ):
+          # set of int literal, use a default range smaller than the max range
+          typ = makeRangeType(c, 0, DefaultSetElements-1, n.info)
+        elif lengthOrd(c.config, typ) > MaxSetElements:
+          message(c.config, n.info, warnAboveMaxSizeSet, "type '" &
+            typeToString(typ, preferDesc) & "' is too big to be a `set` element, " &
+            "assuming a range of 0.." & $(MaxSetElements - 1) &
+            ", explicitly write this range to get rid of warning")
+          typ = makeRangeType(c, 0, MaxSetElements-1, n.info)
+        if expectedElementType == nil:
+          expectedElementType = typ
     addSonSkipIntLit(result.typ, typ, c.idgen)
     for i in 0..<n.len:
       var m: PNode
@@ -2589,13 +2909,19 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType
     # can check if field name matches expected type here
     let expectedElemType = if expected != nil: expected[i] else: nil
     n[i][1] = semExprWithType(c, n[i][1], {}, expectedElemType)
+    if expectedElemType != nil and
+        (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
+      # hasEmpty/nil check is to not break existing code like
+      # `const foo = [(1, {}), (2, {false})]`,
+      # `const foo = if true: (0, nil) else: (1, new(int))`
+      n[i][1] = fitNode(c, expectedElemType, n[i][1], n[i][1].info)
 
     if n[i][1].typ.kind == tyTypeDesc:
       localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.")
       n[i][1].typ = errorType(c)
 
     var f = newSymS(skField, n[i][0], c)
-    f.typ = skipIntLit(n[i][1].typ, c.idgen)
+    f.typ = skipIntLit(n[i][1].typ.skipTypes({tySink}), c.idgen)
     f.position = i
     rawAddSon(typ, f.typ)
     typ.n.add newSymNode(f)
@@ -2615,7 +2941,13 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags; expectedT
   for i in 0..<n.len:
     let expectedElemType = if expected != nil: expected[i] else: nil
     n[i] = semExprWithType(c, n[i], {}, expectedElemType)
-    addSonSkipIntLit(typ, n[i].typ, c.idgen)
+    if expectedElemType != nil and
+        (expectedElemType.kind != tyNil and not hasEmpty(expectedElemType)):
+      # hasEmpty/nil check is to not break existing code like
+      # `const foo = [(1, {}), (2, {false})]`,
+      # `const foo = if true: (0, nil) else: (1, new(int))`
+      n[i] = fitNode(c, expectedElemType, n[i], n[i].info)
+    addSonSkipIntLit(typ, n[i].typ.skipTypes({tySink}), c.idgen)
   result.typ = typ
 
 include semobjconstr
@@ -2623,6 +2955,8 @@ include semobjconstr
 proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
   result = n
   inc(c.p.nestedBlockCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = false
   checkSonsLen(n, 2, c.config)
   openScope(c) # BUGFIX: label is in the scope of block!
   if n[0].kind != nkEmpty:
@@ -2640,6 +2974,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = ni
   if isEmptyType(n.typ): n.transitionSonsKind(nkBlockStmt)
   else: n.transitionSonsKind(nkBlockExpr)
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedBlockCounter)
 
 proc semExportExcept(c: PContext, n: PNode): PNode =
@@ -2667,7 +3002,7 @@ proc semExport(c: PContext, n: PNode): PNode =
   result = newNodeI(nkExportStmt, n.info)
   for i in 0..<n.len:
     let a = n[i]
-    var o: TOverloadIter
+    var o: TOverloadIter = default(TOverloadIter)
     var s = initOverloadIter(o, c, a)
     if s == nil:
       localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a))
@@ -2703,7 +3038,7 @@ proc semExport(c: PContext, n: PNode): PNode =
 
 proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   var tupexp = semTuplePositionsConstr(c, n, flags, expectedType)
-  var isTupleType: bool
+  var isTupleType: bool = false
   if tupexp.len > 0: # don't interpret () as type
     isTupleType = tupexp[0].typ.kind == tyTypeDesc
     # check if either everything or nothing is tyTypeDesc
@@ -2717,24 +3052,50 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PTyp
   else:
     result = tupexp
 
-proc shouldBeBracketExpr(n: PNode): bool =
+proc isExplicitGenericCall(c: PContext, n: PNode): bool =
+  ## checks if a call node `n` is a routine call with explicit generic params
+  ## 
+  ## the callee node needs to be either an nkBracketExpr or a call to a
+  ## symchoice of `[]` in which case it will be transformed into nkBracketExpr
+  ## 
+  ## the LHS of the bracket expr has to either be a symchoice or resolve to
+  ## a routine symbol
+  template checkCallee(n: PNode) =
+    # check subscript LHS, `n` must be mutable
+    if isSymChoice(n):
+      result = true
+    else:
+      let s = qualifiedLookUp(c, n, {})
+      if s != nil and s.kind in routineKinds:
+        result = true
+        n = semSymGenericInstantiation(c, n, s)
   assert n.kind in nkCallKinds
+  result = false
   let a = n[0]
-  if a.kind in nkCallKinds:
+  case a.kind
+  of nkBracketExpr:
+    checkCallee(a[0])
+  of nkCallKinds:
     let b = a[0]
     if b.kind in nkSymChoices:
-      for i in 0..<b.len:
-        if b[i].kind == nkSym and b[i].sym.magic == mArrGet:
-          let be = newNodeI(nkBracketExpr, n.info)
+      let name = b.getPIdent
+      if name != nil and name.s == "[]":
+        checkCallee(a[1])
+        if result:
+          # transform callee into normal bracket expr, only on success
+          let be = newNodeI(nkBracketExpr, a.info)
           for i in 1..<a.len: be.add(a[i])
           n[0] = be
-          return true
+  else:
+    result = false
 
 proc asBracketExpr(c: PContext; n: PNode): PNode =
   proc isGeneric(c: PContext; n: PNode): bool =
     if n.kind in {nkIdent, nkAccQuoted}:
       let s = qualifiedLookUp(c, n, {})
       result = s != nil and isGenericRoutineStrict(s)
+    else:
+      result = false
 
   assert n.kind in nkCallKinds
   if n.len > 1 and isGeneric(c, n[1]):
@@ -2747,6 +3108,18 @@ proc asBracketExpr(c: PContext; n: PNode): PNode =
           return result
   return nil
 
+proc isOpenArraySym(x: PNode): bool =
+  var x = x
+  while true:
+    case x.kind
+    of {nkAddr, nkHiddenAddr}:
+      x = x[0]
+    of {nkHiddenStdConv, nkHiddenDeref}:
+      x = x[1]
+    else:
+      break
+  result = x.kind == nkSym
+
 proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) =
   # This takes care of complicated signatures such as:
   # proc foo(a: int, b = a)
@@ -2764,11 +3137,18 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
   # duty is activated by returning a non-nil value. The caller is responsible
   # for replacing the input to the function with the returned non-nil value.
   # (which is the hoisted symbol)
-  if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym:
+  if defExpr.kind == nkSym and defExpr.sym.kind == skParam and
+      (defExpr.sym.owner == call[0].sym or
+        # symbol was resolved before proc was instantiated:
+        (sfFromGeneric in call[0].sym.flags and
+          defExpr.sym.owner == call[0].sym.instantiatedFrom)):
     let paramPos = defExpr.sym.position + 1
 
-    if call[paramPos].kind != nkSym:
-      let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), nextSymId c.idgen,
+    if call[paramPos].skipAddr.kind != nkSym and not (
+      skipTypes(call[paramPos].typ, abstractVar).kind in {tyOpenArray, tyVarargs} and
+      isOpenArraySym(call[paramPos])
+    ):
+      let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.idgen,
                                  c.p.owner, letSection.info, c.p.owner.options)
       hoistedVarSym.typ = call[paramPos].typ
 
@@ -2779,7 +3159,7 @@ proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode)
 
       call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym
 
-    # arg we refer to is a sym, wether introduced by hoisting or not doesn't matter, we simply reuse it
+    # arg we refer to is a sym, whether introduced by hoisting or not doesn't matter, we simply reuse it
     defExpr = call[paramPos]
   else:
     for i in 0..<defExpr.safeLen:
@@ -2793,8 +3173,8 @@ proc getNilType(c: PContext): PType =
     result.align = c.config.target.ptrSize.int16
     c.nilTypeCache = result
 
-proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
-  var o: TOverloadIter
+proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym; flags: TExprFlags): PNode =
+  var o: TOverloadIter = default(TOverloadIter)
   var i = 0
   var a = initOverloadIter(o, c, n)
   while a != nil:
@@ -2806,7 +3186,7 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode =
   if i <= 1:
     if sfGenSym notin s.flags:
       result = newSymNode(s, info)
-      markUsed(c, info, s)
+      markUsed(c, info, s, efInCall notin flags)
       onUse(info, s)
     else:
       result = n
@@ -2827,6 +3207,55 @@ proc semPragmaStmt(c: PContext; n: PNode) =
   else:
     pragma(c, c.p.owner, n, stmtPragmas, true)
 
+proc resolveIdentToSym(c: PContext, n: PNode, resultNode: var PNode,
+                       flags: TExprFlags, expectedType: PType): PSym =
+  # result is nil on error or if a node that can't produce a sym is resolved
+  let ident = considerQuotedIdent(c, n)
+  var filter = {low(TSymKind)..high(TSymKind)}
+  if efNoEvaluateGeneric in flags or expectedType != nil:
+    # `a[...]` where `a` is a module or package is not possible
+    filter.excl {skModule, skPackage}
+  let includePureEnum = expectedType != nil and
+    expectedType.skipTypes(abstractRange-{tyDistinct}).kind == tyEnum
+  let candidates = lookUpCandidates(c, ident, filter,
+    includePureEnum = includePureEnum)
+  if candidates.len == 0:
+    result = errorUndeclaredIdentifierHint(c, ident, n.info)
+  elif candidates.len == 1 or {efNoEvaluateGeneric, efInCall} * flags != {}:
+    # unambiguous, or we don't care about ambiguity
+    result = candidates[0]
+  else:
+    # ambiguous symbols have 1 last chance as a symchoice
+    var choice = newNodeIT(nkClosedSymChoice, n.info, newTypeS(tyNone, c))
+    for cand in candidates:
+      case cand.kind
+      of skModule, skPackage:
+        discard
+      of skType:
+        choice.add newSymNodeTypeDesc(cand, c.idgen, n.info)
+      else:
+        choice.add newSymNode(cand, n.info)
+    if choice.len == 0:
+      # we know candidates.len > 1, we just couldn't put any in a symchoice
+      errorUseQualifier(c, n.info, candidates)
+      return nil
+    resolveSymChoice(c, choice, flags, expectedType)
+    # choice.len == 1 can be true here but as long as it's a symchoice
+    # it's still not resolved
+    if isSymChoice(choice):
+      result = nil
+      if efAllowSymChoice in flags:
+        resultNode = choice
+      else:
+        errorUseQualifier(c, n.info, candidates)
+    else:
+      if choice.kind == nkSym:
+        result = choice.sym
+      else:
+        # resolution could have generated nkHiddenStdConv etc
+        resultNode = semExpr(c, choice, flags, expectedType)
+        result = nil
+
 proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType = nil): PNode =
   when defined(nimCompilerStacktraceHints):
     setFrameMsg c.config$n.info & " " & $n.kind
@@ -2836,7 +3265,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     defer:
       if isCompilerDebug():
         echo ("<", c.config$n.info, n, ?.result.typ)
-
   template directLiteral(typeKind: TTypeKind) =
     if result.typ == nil:
       if expectedType != nil and (
@@ -2848,27 +3276,27 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
         result.typ = getSysType(c.graph, n.info, typeKind)
 
   result = n
+  when defined(nimsuggest):
+    var expandStarted = false
+    if c.config.ideCmd == ideExpand and not c.config.expandProgress and
+        ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
+          n.info.exactEquals(c.config.expandPosition)) or
+         (n.kind in {nkCall, nkCommand} and
+          n[0].info.exactEquals(c.config.expandPosition))):
+      expandStarted = true
+      c.config.expandProgress = true
+      if c.config.expandLevels == 0:
+        c.config.expandNodeResult = $n
+        suggestQuit()
+
   if c.config.cmd == cmdIdeTools: suggestExpr(c, n)
   if nfSem in n.flags: return
   case n.kind
   of nkIdent, nkAccQuoted:
-    var s: PSym
-    if expectedType != nil and (
-        let expected = expectedType.skipTypes(abstractRange-{tyDistinct});
-        expected.kind == tyEnum):
-      let nameId = considerQuotedIdent(c, n).id
-      for f in expected.n:
-        if f.kind == nkSym and f.sym.name.id == nameId:
-          s = f.sym
-          break
+    let s = resolveIdentToSym(c, n, result, flags, expectedType)
     if s == nil:
-      let checks = if efNoEvaluateGeneric in flags:
-          {checkUndeclared, checkPureEnumFields}
-        elif efInCall in flags:
-          {checkUndeclared, checkModule, checkPureEnumFields}
-        else:
-          {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields}
-      s = qualifiedLookUp(c, n, checks)
+      # resolveIdentToSym either errored or gave a result node
+      return
     if c.matchedConcept == nil: semCaptureSym(s, c.p.owner)
     case s.kind
     of skProc, skFunc, skMethod, skConverter, skIterator:
@@ -2882,23 +3310,35 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
       if optOwnedRefs in c.config.globalOptions:
         result.typ = makeVarType(c, result.typ, tyOwned)
     of skEnumField:
-      result = enumFieldSymChoice(c, n, s)
+      result = enumFieldSymChoice(c, n, s, flags)
     else:
       result = semSym(c, n, s, flags)
-    if expectedType != nil and isSymChoice(result):
-      result = fitNode(c, expectedType, result, n.info)
-      if result.kind == nkSym:
-        result = semSym(c, result, result.sym, flags)
+    if isSymChoice(result):
+      result = semSymChoice(c, result, flags, expectedType)
+  of nkClosedSymChoice, nkOpenSymChoice:
+    result = semSymChoice(c, n, flags, expectedType)
   of nkSym:
+    let s = n.sym
+    if nfDisabledOpenSym in n.flags:
+      let override = genericsOpenSym in c.features
+      let res = semOpenSym(c, n, flags, expectedType,
+        warnDisabled = not override)
+      if res != nil:
+        assert override
+        return res
     # because of the changed symbol binding, this does not mean that we
     # don't have to check the symbol for semantics here again!
-    result = semSym(c, n, n.sym, flags)
+    result = semSym(c, n, s, flags)
+  of nkOpenSym:
+    assert n.len == 1
+    let inner = n[0]
+    result = semOpenSym(c, inner, flags, expectedType)
   of nkEmpty, nkNone, nkCommentStmt, nkType:
     discard
   of nkNilLit:
     if result.typ == nil:
       result.typ = getNilType(c)
-      if expectedType != nil:
+      if expectedType != nil and expectedType.kind notin {tyUntyped, tyTyped}:
         var m = newCandidate(c, result.typ)
         if typeRel(m, expectedType, result.typ) >= isSubtype:
           result.typ = expectedType
@@ -2910,7 +3350,6 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
           expected.kind in {tyInt..tyInt64,
             tyUInt..tyUInt64,
             tyFloat..tyFloat128}):
-        result.typ = expected
         if expected.kind in {tyFloat..tyFloat128}:
           n.transitionIntToFloatKind(nkFloatLit)
         changeType(c, result, expectedType, check=true)
@@ -2950,7 +3389,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     result = semFieldAccess(c, n, flags)
     if result.kind == nkDotCall:
       result.transitionSonsKind(nkCall)
-      result = semExpr(c, result, flags)
+      result = semExpr(c, result, flags, expectedType)
   of nkBind:
     message(c.config, n.info, warnDeprecated, "bind is deprecated")
     result = semExpr(c, n[0], flags, expectedType)
@@ -2959,7 +3398,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
       let modifier = n.modifierTypeKindOfNode
       if modifier != tyNone:
         var baseType = semExpr(c, n[0]).typ.skipTypes({tyTypeDesc})
-        result.typ = c.makeTypeDesc(c.newTypeWithSons(modifier, @[baseType]))
+        result.typ = c.makeTypeDesc(newTypeS(modifier, c, baseType))
         return
     var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc})
     result.typ = makeTypeDesc(c, typ)
@@ -2975,33 +3414,28 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     c.isAmbiguous = false
     var s = qualifiedLookUp(c, n[0], mode)
     if s != nil:
-      #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr:
-      #  pretty.checkUse(n[0][1].info, s)
       case s.kind
       of skMacro, skTemplate:
         result = semDirectOp(c, n, flags, expectedType)
       of skType:
         # XXX think about this more (``set`` procs)
         let ambig = c.isAmbiguous
-        if not (n[0].kind in {nkClosedSymChoice, nkOpenSymChoice, nkIdent} and ambig) and n.len == 2:
-          result = semConv(c, n, expectedType)
-        elif ambig and n.len == 1:
-          errorUseQualifier(c, n.info, s)
+        if not (n[0].kind in nkSymChoices + {nkIdent, nkDotExpr} and ambig) and n.len == 2:
+          result = semConv(c, n, flags, expectedType)
         elif n.len == 1:
-          result = semObjConstr(c, n, flags, expectedType)
+          if ambig:
+            errorUseQualifier(c, n.info, s)
+          else:
+            result = semObjConstr(c, n, flags, expectedType)
         elif s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
         else: result = semMagic(c, n, s, flags, expectedType)
       of skProc, skFunc, skMethod, skConverter, skIterator:
-        if s.magic == mNone: result = semDirectOp(c, n, flags)
+        if s.magic == mNone: result = semDirectOp(c, n, flags, expectedType)
         else: result = semMagic(c, n, s, flags, expectedType)
       else:
         #liMessage(n.info, warnUser, renderTree(n));
         result = semIndirectOp(c, n, flags, expectedType)
-    elif (n[0].kind == nkBracketExpr or shouldBeBracketExpr(n)) and
-        isSymChoice(n[0][0]):
-      # indirectOp can deal with explicit instantiations; the fixes
-      # the 'newSeq[T](x)' bug
-      setGenericParams(c, n[0])
+    elif isExplicitGenericCall(c, n): # this modifies `n` if true
       result = semDirectOp(c, n, flags, expectedType)
     elif nfDotField in n.flags:
       result = semDirectOp(c, n, flags, expectedType)
@@ -3062,15 +3496,15 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     of paTupleFields: result = semTupleFieldsConstr(c, n, flags, expectedType)
     of paSingle: result = semExpr(c, n[0], flags, expectedType)
   of nkCurly: result = semSetConstr(c, n, expectedType)
-  of nkBracket: result = semArrayConstr(c, n, flags, expectedType)
+  of nkBracket:
+    result = semArrayConstr(c, n, flags, expectedType)
   of nkObjConstr: result = semObjConstr(c, n, flags, expectedType)
   of nkLambdaKinds: result = semProcAux(c, n, skProc, lambdaPragmas, flags)
-  of nkDerefExpr: result = semDeref(c, n)
+  of nkDerefExpr: result = semDeref(c, n, flags)
   of nkAddr:
     result = n
     checkSonsLen(n, 1, c.config)
-    result[0] = semAddrArg(c, n[0])
-    result.typ = makePtrType(c, result[0].typ)
+    result = semAddr(c, n[0])
   of nkHiddenAddr, nkHiddenDeref:
     checkSonsLen(n, 1, c.config)
     n[0] = semExpr(c, n[0], flags, expectedType)
@@ -3090,12 +3524,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     considerGenSyms(c, n)
   of nkTableConstr:
     result = semTableConstr(c, n, expectedType)
-  of nkClosedSymChoice, nkOpenSymChoice:
-    # handling of sym choices is context dependent
-    # the node is left intact for now
-    discard
   of nkStaticExpr: result = semStaticExpr(c, n[0], expectedType)
-  of nkAsgn: result = semAsgn(c, n)
+  of nkAsgn, nkFastAsgn: result = semAsgn(c, n)
   of nkBlockStmt, nkBlockExpr: result = semBlock(c, n, flags, expectedType)
   of nkStmtList, nkStmtListExpr: result = semStmtList(c, n, flags, expectedType)
   of nkRaiseStmt: result = semRaise(c, n)
@@ -3153,7 +3583,9 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
   of nkDefer:
     if c.currentScope == c.topLevelScope:
       localError(c.config, n.info, "defer statement not supported at top level")
+    openScope(c)
     n[0] = semExpr(c, n[0])
+    closeScope(c)
     if not n[0].typ.isEmptyType and not implicitlyDiscardable(n[0]):
       localError(c.config, n.info, "'defer' takes a 'void' expression")
     #localError(c.config, n.info, errGenerated, "'defer' not allowed in this context")
@@ -3174,3 +3606,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}, expectedType: PType
     localError(c.config, n.info, "invalid expression: " &
                renderTree(n, {renderNoComments}))
   if result != nil: incl(result.flags, nfSem)
+
+  when defined(nimsuggest):
+    if expandStarted:
+      c.config.expandNodeResult = $result
+      suggestQuit()
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 93184c568..874055cdc 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -64,10 +64,12 @@ type
 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
+    # either 'tup[i]' or 'field' is valid
+    var fc = TFieldInstCtx(
+      c: c.c,
+      field: typ.sym,
+      replaceByFieldName: c.m == mFieldPairs
+    )
     openScope(c.c)
     inc c.c.inUnrolledContext
     let body = instFieldLoopBody(fc, lastSon(forLoop), forLoop)
@@ -109,7 +111,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
   var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true"))
   if trueSymbol == nil:
     localError(c.config, n.info, "system needs: 'true'")
-    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info)
+    trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), c.idgen, getCurrOwner(c), n.info)
     trueSymbol.typ = getSysType(c.graph, n.info, tyBool)
 
   result[0] = newSymNode(trueSymbol, n.info)
@@ -133,29 +135,31 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
       typeMismatch(c.config, calli.info, tupleTypeA, tupleTypeB, calli)
 
   inc(c.p.nestedLoopCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
   if tupleTypeA.kind == tyTuple:
     var loopBody = n[^1]
     for i in 0..<tupleTypeA.len:
       openScope(c)
-      var fc: TFieldInstCtx
-      fc.tupleType = tupleTypeA
-      fc.tupleIndex = i
-      fc.c = c
-      fc.replaceByFieldName = m == mFieldPairs
+      var fc = TFieldInstCtx(
+          tupleType: tupleTypeA,
+          tupleIndex: i,
+          c: c,
+          replaceByFieldName: m == mFieldPairs
+      )
       var body = instFieldLoopBody(fc, loopBody, n)
       inc c.inUnrolledContext
       stmts.add(semStmt(c, body, {}))
       dec c.inUnrolledContext
       closeScope(c)
   else:
-    var fc: TFieldsCtx
-    fc.m = m
-    fc.c = c
+    var fc = TFieldsCtx(m: m, c: c)
     var t = tupleTypeA
     while t.kind == tyObject:
       semForObjectFields(fc, t.n, n, stmts)
-      if t[0] == nil: break
-      t = skipTypes(t[0], skipPtrs)
+      if t.baseClass == nil: break
+      t = skipTypes(t.baseClass, skipPtrs)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
   # for TR macros this 'while true: ...; break' loop is pretty bad, so
   # we avoid it now if we can:
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index f627bffc7..80144ccc0 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -11,10 +11,11 @@
 # and evaluation phase
 
 import
-  strutils, options, ast, trees, nimsets,
-  platform, math, msgs, idents, renderer, types,
-  commands, magicsys, modulegraphs, strtabs, lineinfos
+  options, ast, trees, nimsets,
+  platform, msgs, idents, renderer, types,
+  commands, magicsys, modulegraphs, lineinfos, wordrecg
 
+import std/[strutils, math, strtabs]
 from system/memory import nimCStrLen
 
 when defined(nimPreviewSlimSystem):
@@ -22,13 +23,13 @@ when defined(nimPreviewSlimSystem):
 
 proc errorType*(g: ModuleGraph): PType =
   ## creates a type representing an error state
-  result = newType(tyError, nextTypeId(g.idgen), g.owners[^1])
+  result = newType(tyError, g.idgen, g.owners[^1])
   result.flags.incl tfCheckedForDestructor
 
 proc getIntLitTypeG(g: ModuleGraph; literal: PNode; idgen: IdGenerator): PType =
   # we cache some common integer literal types for performance:
   let ti = getSysType(g, literal.info, tyInt)
-  result = copyType(ti, nextTypeId(idgen), ti.owner)
+  result = copyType(ti, idgen, ti.owner)
   result.n = literal
 
 proc newIntNodeT*(intVal: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
@@ -65,24 +66,34 @@ proc foldAdd(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   let res = a + b
   if checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
 proc foldSub(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   let res = a - b
   if checkInRange(g.config, n, res):
     result = newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
 proc foldUnarySub(a: Int128, n: PNode; idgen: IdGenerator, g: ModuleGraph): PNode =
   if a != firstOrd(g.config, n.typ):
     result = newIntNodeT(-a, n, idgen, g)
+  else:
+    result = nil
 
 proc foldAbs(a: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   if a != firstOrd(g.config, n.typ):
     result = newIntNodeT(abs(a), n, idgen, g)
+  else:
+    result = nil
 
 proc foldMul(a, b: Int128, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   let res = a * b
   if checkInRange(g.config, n, res):
     return newIntNodeT(res, n, idgen, g)
+  else:
+    result = nil
 
 proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
   # because $ has the param ordinal[T], `a` is not necessarily an enum, but an
@@ -94,6 +105,7 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
   of tyChar:
     result = $chr(toInt64(x) and 0xff)
   of tyEnum:
+    result = ""
     var n = t.n
     for i in 0..<n.len:
       if n[i].kind != nkSym: internalError(g.config, a.info, "ordinalValToString")
@@ -110,10 +122,10 @@ proc ordinalValToString*(a: PNode; g: ModuleGraph): string =
     result = $x
 
 proc isFloatRange(t: PType): bool {.inline.} =
-  result = t.kind == tyRange and t[0].kind in {tyFloat..tyFloat128}
+  result = t.kind == tyRange and t.elementType.kind in {tyFloat..tyFloat128}
 
 proc isIntRange(t: PType): bool {.inline.} =
-  result = t.kind == tyRange and t[0].kind in {
+  result = t.kind == tyRange and t.elementType.kind in {
       tyInt..tyInt64, tyUInt8..tyUInt32}
 
 proc pickIntRange(a, b: PType): PType =
@@ -230,7 +242,13 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
   of mMulF64: result = newFloatNodeT(getFloat(a) * getFloat(b), n, g)
   of mDivF64:
     result = newFloatNodeT(getFloat(a) / getFloat(b), n, g)
-  of mIsNil: result = newIntNodeT(toInt128(ord(a.kind == nkNilLit)), n, idgen, g)
+  of mIsNil:
+    let val = a.kind == nkNilLit or
+      # nil closures have the value (nil, nil)
+      (a.typ != nil and skipTypes(a.typ, abstractRange).kind == tyProc and
+        a.kind == nkTupleConstr and a.len == 2 and
+        a[0].kind == nkNilLit and a[1].kind == nkNilLit)
+    result = newIntNodeT(toInt128(ord(val)), n, idgen, g)
   of mLtI, mLtB, mLtEnum, mLtCh:
     result = newIntNodeT(toInt128(ord(getOrdValue(a) < getOrdValue(b))), n, idgen, g)
   of mLeI, mLeB, mLeEnum, mLeCh:
@@ -289,11 +307,9 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P
   of mRepr:
     # BUGFIX: we cannot eval mRepr here for reasons that I forgot.
     discard
-  of mIntToStr, mInt64ToStr: result = newStrNodeT($(getOrdValue(a)), n, g)
   of mBoolToStr:
     if getOrdValue(a) == 0: result = newStrNodeT("false", n, g)
     else: result = newStrNodeT("true", n, g)
-  of mFloatToStr: result = newStrNodeT($getFloat(a), n, g)
   of mCStrToStr, mCharToStr:
     result = newStrNodeT(getStrOrChar(a), n, g)
   of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g)
@@ -346,7 +362,7 @@ proc magicCall(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
 
   var s = n[0].sym
   var a = getConstExpr(m, n[1], idgen, g)
-  var b, c: PNode
+  var b, c: PNode = nil
   if a == nil: return
   if n.len > 2:
     b = getConstExpr(m, n[2], idgen, g)
@@ -371,6 +387,11 @@ proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) =
     localError(g.config, n.info, "cannot convert " & $value &
                                     " to " & typeToString(n.typ))
 
+proc floatRangeCheck(n: PNode, value: BiggestFloat; g: ModuleGraph) =
+  if value < firstFloat(n.typ) or value > lastFloat(n.typ):
+    localError(g.config, n.info, "cannot convert " & $value &
+                                    " to " & typeToString(n.typ))
+
 proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode =
   let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc})
   let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc})
@@ -391,21 +412,25 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
     of tyBool, tyEnum: # xxx shouldn't we disallow `tyEnum`?
       result = a
       result.typ = n.typ
-    else: doAssert false, $srcTyp.kind
+    else:
+      raiseAssert $srcTyp.kind
   of tyInt..tyInt64, tyUInt..tyUInt64:
     case srcTyp.kind
     of tyFloat..tyFloat64:
       result = newIntNodeT(toInt128(getFloat(a)), n, idgen, g)
     of tyChar, tyUInt..tyUInt64, tyInt..tyInt64:
       var val = a.getOrdValue
-      if check: rangeCheck(n, val, g)
-      result = newIntNodeT(val, n, idgen, g)
       if dstTyp.kind in {tyUInt..tyUInt64}:
+        result = newIntNodeT(maskBytes(val, int getSize(g.config, dstTyp)), n, idgen, g)
         result.transitionIntKind(nkUIntLit)
+      else:
+        if check: rangeCheck(n, val, g)
+        result = newIntNodeT(val, n, idgen, g)
     else:
       result = a
       result.typ = n.typ
-    if check and result.kind in {nkCharLit..nkUInt64Lit}:
+    if check and result.kind in {nkCharLit..nkUInt64Lit} and
+          dstTyp.kind notin {tyUInt..tyUInt64}:
       rangeCheck(n, getInt(result), g)
   of tyFloat..tyFloat64:
     case srcTyp.kind
@@ -415,7 +440,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P
       result = a
       result.typ = n.typ
   of tyOpenArray, tyVarargs, tyProc, tyPointer:
-    discard
+    result = nil
   else:
     result = a
     result.typ = n.typ
@@ -442,21 +467,26 @@ proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNo
       result = x.sons[idx]
       if result.kind == nkExprColonExpr: result = result[1]
     else:
+      result = nil
       localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
   of nkBracket:
     idx -= toInt64(firstOrd(g.config, x.typ))
     if idx >= 0 and idx < x.len: result = x[int(idx)]
-    else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
+    else:
+      result = nil
+      localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
   of nkStrLit..nkTripleStrLit:
     result = newNodeIT(nkCharLit, x.info, n.typ)
     if idx >= 0 and idx < x.strVal.len:
       result.intVal = ord(x.strVal[int(idx)])
     else:
       localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n)
-  else: discard
+  else: result = nil
 
 proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   # a real field access; proc calls have already been transformed
+  result = nil
+  if n[1].kind != nkSym: return nil
   var x = getConstExpr(m, n[0], idgen, g)
   if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return
 
@@ -484,11 +514,89 @@ proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
 proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode =
   result = newSymNode(s, info)
   if s.typ.kind != tyTypeDesc:
-    result.typ = newType(tyTypeDesc, idgen.nextTypeId, s.owner)
+    result.typ = newType(tyTypeDesc, idgen, s.owner)
     result.typ.addSonSkipIntLit(s.typ, idgen)
   else:
     result.typ = s.typ
 
+proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
+  result = nil
+  var name = s.name.s
+  let prag = extractPragma(s)
+  if prag != nil:
+    for it in prag:
+      if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
+        let word = whichKeyword(it[0].ident)
+        if word in {wStrDefine, wIntDefine, wBoolDefine, wDefine}:
+          # should be processed in pragmas.nim already
+          if it[1].kind in {nkStrLit, nkRStrLit, nkTripleStrLit}:
+            name = it[1].strVal
+  if isDefined(g.config, name):
+    let str = g.config.symbols[name]
+    case s.magic
+    of mIntDefine:
+      try:
+        result = newIntNodeT(toInt128(str.parseInt), n, idgen, g)
+      except ValueError:
+        localError(g.config, s.info,
+          "{.intdefine.} const was set to an invalid integer: '" &
+            str & "'")
+    of mStrDefine:
+      result = newStrNodeT(str, n, g)
+    of mBoolDefine:
+      try:
+        result = newIntNodeT(toInt128(str.parseBool.int), n, idgen, g)
+      except ValueError:
+        localError(g.config, s.info,
+          "{.booldefine.} const was set to an invalid bool: '" &
+            str & "'")
+    of mGenericDefine:
+      let rawTyp = s.typ
+      # pretend we don't support distinct types
+      let typ = rawTyp.skipTypes(abstractVarRange-{tyDistinct})
+      try:
+        template intNode(value): PNode =
+          let val = toInt128(value)
+          rangeCheck(n, val, g)
+          newIntNodeT(val, n, idgen, g)
+        case typ.kind
+        of tyString, tyCstring:
+          result = newStrNodeT(str, n, g)
+        of tyInt..tyInt64:
+          result = intNode(str.parseBiggestInt)
+        of tyUInt..tyUInt64:
+          result = intNode(str.parseBiggestUInt)
+        of tyBool:
+          result = intNode(str.parseBool.int)
+        of tyEnum:
+          # compile time parseEnum
+          let ident = getIdent(g.cache, str)
+          for e in typ.n:
+            if e.kind != nkSym: internalError(g.config, "foldDefine for enum")
+            let es = e.sym
+            let match =
+              if es.ast.isNil:
+                es.name.id == ident.id
+              else:
+                es.ast.strVal == str
+            if match:
+              result = intNode(es.position)
+              break
+          if result.isNil:
+            raise newException(ValueError, "invalid enum value: " & str)
+        else:
+          localError(g.config, s.info, "unsupported type $1 for define '$2'" %
+            [name, typeToString(rawTyp)])
+      except ValueError as e:
+        localError(g.config, s.info,
+          "could not process define '$1' of type $2; $3" %
+            [name, typeToString(rawTyp), e.msg])
+    else: result = copyTree(s.astdef) # unreachable
+  else:
+    result = copyTree(s.astdef)
+    if result != nil:
+      result.info = n.info
+
 proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode =
   result = nil
   case n.kind
@@ -508,33 +616,12 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
       of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g)
       of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g)
       of mAppType: result = getAppType(n, g)
-      of mIntDefine:
-        if isDefined(g.config, s.name.s):
-          try:
-            result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseInt), n, idgen, g)
-          except ValueError:
-            localError(g.config, s.info,
-              "{.intdefine.} const was set to an invalid integer: '" &
-                g.config.symbols[s.name.s] & "'")
-        else:
-          result = copyTree(s.ast)
-      of mStrDefine:
-        if isDefined(g.config, s.name.s):
-          result = newStrNodeT(g.config.symbols[s.name.s], n, g)
-        else:
-          result = copyTree(s.ast)
-      of mBoolDefine:
-        if isDefined(g.config, s.name.s):
-          try:
-            result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseBool.int), n, idgen, g)
-          except ValueError:
-            localError(g.config, s.info,
-              "{.booldefine.} const was set to an invalid bool: '" &
-                g.config.symbols[s.name.s] & "'")
-        else:
-          result = copyTree(s.ast)
+      of mIntDefine, mStrDefine, mBoolDefine, mGenericDefine:
+        result = foldDefine(m, s, n, idgen, g)
       else:
-        result = copyTree(s.ast)
+        result = copyTree(s.astdef)
+        if result != nil:
+          result.info = n.info
     of skProc, skFunc, skMethod:
       result = n
     of skParam:
@@ -617,13 +704,10 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
     except DivByZeroDefect:
       localError(g.config, n.info, "division by zero")
   of nkAddr:
-    var a = getConstExpr(m, n[0], idgen, g)
-    if a != nil:
-      result = n
-      n[0] = a
+    result = nil # don't fold paths containing nkAddr
   of nkBracket, nkCurly:
     result = copyNode(n)
-    for i, son in n.pairs:
+    for son in n.items:
       var a = getConstExpr(m, son, idgen, g)
       if a == nil: return nil
       result.add a
@@ -647,7 +731,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
     # tuple constructor
     result = copyNode(n)
     if (n.len > 0) and (n[0].kind == nkExprColonExpr):
-      for i, expr in n.pairs:
+      for expr in n.items:
         let exprNew = copyNode(expr) # nkExprColonExpr
         exprNew.add expr[0]
         let a = getConstExpr(m, expr[1], idgen, g)
@@ -655,7 +739,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
         exprNew.add a
         result.add exprNew
     else:
-      for i, expr in n.pairs:
+      for expr in n.items:
         let a = getConstExpr(m, expr, idgen, g)
         if a == nil: return nil
         result.add a
@@ -666,6 +750,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
     if leValueConv(n[1], a) and leValueConv(a, n[2]):
       result = a              # a <= x and x <= b
       result.typ = n.typ
+    elif n.typ.kind in {tyUInt..tyUInt64}:
+      discard "don't check uints"
     else:
       localError(g.config, n.info,
         "conversion from $1 to $2 is invalid" %
@@ -687,7 +773,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode
   of nkCast:
     var a = getConstExpr(m, n[1], idgen, g)
     if a == nil: return
-    if n.typ != nil and n.typ.kind in NilableTypes:
+    if n.typ != nil and n.typ.kind in NilableTypes and
+        not (n.typ.kind == tyProc and a.typ.kind == tyProc):
       # we allow compile-time 'cast' for pointer types:
       result = a
       result.typ = n.typ
diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim
index d1c4acef6..2639aba6c 100644
--- a/compiler/semgnrc.nim
+++ b/compiler/semgnrc.nim
@@ -50,52 +50,83 @@ proc semGenericStmtScope(c: PContext, n: PNode,
   result = semGenericStmt(c, n, flags, ctx)
   closeScope(c)
 
-template macroToExpand(s): untyped =
-  s.kind in {skMacro, skTemplate} and (s.typ.len == 1 or sfAllUntyped in s.flags)
-
-template macroToExpandSym(s): untyped =
-  sfCustomPragma notin s.flags and s.kind in {skMacro, skTemplate} and
-    (s.typ.len == 1) and not fromDotExpr
-
 template isMixedIn(sym): bool =
   let s = sym
   s.name.id in ctx.toMixin or (withinConcept in flags and
                                s.magic == mNone and
                                s.kind in OverloadableSyms)
 
+template canOpenSym(s): bool =
+  {withinMixin, withinConcept} * flags == {withinMixin} and s.id notin ctx.toBind
+
 proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
                           ctx: var GenericCtx; flags: TSemGenericFlags,
+                          isAmbiguous: bool,
                           fromDotExpr=false): PNode =
+  result = nil
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
   incl(s.flags, sfUsed)
+  template maybeDotChoice(c: PContext, n: PNode, s: PSym, fromDotExpr: bool) =
+    if fromDotExpr:
+      result = symChoice(c, n, s, scForceOpen)
+      if result.kind == nkOpenSymChoice and result.len == 1:
+        result.transitionSonsKind(nkClosedSymChoice)
+    else:
+      result = symChoice(c, n, s, scOpen)
+      if canOpenSym(s):
+        if openSym in c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of skProc, skFunc, skMethod, skIterator, skConverter, skModule:
-    result = symChoice(c, n, s, scOpen)
-  of skTemplate:
-    if macroToExpandSym(s):
+  of skProc, skFunc, skMethod, skIterator, skConverter, skModule, skEnumField:
+    maybeDotChoice(c, n, s, fromDotExpr)
+  of skTemplate, skMacro:
+    # alias syntax, see semSym for skTemplate, skMacro
+    if sfNoalias notin s.flags and not fromDotExpr:
       onUse(n.info, s)
-      result = semTemplateExpr(c, n, s, {efNoSemCheck})
-      result = semGenericStmt(c, result, {}, ctx)
-    else:
-      result = symChoice(c, n, s, scOpen)
-  of skMacro:
-    if macroToExpandSym(s):
-      onUse(n.info, s)
-      result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+      case s.kind
+      of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
+      of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+      else: discard # unreachable
+      c.friendModules.add(s.owner.getModule)
       result = semGenericStmt(c, result, {}, ctx)
+      discard c.friendModules.pop()
     else:
-      result = symChoice(c, n, s, scOpen)
+      maybeDotChoice(c, n, s, fromDotExpr)
   of skGenericParam:
     if s.typ != nil and s.typ.kind == tyStatic:
       if s.typ.n != nil:
         result = s.typ.n
+      elif c.inGenericContext > 0 and withinConcept notin flags:
+        # don't leave generic param as identifier node in generic type,
+        # sigmatch will try to instantiate generic type AST without all params
+        # fine to give a symbol node a generic type here since
+        # we are in a generic context and `prepareNode` will be called
+        result = newSymNodeTypeDesc(s, c.idgen, n.info)
+        if canOpenSym(result.sym):
+          if openSym in c.features:
+            result = newOpenSym(result)
+          else:
+            result.flags.incl nfDisabledOpenSym
+            result.typ = nil
       else:
         result = n
     else:
       result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     onUse(n.info, s)
   of skParam:
     result = n
@@ -103,14 +134,41 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym,
   of skType:
     if (s.typ != nil) and
        (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}):
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        maybeDotChoice(c, n, s, fromDotExpr)
+        return
       result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
+    elif c.inGenericContext > 0 and withinConcept notin flags:
+      # don't leave generic param as identifier node in generic type,
+      # sigmatch will try to instantiate generic type AST without all params
+      # fine to give a symbol node a generic type here since
+      # we are in a generic context and `prepareNode` will be called
+      result = newSymNodeTypeDesc(s, c.idgen, n.info)
+      if canOpenSym(result.sym):
+        if openSym in c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     else:
       result = n
     onUse(n.info, s)
-  of skEnumField:
-    result = symChoice(c, n, s, scOpen)
   else:
     result = newSymNode(s, n.info)
+    if canOpenSym(result.sym):
+      if openSym in c.features:
+        result = newOpenSym(result)
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
     onUse(n.info, s)
 
 proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
@@ -118,7 +176,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
   result = n
   let ident = considerQuotedIdent(c, n)
   var amb = false
-  var s = searchInScopes(c, ident, amb).skipAlias(n, c.config)
+  var s = searchInScopes(c, ident, amb)
   if s == nil:
     s = strTableGet(c.pureEnumFields, ident)
     #if s != nil and contains(c.ambiguousSymbols, s.id):
@@ -132,7 +190,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags,
     elif s.isMixedIn:
       result = symChoice(c, n, s, scForceOpen)
     else:
-      result = semGenericStmtSymbol(c, n, s, ctx, flags)
+      result = semGenericStmtSymbol(c, n, s, ctx, flags, amb)
   # else: leave as nkIdent
 
 proc newDot(n, b: PNode): PNode =
@@ -141,36 +199,47 @@ proc newDot(n, b: PNode): PNode =
   result.add(b)
 
 proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags,
-                 ctx: var GenericCtx; isMacro: var bool): PNode =
+                 ctx: var GenericCtx; isMacro: var bool;
+                 inCall = false): PNode =
   assert n.kind == nkDotExpr
   semIdeForTemplateOrGenericCheck(c.config, n, ctx.cursorInBody)
 
   let luf = if withinMixin notin flags: {checkUndeclared, checkModule} else: {checkModule}
 
+  c.isAmbiguous = false
   var s = qualifiedLookUp(c, n, luf)
   if s != nil:
-    result = semGenericStmtSymbol(c, n, s, ctx, flags)
+    isMacro = s.kind in {skTemplate, skMacro}
+    result = semGenericStmtSymbol(c, n, s, ctx, flags, c.isAmbiguous)
   else:
     n[0] = semGenericStmt(c, n[0], flags, ctx)
     result = n
     let n = n[1]
     let ident = considerQuotedIdent(c, n)
-    var candidates = searchInScopesFilterBy(c, ident, routineKinds) # .skipAlias(n, c.config)
+    # could be type conversion if like a.T and not a.T()
+    let symKinds = if inCall: routineKinds else: routineKinds+{skType}
+    var candidates = searchInScopesFilterBy(c, ident, symKinds)
     if candidates.len > 0:
       let s = candidates[0] # XXX take into account the other candidates!
       isMacro = s.kind in {skTemplate, skMacro}
       if withinBind in flags or s.id in ctx.toBind:
-        result = newDot(result, symChoice(c, n, s, scClosed))
+        if s.kind == skType: # don't put types in sym choice
+          var ambig = false
+          if candidates.len > 1:
+            let s2 = searchInScopes(c, ident, ambig)
+          result = newDot(result, semGenericStmtSymbol(c, n, s, ctx, flags,
+            isAmbiguous = ambig, fromDotExpr = true))
+        else:
+          result = newDot(result, symChoice(c, n, s, scClosed))
       elif s.isMixedIn:
         result = newDot(result, symChoice(c, n, s, scForceOpen))
       else:
-        let syms = semGenericStmtSymbol(c, n, s, ctx, flags, fromDotExpr=true)
-        if syms.kind == nkSym:
-          let choice = symChoice(c, n, s, scForceOpen)
-          choice.transitionSonsKind(nkClosedSymChoice)
-          result = newDot(result, choice)
-        else:
-          result = newDot(result, syms)
+        var ambig = false
+        if s.kind == skType and candidates.len > 1:
+          discard searchInScopes(c, ident, ambig)
+        let syms = semGenericStmtSymbol(c, n, s, ctx, flags,
+          isAmbiguous = ambig, fromDotExpr = true)
+        result = newDot(result, syms)
 
 proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   let s = newSymS(skUnknown, getIdentNode(c, n), c)
@@ -178,6 +247,18 @@ proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) =
   styleCheckDef(c, n.info, s, kind)
   onDef(n.info, s)
 
+proc addTempDeclToIdents(c: PContext; n: PNode; kind: TSymKind; inCall: bool) =
+  case n.kind 
+  of nkIdent:
+    if inCall:
+      addTempDecl(c, n, kind)
+  of nkCallKinds:
+    for s in n:
+      addTempDeclToIdents(c, s, kind, true)  
+  else:
+    for s in n:
+      addTempDeclToIdents(c, s, kind, inCall)
+
 proc semGenericStmt(c: PContext, n: PNode,
                     flags: TSemGenericFlags, ctx: var GenericCtx): PNode =
   result = n
@@ -199,7 +280,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     #var s = qualifiedLookUp(c, n, luf)
     #if s != nil: result = semGenericStmtSymbol(c, n, s)
     # XXX for example: ``result.add`` -- ``add`` needs to be looked up here...
-    var dummy: bool
+    var dummy: bool = false
     result = fuzzyLookup(c, n, flags, ctx, dummy)
   of nkSym:
     let a = n.sym
@@ -224,7 +305,9 @@ proc semGenericStmt(c: PContext, n: PNode,
     # check if it is an expression macro:
     checkMinSonsLen(n, 1, c.config)
     let fn = n[0]
+    c.isAmbiguous = false
     var s = qualifiedLookUp(c, fn, {})
+    let ambig = c.isAmbiguous
     if s == nil and
         {withinMixin, withinConcept}*flags == {} and
         fn.kind in {nkIdent, nkAccQuoted} and
@@ -241,20 +324,17 @@ proc semGenericStmt(c: PContext, n: PNode,
                         else: scOpen
       let sc = symChoice(c, fn, s, whichChoice)
       case s.kind
-      of skMacro:
-        if macroToExpand(s) and sc.safeLen <= 1:
+      of skMacro, skTemplate:
+        # unambiguous macros/templates are expanded if all params are untyped
+        if sfAllUntyped in s.flags and sc.safeLen <= 1:
           onUse(fn.info, s)
-          result = semMacroExpr(c, n, n, s, {efNoSemCheck})
-          result = semGenericStmt(c, result, flags, ctx)
-        else:
-          n[0] = sc
-          result = n
-        mixinContext = true
-      of skTemplate:
-        if macroToExpand(s) and sc.safeLen <= 1:
-          onUse(fn.info, s)
-          result = semTemplateExpr(c, n, s, {efNoSemCheck})
+          case s.kind
+          of skMacro: result = semMacroExpr(c, n, n, s, {efNoSemCheck})
+          of skTemplate: result = semTemplateExpr(c, n, s, {efNoSemCheck})
+          else: discard # unreachable
+          c.friendModules.add(s.owner.getModule)
           result = semGenericStmt(c, result, flags, ctx)
+          discard c.friendModules.pop()
         else:
           n[0] = sc
           result = n
@@ -280,7 +360,12 @@ proc semGenericStmt(c: PContext, n: PNode,
       of skType:
         # bad hack for generics:
         if (s.typ != nil) and (s.typ.kind != tyGenericParam):
-          result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
+          if ambig:
+            # ambiguous types should be symchoices since lookup behaves
+            # differently for them in regular expressions
+            result[0] = sc
+          else:
+            result[0] = newSymNodeTypeDesc(s, c.idgen, fn.info)
           onUse(fn.info, s)
           first = 1
       else:
@@ -288,7 +373,7 @@ proc semGenericStmt(c: PContext, n: PNode,
         onUse(fn.info, s)
         first = 1
     elif fn.kind == nkDotExpr:
-      result[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext)
+      result[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext, inCall = true)
       first = 1
     # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)'
     # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which
@@ -306,7 +391,7 @@ proc semGenericStmt(c: PContext, n: PNode,
     result.add newIdentNode(getIdent(c.cache, "[]"), n.info)
     for i in 0..<n.len: result.add(n[i])
     result = semGenericStmt(c, result, flags, ctx)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     checkSonsLen(n, 2, c.config)
     let a = n[0]
     let b = n[1]
@@ -353,7 +438,9 @@ proc semGenericStmt(c: PContext, n: PNode,
       var a = n[i]
       checkMinSonsLen(a, 1, c.config)
       for j in 0..<a.len-1:
-        a[j] = semGenericStmt(c, a[j], flags, ctx)
+        a[j] = semGenericStmt(c, a[j], flags+{withinMixin}, ctx)
+        addTempDeclToIdents(c, a[j], skVar, false)
+
       a[^1] = semGenericStmtScope(c, a[^1], flags, ctx)
     closeScope(c)
   of nkForStmt, nkParForStmt:
@@ -444,14 +531,53 @@ proc semGenericStmt(c: PContext, n: PNode,
       if n[0].kind != nkEmpty:
         n[0] = semGenericStmt(c, n[0], flags+{withinTypeDesc}, ctx)
       for i in 1..<n.len:
-        var a: PNode
+        var a: PNode = nil
         case n[i].kind
         of nkEnumFieldDef: a = n[i][0]
         of nkIdent: a = n[i]
         else: illFormedAst(n, c.config)
         addDecl(c, newSymS(skUnknown, getIdentNode(c, a), c))
-  of nkObjectTy, nkTupleTy, nkTupleClassTy:
-    discard
+  of nkTupleTy:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        checkMinSonsLen(a, 3, c.config)
+        a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+        a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+        for j in 0..<a.len-2:
+          addTempDecl(c, getIdentNode(c, a[j]), skField)
+      else:
+        illFormedAst(a, c.config)
+  of nkObjectTy:
+    if n.len > 0:
+      openScope(c)
+      for i in 0..<n.len:
+        result[i] = semGenericStmt(c, n[i], flags, ctx)
+      closeScope(c)
+  of nkRecList:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        checkMinSonsLen(a, 3, c.config)
+        a[^2] = semGenericStmt(c, a[^2], flags+{withinTypeDesc}, ctx)
+        a[^1] = semGenericStmt(c, a[^1], flags, ctx)
+        for j in 0..<a.len-2:
+          addTempDecl(c, getIdentNode(c, a[j]), skField)
+      of nkRecCase, nkRecWhen:
+        n[i] = semGenericStmt(c, a, flags, ctx)
+      else:
+        illFormedAst(a, c.config)
+  of nkRecCase:
+    checkSonsLen(n[0], 3, c.config)
+    n[0][^2] = semGenericStmt(c, n[0][^2], flags+{withinTypeDesc}, ctx)
+    n[0][^1] = semGenericStmt(c, n[0][^1], flags, ctx)
+    addTempDecl(c, getIdentNode(c, n[0][0]), skField)
+    for i in 1..<n.len:
+      n[i] = semGenericStmt(c, n[i], flags, ctx)
   of nkFormalParams:
     checkMinSonsLen(n, 1, c.config)
     for i in 1..<n.len:
@@ -476,7 +602,7 @@ proc semGenericStmt(c: PContext, n: PNode,
                                               flags, ctx)
     if n[paramsPos].kind != nkEmpty:
       if n[paramsPos][0].kind != nkEmpty:
-        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, nil, n.info))
+        addPrelimDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, nil, n.info))
       n[paramsPos] = semGenericStmt(c, n[paramsPos], flags, ctx)
     n[pragmasPos] = semGenericStmt(c, n[pragmasPos], flags, ctx)
     var body: PNode
@@ -487,12 +613,27 @@ proc semGenericStmt(c: PContext, n: PNode,
       else:
         body = getBody(c.graph, s)
     else: body = n[bodyPos]
-    n[bodyPos] = semGenericStmtScope(c, body, flags, ctx)
+    let bodyFlags = if n.kind == nkTemplateDef: flags + {withinMixin} else: flags
+    n[bodyPos] = semGenericStmtScope(c, body, bodyFlags, ctx)
     closeScope(c)
   of nkPragma, nkPragmaExpr: discard
   of nkExprColonExpr, nkExprEqExpr:
     checkMinSonsLen(n, 2, c.config)
     result[1] = semGenericStmt(c, n[1], flags, ctx)
+  of nkObjConstr:
+    for i in 0..<n.len:
+      result[i] = semGenericStmt(c, n[i], flags, ctx)
+    if result[0].kind == nkSym:
+      let fmoduleId = getModule(result[0].sym).id
+      var isVisable = false
+      for module in c.friendModules:
+        if module.id == fmoduleId:
+          isVisable = true
+          break
+      if isVisable:
+        for i in 1..<result.len:
+          if result[i].kind == nkExprColonExpr:
+            result[i][1].flags.incl nfSkipFieldChecking
   else:
     for i in 0..<n.len:
       result[i] = semGenericStmt(c, n[i], flags, ctx)
@@ -501,16 +642,18 @@ proc semGenericStmt(c: PContext, n: PNode,
     if withinTypeDesc in flags: dec c.inTypeContext
 
 proc semGenericStmt(c: PContext, n: PNode): PNode =
-  var ctx: GenericCtx
-  ctx.toMixin = initIntSet()
-  ctx.toBind = initIntSet()
+  var ctx = GenericCtx(
+    toMixin: initIntSet(),
+    toBind: initIntSet()
+  )
   result = semGenericStmt(c, n, {}, ctx)
   semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
 
 proc semConceptBody(c: PContext, n: PNode): PNode =
-  var ctx: GenericCtx
-  ctx.toMixin = initIntSet()
-  ctx.toBind = initIntSet()
+  var ctx = GenericCtx(
+    toMixin: initIntSet(),
+    toBind: initIntSet()
+  )
   result = semGenericStmt(c, n, {withinConcept}, ctx)
   semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody)
 
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index bd5eb1ec3..1bc6d31a2 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -29,25 +29,21 @@ proc addObjFieldsToLocalScope(c: PContext; n: PNode) =
   else: discard
 
 proc pushProcCon*(c: PContext; owner: PSym) =
-  var x: PProcCon
-  new(x)
-  x.owner = owner
-  x.next = c.p
-  c.p = x
+  c.p = PProcCon(owner: owner, next: c.p)
 
 const
   errCannotInstantiateX = "cannot instantiate: '$1'"
 
-iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TypeMapping): PSym =
   internalAssert c.config, n.kind == nkGenericParams
-  for i, a in n.pairs:
+  for a in n.items:
     internalAssert c.config, a.kind == nkSym
     var q = a.sym
     if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses:
       let symKind = if q.typ.kind == tyStatic: skConst else: skType
-      var s = newSym(symKind, q.name, nextSymId(c.idgen), getCurrOwner(c), q.info)
+      var s = newSym(symKind, q.name, c.idgen, getCurrOwner(c), q.info)
       s.flags.incl {sfUsed, sfFromGeneric}
-      var t = PType(idTableGet(pt, q.typ))
+      var t = idTableGet(pt, q.typ)
       if t == nil:
         if tfRetType in q.typ.flags:
           # keep the generic type and allow the return type to be bound
@@ -57,7 +53,16 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym
           if q.typ.kind != tyCompositeTypeClass:
             localError(c.config, a.info, errCannotInstantiateX % s.name.s)
           t = errorType(c)
-      elif t.kind in {tyGenericParam, tyConcept}:
+      elif t.kind in {tyGenericParam, tyConcept, tyFromExpr}:
+        localError(c.config, a.info, errCannotInstantiateX % q.name.s)
+        t = errorType(c)
+      elif isUnresolvedStatic(t) and (q.typ.kind == tyStatic or
+            (q.typ.kind == tyGenericParam and
+              q.typ.genericParamHasConstraints and
+              q.typ.genericConstraint.kind == tyStatic)) and
+          c.inGenericContext == 0 and c.matchedConcept == nil:
+        # generic/concept type bodies will try to instantiate static values but
+        # won't actually use them
         localError(c.config, a.info, errCannotInstantiateX % q.name.s)
         t = errorType(c)
       elif t.kind == tyGenericInvocation:
@@ -76,9 +81,12 @@ proc sameInstantiation(a, b: TInstantiation): bool =
                                    ExactGcSafety,
                                    PickyCAliases}): return
     result = true
+  else:
+    result = false
 
 proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation;
                      id: CompilesId): PSym =
+  result = nil
   for inst in procInstCacheItems(g, genericSym):
     if (inst.compilesId == 0 or inst.compilesId == id) and sameInstantiation(entry, inst[]):
       return inst.sym
@@ -87,7 +95,7 @@ when false:
   proc `$`(x: PSym): string =
     result = x.name.s & " " & " id " & $x.id
 
-proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable) =
+proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var SymMapping) =
   # we need to create a fresh set of gensym'ed symbols:
   #if n.kind == nkSym and sfGenSym in n.sym.flags:
   #  if n.sym.owner != orig:
@@ -95,12 +103,12 @@ proc freshGenSyms(c: PContext; n: PNode, owner, orig: PSym, symMap: var TIdTable
   if n.kind == nkSym and sfGenSym in n.sym.flags: # and
     #  (n.sym.owner == orig or n.sym.owner.kind in {skPackage}):
     let s = n.sym
-    var x = PSym(idTableGet(symMap, s))
+    var x = idTableGet(symMap, s)
     if x != nil:
       n.sym = x
     elif s.owner == nil or s.owner.kind == skPackage:
       #echo "copied this ", s.name.s
-      x = copySym(s, nextSymId c.idgen)
+      x = copySym(s, c.idgen)
       x.owner = owner
       idTablePut(symMap, s, x)
       n.sym = x
@@ -119,19 +127,25 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) =
     inc c.inGenericInst
     # add it here, so that recursive generic procs are possible:
     var b = n[bodyPos]
-    var symMap: TIdTable
-    initIdTable symMap
+    var symMap = initSymMapping()
     if params != nil:
       for i in 1..<params.len:
         let param = params[i].sym
         if sfGenSym in param.flags:
           idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym)
     freshGenSyms(c, b, result, orig, symMap)
-    
-    if sfBorrow notin orig.flags: 
+
+    if sfBorrow notin orig.flags:
       # We do not want to generate a body for generic borrowed procs.
       # As body is a sym to the borrowed proc.
-      b = semProcBody(c, b)
+      let resultType = # todo probably refactor it into a function
+        if result.kind == skMacro:
+          sysTypeFromName(c.graph, n.info, "NimNode")
+        elif not isInlineIterator(result.typ):
+          result.typ.returnType
+        else:
+          nil
+      b = semProcBody(c, b, resultType)
     result.ast[bodyPos] = hloBody(c, b)
     excl(result.flags, sfForward)
     trackProc(c, result, result.ast[bodyPos])
@@ -163,17 +177,12 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
                           allowMetaTypes = false): PType =
   internalAssert c.config, header.kind == tyGenericInvocation
 
-  var
-    cl: TReplTypeVars
-
-  initIdTable(cl.symMap)
-  initIdTable(cl.localCache)
-  cl.typeMap = LayeredIdTable()
-  initIdTable(cl.typeMap.topLayer)
+  var cl: TReplTypeVars = TReplTypeVars(symMap: initSymMapping(),
+        localCache: initTypeMapping(), typeMap: LayeredIdTable(),
+        info: info, c: c, allowMetaTypes: allowMetaTypes
+      )
 
-  cl.info = info
-  cl.c = c
-  cl.allowMetaTypes = allowMetaTypes
+  cl.typeMap.topLayer = initTypeMapping()
 
   # We must add all generic params in scope, because the generic body
   # may include tyFromExpr nodes depending on these generic params.
@@ -181,12 +190,11 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
   # perhaps the code can be extracted in a shared function.
   openScope(c)
   let genericTyp = header.base
-  for i in 0..<genericTyp.len - 1:
-    let genParam = genericTyp[i]
+  for i, genParam in genericBodyParams(genericTyp):
     var param: PSym
 
     template paramSym(kind): untyped =
-      newSym(kind, genParam.sym.name, nextSymId c.idgen, genericTyp.sym, genParam.sym.info)
+      newSym(kind, genParam.sym.name, c.idgen, genericTyp.sym, genParam.sym.info)
 
     if genParam.kind == tyStatic:
       param = paramSym skConst
@@ -212,7 +220,7 @@ proc referencesAnotherParam(n: PNode, p: PSym): bool =
       if referencesAnotherParam(n[i], p): return true
     return false
 
-proc instantiateProcType(c: PContext, pt: TIdTable,
+proc instantiateProcType(c: PContext, pt: TypeMapping,
                          prc: PSym, info: TLineInfo) =
   # XXX: Instantiates a generic proc signature, while at the same
   # time adding the instantiated proc params into the current scope.
@@ -234,20 +242,26 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   var result = instCopyType(cl, prc.typ)
   let originalParams = result.n
   result.n = originalParams.shallowCopy
-  for i in 1..<result.len:
+  for i, resulti in paramTypes(result):
     # twrong_field_caching requires these 'resetIdTable' calls:
-    if i > 1:
+    if i > FirstParamAt:
       resetIdTable(cl.symMap)
       resetIdTable(cl.localCache)
 
     # take a note of the original type. If't a free type or static parameter
     # we'll need to keep it unbound for the `fitNode` operation below...
-    var typeToFit = result[i]
+    var typeToFit = resulti
 
-    let needsStaticSkipping = result[i].kind == tyFromExpr
-    result[i] = replaceTypeVarsT(cl, result[i])
+    let needsStaticSkipping = resulti.kind == tyFromExpr
+    let needsTypeDescSkipping = resulti.kind == tyTypeDesc and tfUnresolved in resulti.flags
+    if resulti.kind == tyFromExpr:
+      resulti.flags.incl tfNonConstExpr
+    result[i] = replaceTypeVarsT(cl, resulti)
     if needsStaticSkipping:
       result[i] = result[i].skipTypes({tyStatic})
+    if needsTypeDescSkipping:
+      result[i] = result[i].skipTypes({tyTypeDesc})
+      typeToFit = result[i]
 
     # ...otherwise, we use the instantiated type in `fitNode`
     if (typeToFit.kind != tyTypeDesc or typeToFit.base.kind != tyNone) and
@@ -256,7 +270,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
 
     internalAssert c.config, originalParams[i].kind == nkSym
     let oldParam = originalParams[i].sym
-    let param = copySym(oldParam, nextSymId c.idgen)
+    let param = copySym(oldParam, c.idgen)
     param.owner = prc
     param.typ = result[i]
 
@@ -265,11 +279,14 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
     # call head symbol, because this leads to infinite recursion.
     if oldParam.ast != nil:
       var def = oldParam.ast.copyTree
-      if def.kind == nkCall:
-        for i in 1..<def.len:
-          def[i] = replaceTypeVarsN(cl, def[i])
-
-      def = semExprWithType(c, def)
+      if def.typ.kind == tyFromExpr:
+        def.typ.flags.incl tfNonConstExpr
+      if not isIntLit(def.typ):
+        def = prepareNode(cl, def)
+
+      # allow symchoice since node will be fit later
+      # although expectedType should cover it
+      def = semExprWithType(c, def, {efAllowSymChoice}, typeToFit)
       if def.referencesAnotherParam(getCurrOwner(c)):
         def.flags.incl nfDefaultRefsParam
 
@@ -282,6 +299,8 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
         # the user calls an explicit instantiation of the proc (this is
         # the only way the default value might be inserted).
         param.ast = errorNode(c, def)
+        # we know the node is empty, we need the actual type for error message
+        param.ast.typ = def.typ
       else:
         param.ast = fitNodePostMatch(c, typeToFit, converted)
       param.typ = result[i]
@@ -293,7 +312,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   resetIdTable(cl.symMap)
   resetIdTable(cl.localCache)
   cl.isReturnType = true
-  result[0] = replaceTypeVarsT(cl, result[0])
+  result.setReturnType replaceTypeVarsT(cl, result.returnType)
   cl.isReturnType = false
   result.n[0] = originalParams[0].copyTree
   if result[0] != nil:
@@ -305,6 +324,22 @@ proc instantiateProcType(c: PContext, pt: TIdTable,
   prc.typ = result
   popInfoContext(c.config)
 
+proc instantiateOnlyProcType(c: PContext, pt: TypeMapping, prc: PSym, info: TLineInfo): PType =
+  # instantiates only the type of a given proc symbol
+  # used by sigmatch for explicit generics
+  # wouldn't be needed if sigmatch could handle complex cases,
+  # examples are in texplicitgenerics
+  # might be buggy, see rest of generateInstance if problems occur
+  let fakeSym = copySym(prc, c.idgen)
+  incl(fakeSym.flags, sfFromGeneric)
+  fakeSym.instantiatedFrom = prc
+  openScope(c)
+  for s in instantiateGenericParamList(c, prc.ast[genericParamsPos], pt):
+    addDecl(c, s)
+  instantiateProcType(c, pt, fakeSym, info)
+  closeScope(c)
+  result = fakeSym.typ
+
 proc fillMixinScope(c: PContext) =
   var p = c.p
   while p != nil:
@@ -313,8 +348,20 @@ proc fillMixinScope(c: PContext) =
         addSym(c.currentScope, n.sym)
     p = p.next
 
-proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
-                      info: TLineInfo): PSym {.nosinks.} =
+proc getLocalPassC(c: PContext, s: PSym): string =
+  when defined(nimsuggest): return ""
+  if s.ast == nil or s.ast.len == 0: return ""
+  result = ""
+  template extractPassc(p: PNode) =
+    if p.kind == nkPragma and p[0][0].ident == c.cache.getIdent"localpassc":
+      return p[0][1].strVal
+  extractPassc(s.ast[0]) #it is set via appendToModule in pragmas (fast access)
+  for n in s.ast:
+    for p in n:
+      extractPassc(p)
+
+proc generateInstance(c: PContext, fn: PSym, pt: TypeMapping,
+                      info: TLineInfo): PSym =
   ## Generates a new instance of a generic procedure.
   ## The `pt` parameter is a type-unsafe mapping table used to link generic
   ## parameters to their concrete types within the generic instance.
@@ -323,19 +370,28 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # generates an instantiated proc
   if c.instCounter > 50:
     globalError(c.config, info, "generic instantiation too nested")
-  inc(c.instCounter)
+  inc c.instCounter
+  defer: dec c.instCounter
   # careful! we copy the whole AST including the possibly nil body!
   var n = copyTree(fn.ast)
   # NOTE: for access of private fields within generics from a different module
   # we set the friend module:
-  c.friendModules.add(getModule(fn))
+  let producer = getModule(fn)
+  c.friendModules.add(producer)
   let oldMatchedConcept = c.matchedConcept
   c.matchedConcept = nil
   let oldScope = c.currentScope
   while not isTopLevel(c): c.currentScope = c.currentScope.parent
-  result = copySym(fn, nextSymId c.idgen)
+  result = copySym(fn, c.idgen)
   incl(result.flags, sfFromGeneric)
-  result.owner = fn
+  result.instantiatedFrom = fn
+  if sfGlobal in result.flags and c.config.symbolFiles != disabledSf:
+    let passc = getLocalPassC(c, producer)
+    if passc != "": #pass the local compiler options to the consumer module too
+      extccomp.addLocalCompileOption(c.config, passc, toFullPathConsiderDirty(c.config, c.module.info.fileIndex))
+    result.owner = c.module
+  else:
+    result.owner = fn
   result.ast = n
   pushOwner(c, result)
 
@@ -345,7 +401,9 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
 
   openScope(c)
   let gp = n[genericParamsPos]
-  internalAssert c.config, gp.kind == nkGenericParams
+  if gp.kind != nkGenericParams:
+    # bug #22137
+    globalError(c.config, info, "generic instantiation too nested")
   n[namePos] = newSymNode(result)
   pushInfoContext(c.config, info, fn.detailedInfo)
   var entry = TInstantiation.new
@@ -354,16 +412,20 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   # generic[void](), generic[int]()
   # see ttypeor.nim test.
   var i = 0
-  newSeq(entry.concreteTypes, fn.typ.len+gp.len-1)
+  newSeq(entry.concreteTypes, fn.typ.paramsLen+gp.len)
+  # let param instantiation know we are in a concept for unresolved statics:
+  c.matchedConcept = oldMatchedConcept
   for s in instantiateGenericParamList(c, gp, pt):
     addDecl(c, s)
     entry.concreteTypes[i] = s.typ
     inc i
+  c.matchedConcept = nil
   pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
-  for j in 1..<result.typ.len:
-    entry.concreteTypes[i] = result.typ[j]
+  for _, param in paramTypes(result.typ):
+    entry.concreteTypes[i] = param
     inc i
+  #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " ", entry.concreteTypes.len
   if tfTriggersCompileTime in result.typ.flags:
     incl(result.flags, sfCompileTime)
   n[genericParamsPos] = c.graph.emptyNode
@@ -377,17 +439,24 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
     entry.compilesId = c.compilesContextId
     addToGenericProcCache(c, fn, entry)
     c.generics.add(makeInstPair(fn, entry))
+    # bug #12985 bug #22913
+    # TODO: use the context of the declaration of generic functions instead
+    # TODO: consider fixing options as well
+    let otherPragmas = c.optionStack[^1].otherPragmas
+    c.optionStack[^1].otherPragmas = nil
     if n[pragmasPos].kind != nkEmpty:
       pragma(c, result, n[pragmasPos], allRoutinePragmas)
     if isNil(n[bodyPos]):
       n[bodyPos] = copyTree(getBody(c.graph, fn))
-    if c.inGenericContext == 0:
-      instantiateBody(c, n, fn.typ.n, result, fn)
+    instantiateBody(c, n, fn.typ.n, result, fn)
+    c.optionStack[^1].otherPragmas = otherPragmas
     sideEffectsCheck(c, result)
     if result.magic notin {mSlice, mTypeOf}:
       # 'toOpenArray' is special and it is allowed to return 'openArray':
       paramsTypeCheck(c, result.typ)
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- NEW PROC!", " ", entry.concreteTypes.len
   else:
+    #echo "INSTAN ", fn.name.s, " ", typeToString(result.typ), " <-- CACHED! ", typeToString(oldPrc.typ), " ", entry.concreteTypes.len
     result = oldPrc
   popProcCon(c)
   popInfoContext(c.config)
@@ -396,7 +465,6 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   popOwner(c)
   c.currentScope = oldScope
   discard c.friendModules.pop()
-  dec(c.instCounter)
   c.matchedConcept = oldMatchedConcept
   if result.kind == skMethod: finishMethod(c, result)
 
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index aebee8998..727f36470 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -35,12 +35,12 @@ proc ithField(n: PNode, field: var int): PSym =
   else: discard
 
 proc ithField(t: PType, field: var int): PSym =
-  var base = t[0]
+  var base = t.baseClass
   while base != nil:
     let b = skipTypes(base, skipPtrs)
     result = ithField(b.n, field)
     if result != nil: return result
-    base = b[0]
+    base = b.baseClass
   result = ithField(t.n, field)
 
 proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
@@ -51,6 +51,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
   of nkObjConstr:
     let x = t.skipTypes(abstractPtrs)
     n.typ = t
+    n[0].typ = t
     for i in 1..<n.len:
       var j = i-1
       let field = x.ithField(j)
@@ -63,10 +64,31 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) =
     if x.kind == tyTuple:
       n.typ = t
       for i in 0..<n.len:
-        if i >= x.len: globalError conf, n.info, "invalid field at index " & $i
+        if i >= x.kidsLen: globalError conf, n.info, "invalid field at index " & $i
         else: annotateType(n[i], x[i], conf)
     elif x.kind == tyProc and x.callConv == ccClosure:
       n.typ = t
+    elif x.kind == tyOpenArray: # `opcSlice` transforms slices into tuples
+      if n.kind == nkTupleConstr:
+        let
+          bracketExpr = newNodeI(nkBracket, n.info)
+          left = int n[1].intVal
+          right = int n[2].intVal
+        bracketExpr.flags = n.flags
+        case n[0].kind # is this a string slice or a array slice
+        of nkStrKinds:
+          for i in left..right:
+            bracketExpr.add newIntNode(nkCharLit, BiggestInt n[0].strVal[i])
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        of nkBracket:
+          for i in left..right:
+            bracketExpr.add n[0][i]
+            annotateType(bracketExpr[^1], x.elementType, conf)
+        else:
+          globalError(conf, n.info, "Incorrectly generated tuple constr")
+        n[] = bracketExpr[]
+
+      n.typ = t
     else:
       globalError(conf, n.info, "() must have a tuple type")
   of nkBracket:
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 48ea69648..a12e933e7 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -10,13 +10,35 @@
 # This include file implements the semantic checking for magics.
 # included from sem.nim
 
-proc semAddrArg(c: PContext; n: PNode): PNode =
+proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode
+
+
+proc addDefaultFieldForNew(c: PContext, n: PNode): PNode =
+  result = n
+  let typ = result[1].typ # new(x)
+  if typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyRef and typ.skipTypes({tyGenericInst, tyAlias, tySink})[0].kind == tyObject:
+    var asgnExpr = newTree(nkObjConstr, newNodeIT(nkType, result[1].info, typ))
+    asgnExpr.typ = typ
+    var t = typ.skipTypes({tyGenericInst, tyAlias, tySink})[0]
+    while true:
+      asgnExpr.sons.add defaultFieldsForTheUninitialized(c, t.n, false)
+      let base = t.baseClass
+      if base == nil:
+        break
+      t = skipTypes(base, skipPtrs)
+
+    if asgnExpr.sons.len > 1:
+      result = newTree(nkAsgn, result[1], asgnExpr)
+
+proc semAddr(c: PContext; n: PNode): PNode =
+  result = newNodeI(nkAddr, n.info)
   let x = semExprWithType(c, n)
   if x.kind == nkSym:
     x.sym.flags.incl(sfAddrTaken)
   if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}:
     localError(c.config, n.info, errExprHasNoAddress)
-  result = x
+  result.add x
+  result.typ = makePtrType(c, x.typ)
 
 proc semTypeOf(c: PContext; n: PNode): PNode =
   var m = BiggestInt 1 # typeOfIter
@@ -27,8 +49,12 @@ proc semTypeOf(c: PContext; n: PNode): PNode =
     else:
       m = mode.intVal
   result = newNodeI(nkTypeOfExpr, n.info)
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let typExpr = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
   result.add typExpr
+  if typExpr.typ.kind == tyFromExpr:
+    typExpr.typ.flags.incl tfNonConstExpr
   result.typ = makeTypeDesc(c, typExpr.typ)
 
 type
@@ -44,22 +70,31 @@ proc semArrGet(c: PContext; n: PNode; flags: TExprFlags): PNode =
   if result.isNil:
     let x = copyTree(n)
     x[0] = newIdentNode(getIdent(c.cache, "[]"), n.info)
-    bracketNotFoundError(c, x)
+    if c.inGenericContext > 0:
+      for i in 0..<n.len:
+        let a = n[i]
+        if a.typ != nil and a.typ.kind in {tyGenericParam, tyFromExpr}:
+          # expression is compiled early in a generic body
+          result = semGenericStmt(c, x)
+          result.typ = makeTypeFromExpr(c, copyTree(result))
+          result.typ.flags.incl tfNonConstExpr
+          return
+    bracketNotFoundError(c, x, flags)
     #localError(c.config, n.info, "could not resolve: " & $n)
-    result = n
+    result = errorNode(c, n)
 
 proc semArrPut(c: PContext; n: PNode; flags: TExprFlags): PNode =
   # rewrite `[]=`(a, i, x)  back to ``a[i] = x``.
   let b = newNodeI(nkBracketExpr, n.info)
-  b.add(n[1].skipAddr)
+  b.add(n[1].skipHiddenAddr)
   for i in 2..<n.len-1: b.add(n[i])
   result = newNodeI(nkAsgn, n.info, 2)
   result[0] = b
   result[1] = n.lastSon
   result = semAsgn(c, result, noOverloadedSubscript)
 
-proc semAsgnOpr(c: PContext; n: PNode): PNode =
-  result = newNodeI(nkAsgn, n.info, 2)
+proc semAsgnOpr(c: PContext; n: PNode; k: TNodeKind): PNode =
+  result = newNodeI(k, n.info, 2)
   result[0] = n[1]
   result[1] = n[2]
   result = semAsgn(c, result, noOverloadedAsgn)
@@ -72,7 +107,9 @@ 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(c.config, n.info, errIntLiteralExpected)
+  else:
+    result = 0
+    localError(c.config, n.info, errIntLiteralExpected)
 
 proc semInstantiationInfo(c: PContext, n: PNode): PNode =
   result = newNodeIT(nkTupleConstr, n.info, n.typ)
@@ -110,14 +147,22 @@ proc uninstantiate(t: PType): PType =
   result = case t.kind
     of tyMagicGenerics: t
     of tyUserDefinedGenerics: t.base
-    of tyCompositeTypeClass: uninstantiate t[1]
+    of tyCompositeTypeClass: uninstantiate t.firstGenericParam
     else: t
 
 proc getTypeDescNode(c: PContext; typ: PType, sym: PSym, info: TLineInfo): PNode =
-  var resType = newType(tyTypeDesc, nextTypeId c.idgen, sym)
+  var resType = newType(tyTypeDesc, c.idgen, sym)
   rawAddSon(resType, typ)
   result = toNode(resType, info)
 
+proc buildBinaryPredicate(kind: TTypeKind; c: PContext; context: PSym; a, b: sink PType): PType =
+  result = newType(kind, c.idgen, context)
+  result.rawAddSon a
+  result.rawAddSon b
+
+proc buildNotPredicate(c: PContext; context: PSym; a: sink PType): PType =
+  result = newType(tyNot, c.idgen, context, a)
+
 proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym): PNode =
   const skippedTypes = {tyTypeDesc, tyAlias, tySink}
   let trait = traitCall[0]
@@ -127,20 +172,17 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
   template operand2: PType =
     traitCall[2].typ.skipTypes({tyTypeDesc})
 
-  template typeWithSonsResult(kind, sons): PNode =
-    newTypeWithSons(context, kind, sons, c.idgen).toNode(traitCall.info)
-
   if operand.kind == tyGenericParam or (traitCall.len > 2 and operand2.kind == tyGenericParam):
     return traitCall  ## too early to evaluate
 
   let s = trait.sym.name.s
   case s
   of "or", "|":
-    return typeWithSonsResult(tyOr, @[operand, operand2])
+    return buildBinaryPredicate(tyOr, c, context, operand, operand2).toNode(traitCall.info)
   of "and":
-    return typeWithSonsResult(tyAnd, @[operand, operand2])
+    return buildBinaryPredicate(tyAnd, c, context, operand, operand2).toNode(traitCall.info)
   of "not":
-    return typeWithSonsResult(tyNot, @[operand])
+    return buildNotPredicate(c, context, operand).toNode(traitCall.info)
   of "typeToString":
     var prefer = preferTypeName
     if traitCall.len >= 2:
@@ -155,7 +197,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     result.info = traitCall.info
   of "arity":
     result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
-    result.typ = newType(tyInt, nextTypeId c.idgen, context)
+    result.typ = newType(tyInt, c.idgen, context)
     result.info = traitCall.info
   of "genericHead":
     var arg = operand
@@ -167,7 +209,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     #   result = toNode(resType, traitCall.info) # doesn't work yet
     else:
       localError(c.config, traitCall.info, "expected generic type, got: type $2 of kind $1" % [arg.kind.toHumanStr, typeToString(operand)])
-      result = newType(tyError, nextTypeId c.idgen, context).toNode(traitCall.info)
+      result = newType(tyError, c.idgen, context).toNode(traitCall.info)
   of "stripGenericParams":
     result = uninstantiate(operand).toNode(traitCall.info)
   of "supportsCopyMem":
@@ -175,6 +217,8 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
     let complexObj = containsGarbageCollectedRef(t) or
                      hasDestructor(t)
     result = newIntNodeT(toInt128(ord(not complexObj)), traitCall, c.idgen, c.graph)
+  of "hasDefaultValue":
+    result = newIntNodeT(toInt128(ord(not operand.requiresInit)), traitCall, c.idgen, c.graph)
   of "isNamedTuple":
     var operand = operand.skipTypes({tyGenericInst})
     let cond = operand.kind == tyTuple and operand.n != nil
@@ -190,6 +234,16 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
       arg = arg.base.skipTypes(skippedTypes + {tyGenericInst})
       if not rec: break
     result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
+  of "rangeBase":
+    # return the base type of a range type
+    var arg = operand.skipTypes({tyGenericInst})
+    if arg.kind == tyRange:
+      arg = arg.base
+    result = getTypeDescNode(c, arg, operand.owner, traitCall.info)
+  of "isCyclic":
+    var operand = operand.skipTypes({tyGenericInst})
+    let isCyclic = canFormAcycle(c.graph, operand)
+    result = newIntNodeT(toInt128(ord(isCyclic)), traitCall, c.idgen, c.graph)
   else:
     localError(c.config, traitCall.info, "unknown trait: " & s)
     result = newNodeI(nkEmpty, traitCall.info)
@@ -197,7 +251,7 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym)
 proc semTypeTraits(c: PContext, n: PNode): PNode =
   checkMinSonsLen(n, 2, c.config)
   let t = n[1].typ
-  internalAssert c.config, t != nil and t.kind == tyTypeDesc
+  internalAssert c.config, t != nil and t.skipTypes({tyAlias}).kind == tyTypeDesc
   if t.len > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
@@ -258,6 +312,7 @@ proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode):
     # we need to mark all symbols:
     result = symChoice(c, id, s, TSymChoiceRule(isMixin))
   else:
+    result = nil
     errorUndeclaredIdentifier(c, info.info, if n.kind == nkIdent: n.ident.s
       else: n.strVal)
   c.currentScope = tmpScope
@@ -353,18 +408,18 @@ proc semUnown(c: PContext; n: PNode): PNode =
         elems[i] = unownedType(c, t[i])
         if elems[i] != t[i]: someChange = true
       if someChange:
-        result = newType(tyTuple, nextTypeId c.idgen, t.owner)
+        result = newType(tyTuple, c.idgen, t.owner)
         # we have to use 'rawAddSon' here so that type flags are
         # properly computed:
         for e in elems: result.rawAddSon(e)
       else:
         result = t
-    of tyOwned: result = t[0]
+    of tyOwned: result = t.elementType
     of tySequence, tyOpenArray, tyArray, tyVarargs, tyVar, tyLent,
        tyGenericInst, tyAlias:
       let b = unownedType(c, t[^1])
       if b != t[^1]:
-        result = copyType(t, nextTypeId c.idgen, t.owner)
+        result = copyType(t, c.idgen, t.owner)
         copyTypeProps(c.graph, c.idgen.module, result, t)
 
         result[^1] = b
@@ -384,34 +439,30 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym
   # Replace nkDerefExpr by nkHiddenDeref
   # nkDeref is for 'ref T':  x[].field
   # nkHiddenDeref is for 'var T': x<hidden deref [] here>.field
-  proc transform(c: PContext; procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
+  proc transform(c: PContext; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode =
     result = shallowCopy(n)
     if sameTypeOrNil(n.typ, old):
       result.typ = fresh
-    if n.kind == nkSym:
-      if n.sym == oldParam:
-        result.sym = newParam
-      elif n.sym.owner == orig:
-        result.sym = copySym(n.sym, nextSymId c.idgen)
-        result.sym.owner = procSym
+    if n.kind == nkSym and n.sym == oldParam:
+      result.sym = newParam
     for i in 0 ..< safeLen(n):
-      result[i] = transform(c, procSym, n[i], old, fresh, oldParam, newParam)
+      result[i] = transform(c, n[i], old, fresh, oldParam, newParam)
     #if n.kind == nkDerefExpr and sameType(n[0].typ, old):
     #  result =
 
-  result = copySym(orig, nextSymId c.idgen)
+  result = copySym(orig, c.idgen)
   result.info = info
   result.flags.incl sfFromGeneric
   result.owner = orig
-  let origParamType = orig.typ[1]
+  let origParamType = orig.typ.firstParamType
   let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen)
   let oldParam = orig.typ.n[1].sym
-  let newParam = newSym(skParam, oldParam.name, nextSymId c.idgen, result, result.info)
+  let newParam = newSym(skParam, oldParam.name, c.idgen, result, result.info)
   newParam.typ = newParamType
   # proc body:
-  result.ast = transform(c, result, orig.ast, origParamType, newParamType, oldParam, newParam)
+  result.ast = transform(c, orig.ast, origParamType, newParamType, oldParam, newParam)
   # proc signature:
-  result.typ = newProcType(result.info, nextTypeId c.idgen, result)
+  result.typ = newProcType(result.info, c.idgen, result)
   result.typ.addParam newParam
 
 proc semQuantifier(c: PContext; n: PNode): PNode =
@@ -467,42 +518,56 @@ proc semNewFinalize(c: PContext; n: PNode): PNode =
         localError(c.config, n.info, "finalizer must be a direct reference to a proc")
 
       # check if we converted this finalizer into a destructor already:
-      let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef}))
+      let t = whereToBindTypeHook(c, fin.typ.firstParamType.skipTypes(abstractInst+{tyRef}))
       if t != nil and getAttachedOp(c.graph, t, attachedDestructor) != nil and
           getAttachedOp(c.graph, t, attachedDestructor).owner == fin:
         discard "already turned this one into a finalizer"
       else:
-        if sfForward in fin.flags:
-          let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), nextSymId c.idgen, fin.owner, fin.info)
-          let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, nextSymId c.idgen))
-          wrapperSym.flags.incl sfUsed
-          let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
-            params = nkFormalParams.newTree(c.graph.emptyNode,
-                    newTree(nkIdentDefs, selfSymNode, fin.ast[paramsPos][1][1], c.graph.emptyNode)
-                    ),
-            name = newSymNode(wrapperSym), pattern = c.graph.emptyNode,
-            genericParams = c.graph.emptyNode, pragmas = c.graph.emptyNode, exceptions = c.graph.emptyNode), {})
-          var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
-          transFormedSym.owner = fin
-          if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
-            let origParamType = transFormedSym.ast[bodyPos][1].typ
-            let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
-            let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
-            selfPtr.add transFormedSym.ast[bodyPos][1]
-            selfPtr.typ = selfSymbolType
-            transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
-          bindTypeHook(c, transFormedSym, n, attachedDestructor)
-        else:
-          bindTypeHook(c, turnFinalizerIntoDestructor(c, fin, n.info), n, attachedDestructor)
-  result = n
+        if fin.instantiatedFrom != nil and fin.instantiatedFrom != fin.owner: #undo move
+          fin.owner = fin.instantiatedFrom
+        let wrapperSym = newSym(skProc, getIdent(c.graph.cache, fin.name.s & "FinalizerWrapper"), c.idgen, fin.owner, fin.info)
+        let selfSymNode = newSymNode(copySym(fin.ast[paramsPos][1][0].sym, c.idgen))
+        selfSymNode.typ = fin.typ.firstParamType
+        wrapperSym.flags.incl sfUsed
+
+        let wrapper = c.semExpr(c, newProcNode(nkProcDef, fin.info, body = newTree(nkCall, newSymNode(fin), selfSymNode),
+          params = nkFormalParams.newTree(c.graph.emptyNode,
+                  newTree(nkIdentDefs, selfSymNode, newNodeIT(nkType,
+                  fin.ast[paramsPos][1][1].info, fin.typ.firstParamType), c.graph.emptyNode)
+                  ),
+          name = newSymNode(wrapperSym), pattern = fin.ast[patternPos],
+          genericParams = fin.ast[genericParamsPos], pragmas = fin.ast[pragmasPos], exceptions = fin.ast[miscPos]), {})
+
+        var transFormedSym = turnFinalizerIntoDestructor(c, wrapperSym, wrapper.info)
+        transFormedSym.owner = fin
+        if c.config.backend == backendCpp or sfCompileToCpp in c.module.flags:
+          let origParamType = transFormedSym.ast[bodyPos][1].typ
+          let selfSymbolType = makePtrType(c, origParamType.skipTypes(abstractPtrs))
+          let selfPtr = newNodeI(nkHiddenAddr, transFormedSym.ast[bodyPos][1].info)
+          selfPtr.add transFormedSym.ast[bodyPos][1]
+          selfPtr.typ = selfSymbolType
+          transFormedSym.ast[bodyPos][1] = c.semExpr(c, selfPtr)
+        # TODO: suppress var destructor warnings; if newFinalizer is not
+        # TODO: deprecated, try to implement plain T destructor
+        bindTypeHook(c, transFormedSym, n, attachedDestructor, suppressVarDestructorWarning = true)
+  result = addDefaultFieldForNew(c, n)
 
 proc semPrivateAccess(c: PContext, n: PNode): PNode =
-  let t = n[1].typ[0].toObjectFromRefPtrGeneric
-  c.currentScope.allowPrivateAccess.add t.sym
+  let t = n[1].typ.elementType.toObjectFromRefPtrGeneric
+  if t.kind == tyObject:
+    assert t.sym != nil
+    c.currentScope.allowPrivateAccess.add t.sym
   result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid))
 
+proc checkDefault(c: PContext, n: PNode): PNode =
+  result = n
+  c.config.internalAssert result[1].typ.kind == tyTypeDesc
+  let constructed = result[1].typ.base
+  if constructed.requiresInit:
+    message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
+
 proc magicsAfterOverloadResolution(c: PContext, n: PNode,
-                                   flags: TExprFlags): PNode =
+                                   flags: TExprFlags; expectedType: PType = nil): PNode =
   ## This is the preferred code point to implement magics.
   ## ``c`` the current module, a symbol table to a very good approximation
   ## ``n`` the ast like it would be passed to a real macro
@@ -512,9 +577,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2, c.config)
-    result = n
-    result[1] = semAddrArg(c, n[1])
-    result.typ = makePtrType(c, result[1].typ)
+    result = semAddr(c, n[1])
   of mTypeOf:
     result = semTypeOf(c, n)
   of mSizeOf:
@@ -529,7 +592,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     result = semArrPut(c, n, flags)
   of mAsgn:
     if n[0].sym.name.s == "=":
-      result = semAsgnOpr(c, n)
+      result = semAsgnOpr(c, n, nkAsgn)
+    elif n[0].sym.name.s == "=sink":
+      result = semAsgnOpr(c, n, nkSinkAsgn)
     else:
       result = semShallowCopy(c, n, flags)
   of mIsPartOf: result = semIsPartOf(c, n, flags)
@@ -559,6 +624,11 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
       result = n
     else:
       result = plugin(c, n)
+  of mNew:
+    if n[0].sym.name.s == "unsafeNew": # special case for unsafeNew
+      result = n
+    else:
+      result = addDefaultFieldForNew(c, n)
   of mNewFinalize:
     result = semNewFinalize(c, n)
   of mDestroy:
@@ -567,12 +637,37 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     let op = getAttachedOp(c.graph, t, attachedDestructor)
     if op != nil:
       result[0] = newSymNode(op)
+      if op.typ != nil and op.typ.len == 2 and op.typ.firstParamType.kind != tyVar:
+        if n[1].kind == nkSym and n[1].sym.kind == skParam and
+            n[1].typ.kind == tyVar:
+          result[1] = genDeref(n[1])
+        else:
+          result[1] = skipAddr(n[1])
   of mTrace:
     result = n
     let t = n[1].typ.skipTypes(abstractVar)
     let op = getAttachedOp(c.graph, t, attachedTrace)
     if op != nil:
       result[0] = newSymNode(op)
+  of mDup:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedDup)
+    if op != nil:
+      result[0] = newSymNode(op)
+      if op.typ.len == 3:
+        let boolLit = newIntLit(c.graph, n.info, 1)
+        boolLit.typ = getSysType(c.graph, n.info, tyBool)
+        result.add boolLit
+  of mWasMoved:
+    result = n
+    let t = n[1].typ.skipTypes(abstractVar)
+    let op = getAttachedOp(c.graph, t, attachedWasMoved)
+    if op != nil:
+      result[0] = newSymNode(op)
+      let addrExp = newNodeIT(nkHiddenAddr, result[1].info, makePtrType(c, t))
+      addrExp.add result[1]
+      result[1] = addrExp
   of mUnown:
     result = semUnown(c, n)
   of mExists, mForall:
@@ -587,20 +682,28 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
     if seqType.kind == tySequence and seqType.base.requiresInit:
       message(c.config, n.info, warnUnsafeSetLen, typeToString(seqType.base))
   of mDefault:
-    result = n
-    c.config.internalAssert result[1].typ.kind == tyTypeDesc
-    let constructed = result[1].typ.base
-    if constructed.requiresInit:
-      message(c.config, n.info, warnUnsafeDefault, typeToString(constructed))
+    result = checkDefault(c, n)
+    let typ = result[^1].typ.skipTypes({tyTypeDesc})
+    let defaultExpr = defaultNodeField(c, result[^1], typ, false)
+    if defaultExpr != nil:
+      result = defaultExpr
+  of mZeroDefault:
+    result = checkDefault(c, n)
   of mIsolate:
     if not checkIsolate(n[1]):
       localError(c.config, n.info, "expression cannot be isolated: " & $n[1])
     result = n
-  of mPred:
-    if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}:
-      n[0].sym.magic = mSubU
-    result = n
   of mPrivateAccess:
     result = semPrivateAccess(c, n)
+  of mArrToSeq:
+    result = n
+    if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and
+        expectedType.kind == tySequence and result.typ.elementType.kind == tyEmpty:
+      result.typ = expectedType # type inference for empty sequence # bug #21377
+  of mEnsureMove:
+    result = n
+    if n[1].kind in {nkStmtListExpr, nkBlockExpr,
+              nkIfExpr, nkCaseStmt, nkTryStmt}:
+      localError(c.config, n.info, "Nested expressions cannot be moved: '" & $n[1] & "'")
   else:
     result = n
diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim
index 77bd6b975..048053115 100644
--- a/compiler/semobjconstr.nim
+++ b/compiler/semobjconstr.nim
@@ -11,7 +11,7 @@
 
 # included from sem.nim
 
-from sugar import dup
+from std/sugar import dup
 
 type
   ObjConstrContext = object
@@ -21,6 +21,7 @@ type
                              # set this to true while visiting
                              # parent types.
     missingFields: seq[PSym] # Fields that the user failed to specify
+    checkDefault: bool       # Checking defaults
 
   InitStatus = enum # This indicates the result of object construction
     initUnknown
@@ -29,6 +30,10 @@ type
     initNone     # None of the fields have been initialized
     initConflict # Fields from different branches have been initialized
 
+
+proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
+                        flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]]
+
 proc mergeInitStatus(existing: var InitStatus, newStatus: InitStatus) =
   case newStatus
   of initConflict:
@@ -57,14 +62,13 @@ proc invalidObjConstr(c: PContext, n: PNode) =
 
 proc locateFieldInInitExpr(c: PContext, field: PSym, initExpr: PNode): PNode =
   # Returns the assignment nkExprColonExpr node or nil
+  result = nil
   let fieldId = field.name.id
   for i in 1..<initExpr.len:
     let assignment = initExpr[i]
     if assignment.kind != nkExprColonExpr:
       invalidObjConstr(c, assignment)
-      continue
-
-    if fieldId == considerQuotedIdent(c, assignment[0]).id:
+    elif fieldId == considerQuotedIdent(c, assignment[0]).id:
       return assignment
 
 proc semConstrField(c: PContext, flags: TExprFlags,
@@ -72,7 +76,9 @@ proc semConstrField(c: PContext, flags: TExprFlags,
   let assignment = locateFieldInInitExpr(c, field, initExpr)
   if assignment != nil:
     if nfSem in assignment.flags: return assignment[1]
-    if not fieldVisible(c, field):
+    if nfSkipFieldChecking in assignment[1].flags:
+      discard
+    elif not fieldVisible(c, field):
       localError(c.config, initExpr.info,
         "the field '$1' is not accessible." % [field.name.s])
       return
@@ -80,19 +86,13 @@ proc semConstrField(c: PContext, flags: TExprFlags,
     var initValue = semExprFlagDispatched(c, assignment[1], flags, field.typ)
     if initValue != nil:
       initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info)
+    initValue.flags.incl nfSkipFieldChecking
     assignment[0] = newSymNode(field)
     assignment[1] = initValue
     assignment.flags.incl nfSem
-    return initValue
-
-proc caseBranchMatchesExpr(branch, matched: PNode): bool =
-  for i in 0..<branch.len-1:
-    if branch[i].kind == nkRange:
-      if overlap(branch[i], matched): return true
-    elif exprStructuralEquivalent(branch[i], matched):
-      return true
-
-  return false
+    result = initValue
+  else:
+    result = nil
 
 proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
                 isStmtBranch: bool): IntSet =
@@ -107,6 +107,7 @@ proc branchVals(c: PContext, caseNode: PNode, caseIdx: int,
         result.excl(val)
 
 proc findUsefulCaseContext(c: PContext, discrimator: PNode): (PNode, int) =
+  result = (nil, 0)
   for i in countdown(c.p.caseContext.high, 0):
     let
       (caseNode, index) = c.p.caseContext[i]
@@ -122,7 +123,9 @@ proc pickCaseBranch(caseExpr, matched: PNode): PNode =
       return caseExpr[i]
 
   if endsWithElse:
-    return caseExpr[^1]
+    result = caseExpr[^1]
+  else:
+    result = nil
 
 iterator directFieldsInRecList(recList: PNode): PNode =
   # XXX: We can remove this case by making all nkOfBranch nodes
@@ -133,8 +136,8 @@ iterator directFieldsInRecList(recList: PNode): PNode =
   else:
     doAssert recList.kind == nkRecList
     for field in recList:
-      if field.kind != nkSym: continue
-      yield field
+      if field.kind == nkSym:
+        yield field
 
 template quoteStr(s: string): string = "'" & s & "'"
 
@@ -145,48 +148,90 @@ proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): strin
       if result.len != 0: result.add ", "
       result.add field.sym.name.s.quoteStr
 
+proc locateFieldInDefaults(sym: PSym, defaults: seq[PNode]): bool =
+  result = false
+  for d in defaults:
+    if sym.id == d[0].sym.id:
+      return true
+
 proc collectMissingFields(c: PContext, fieldsRecList: PNode,
-                          constrCtx: var ObjConstrContext) =
+                          constrCtx: var ObjConstrContext, defaults: seq[PNode]
+                          ): seq[PSym] =
+  result = @[]
   for r in directFieldsInRecList(fieldsRecList):
-    if constrCtx.needsFullInit or
-       sfRequiresInit in r.sym.flags or
-       r.sym.typ.requiresInit:
-      let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr)
-      if assignment == nil:
+    let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr)
+    if assignment == nil and not locateFieldInDefaults(r.sym, defaults):
+      if constrCtx.needsFullInit or
+        sfRequiresInit in r.sym.flags or
+          r.sym.typ.requiresInit:
         constrCtx.missingFields.add r.sym
+      else:
+        result.add r.sym
 
-
-proc semConstructFields(c: PContext, n: PNode,
-                        constrCtx: var ObjConstrContext,
-                        flags: TExprFlags): InitStatus =
-  result = initUnknown
-
+proc collectMissingCaseFields(c: PContext, branchNode: PNode,
+                          constrCtx: var ObjConstrContext, defaults: seq[PNode]): seq[PSym] =
+  if branchNode != nil:
+    let fieldsRecList = branchNode[^1]
+    result = collectMissingFields(c, fieldsRecList, constrCtx, defaults)
+  else:
+    result = @[]
+
+proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode,
+                          constrCtx: var ObjConstrContext, defaults: var seq[PNode]) =
+  let res = collectMissingCaseFields(c, branchNode, constrCtx, defaults)
+  for sym in res:
+    let asgnType = newType(tyTypeDesc, c.idgen, sym.typ.owner)
+    let recTyp = sym.typ.skipTypes(defaultFieldsSkipTypes)
+    rawAddSon(asgnType, recTyp)
+    let asgnExpr = newTree(nkCall,
+          newSymNode(getSysMagic(c.graph, constrCtx.initExpr.info, "zeroDefault", mZeroDefault)),
+          newNodeIT(nkType, constrCtx.initExpr.info, asgnType)
+        )
+    asgnExpr.flags.incl nfSkipFieldChecking
+    asgnExpr.typ = recTyp
+    defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr)
+
+proc collectBranchFields(c: PContext, n: PNode, discriminatorVal: PNode,
+                          constrCtx: var ObjConstrContext, flags: TExprFlags) =
+  # All bets are off. If any of the branches has a mandatory
+  # fields we must produce an error:
+  for i in 1..<n.len:
+    let branchNode = n[i]
+    if branchNode != nil:
+      let oldCheckDefault = constrCtx.checkDefault
+      constrCtx.checkDefault = true
+      let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
+      constrCtx.checkDefault = oldCheckDefault
+      if len(defaults) > 0:
+        localError(c.config, discriminatorVal.info, "branch initialization " &
+                    "with a runtime discriminator is not supported " &
+                    "for a branch whose fields have default values.")
+    discard collectMissingCaseFields(c, n[i], constrCtx, @[])
+
+proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext,
+                        flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
+  result = (initUnknown, @[])
   case n.kind
   of nkRecList:
     for field in n:
-      let status = semConstructFields(c, field, constrCtx, flags)
-      mergeInitStatus(result, status)
-
+      let (subSt, subDf) = semConstructFields(c, field, constrCtx, flags)
+      result.status.mergeInitStatus subSt
+      result.defaults.add subDf
   of nkRecCase:
     template fieldsPresentInBranch(branchIdx: int): string =
       let branch = n[branchIdx]
       let fields = branch[^1]
       fieldsPresentInInitExpr(c, fields, constrCtx.initExpr)
 
-    template collectMissingFields(branchNode: PNode) =
-      if branchNode != nil:
-        let fields = branchNode[^1]
-        collectMissingFields(c, fields, constrCtx)
-
     let discriminator = n[0]
     internalAssert c.config, discriminator.kind == nkSym
     var selectedBranch = -1
 
     for i in 1..<n.len:
       let innerRecords = n[i][^1]
-      let status = semConstructFields(c, innerRecords, constrCtx, flags)
+      let (status, _) = semConstructFields(c, innerRecords, constrCtx, flags) # todo
       if status notin {initNone, initUnknown}:
-        mergeInitStatus(result, status)
+        result.status.mergeInitStatus status
         if selectedBranch != -1:
           let prevFields = fieldsPresentInBranch(selectedBranch)
           let currentFields = fieldsPresentInBranch(i)
@@ -194,7 +239,7 @@ proc semConstructFields(c: PContext, n: PNode,
             ("The fields '$1' and '$2' cannot be initialized together, " &
             "because they are from conflicting branches in the case object.") %
             [prevFields, currentFields])
-          result = initConflict
+          result.status = initConflict
         else:
           selectedBranch = i
 
@@ -206,7 +251,7 @@ proc semConstructFields(c: PContext, n: PNode,
             ("cannot prove that it's safe to initialize $1 with " &
             "the runtime value for the discriminator '$2' ") %
             [fields, discriminator.sym.name.s])
-        mergeInitStatus(result, initNone)
+        mergeInitStatus(result.status, initNone)
 
       template wrongBranchError(i) =
         if c.inUncheckedAssignSection == 0:
@@ -289,56 +334,92 @@ proc semConstructFields(c: PContext, n: PNode,
           else:
             wrongBranchError(failedBranch)
 
+      let (_, defaults) = semConstructFields(c, branchNode[^1], constrCtx, flags)
+      result.defaults.add defaults
+
       # When a branch is selected with a partial match, some of the fields
       # that were not initialized may be mandatory. We must check for this:
-      if result == initPartial:
-        collectMissingFields branchNode
-
+      if result.status == initPartial:
+        collectOrAddMissingCaseFields(c, branchNode, constrCtx, result.defaults)
     else:
-      result = initNone
+      result.status = initNone
       let discriminatorVal = semConstrField(c, flags + {efPreferStatic},
                                             discriminator.sym,
                                             constrCtx.initExpr)
       if discriminatorVal == nil:
-        # None of the branches were explicitly selected by the user and no
-        # 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 defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
-        let matchedBranch = n.pickCaseBranch defaultValue
-        collectMissingFields matchedBranch
+        if discriminator.sym.ast != nil:
+          # branch is selected by the default field value of discriminator
+          let discriminatorDefaultVal = discriminator.sym.ast
+          result.status = initUnknown
+          result.defaults.add newTree(nkExprColonExpr, n[0], discriminatorDefaultVal)
+          if discriminatorDefaultVal.kind == nkIntLit:
+            let matchedBranch = n.pickCaseBranch discriminatorDefaultVal
+            if matchedBranch != nil:
+              let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
+              result.defaults.add defaults
+              collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
+          else:
+            collectBranchFields(c, n, discriminatorDefaultVal, constrCtx, flags)
+        else:
+          # None of the branches were explicitly selected by the user and no
+          # 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 defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0)
+          let matchedBranch = n.pickCaseBranch defaultValue
+          discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[])
       else:
-        result = initPartial
+        result.status = initPartial
         if discriminatorVal.kind == nkIntLit:
           # When the discriminator is a compile-time value, we also know
           # which branch will be selected:
           let matchedBranch = n.pickCaseBranch discriminatorVal
-          if matchedBranch != nil: collectMissingFields matchedBranch
+          if matchedBranch != nil:
+            let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags)
+            result.defaults.add defaults
+            collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults)
         else:
-          # All bets are off. If any of the branches has a mandatory
-          # fields we must produce an error:
-          for i in 1..<n.len: collectMissingFields n[i]
+          collectBranchFields(c, n, discriminatorVal, constrCtx, flags)
 
   of nkSym:
     let field = n.sym
     let e = semConstrField(c, flags, field, constrCtx.initExpr)
-    result = if e != nil: initFull else: initNone
-
+    if e != nil:
+      result.status = initFull
+    elif field.ast != nil:
+      if efIgnoreDefaults notin flags:
+        result.status = initUnknown
+        result.defaults.add newTree(nkExprColonExpr, n, field.ast)
+      else:
+        result.status = initNone
+    else:
+      if {efWantNoDefaults, efIgnoreDefaults} * flags == {}: # cannot compute defaults at the typeRightPass
+        let defaultExpr = defaultNodeField(c, n, constrCtx.checkDefault)
+        if defaultExpr != nil:
+          result.status = initUnknown
+          result.defaults.add newTree(nkExprColonExpr, n, defaultExpr)
+        else:
+          result.status = initNone
+      else:
+        result.status = initNone
   else:
     internalAssert c.config, false
 
 proc semConstructTypeAux(c: PContext,
                          constrCtx: var ObjConstrContext,
-                         flags: TExprFlags): InitStatus =
-  result = initUnknown
+                         flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] =
+  result = (initUnknown, @[])
   var t = constrCtx.typ
   while true:
-    let status = semConstructFields(c, t.n, constrCtx, flags)
-    mergeInitStatus(result, status)
+    let (status, defaults) = semConstructFields(c, t.n, constrCtx, flags)
+    result.status.mergeInitStatus status
+    result.defaults.add defaults
     if status in {initPartial, initNone, initUnknown}:
-      collectMissingFields c, t.n, constrCtx
-    let base = t[0]
-    if base == nil: break
+      discard collectMissingFields(c, t.n, constrCtx, result.defaults)
+    let base = t.baseClass
+    if base == nil or base.id == t.id or
+        base.kind in {tyRef, tyPtr} and base.elementType.id == t.id:
+      break
     t = skipTypes(base, skipPtrs)
     if t.kind != tyObject:
       # XXX: This is not supposed to happen, but apparently
@@ -355,17 +436,17 @@ proc initConstrContext(t: PType, initExpr: PNode): ObjConstrContext =
 proc computeRequiresInit(c: PContext, t: PType): bool =
   assert t.kind == tyObject
   var constrCtx = initConstrContext(t, newNode(nkObjConstr))
-  let initResult = semConstructTypeAux(c, constrCtx, {})
+  let initResult = semConstructTypeAux(c, constrCtx, {efWantNoDefaults})
   constrCtx.missingFields.len > 0
 
 proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
   var objType = t
   while objType.kind notin {tyObject, tyDistinct}:
-    objType = objType.lastSon
+    objType = objType.last
     assert objType != nil
   if objType.kind == tyObject:
     var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info))
-    let initResult = semConstructTypeAux(c, constrCtx, {})
+    let initResult = semConstructTypeAux(c, constrCtx, {efIgnoreDefaults})
     if constrCtx.missingFields.len > 0:
       localError(c.config, info,
         "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)])
@@ -378,11 +459,12 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) =
 proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType = nil): PNode =
   var t = semTypeNode(c, n[0], nil)
   result = newNodeIT(nkObjConstr, n.info, t)
-  for child in n: result.add child
+  for i in 0..<n.len:
+    result.add n[i]
 
   if t == nil:
     return localErrorNode(c, result, "object constructor needs an object type")
-  
+
   if t.skipTypes({tyGenericInst,
       tyAlias, tySink, tyOwned, tyRef}).kind != tyObject and
       expectedType != nil and expectedType.skipTypes({tyGenericInst,
@@ -391,7 +473,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
 
   t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
   if t.kind == tyRef:
-    t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned})
+    t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
     if optOwnedRefs in c.config.globalOptions:
       result.typ = makeVarType(c, result.typ, tyOwned)
       # we have to watch out, there are also 'owned proc' types that can be used
@@ -399,7 +481,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
       result.typ.flags.incl tfHasOwned
   if t.kind != tyObject:
     return localErrorNode(c, result, if t.kind != tyGenericBody:
-      "object constructor needs an object type".dup(addDeclaredLoc(c.config, t))
+      "object constructor needs an object type".dup(addTypeNodeDeclaredLoc(c.config, t))
       else: "cannot instantiate: '" &
         typeToString(t, preferDesc) &
         "'; the object's generic parameters cannot be inferred and must be explicitly given"
@@ -409,7 +491,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
   # field (if this is a case object, initialized fields in two different
   # branches will be reported as an error):
   var constrCtx = initConstrContext(t, result)
-  let initResult = semConstructTypeAux(c, constrCtx, flags)
+  let (initResult, defaults) = semConstructTypeAux(c, constrCtx, flags)
   var hasError = false # needed to split error detect/report for better msgs
 
   # It's possible that the object was not fully initialized while
@@ -445,6 +527,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags; expectedType: PType
       hasError = true
       break
 
+  result.sons.add defaults
+
   if initResult == initFull:
     incl result.flags, nfAllFieldsSet
 
diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim
index f696f6a0a..23a8e6362 100644
--- a/compiler/semparallel.nim
+++ b/compiler/semparallel.nim
@@ -25,8 +25,8 @@ import
   ast, astalgo, idents, lowerings, magicsys, guards, msgs,
   renderer, types, modulegraphs, options, spawn, lineinfos
 
-from trees import getMagic, isTrue, getRoot
-from strutils import `%`
+from trees import getMagic, getRoot
+from std/strutils import `%`
 
 discard """
 
@@ -77,12 +77,12 @@ type
     graph: ModuleGraph
 
 proc initAnalysisCtx(g: ModuleGraph): AnalysisCtx =
-  result.locals = @[]
-  result.slices = @[]
-  result.args = @[]
+  result = AnalysisCtx(locals: @[],
+    slices: @[],
+    args: @[],
+    graph: g)
   result.guards.s = @[]
   result.guards.g = g
-  result.graph = g
 
 proc lookupSlot(c: AnalysisCtx; s: PSym): int =
   for i in 0..<c.locals.len:
@@ -184,7 +184,10 @@ proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
     let s = c.lookupSlot(n.sym)
     if s >= 0 and c.locals[s].stride != nil:
       result = c.locals[s].stride.intVal
+    else:
+      result = 0
   else:
+    result = 0
     for i in 0..<n.safeLen: result += stride(c, n[i])
 
 proc subStride(c: AnalysisCtx; n: PNode): PNode =
@@ -323,7 +326,7 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
 
 proc analyse(c: var AnalysisCtx; n: PNode) =
   case n.kind
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     let y = n[1].skipConv
     if n[0].isSingleAssignable and y.isLocal:
       let slot = c.getSlot(y.sym)
@@ -402,6 +405,9 @@ proc transformSlices(g: ModuleGraph; idgen: IdGenerator; n: PNode): PNode =
     let op = n[0].sym
     if op.name.s == "[]" and op.fromSystem:
       result = copyNode(n)
+      var typ = newType(tyOpenArray, idgen, result.typ.owner)
+      typ.add result.typ.elementType
+      result.typ = typ
       let opSlice = newSymNode(createMagic(g, idgen, "slice", mSlice))
       opSlice.typ = getSysType(g, n.info, tyInt)
       result.add opSlice
@@ -435,16 +441,16 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier:
         if result.isNil:
           result = newNodeI(nkStmtList, n.info)
           result.add n
-        let t = b[1][0].typ[0]
+        let t = b[1][0].typ.returnType
         if spawnResult(t, true) == srByVar:
           result.add wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, it[0])
           it[^1] = newNodeI(nkEmpty, it.info)
         else:
           it[^1] = wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, nil)
     if result.isNil: result = n
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     let b = n[1]
-    if getMagic(b) == mSpawn and (let t = b[1][0].typ[0];
+    if getMagic(b) == mSpawn and (let t = b[1][0].typ.returnType;
         spawnResult(t, true) == srByVar):
       let m = transformSlices(g, idgen, b)
       return wrapProcForSpawn(g, idgen, owner, m, b.typ, barrier, n[0])
@@ -483,7 +489,7 @@ proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): P
   checkArgs(a, body)
 
   var varSection = newNodeI(nkVarSection, n.info)
-  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextSymId idgen, owner, n.info)
+  var temp = newSym(skTemp, getIdent(g.cache, "barrier"), idgen, owner, n.info)
   temp.typ = magicsys.getCompilerProc(g, "Barrier").typ
   incl(temp.flags, sfFromGeneric)
   let tempNode = newSymNode(temp)
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 940291f53..0a160897f 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -8,9 +8,12 @@
 #
 
 import
-  intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
-  wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
-  modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables
+  ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
+  wordrecg, options, guards, lineinfos, semfold, semdata,
+  modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling,
+  semstrictfuncs, suggestsymdb, pushpoppragmas
+
+import std/[tables, intsets, strutils, sequtils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -63,34 +66,69 @@ discard """
 """
 
 type
+  CaughtExceptionsStack = object
+    nodes: seq[seq[PType]]
   TEffects = object
     exc: PNode  # stack of exceptions
+    when defined(nimsuggest):
+      caughtExceptions: CaughtExceptionsStack
     tags: PNode # list of tags
     forbids: PNode # list of tags
-    bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt: int
+    bottom, inTryStmt, inExceptOrFinallyStmt, leftPartOfAsgn, inIfStmt, currentBlock: int
     owner: PSym
     ownerModule: PSym
     init: seq[int] # list of initialized variables
+    scopes: Table[int, int] # maps var-id to its scope (see also `currentBlock`).
     guards: TModel # nested guards
     locked: seq[PNode] # locked locations
     gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
-    hasDangerousAssign, isInnerProc: bool
+    isInnerProc: bool
     inEnforcedNoSideEffects: bool
-    maxLockLevel, currLockLevel: TLockLevel
     currOptions: TOptions
+    optionsStack: seq[(TOptions, TNoteKinds)]
     config: ConfigRef
     graph: ModuleGraph
     c: PContext
     escapingParams: IntSet
   PEffects = var TEffects
 
-proc `<`(a, b: TLockLevel): bool {.borrow.}
-proc `<=`(a, b: TLockLevel): bool {.borrow.}
-proc `==`(a, b: TLockLevel): bool {.borrow.}
-proc max(a, b: TLockLevel): TLockLevel {.borrow.}
+const
+  errXCannotBeAssignedTo = "'$1' cannot be assigned to"
+  errLetNeedsInit = "'let' symbol requires an initialization"
+
+proc getObjDepth(t: PType): (int, ItemId) =
+  var x = t
+  result = (-1, default(ItemId))
+  var stack = newSeq[ItemId]()
+  while x != nil:
+    x = skipTypes(x, skipPtrs)
+    if x.kind != tyObject:
+      return (-3, default(ItemId))
+    stack.add x.itemId
+    x = x.baseClass
+    inc(result[0])
+  result[1] = stack[^2]
+
+proc collectObjectTree(graph: ModuleGraph, n: PNode) =
+  for section in n:
+    if section.kind == nkTypeDef and section[^1].kind in {nkObjectTy, nkRefTy, nkPtrTy} and section[^1].typ != nil:
+      let typ = section[^1].typ.skipTypes(skipPtrs)
+      if typ.kind == tyObject and typ.baseClass != nil:
+        let (depthLevel, root) = getObjDepth(typ)
+        if depthLevel != -3:
+          if depthLevel == 1:
+            graph.objectTree[root] = @[]
+          else:
+            if root notin graph.objectTree:
+              graph.objectTree[root] = @[(depthLevel, typ)]
+            else:
+              graph.objectTree[root].add (depthLevel, typ)
 
 proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
-  if typ == nil: return
+  if typ == nil or sfGeneratedOp in tracked.owner.flags:
+    # don't create type bound ops for anything in a function with a `nodestroy` pragma
+    # bug #21987
+    return
   when false:
     let realType = typ.skipTypes(abstractInst)
     if realType.kind == tyRef and
@@ -102,38 +140,16 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
       optSeqDestructors in tracked.config.globalOptions:
     tracked.owner.flags.incl sfInjectDestructors
 
-proc isLocalVar(a: PEffects, s: PSym): bool =
-  # and (s.kind != skParam or s.typ.kind == tyOut)
-  s.kind in {skVar, skResult} and sfGlobal notin s.flags and
-    s.owner == a.owner and s.typ != nil
-
-proc getLockLevel(t: PType): TLockLevel =
-  var t = t
-  # tyGenericInst(TLock {tyGenericBody}, tyStatic, tyObject):
-  if t.kind == tyGenericInst and t.len == 3: t = t[1]
-  if t.kind == tyStatic and t.n != nil and t.n.kind in {nkCharLit..nkInt64Lit}:
-    result = t.n.intVal.TLockLevel
+proc isLocalSym(a: PEffects, s: PSym): bool =
+  s.typ != nil and (s.kind in {skLet, skVar, skResult} or (s.kind == skParam and isOutParam(s.typ))) and
+    sfGlobal notin s.flags and s.owner == a.owner
 
 proc lockLocations(a: PEffects; pragma: PNode) =
   if pragma.kind != nkExprColonExpr:
     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(a.config, x.info, "invalid lock level: " & $thisLL)
-      elif firstLL < 0.TLockLevel: firstLL = thisLL
-      elif firstLL != thisLL:
-        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(a.config, pragma.info, "invalid nested locking")
-    a.currLockLevel = firstLL
 
 proc guardGlobal(a: PEffects; n: PNode; guard: PSym) =
   # check whether the corresponding lock is held:
@@ -189,23 +205,54 @@ proc makeVolatile(a: PEffects; s: PSym) {.inline.} =
   if a.inTryStmt > 0 and a.config.exc == excSetjmp:
     incl(s.flags, sfVolatile)
 
+proc varDecl(a: PEffects; n: PNode) {.inline.} =
+  if n.kind == nkSym:
+    a.scopes[n.sym.id] = a.currentBlock
+
+proc skipHiddenDeref(n: PNode): PNode {.inline.} =
+  result = if n.kind == nkHiddenDeref: n[0] else: n
+
 proc initVar(a: PEffects, n: PNode; volatileCheck: bool) =
+  let n = skipHiddenDeref(n)
   if n.kind != nkSym: return
   let s = n.sym
-  if isLocalVar(a, s):
+  if isLocalSym(a, s):
     if volatileCheck: makeVolatile(a, s)
     for x in a.init:
-      if x == s.id: return
+      if x == s.id:
+        if strictDefs in a.c.features and s.kind == skLet:
+          localError(a.config, n.info, errXCannotBeAssignedTo %
+                    renderTree(n, {renderNoComments}
+                ))
+        return
     a.init.add s.id
+    if a.scopes.getOrDefault(s.id) == a.currentBlock:
+      #[ Consider this case:
+
+      var x: T
+      while true:
+        if cond:
+          x = T() #1
+        else:
+          x = T() #2
+        use x
+
+      Even though both #1 and #2 are first writes we must use the `=copy`
+      here so that the old value is destroyed because `x`'s destructor is
+      run outside of the while loop. This is why we need the check here that
+      the assignment is done in the same logical block as `x` was declared in.
+      ]#
+      n.flags.incl nfFirstWrite
 
 proc initVarViaNew(a: PEffects, n: PNode) =
+  let n = skipHiddenDeref(n)
   if n.kind != nkSym: return
   let s = n.sym
   if {tfRequiresInit, tfNotNil} * s.typ.flags <= {tfNotNil}:
     # 'x' is not nil, but that doesn't mean its "not nil" children
     # are initialized:
     initVar(a, n, volatileCheck=true)
-  elif isLocalVar(a, s):
+  elif isLocalSym(a, s):
     makeVolatile(a, s)
 
 proc warnAboutGcUnsafe(n: PNode; conf: ConfigRef) =
@@ -224,7 +271,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) =
       if reason.kind == nkSym:
         a.owner.gcUnsafetyReason = reason.sym
       else:
-        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextSymId a.c.idgen,
+        a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, a.c.idgen,
                                           a.owner, reason.info, {})
 
 proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
@@ -237,7 +284,7 @@ proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) =
           sym = reason.sym
         else:
           let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown
-          sym = newSym(kind, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {})
+          sym = newSym(kind, a.owner.name, a.c.idgen, a.owner, reason.info, {})
       else:
         sym = reason
       a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym)
@@ -260,10 +307,15 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: Co
     of routineKinds:
       # recursive call *always* produces only a warning so the full error
       # message is printed:
-      listGcUnsafety(u, true, cycleCheck, conf)
-      message(conf, s.info, msgKind,
-        "'$#' is not GC-safe as it calls '$#'" %
-        [s.name.s, u.name.s])
+      if u.kind == skMethod and {sfBase, sfThread} * u.flags == {sfBase}:
+        message(conf, u.info, msgKind,
+          "Base method '$#' requires explicit '{.gcsafe.}' to be GC-safe" %
+          [u.name.s])
+      else:
+        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(conf, s.info, msgKind,
         "'$#' is not GC-safe as it performs an indirect call via '$#'" %
@@ -318,7 +370,7 @@ proc useVar(a: PEffects, n: PNode) =
   let s = n.sym
   if a.inExceptOrFinallyStmt > 0:
     incl s.flags, sfUsedInFinallyOrExcept
-  if isLocalVar(a, s):
+  if isLocalSym(a, s):
     if sfNoInit in s.flags:
       # If the variable is explicitly marked as .noinit. do not emit any error
       a.init.add s.id
@@ -326,21 +378,34 @@ proc useVar(a: PEffects, n: PNode) =
       if s.typ.requiresInit:
         message(a.config, n.info, warnProveInit, s.name.s)
       elif a.leftPartOfAsgn <= 0:
-        message(a.config, n.info, warnUninit, s.name.s)
+        if strictDefs in a.c.features:
+          if s.kind == skLet:
+            localError(a.config, n.info, errLetNeedsInit)
+          else:
+            message(a.config, n.info, warnUninit, s.name.s)
       # prevent superfluous warnings about the same variable:
       a.init.add s.id
   useVarNoInitCheck(a, n, s)
 
+type
+  BreakState = enum
+    bsNone
+    bsBreakOrReturn
+    bsNoReturn
 
 type
   TIntersection = seq[tuple[id, count: int]] # a simple count table
 
-proc addToIntersection(inter: var TIntersection, s: int) =
+proc addToIntersection(inter: var TIntersection, s: int, state: BreakState) =
   for j in 0..<inter.len:
     if s == inter[j].id:
-      inc inter[j].count
+      if state == bsNone:
+        inc inter[j].count
       return
-  inter.add((id: s, count: 1))
+  if state == bsNone:
+    inter.add((id: s, count: 1))
+  else:
+    inter.add((id: s, count: 0))
 
 proc throws(tracked, n, orig: PNode) =
   if n.typ == nil or n.typ.kind != tyError:
@@ -351,7 +416,7 @@ proc throws(tracked, n, orig: PNode) =
     else:
       tracked.add n
 
-proc getEbase(g: ModuleGraph; info: TLineInfo): PType =
+proc getEbase*(g: ModuleGraph; info: TLineInfo): PType =
   result = g.sysTypeFromName(info, "Exception")
 
 proc excType(g: ModuleGraph; n: PNode): PType =
@@ -378,7 +443,7 @@ proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) =
     if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return
 
   if e.typ != nil:
-    if optNimV1Emulation in a.config.globalOptions or not isDefectException(e.typ):
+    if not isDefectException(e.typ):
       throws(a.exc, e, comesFrom)
 
 proc addTag(a: PEffects, e, comesFrom: PNode) =
@@ -411,8 +476,6 @@ proc listEffects(a: PEffects) =
   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))
   for e in items(a.forbids): message(a.config, e.info, hintUser, typeToString(e.typ))
-  #if a.maxLockLevel != 0:
-  #  message(e.info, hintUser, "lockLevel: " & a.maxLockLevel)
 
 proc catches(tracked: PEffects, e: PType) =
   let e = skipTypes(e, skipPtrs)
@@ -434,6 +497,18 @@ proc catchesAll(tracked: PEffects) =
   if tracked.exc.len > 0:
     setLen(tracked.exc.sons, tracked.bottom)
 
+proc push(s: var CaughtExceptionsStack) =
+  s.nodes.add(@[])
+
+proc pop(s: var CaughtExceptionsStack) =
+  s.nodes.del(high(s.nodes))
+
+proc addCatch(s: var CaughtExceptionsStack, e: PType) =
+  s.nodes[high(s.nodes)].add(e)
+
+proc addCatchAll(s: var CaughtExceptionsStack) =
+  s.nodes[high(s.nodes)].add(nil)
+
 proc track(tracked: PEffects, n: PNode)
 proc trackTryStmt(tracked: PEffects, n: PNode) =
   let oldBottom = tracked.bottom
@@ -442,11 +517,32 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
   let oldState = tracked.init.len
   var inter: TIntersection = @[]
 
+  when defined(nimsuggest):
+    tracked.caughtExceptions.push
+    for i in 1..<n.len:
+      let b = n[i]
+      if b.kind == nkExceptBranch:
+        if b.len == 1:
+          tracked.caughtExceptions.addCatchAll
+        else:
+          for j in 0..<b.len - 1:
+            if b[j].isInfixAs():
+              assert(b[j][1].kind == nkType)
+              tracked.caughtExceptions.addCatch(b[j][1].typ)
+            else:
+              assert(b[j].kind == nkType)
+              tracked.caughtExceptions.addCatch(b[j].typ)
+      else:
+        assert b.kind == nkFinally
+
   inc tracked.inTryStmt
   track(tracked, n[0])
   dec tracked.inTryStmt
   for i in oldState..<tracked.init.len:
-    addToIntersection(inter, tracked.init[i])
+    addToIntersection(inter, tracked.init[i], bsNone)
+
+  when defined(nimsuggest):
+    tracked.caughtExceptions.pop
 
   var branches = 1
   var hasFinally = false
@@ -475,9 +571,13 @@ proc trackTryStmt(tracked: PEffects, n: PNode) =
     let b = n[i]
     if b.kind == nkExceptBranch:
       setLen(tracked.init, oldState)
+      for j in 0..<b.len - 1:
+        if b[j].isInfixAs(): # skips initialization checks
+          assert(b[j][2].kind == nkSym)
+          tracked.init.add b[j][2].sym.id
       track(tracked, b[^1])
       for i in oldState..<tracked.init.len:
-        addToIntersection(inter, tracked.init[i])
+        addToIntersection(inter, tracked.init[i], bsNone)
     else:
       setLen(tracked.init, oldState)
       track(tracked, b[^1])
@@ -497,7 +597,7 @@ proc isIndirectCall(tracked: PEffects; n: PNode): bool =
   if n.kind != nkSym:
     result = true
   elif n.sym.kind == skParam:
-    if strictEffects in tracked.c.features:
+    if laxEffects notin tracked.c.config.legacyFeatures:
       if tracked.owner == n.sym.owner and sfEffectsDelayed in n.sym.flags:
         result = false # it is not a harmful call
       else:
@@ -506,6 +606,8 @@ proc isIndirectCall(tracked: PEffects; n: PNode): bool =
       result = tracked.owner != n.sym.owner or tracked.owner == nil
   elif n.sym.kind notin routineKinds:
     result = true
+  else:
+    result = false
 
 proc isForwardedProc(n: PNode): bool =
   result = n.kind == nkSym and sfForward in n.sym.flags
@@ -514,9 +616,16 @@ proc trackPragmaStmt(tracked: PEffects, n: PNode) =
   for i in 0..<n.len:
     var it = n[i]
     let pragma = whichPragma(it)
-    if pragma == wEffects:
+    case pragma
+    of wEffects:
       # list the computed effects up to here:
       listEffects(tracked)
+    of wPush:
+      processPushBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions, n, i+1)
+    of wPop:
+      processPopBackendOption(tracked.c.config, tracked.optionsStack, tracked.currOptions)
+    else:
+      discard
 
 template notGcSafe(t): untyped = {tfGcSafe, tfNoSideEffect} * t.flags == {}
 
@@ -524,25 +633,6 @@ proc importedFromC(n: PNode): bool =
   # when imported from C, we assume GC-safety.
   result = n.kind == nkSym and sfImportc in n.sym.flags
 
-proc getLockLevel(s: PSym): TLockLevel =
-  result = s.typ.lockLevel
-  if result == UnspecifiedLockLevel:
-    if {sfImportc, sfNoSideEffect} * s.flags != {} or
-       tfNoSideEffect in s.typ.flags:
-      result = 0.TLockLevel
-    else:
-      result = UnknownLockLevel
-      #message(??.config, s.info, warnUser, "FOR THIS " & s.name.s)
-
-proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) =
-  if lockLevel >= tracked.currLockLevel:
-    # if in lock section:
-    if tracked.currLockLevel > 0.TLockLevel:
-      localError tracked.config, n.info, errGenerated,
-        "expected lock level < " & $tracked.currLockLevel &
-        " but got lock level " & $lockLevel
-    tracked.maxLockLevel = max(tracked.maxLockLevel, lockLevel)
-
 proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
   let pragma = s.ast[pragmasPos]
   let spec = effectSpec(pragma, wRaises)
@@ -556,7 +646,6 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
     markGcUnsafe(tracked, s)
   if tfNoSideEffect notin s.typ.flags:
     markSideEffect(tracked, s, n.info)
-  mergeLockLevels(tracked, n, s.getLockLevel)
 
 proc procVarCheck(n: PNode; conf: ConfigRef) =
   if n.kind in nkSymChoices:
@@ -575,7 +664,7 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
   if paramType != nil and tfNotNil in paramType.flags and n.typ != nil:
     let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink})
     if ntyp != nil and tfNotNil notin ntyp.flags:
-      if isAddrNode(n):
+      if n.kind in {nkAddr, nkHiddenAddr}:
         # addr(x[]) can't be proven, but addr(x) can:
         if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return
       elif (n.kind == nkSym and n.sym.kind in routineKinds) or
@@ -596,11 +685,6 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
 proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) =
   addRaiseEffect(tracked, createRaise(tracked.graph, n), nil)
   addTag(tracked, createTag(tracked.graph, n), nil)
-  let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel
-                  else: op.lockLevel
-  #if lockLevel == UnknownLockLevel:
-  #  message(??.config, n.info, warnUser, "had to assume the worst here")
-  mergeLockLevels(tracked, n, lockLevel)
 
 proc isOwnedProcVar(tracked: PEffects; n: PNode): bool =
   # XXX prove the soundness of this effect system rule
@@ -608,7 +692,7 @@ proc isOwnedProcVar(tracked: PEffects; n: PNode): bool =
     tracked.owner == n.sym.owner
   #if result and sfPolymorphic notin n.sym.flags:
   #  echo tracked.config $ n.info, " different here!"
-  if strictEffects in tracked.c.features:
+  if laxEffects notin tracked.c.config.legacyFeatures:
     result = result and sfEffectsDelayed in n.sym.flags
 
 proc isNoEffectList(n: PNode): bool {.inline.} =
@@ -621,11 +705,11 @@ proc isTrival(caller: PNode): bool {.inline.} =
 proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; argIndex: int; caller: PNode) =
   let a = skipConvCastAndClosure(n)
   let op = a.typ
-  let param = if formals != nil and argIndex < formals.len and formals.n != nil: formals.n[argIndex].sym else: nil
+  let param = if formals != nil and formals.n != nil and argIndex < formals.n.len: formals.n[argIndex].sym else: nil
   # assume indirect calls are taken here:
   if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and
       not isTrival(caller) and
-      ((param != nil and sfEffectsDelayed in param.flags) or strictEffects notin tracked.c.features):
+      ((param != nil and sfEffectsDelayed in param.flags) or laxEffects in tracked.c.config.legacyFeatures):
 
     internalAssert tracked.config, op.n[0].kind == nkEffectList
     var effectList = op.n[0]
@@ -656,10 +740,10 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
         markGcUnsafe(tracked, a)
       elif tfNoSideEffect notin op.flags:
         markSideEffect(tracked, a, n.info)
-  let paramType = if formals != nil and argIndex < formals.len: formals[argIndex] else: nil
+  let paramType = if formals != nil and argIndex < formals.signatureLen: formals[argIndex] else: nil
   if paramType != nil and paramType.kind in {tyVar}:
     invalidateFacts(tracked.guards, n)
-    if n.kind == nkSym and isLocalVar(tracked, n.sym):
+    if n.kind == nkSym and isLocalSym(tracked, n.sym):
       makeVolatile(tracked, n.sym)
   if paramType != nil and paramType.kind == tyProc and tfGcSafe in paramType.flags:
     let argtype = skipTypes(a.typ, abstractInst)
@@ -669,15 +753,50 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, formals: PType; ar
       localError(tracked.config, n.info, $n & " is not GC safe")
   notNilCheck(tracked, n, paramType)
 
-proc breaksBlock(n: PNode): bool =
+
+proc breaksBlock(n: PNode): BreakState =
   # semantic check doesn't allow statements after raise, break, return or
   # call to noreturn proc, so it is safe to check just the last statements
   var it = n
   while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0:
     it = it.lastSon
 
-  result = it.kind in {nkBreakStmt, nkReturnStmt, nkRaiseStmt} or
-    it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+  case it.kind
+  of nkBreakStmt, nkReturnStmt:
+    result = bsBreakOrReturn
+  of nkRaiseStmt:
+    result = bsNoReturn
+  of nkCallKinds:
+    if it[0].kind == nkSym and sfNoReturn in it[0].sym.flags:
+      result = bsNoReturn
+    else:
+      result = bsNone
+  else:
+    result = bsNone
+
+proc addIdToIntersection(tracked: PEffects, inter: var TIntersection, resCounter: var int,
+            hasBreaksBlock: BreakState, oldState: int, resSym: PSym, hasResult: bool) =
+  if hasResult:
+    var alreadySatisfy = false
+
+    if hasBreaksBlock == bsNoReturn:
+      alreadySatisfy = true
+      inc resCounter
+
+    for i in oldState..<tracked.init.len:
+      if tracked.init[i] == resSym.id:
+        if not alreadySatisfy:
+          inc resCounter
+          alreadySatisfy = true
+      else:
+        addToIntersection(inter, tracked.init[i], hasBreaksBlock)
+  else:
+    for i in oldState..<tracked.init.len:
+      addToIntersection(inter, tracked.init[i], hasBreaksBlock)
+
+template hasResultSym(s: PSym): bool =
+  s != nil and s.kind in {skProc, skFunc, skConverter, skMethod} and
+    not isEmptyType(s.typ.returnType)
 
 proc trackCase(tracked: PEffects, n: PNode) =
   track(tracked, n[0])
@@ -687,9 +806,13 @@ proc trackCase(tracked: PEffects, n: PNode) =
   let stringCase = n[0].typ != nil and skipTypes(n[0].typ,
         abstractVarRange-{tyTypeDesc}).kind in {tyFloat..tyFloat128, tyString, tyCstring}
   let interesting = not stringCase and interestingCaseExpr(n[0]) and
-        tracked.config.hasWarn(warnProveField)
+        (tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features)
   var inter: TIntersection = @[]
   var toCover = 0
+  let hasResult = hasResultSym(tracked.owner)
+  let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
+  var resCounter = 0
+
   for i in 1..<n.len:
     let branch = n[i]
     setLen(tracked.init, oldState)
@@ -698,12 +821,15 @@ proc trackCase(tracked: PEffects, n: PNode) =
       addCaseBranchFacts(tracked.guards, n, i)
     for i in 0..<branch.len:
       track(tracked, branch[i])
-    if not breaksBlock(branch.lastSon): inc toCover
-    for i in oldState..<tracked.init.len:
-      addToIntersection(inter, tracked.init[i])
+    let hasBreaksBlock = breaksBlock(branch.lastSon)
+    if hasBreaksBlock == bsNone:
+      inc toCover
+    addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
 
   setLen(tracked.init, oldState)
   if not stringCase or lastSon(n).kind == nkElse:
+    if hasResult and resCounter == n.len-1:
+        tracked.init.add resSym.id
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge
@@ -717,12 +843,17 @@ proc trackIf(tracked: PEffects, n: PNode) =
   addFact(tracked.guards, n[0][0])
   let oldState = tracked.init.len
 
+  let hasResult = hasResultSym(tracked.owner)
+  let resSym = if hasResult: tracked.owner.ast[resultPos].sym else: nil
+  var resCounter = 0
+
   var inter: TIntersection = @[]
   var toCover = 0
   track(tracked, n[0][1])
-  if not breaksBlock(n[0][1]): inc toCover
-  for i in oldState..<tracked.init.len:
-    addToIntersection(inter, tracked.init[i])
+  let hasBreaksBlock = breaksBlock(n[0][1])
+  if hasBreaksBlock == bsNone:
+    inc toCover
+  addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
 
   for i in 1..<n.len:
     let branch = n[i]
@@ -734,11 +865,15 @@ proc trackIf(tracked: PEffects, n: PNode) =
     setLen(tracked.init, oldState)
     for i in 0..<branch.len:
       track(tracked, branch[i])
-    if not breaksBlock(branch.lastSon): inc toCover
-    for i in oldState..<tracked.init.len:
-      addToIntersection(inter, tracked.init[i])
+    let hasBreaksBlock = breaksBlock(branch.lastSon)
+    if hasBreaksBlock == bsNone:
+      inc toCover
+    addIdToIntersection(tracked, inter, resCounter, hasBreaksBlock, oldState, resSym, hasResult)
+
   setLen(tracked.init, oldState)
   if lastSon(n).len == 1:
+    if hasResult and resCounter == n.len:
+        tracked.init.add resSym.id
     for id, count in items(inter):
       if count >= toCover: tracked.init.add id
     # else we can't merge as it is not exhaustive
@@ -824,9 +959,22 @@ proc passedToEffectsDelayedParam(tracked: PEffects; n: PNode) =
 ]#
 
 proc checkForSink(tracked: PEffects; n: PNode) =
-  if tracked.inIfStmt == 0:
+  if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options:
     checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n)
 
+proc markCaughtExceptions(tracked: PEffects; g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) =
+  when defined(nimsuggest):
+    proc internalMarkCaughtExceptions(tracked: PEffects; q: var SuggestFileSymbolDatabase; info: TLineInfo) =
+      var si = q.findSymInfoIndex(info)
+      if si != -1:
+        q.caughtExceptionsSet[si] = true
+        for w1 in tracked.caughtExceptions.nodes:
+          for w2 in w1:
+            q.caughtExceptions[si].add(w2)
+
+    if optIdeExceptionInlayHints in tracked.config.globalOptions:
+      internalMarkCaughtExceptions(tracked, g.suggestSymbols.mgetOrPut(info.fileIndex, newSuggestFileSymbolDatabase(info.fileIndex, true)), info)
+
 proc trackCall(tracked: PEffects; n: PNode) =
   template gcsafeAndSideeffectCheck() =
     if notGcSafe(op) and not importedFromC(a):
@@ -838,7 +986,6 @@ proc trackCall(tracked: PEffects; n: PNode) =
       # and it's not a recursive call:
       if not (a.kind == nkSym and a.sym == tracked.owner):
         markSideEffect(tracked, a, n.info)
-
   # p's effects are ours too:
   var a = n[0]
   #if canRaise(a):
@@ -847,7 +994,16 @@ proc trackCall(tracked: PEffects; n: PNode) =
   if n.typ != nil:
     if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray:
       createTypeBoundOps(tracked, n.typ, n.info)
-  if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil:
+
+  when defined(nimsuggest):
+    var actualLoc = a.info
+    if n.kind == nkHiddenCallConv:
+      actualLoc = n.info
+    if a.kind == nkSym:
+      markCaughtExceptions(tracked, tracked.graph, actualLoc, a.sym, tracked.graph.usageSym)
+
+  let notConstExpr = getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) == nil
+  if notConstExpr:
     if a.kind == nkCast and a[1].typ.kind == tyProc:
       a = a[1]
     # XXX: in rare situations, templates and macros will reach here after
@@ -858,12 +1014,14 @@ proc trackCall(tracked: PEffects; n: PNode) =
       if a.kind == nkSym:
         if a.sym == tracked.owner: tracked.isRecursive = true
         # even for recursive calls we need to check the lock levels (!):
-        mergeLockLevels(tracked, n, a.sym.getLockLevel)
         if sfSideEffect in a.sym.flags: markSideEffect(tracked, a, n.info)
       else:
-        mergeLockLevels(tracked, n, op.lockLevel)
+        discard
       var effectList = op.n[0]
       if a.kind == nkSym and a.sym.kind == skMethod:
+        if {sfBase, sfThread} * a.sym.flags == {sfBase}:
+          if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config)
+          markGcUnsafe(tracked, a)
         propagateEffects(tracked, n, a.sym)
       elif isNoEffectList(effectList):
         if isForwardedProc(a):
@@ -872,7 +1030,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
           assumeTheWorst(tracked, n, op)
           gcsafeAndSideeffectCheck()
         else:
-          if strictEffects in tracked.c.features and a.kind == nkSym and
+          if laxEffects notin tracked.c.config.legacyFeatures and a.kind == nkSym and
               a.sym.kind in routineKinds:
             propagateEffects(tracked, n, a.sym)
       else:
@@ -886,7 +1044,7 @@ proc trackCall(tracked: PEffects; n: PNode) =
       # may not look like an assignment, but it is:
       let arg = n[1]
       initVarViaNew(tracked, arg)
-      if arg.typ.len != 0 and {tfRequiresInit} * arg.typ.lastSon.flags != {}:
+      if arg.typ.hasElementType and {tfRequiresInit} * arg.typ.elementType.flags != {}:
         if a.sym.magic == mNewSeq and n[2].kind in {nkCharLit..nkUInt64Lit} and
             n[2].intVal == 0:
           # var s: seq[notnil];  newSeq(s, 0)  is a special case!
@@ -895,8 +1053,8 @@ proc trackCall(tracked: PEffects; n: PNode) =
           message(tracked.config, arg.info, warnProveInit, $arg)
 
       # check required for 'nim check':
-      if n[1].typ.len > 0:
-        createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
+      if n[1].typ.hasElementType:
+        createTypeBoundOps(tracked, n[1].typ.elementType, n.info)
         createTypeBoundOps(tracked, n[1].typ, n.info)
         # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'?
 
@@ -904,9 +1062,6 @@ proc trackCall(tracked: PEffects; n: PNode) =
         optStaticBoundsCheck in tracked.currOptions:
       checkBounds(tracked, n[1], n[2])
 
-    if a.kind != nkSym or a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}:
-      for i in 0..<n.safeLen:
-        track(tracked, n[i])
 
   if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
         tracked.owner.kind != skMacro:
@@ -922,24 +1077,36 @@ proc trackCall(tracked: PEffects; n: PNode) =
           n[0].sym = op
 
   if op != nil and op.kind == tyProc:
-    for i in 1..<min(n.safeLen, op.len):
-      case op[i].kind
+    for i in 1..<min(n.safeLen, op.signatureLen):
+      let paramType = op[i]
+      case paramType.kind
       of tySink:
-        createTypeBoundOps(tracked,  op[i][0], n.info)
+        createTypeBoundOps(tracked, paramType.elementType, n.info)
         checkForSink(tracked, n[i])
       of tyVar:
-        tracked.hasDangerousAssign = true
-      #of tyOut:
-      # consider this case: p(out x, x); we want to remark that 'x' is not
-      # initialized until after the call. Since we do this after we analysed the
-      # call, this is fine.
-      # initVar(tracked, n[i].skipAddr, false)
+        if isOutParam(paramType):
+          # consider this case: p(out x, x); we want to remark that 'x' is not
+          # initialized until after the call. Since we do this after we analysed the
+          # call, this is fine.
+          initVar(tracked, n[i].skipHiddenAddr, false)
+        if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
+           isDangerousLocation(n[i].skipHiddenAddr, tracked.owner):
+          if sfNoSideEffect in tracked.owner.flags:
+            localError(tracked.config, n[i].info,
+              "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i]))
+          tracked.hasSideEffect = true
       else: discard
 
+  if notConstExpr and (a.kind != nkSym or
+      a.sym.magic notin {mRunnableExamples, mNBindSym, mExpandToAst, mQuoteAst}
+  ):
+    # tracked after out analysis
+    for i in 0..<n.safeLen:
+      track(tracked, n[i])
+
 type
   PragmaBlockContext = object
     oldLocked: int
-    oldLockLevel: TLockLevel
     enforcedGcSafety, enforceNoSideEffects: bool
     oldExc, oldTags, oldForbids: int
     exc, tags, forbids: PNode
@@ -948,7 +1115,6 @@ proc createBlockContext(tracked: PEffects): PragmaBlockContext =
   var oldForbidsLen = 0
   if tracked.forbids != nil: oldForbidsLen = tracked.forbids.len
   result = PragmaBlockContext(oldLocked: tracked.locked.len,
-    oldLockLevel: tracked.currLockLevel,
     enforcedGcSafety: false, enforceNoSideEffects: false,
     oldExc: tracked.exc.len, oldTags: tracked.tags.len,
     oldForbids: oldForbidsLen)
@@ -961,7 +1127,6 @@ proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
   if bc.enforcedGcSafety: tracked.inEnforcedGcSafe = false
   if bc.enforceNoSideEffects: tracked.inEnforcedNoSideEffects = false
   setLen(tracked.locked, bc.oldLocked)
-  tracked.currLockLevel = bc.oldLockLevel
   if bc.exc != nil:
     # beware that 'raises: []' is very different from not saying
     # anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
@@ -1045,9 +1210,10 @@ proc track(tracked: PEffects, n: PNode) =
     if n.sym.typ != nil and tfHasAsgn in n.sym.typ.flags:
       tracked.owner.flags.incl sfInjectDestructors
       # bug #15038: ensure consistency
-      if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ
+      if n.typ == nil or (not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ)): n.typ = n.sym.typ
   of nkHiddenAddr, nkAddr:
-    if n[0].kind == nkSym and isLocalVar(tracked, n[0].sym):
+    if n[0].kind == nkSym and isLocalSym(tracked, n[0].sym) and
+          n.typ.kind notin {tyVar, tyLent}:
       useVarNoInitCheck(tracked, n[0], n[0].sym)
     else:
       track(tracked, n[0])
@@ -1068,14 +1234,17 @@ proc track(tracked: PEffects, n: PNode) =
     trackCall(tracked, n)
   of nkDotExpr:
     guardDotAccess(tracked, n)
+    let oldLeftPartOfAsgn = tracked.leftPartOfAsgn
+    tracked.leftPartOfAsgn = 0
     for i in 0..<n.len: track(tracked, n[i])
+    tracked.leftPartOfAsgn = oldLeftPartOfAsgn
   of nkCheckedFieldExpr:
     track(tracked, n[0])
-    if tracked.config.hasWarn(warnProveField):
-      checkFieldAccess(tracked.guards, n, tracked.config)
+    if tracked.config.hasWarn(warnProveField) or strictCaseObjects in tracked.c.features:
+      checkFieldAccess(tracked.guards, n, tracked.config, strictCaseObjects in tracked.c.features)
   of nkTryStmt: trackTryStmt(tracked, n)
   of nkPragma: trackPragmaStmt(tracked, n)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     track(tracked, n[1])
     initVar(tracked, n[0], volatileCheck=true)
     invalidateFacts(tracked.guards, n[0])
@@ -1087,10 +1256,14 @@ proc track(tracked: PEffects, n: PNode) =
     when false: cstringCheck(tracked, n)
     if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}:
       createTypeBoundOps(tracked, n[0].typ, n.info)
-    if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym):
+    if n[0].kind != nkSym or not isLocalSym(tracked, n[0].sym):
       checkForSink(tracked, n[1])
-      if not tracked.hasDangerousAssign and n[0].kind != nkSym:
-        tracked.hasDangerousAssign = true
+      if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and
+         isDangerousLocation(n[0], tracked.owner):
+        tracked.hasSideEffect = true
+        if sfNoSideEffect in tracked.owner.flags:
+          localError(tracked.config, n[0].info,
+              "cannot mutate location $1 within a strict func" % renderTree(n[0]))
   of nkVarSection, nkLetSection:
     for child in n:
       let last = lastSon(child)
@@ -1101,18 +1274,23 @@ proc track(tracked: PEffects, n: PNode) =
           for i in 0..<child.len-2:
             createTypeBoundOps(tracked, child[i].typ, child.info)
         else:
-          createTypeBoundOps(tracked, child[0].typ, child.info)
-      if child.kind == nkIdentDefs and last.kind != nkEmpty:
+          createTypeBoundOps(tracked, skipPragmaExpr(child[0]).typ, child.info)
+      if child.kind == nkIdentDefs:
         for i in 0..<child.len-2:
-          initVar(tracked, child[i], volatileCheck=false)
-          addAsgnFact(tracked.guards, child[i], last)
-          notNilCheck(tracked, last, child[i].typ)
-      elif child.kind == nkVarTuple and last.kind != nkEmpty:
+          let a = skipPragmaExpr(child[i])
+          varDecl(tracked, a)
+          if last.kind != nkEmpty:
+            initVar(tracked, a, volatileCheck=false)
+            addAsgnFact(tracked.guards, a, last)
+            notNilCheck(tracked, last, a.typ)
+      elif child.kind == nkVarTuple:
         for i in 0..<child.len-1:
           if child[i].kind == nkEmpty or
-            child[i].kind == nkSym and child[i].sym.name.s == "_":
+            child[i].kind == nkSym and child[i].sym.name.id == ord(wUnderscore):
             continue
-          initVar(tracked, child[i], volatileCheck=false)
+          varDecl(tracked, child[i])
+          if last.kind != nkEmpty:
+            initVar(tracked, child[i], volatileCheck=false)
           if last.kind in {nkPar, nkTupleConstr}:
             addAsgnFact(tracked.guards, child[i], last[i])
             notNilCheck(tracked, last[i], child[i].typ)
@@ -1127,6 +1305,7 @@ proc track(tracked: PEffects, n: PNode) =
   of nkBlockStmt, nkBlockExpr: trackBlock(tracked, n[1])
   of nkWhileStmt:
     # 'while true' loop?
+    inc tracked.currentBlock
     if isTrue(n[0]):
       trackBlock(tracked, n[1])
     else:
@@ -1138,8 +1317,10 @@ proc track(tracked: PEffects, n: PNode) =
       track(tracked, n[1])
       setLen(tracked.init, oldState)
       setLen(tracked.guards.s, oldFacts)
+    dec tracked.currentBlock
   of nkForStmt, nkParForStmt:
     # we are very conservative here and assume the loop is never executed:
+    inc tracked.currentBlock
     let oldState = tracked.init.len
 
     let oldFacts = tracked.guards.s.len
@@ -1181,6 +1362,8 @@ proc track(tracked: PEffects, n: PNode) =
     track(tracked, loopBody)
     setLen(tracked.init, oldState)
     setLen(tracked.guards.s, oldFacts)
+    dec tracked.currentBlock
+
   of nkObjConstr:
     when false: track(tracked, n[0])
     let oldFacts = tracked.guards.s.len
@@ -1202,11 +1385,12 @@ proc track(tracked: PEffects, n: PNode) =
     if tracked.owner.kind != skMacro:
       # XXX n.typ can be nil in runnableExamples, we need to do something about it.
       if n.typ != nil and n.typ.skipTypes(abstractInst).kind == tyRef:
-        createTypeBoundOps(tracked, n.typ.lastSon, n.info)
+        createTypeBoundOps(tracked, n.typ.elementType, n.info)
       createTypeBoundOps(tracked, n.typ, n.info)
   of nkTupleConstr:
     for i in 0..<n.len:
       track(tracked, n[i])
+      notNilCheck(tracked, n[i].skipColon, n[i].typ)
       if tracked.owner.kind != skMacro:
         if n[i].kind == nkExprColonExpr:
           createTypeBoundOps(tracked, n[i][0].typ, n.info)
@@ -1236,8 +1420,11 @@ proc track(tracked: PEffects, n: PNode) =
   of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo:
     if n[0].kind == nkSym and n[0].sym.ast != nil:
       trackInnerProc(tracked, getBody(tracked.graph, n[0].sym))
-  of nkTypeSection, nkMacroDef, nkTemplateDef:
+  of nkMacroDef, nkTemplateDef:
     discard
+  of nkTypeSection:
+    if tracked.isTopLevel:
+      collectObjectTree(tracked.graph, n)
   of nkCast:
     if n.len == 2:
       track(tracked, n[1])
@@ -1250,6 +1437,11 @@ proc track(tracked: PEffects, n: PNode) =
       message(tracked.config, n.info, warnCstringConv,
         "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" %
           $n[1])
+    if n.typ.skipTypes(abstractInst).kind == tyCstring and
+        isCharArrayPtr(n[1].typ, true):
+      message(tracked.config, n.info, warnPtrToCstringConv,
+          $n[1].typ)
+
 
     let t = n.typ.skipTypes(abstractInst)
     if t.kind == tyEnum:
@@ -1297,7 +1489,8 @@ proc track(tracked: PEffects, n: PNode) =
 
 proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool =
   if spec.typ.kind == tyOr:
-    for t in spec.typ.sons:
+    result = false
+    for t in spec.typ.kids:
       if safeInheritanceDiff(g.excType(real), t) <= 0:
         return true
   else:
@@ -1358,17 +1551,6 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) =
       localError(g.config, branch.info, "for method '" & branch.name.s &
         "' the `.requires` or `.ensures` properties are incompatible.")
 
-  if branch.typ.lockLevel > disp.typ.lockLevel:
-    when true:
-      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(g.config, branch.info,
-        "base method has lock level $1, but dispatcher has $2" %
-          [$branch.typ.lockLevel, $disp.typ.lockLevel])
-
 proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) =
   var effects = t.n[0]
   if t.kind != tyProc or effects.kind != nkEffectList: return
@@ -1414,26 +1596,21 @@ proc rawInitEffects(g: ModuleGraph; effects: PNode) =
   effects[ensuresEffects] = g.emptyNode
   effects[pragmasEffects] = g.emptyNode
 
-proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) =
+proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; c: PContext): TEffects =
   rawInitEffects(g, effects)
 
-  t.exc = effects[exceptionEffects]
-  t.tags = effects[tagEffects]
-  t.forbids = effects[forbiddenEffects]
-  t.owner = s
-  t.ownerModule = s.getModule
-  t.init = @[]
-  t.guards.s = @[]
-  t.guards.g = g
+  result = TEffects(exc: effects[exceptionEffects], tags: effects[tagEffects],
+            forbids: effects[forbiddenEffects], owner: s, ownerModule: s.getModule,
+            init: @[], locked: @[], graph: g, config: g.config, c: c,
+            currentBlock: 1, optionsStack: @[(g.config.options, g.config.notes)]
+  )
+  result.guards.s = @[]
+  result.guards.g = g
   when defined(drnim):
-    t.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
+    result.currOptions = g.config.options + s.options - {optStaticBoundsCheck}
   else:
-    t.currOptions = g.config.options + s.options
-  t.guards.beSmart = optStaticBoundsCheck in t.currOptions
-  t.locked = @[]
-  t.graph = g
-  t.config = g.config
-  t.c = c
+    result.currOptions = g.config.options + s.options
+  result.guards.beSmart = optStaticBoundsCheck in result.currOptions
 
 proc hasRealBody(s: PSym): bool =
   ## also handles importc procs with runnableExamples, which requires `=`,
@@ -1442,6 +1619,9 @@ proc hasRealBody(s: PSym): bool =
 
 proc trackProc*(c: PContext; s: PSym, body: PNode) =
   let g = c.graph
+  when defined(nimsuggest):
+    if g.config.expandDone():
+      return
   var effects = s.typ.n[0]
   if effects.kind != nkEffectList: return
   # effects already computed?
@@ -1451,9 +1631,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
 
   var inferredEffects = newNodeI(nkEffectList, s.info)
 
-  var t: TEffects
-  initEffects(g, inferredEffects, s, t, c)
+  var t: TEffects = initEffects(g, inferredEffects, s, c)
   rawInitEffects g, effects
+
+  if not isEmptyType(s.typ.returnType) and
+     s.kind in {skProc, skFunc, skConverter, skMethod}:
+    var res = s.ast[resultPos].sym # get result symbol
+    t.scopes[res.id] = t.currentBlock
+
   track(t, body)
 
   if s.kind != skMacro:
@@ -1462,23 +1647,27 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
       let param = params[i].sym
       let typ = param.typ
       if isSinkTypeForParam(typ) or
-          (t.config.selectedGC in {gcArc, gcOrc} and
+          (t.config.selectedGC in {gcArc, gcOrc, gcAtomicArc} and
             (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)):
         createTypeBoundOps(t, typ, param.info)
-      when false:
-        if typ.kind == tyOut and param.id notin t.init:
-          message(g.config, param.info, warnProveInit, param.name.s)
+      if isOutParam(typ) and param.id notin t.init:
+        message(g.config, param.info, warnProveInit, param.name.s)
 
-  if not isEmptyType(s.typ[0]) and
-     (s.typ[0].requiresInit or s.typ[0].skipTypes(abstractInst).kind == tyVar) and
-     s.kind in {skProc, skFunc, skConverter, skMethod}:
+  if not isEmptyType(s.typ.returnType) and
+     (s.typ.returnType.requiresInit or s.typ.returnType.skipTypes(abstractInst).kind == tyVar or
+       strictDefs in c.features) and
+     s.kind in {skProc, skFunc, skConverter, skMethod} and s.magic == mNone:
     var res = s.ast[resultPos].sym # get result symbol
-    if res.id notin t.init:
-      message(g.config, body.info, warnProveInit, "result")
+    if res.id notin t.init and breaksBlock(body) != bsNoReturn:
+      if tfRequiresInit in s.typ.returnType.flags:
+        localError(g.config, body.info, "'$1' requires explicit initialization" % "result")
+      else:
+        message(g.config, body.info, warnProveInit, "result")
   let p = s.ast[pragmasPos]
   let raisesSpec = effectSpec(p, wRaises)
   if not isNil(raisesSpec):
-    checkRaisesSpec(g, false, raisesSpec, t.exc, "can raise an unlisted exception: ",
+    let useWarning = s.name.s == "=destroy"
+    checkRaisesSpec(g, useWarning, raisesSpec, t.exc, "can raise an unlisted exception: ",
                     hints=on, subtypeRelation, hintsArg=s.ast[0])
     # after the check, use the formal spec:
     effects[exceptionEffects] = raisesSpec
@@ -1512,17 +1701,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
     effects[ensuresEffects] = ensuresSpec
 
   var mutationInfo = MutationInfo()
-  var hasMutationSideEffect = false
-  if {strictFuncs, views} * c.features != {}:
-    var goals: set[Goal] = {}
-    if strictFuncs in c.features: goals.incl constParameters
-    if views in c.features: goals.incl borrowChecking
-    var partitions = computeGraphPartitions(s, body, g, goals)
-    if not t.hasSideEffect and t.hasDangerousAssign:
-      t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo)
-      hasMutationSideEffect = t.hasSideEffect
-    if views in c.features:
-      checkBorrowedLocations(partitions, body, g.config)
+  if views in c.features:
+    var partitions = computeGraphPartitions(s, body, g, {borrowChecking})
+    checkBorrowedLocations(partitions, body, g.config)
 
   if sfThread in s.flags and t.gcUnsafe:
     if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions:
@@ -1535,9 +1716,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
     when false:
       listGcUnsafety(s, onlyWarning=false, g.config)
     else:
-      if hasMutationSideEffect:
-        localError(g.config, s.info, "'$1' can have side effects$2" % [s.name.s, g.config $ mutationInfo])
-      elif c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context
+      if c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context
         var msg = ""
         listSideEffects(msg, s, g.config, t.c)
         message(g.config, s.info, errGenerated, msg)
@@ -1547,13 +1726,6 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
     s.typ.flags.incl tfGcSafe
   if not t.hasSideEffect and sfSideEffect notin s.flags:
     s.typ.flags.incl tfNoSideEffect
-  if s.typ.lockLevel == UnspecifiedLockLevel:
-    s.typ.lockLevel = t.maxLockLevel
-  elif t.maxLockLevel > s.typ.lockLevel:
-    #localError(s.info,
-    message(g.config, s.info, warnLockLevel,
-      "declared lock level is $1, but real lock level is $2" %
-        [$s.typ.lockLevel, $t.maxLockLevel])
   when defined(drnim):
     if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, s, body)
   when defined(useDfa):
@@ -1561,18 +1733,22 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) =
       dataflowAnalysis(s, body)
 
       when false: trackWrites(s, body)
-  if strictNotNil in c.features and s.kind == skProc:
+  if strictNotNil in c.features and s.kind in {skProc, skFunc, skMethod, skConverter}:
     checkNil(s, body, g.config, c.idgen)
 
 proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) =
-  if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
-                nkTypeSection, nkConverterDef, nkMethodDef, nkIteratorDef}:
-    return
-  let g = c.graph
-  var effects = newNodeI(nkEffectList, n.info)
-  var t: TEffects
-  initEffects(g, effects, module, t, c)
-  t.isTopLevel = isTopLevel
-  track(t, n)
-  when defined(drnim):
-    if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
+  case n.kind
+  of {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef,
+                nkConverterDef, nkMethodDef, nkIteratorDef}:
+    discard
+  of nkTypeSection:
+    if isTopLevel:
+      collectObjectTree(c.graph, n)
+  else:
+    let g = c.graph
+    var effects = newNodeI(nkEffectList, n.info)
+    var t: TEffects = initEffects(g, effects, module, c)
+    t.isTopLevel = isTopLevel
+    track(t, n)
+    when defined(drnim):
+      if c.graph.strongSemCheck != nil: c.graph.strongSemCheck(c.graph, module, n)
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index fd8f2180b..f5f8fea0c 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -36,6 +36,19 @@ const
   errRecursiveDependencyX = "recursive dependency: '$1'"
   errRecursiveDependencyIteratorX = "recursion is not supported in iterators: '$1'"
   errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1"
+  errCannotAssignToGlobal = "cannot assign local to global variable"
+
+proc implicitlyDiscardable(n: PNode): bool
+
+proc hasEmpty(typ: PType): bool =
+  if typ.kind in {tySequence, tyArray, tySet}:
+    result = typ.elementType.kind == tyEmpty
+  elif typ.kind == tyTuple:
+    result = false
+    for s in typ.kids:
+      result = result or hasEmpty(s)
+  else:
+    result = false
 
 proc semDiscard(c: PContext, n: PNode): PNode =
   result = n
@@ -44,7 +57,9 @@ proc semDiscard(c: PContext, n: PNode): PNode =
     n[0] = semExprWithType(c, n[0])
     let sonType = n[0].typ
     let sonKind = n[0].kind
-    if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr:
+    if isEmptyType(sonType) or hasEmpty(sonType) or
+          sonType.kind in {tyNone, tyTypeDesc} or
+          sonKind == nkTypeOfExpr:
       localError(c.config, n.info, errInvalidDiscard)
     if sonType.kind == tyProc and sonKind notin nkCallKinds:
       # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant.
@@ -55,7 +70,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
   checkSonsLen(n, 1, c.config)
   if n[0].kind != nkEmpty:
     if n.kind != nkContinueStmt:
-      var s: PSym
+      var s: PSym = nil
       case n[0].kind
       of nkIdent: s = lookUp(c, n[0])
       of nkSym: s = n[0].sym
@@ -72,6 +87,8 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode =
         localError(c.config, n.info, errInvalidControlFlowX % s.name.s)
     else:
       localError(c.config, n.info, errGenerated, "'continue' cannot have a label")
+  elif c.p.nestedBlockCounter > 0 and n.kind == nkBreakStmt and not c.p.breakInLoop:
+    localError(c.config, n.info, warnUnnamedBreak)
   elif (c.p.nestedLoopCounter <= 0) and ((c.p.nestedBlockCounter <= 0) or n.kind == nkContinueStmt):
     localError(c.config, n.info, errInvalidControlFlowX %
                renderTree(n, {renderNoComments}))
@@ -88,13 +105,18 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode =
   openScope(c)
   n[0] = forceBool(c, semExprWithType(c, n[0], expectedType = getSysType(c.graph, n.info, tyBool)))
   inc(c.p.nestedLoopCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
   n[1] = semStmt(c, n[1], flags)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
   closeScope(c)
   if n[1].typ == c.enforceVoidContext:
     result.typ = c.enforceVoidContext
   elif efInTypeof in flags:
     result.typ = n[1].typ
+  elif implicitlyDiscardable(n[1]):
+    result[1].typ = c.enforceVoidContext
 
 proc semProc(c: PContext, n: PNode): PNode
 
@@ -110,17 +132,141 @@ proc semExprBranchScope(c: PContext, n: PNode; expectedType: PType = nil): PNode
   closeScope(c)
 
 const
-  skipForDiscardable = {nkIfStmt, nkIfExpr, nkCaseStmt, nkOfBranch,
-    nkElse, nkStmtListExpr, nkTryStmt, nkFinally, nkExceptBranch,
+  skipForDiscardable = {nkStmtList, nkStmtListExpr,
+    nkOfBranch, nkElse, nkFinally, nkExceptBranch,
     nkElifBranch, nkElifExpr, nkElseExpr, nkBlockStmt, nkBlockExpr,
-    nkHiddenStdConv, nkHiddenDeref}
+    nkHiddenStdConv, nkHiddenSubConv, nkHiddenDeref}
 
 proc implicitlyDiscardable(n: PNode): bool =
-  var n = n
-  while n.kind in skipForDiscardable: n = n.lastSon
-  result = n.kind in nkLastBlockStmts or
-           (isCallExpr(n) and n[0].kind == nkSym and
-           sfDiscardable in n[0].sym.flags)
+  # same traversal as endsInNoReturn
+  template checkBranch(branch) =
+    if not implicitlyDiscardable(branch):
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  while it.kind in skipForDiscardable and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkCaseStmt:
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in implicitlyDiscardable"
+    # all branches are discardable
+    result = true
+  of nkTryStmt:
+    checkBranch(it[0])
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      if branch.kind != nkFinally:
+        checkBranch(branch[^1])
+    # all branches are discardable
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and {sfDiscardable, sfNoReturn} * it[0].sym.flags != {}
+  of nkLastBlockStmts:
+    result = true
+  else:
+    result = false
+
+proc endsInNoReturn(n: PNode, returningNode: var PNode; discardableCheck = false): bool =
+  ## check if expr ends the block like raising or call of noreturn procs do
+  result = false # assume it does return
+
+  template checkBranch(branch) =
+    if not endsInNoReturn(branch, returningNode, discardableCheck):
+      # proved a branch returns
+      return false
+
+  var it = n
+  # skip these beforehand, no special handling needed
+  let skips = if discardableCheck: skipForDiscardable else: skipForDiscardable-{nkBlockExpr, nkBlockStmt}
+  while it.kind in skips and it.len > 0:
+    it = it.lastSon
+
+  case it.kind
+  of nkIfExpr, nkIfStmt:
+    var hasElse = false
+    for branch in it:
+      checkBranch:
+        if branch.len == 2:
+          branch[1]
+        elif branch.len == 1:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `if` statement during endsInNoReturn"
+    # none of the branches returned
+    result = hasElse # Only truly a no-return when it's exhaustive
+  of nkCaseStmt:
+    let caseTyp = skipTypes(it[0].typ, abstractVar-{tyTypeDesc})
+    # semCase should already have checked for exhaustiveness in this case
+    # effectively the same as having an else
+    var hasElse = caseTyp.shouldCheckCaseCovered()
+
+    # actual noreturn checks
+    for i in 1 ..< it.len:
+      let branch = it[i]
+      checkBranch:
+        case branch.kind
+        of nkOfBranch:
+          branch[^1]
+        of nkElifBranch:
+          branch[1]
+        of nkElse:
+          hasElse = true
+          branch[0]
+        else:
+          raiseAssert "Malformed `case` statement in endsInNoReturn"
+    # Can only guarantee a noreturn if there is an else or it's exhaustive
+    result = hasElse
+  of nkTryStmt:
+    checkBranch(it[0])
+    var lastIndex = it.len - 1
+    if it[lastIndex].kind == nkFinally:
+      # if finally is noreturn, then the entire statement is noreturn
+      if endsInNoReturn(it[lastIndex][^1], returningNode, discardableCheck):
+        return true
+      dec lastIndex
+    for i in 1 .. lastIndex:
+      let branch = it[i]
+      checkBranch(branch[^1])
+    # none of the branches returned
+    result = true
+  of nkLastBlockStmts:
+    result = true
+  of nkCallKinds:
+    result = it[0].kind == nkSym and sfNoReturn in it[0].sym.flags
+    if not result:
+      returningNode = it
+  else:
+    result = false
+    returningNode = it
+
+proc endsInNoReturn(n: PNode): bool =
+  var dummy: PNode = nil
+  result = endsInNoReturn(n, dummy)
 
 proc fixNilType(c: PContext; n: PNode) =
   if isAtom(n):
@@ -138,22 +284,25 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) =
     if implicitlyDiscardable(result):
       var n = newNodeI(nkDiscardStmt, result.info, 1)
       n[0] = result
+      # notes that it doesn't transform nodes into discard statements
     elif result.typ.kind != tyError and c.config.cmd != cmdInteractive:
       if result.typ.kind == tyNone:
         localError(c.config, result.info, "expression has no type: " &
                renderTree(result, {renderNoComments}))
-      var n = result
-      while n.kind in skipForDiscardable:
-        if n.kind == nkTryStmt: n = n[0]
-        else: n = n.lastSon
-      var s = "expression '" & $n & "' is of type '" &
-          result.typ.typeToString & "' and has to be used (or discarded)"
-      if result.info.line != n.info.line or
-          result.info.fileIndex != n.info.fileIndex:
-        s.add "; start of expression here: " & c.config$result.info
-      if result.typ.kind == tyProc:
-        s.add "; for a function call use ()"
-      localError(c.config, n.info, s)
+      else:
+        # Ignore noreturn procs since they don't have a type
+        var n = result
+        if result.endsInNoReturn(n, discardableCheck = true):
+          return
+
+        var s = "expression '" & $n & "' is of type '" &
+            result.typ.typeToString & "' and has to be used (or discarded)"
+        if result.info.line != n.info.line or
+            result.info.fileIndex != n.info.fileIndex:
+          s.add "; start of expression here: " & c.config$result.info
+        if result.typ.kind == tyProc:
+          s.add "; for a function call use ()"
+        localError(c.config, n.info, s)
 
 proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil): PNode =
   result = n
@@ -167,13 +316,15 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil):
       it[0] = forceBool(c, semExprWithType(c, it[0], expectedType = getSysType(c.graph, n.info, tyBool)))
       it[1] = semExprBranch(c, it[1], flags, expectedType)
       typ = commonType(c, typ, it[1])
-      expectedType = typ
+      if not endsInNoReturn(it[1]):
+        expectedType = typ
       closeScope(c)
     elif it.len == 1:
       hasElse = true
       it[0] = semExprBranchScope(c, it[0], expectedType)
       typ = commonType(c, typ, it[0])
-      expectedType = typ
+      if not endsInNoReturn(it[0]):
+        expectedType = typ
     else: illFormedAst(it, c.config)
   if isEmptyType(typ) or typ.kind in {tyNil, tyUntyped} or
       (not hasElse and efInTypeof notin flags):
@@ -199,6 +350,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
       isImported = true
     elif not isException(typ):
       localError(c.config, typeNode.info, errExprCannotBeRaised)
+    elif not isDefectOrCatchableError(typ):
+      message(c.config, a.info, warnBareExcept, "catch a more precise Exception deriving from CatchableError or Defect.")
 
     if containsOrIncl(check, typ.id):
       localError(c.config, typeNode.info, errExceptionAlreadyHandled)
@@ -211,8 +364,9 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
   var typ = commonTypeBegin
   var expectedType = expectedType
   n[0] = semExprBranchScope(c, n[0], expectedType)
-  typ = commonType(c, typ, n[0].typ)
-  expectedType = typ
+  if not endsInNoReturn(n[0]):
+    typ = commonType(c, typ, n[0].typ)
+    expectedType = typ
 
   var last = n.len - 1
   var catchAllExcepts = 0
@@ -225,7 +379,7 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
 
       if a.len == 2 and a[0].kind == nkBracket:
         # rewrite ``except [a, b, c]: body`` -> ```except a, b, c: body```
-        a.sons[0..0] = a[0].sons
+        a.sons[0..0] = move a[0].sons
 
       if a.len == 2 and a[0].isInfixAs():
         # support ``except Exception as ex: body``
@@ -240,14 +394,15 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
       elif a.len == 1:
         # count number of ``except: body`` blocks
         inc catchAllExcepts
-
+        message(c.config, a.info, warnBareExcept,
+                  "The bare except clause is deprecated; use `except CatchableError:` instead")
       else:
         # support ``except KeyError, ValueError, ... : body``
         if catchAllExcepts > 0:
           # if ``except: body`` already encountered,
           # cannot be followed by a ``except KeyError, ... : body`` block
           inc catchAllExcepts
-        var isNative, isImported: bool
+        var isNative, isImported: bool = false
         for j in 0..<a.len-1:
           let tmp = semExceptBranchType(a[j])
           if tmp: isImported = true
@@ -272,7 +427,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
     if a.kind != nkFinally:
       a[^1] = semExprBranchScope(c, a[^1], expectedType)
       typ = commonType(c, typ, a[^1])
-      expectedType = typ
+      if not endsInNoReturn(a[^1]):
+        expectedType = typ
     else:
       a[^1] = semExprBranchScope(c, a[^1])
       dec last
@@ -285,7 +441,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil)
       result.typ = c.enforceVoidContext
   else:
     if n.lastSon.kind == nkFinally: discardCheck(c, n.lastSon.lastSon, flags)
-    n[0] = fitNode(c, typ, n[0], n[0].info)
+    if not endsInNoReturn(n[0]):
+      n[0] = fitNode(c, typ, n[0], n[0].info)
     for i in 1..last:
       var it = n[i]
       let j = it.len-1
@@ -303,6 +460,8 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
       result.typ = typ
       if not floatRangeCheck(result.floatVal, typ):
         localError(c.config, n.info, errFloatToString % [$result.floatVal, typeToString(typ)])
+    elif r1.kind == nkSym and typ.skipTypes(abstractRange).kind == tyCstring:
+      discard "keep nkHiddenStdConv for cstring conversions"
     else:
       changeType(c, r1, typ, check=true)
       result = r1
@@ -310,6 +469,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode =
     changeType(c, result, typ, check=false)
 
 proc findShadowedVar(c: PContext, v: PSym): PSym =
+  result = nil
   for scope in localScopesFrom(c, c.currentScope.parent):
     let shadowed = strTableGet(scope.symbols, v.name)
     if shadowed != nil and shadowed.kind in skLocalVars:
@@ -322,7 +482,7 @@ proc identWithin(n: PNode, s: PIdent): bool =
 
 proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym =
   if isTopLevel(c):
-    result = semIdentWithPragma(c, kind, n, {sfExported})
+    result = semIdentWithPragma(c, kind, n, {sfExported}, fromTopLevel = true)
     incl(result.flags, sfGlobal)
     #if kind in {skVar, skLet}:
     #  echo "global variable here ", n.info, " ", result.name.s
@@ -370,9 +530,11 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) =
     result.add identDefs
 
 proc isDiscardUnderscore(v: PSym): bool =
-  if v.name.s == "_":
+  if v.name.id == ord(wUnderscore):
     v.flags.incl(sfGenSym)
     result = true
+  else:
+    result = false
 
 proc semUsing(c: PContext; n: PNode): PNode =
   result = c.graph.emptyNode
@@ -397,13 +559,6 @@ proc semUsing(c: PContext; n: PNode): PNode =
     if a[^1].kind != nkEmpty:
       localError(c.config, a.info, "'using' sections cannot contain assignments")
 
-proc hasEmpty(typ: PType): bool =
-  if typ.kind in {tySequence, tyArray, tySet}:
-    result = typ.lastSon.kind == tyEmpty
-  elif typ.kind == tyTuple:
-    for s in typ.sons:
-      result = result or hasEmpty(s)
-
 proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool =
   result = tfUnresolved in n.typ.flags
   when false:
@@ -424,16 +579,16 @@ proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool =
 proc makeDeref(n: PNode): PNode =
   var t = n.typ
   if t.kind in tyUserTypeClasses and t.isResolvedUserTypeClass:
-    t = t.lastSon
+    t = t.last
   t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned})
   result = n
   if t.kind in {tyVar, tyLent}:
-    result = newNodeIT(nkHiddenDeref, n.info, t[0])
+    result = newNodeIT(nkHiddenDeref, n.info, t.elementType)
     result.add n
-    t = skipTypes(t[0], {tyGenericInst, tyAlias, tySink, tyOwned})
+    t = skipTypes(t.elementType, {tyGenericInst, tyAlias, tySink, tyOwned})
   while t.kind in {tyPtr, tyRef}:
     var a = result
-    let baseTyp = t.lastSon
+    let baseTyp = t.elementType
     result = newNodeIT(nkHiddenDeref, n.info, baseTyp)
     result.add a
     t = skipTypes(baseTyp, {tyGenericInst, tyAlias, tySink, tyOwned})
@@ -444,7 +599,7 @@ proc fillPartialObject(c: PContext; n: PNode; typ: PType) =
     let y = considerQuotedIdent(c, n[1])
     let obj = x.typ.skipTypes(abstractPtrs)
     if obj.kind == tyObject and tfPartial in obj.flags:
-      let field = newSym(skField, getIdent(c.cache, y.s), nextSymId c.idgen, obj.sym, n[1].info)
+      let field = newSym(skField, getIdent(c.cache, y.s), c.idgen, obj.sym, n[1].info)
       field.typ = skipIntLit(typ, c.idgen)
       field.position = obj.n.len
       obj.n.add newSymNode(field)
@@ -467,15 +622,15 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
 proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool =
   # make sure it's not a normal pragma, and calls an identifier
   # considerQuotedIdent below will fail on non-identifiers
-  result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds
+  result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds+{nkDotExpr}
   if result:
     # make sure it's not a user pragma
-    let ident = considerQuotedIdent(c, key)
-    result = strTableGet(c.userPragmas, ident) == nil
+    if key.kind != nkDotExpr:
+      let ident = considerQuotedIdent(c, key)
+      result = strTableGet(c.userPragmas, ident) == nil
     if result:
       # make sure it's not a custom pragma
-      var amb = false
-      let sym = searchInScopes(c, ident, amb)
+      let sym = qualifiedLookUp(c, key, {})
       result = sym == nil or sfCustomPragma notin sym.flags
 
 proc copyExcept(n: PNode, i: int): PNode =
@@ -498,12 +653,7 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
       let it = pragmas[i]
       let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
 
-      when false:
-        let lhs = b[0]
-        let clash = strTableGet(c.currentScope.symbols, lhs.ident)
-        if clash != nil:
-          # refs https://github.com/nim-lang/Nim/issues/8275
-          wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info)
+      trySuggestPragmas(c, key)
 
       if isPossibleMacroPragma(c, it, key):
         # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and
@@ -551,44 +701,113 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode =
 
         doAssert result != nil
 
-        # since a macro pragma can set pragmas, we process these here again.
-        # This is required for SqueakNim-like export pragmas.
-        if false and result.kind in {nkVarSection, nkLetSection, nkConstSection}:
-          var validPragmas: TSpecialWords
-          case result.kind
-          of nkVarSection:
-            validPragmas = varPragmas
-          of nkLetSection:
-            validPragmas = letPragmas
-          of nkConstSection:
-            validPragmas = constPragmas
-          else:
-            # unreachable
-            discard
-          for defs in result:
-            for i in 0 ..< defs.len - 2:
-              let ex = defs[i]
-              if ex.kind == nkPragmaExpr and
-                  ex[namePos].kind == nkSym and
-                  ex[pragmaPos].kind != nkEmpty:
-                pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas)
         return result
 
-proc errorSymChoiceUseQualifier(c: PContext; n: PNode) =
-  assert n.kind in nkSymChoices
-  var err = "ambiguous identifier: '" & $n[0] & "'"
-  var i = 0
-  for child in n:
-    let candidate = child.sym
-    if i == 0: err.add " -- use one of the following:\n"
-    else: err.add "\n"
-    err.add "  " & candidate.owner.name.s & "." & candidate.name.s
-    inc i
-  localError(c.config, n.info, errGenerated, err)
+template isLocalSym(sym: PSym): bool =
+  sym.kind in {skVar, skLet, skParam} and not
+    ({sfGlobal, sfPure} * sym.flags != {} or
+      sym.typ.kind == tyTypeDesc or
+      sfCompileTime in sym.flags) or
+      sym.kind in {skProc, skFunc, skIterator} and
+      sfGlobal notin sym.flags
+
+template isLocalVarSym(n: PNode): bool =
+  n.kind == nkSym and isLocalSym(n.sym)
+
+proc usesLocalVar(n: PNode): bool =
+  result = false
+  for z in 1 ..< n.len:
+    if n[z].isLocalVarSym:
+      return true
+    elif n[z].kind in nkCallKinds:
+      if usesLocalVar(n[z]):
+        return true
+
+proc globalVarInitCheck(c: PContext, n: PNode) =
+  if n.isLocalVarSym or n.kind in nkCallKinds and usesLocalVar(n):
+    localError(c.config, n.info, errCannotAssignToGlobal)
+
+const
+  errTupleUnpackingTupleExpected = "tuple expected for tuple unpacking, but got '$1'"
+  errTupleUnpackingDifferentLengths = "tuple with $1 elements expected, but got '$2' with $3 elements"
+
+proc makeVarTupleSection(c: PContext, n, a, def: PNode, typ: PType, symkind: TSymKind, origResult: var PNode): PNode =
+  ## expand tuple unpacking assignments into new var/let/const section
+  ##
+  ## mirrored with semexprs.makeTupleAssignments
+  if typ.kind != tyTuple:
+    localError(c.config, a.info, errTupleUnpackingTupleExpected %
+      [typeToString(typ, preferDesc)])
+  elif a.len-2 != typ.len:
+    localError(c.config, a.info, errTupleUnpackingDifferentLengths %
+      [$(a.len-2), typeToString(typ, preferDesc), $typ.len])
+  var
+    tempNode: PNode = nil
+    lastDef: PNode
+  let defkind = if symkind == skConst: nkConstDef else: nkIdentDefs
+  # temporary not needed if not const and RHS is tuple literal
+  # const breaks with seqs without temporary
+  let useTemp = def.kind notin {nkPar, nkTupleConstr} or symkind == skConst
+  if useTemp:
+    # use same symkind for compatibility with original section
+    let temp = newSym(symkind, getIdent(c.cache, "tmpTuple"), c.idgen, getCurrOwner(c), n.info)
+    temp.typ = typ
+    temp.flags.incl(sfGenSym)
+    lastDef = newNodeI(defkind, a.info)
+    newSons(lastDef, 3)
+    lastDef[0] = newSymNode(temp)
+    # NOTE: at the moment this is always ast.emptyNode, see parser.nim
+    lastDef[1] = a[^2]
+    lastDef[2] = def
+    temp.ast = lastDef
+    addToVarSection(c, origResult, n, lastDef)
+    tempNode = newSymNode(temp)
+  result = newNodeI(n.kind, a.info)
+  for j in 0..<a.len-2:
+    let name = a[j]
+    if useTemp and name.kind == nkIdent and name.ident.id == ord(wUnderscore):
+      # skip _ assignments if we are using a temp as they are already evaluated
+      continue
+    if name.kind == nkVarTuple:
+      # nested tuple
+      lastDef = newNodeI(nkVarTuple, name.info)
+      newSons(lastDef, name.len)
+      for k in 0..<name.len-2:
+        lastDef[k] = name[k]
+    else:
+      lastDef = newNodeI(defkind, name.info)
+      newSons(lastDef, 3)
+      lastDef[0] = name
+    lastDef[^2] = c.graph.emptyNode
+    if useTemp:
+      lastDef[^1] = newTupleAccessRaw(tempNode, j)
+    else:
+      var val = def[j]
+      if val.kind == nkExprColonExpr: val = val[1]
+      lastDef[^1] = val
+    result.add(lastDef)
 
 proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
   var b: PNode
   result = copyNode(n)
+
+  # transform var x, y = 12 into var x = 12; var y = 12
+  # bug #18104; transformation should be finished before templates expansion
+  # TODO: move warnings for tuple here
+  var transformed = copyNode(n)
+  for i in 0..<n.len:
+    var a = n[i]
+    if a.kind == nkIdentDefs and a.len > 3 and a[^1].kind != nkEmpty:
+      for j in 0..<a.len-2:
+        var b = newNodeI(nkIdentDefs, a.info)
+        b.add a[j]
+        b.add a[^2]
+        b.add copyTree(a[^1])
+        transformed.add b
+    else:
+      transformed.add a
+  let n = transformed
+
   for i in 0..<n.len:
     var a = n[i]
     if c.config.cmd == cmdIdeTools: suggestStmt(c, a)
@@ -601,19 +820,24 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       addToVarSection(c, result, b)
       continue
 
+    var hasUserSpecifiedType = false
     var typ: PType = nil
     if a[^2].kind != nkEmpty:
       typ = semTypeNode(c, a[^2], nil)
+      hasUserSpecifiedType = true
 
-    var typFlags: TTypeAllowedFlags
+    var typFlags: TTypeAllowedFlags = {}
 
     var def: PNode = c.graph.emptyNode
+    if typ != nil and typ.kind == tyRange and
+        c.graph.config.isDefined("nimPreviewRangeDefault") and
+        a[^1].kind == nkEmpty:
+      a[^1] = firstRange(c.config, typ)
+
     if a[^1].kind != nkEmpty:
-      def = semExprWithType(c, a[^1], {}, typ)
+      def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
-      if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum:
-        errorSymChoiceUseQualifier(c, def)
-      elif def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
+      if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
         typFlags.incl taIsTemplateOrMacro
       elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro:
         typFlags.incl taProcContextIsNotMacro
@@ -630,12 +854,18 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
       else:
         typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen)
         if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass:
-          typ = typ.lastSon
+          typ = typ.last
         if hasEmpty(typ):
           localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr)
         elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast):
-          # tfUnresolved in typ.flags:
-          localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree)
+          let owner = typ.owner
+          let err =
+            # consistent error message with evaltempl/semMacroExpr
+            if owner != nil and owner.kind in {skTemplate, skMacro}:
+              errMissingGenericParamsForTemplate % def.renderTree
+            else:
+              errProcHasNoConcreteType % def.renderTree
+          localError(c.config, def.info, err)
         when false:
           # XXX This typing rule is neither documented nor complete enough to
           # justify it. Instead use the newer 'unowned x' until we figured out
@@ -654,93 +884,81 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
 
     var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink})
     if a.kind == nkVarTuple:
-      if tup.kind != tyTuple:
-        localError(c.config, a.info, errXExpected, "tuple")
-      elif a.len-2 != tup.len:
-        localError(c.config, a.info, errWrongNumberOfVariables)
-      b = newNodeI(nkVarTuple, a.info)
-      newSons(b, a.len)
-      # keep type desc for doc generator
-      # NOTE: at the moment this is always ast.emptyNode, see parser.nim
-      b[^2] = a[^2]
-      b[^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(c.config, a.info, warnEachIdentIsTuple)
-
-    for j in 0..<a.len-2:
-      if a[j].kind == nkDotExpr:
-        fillPartialObject(c, a[j],
-          if a.kind != nkVarTuple: typ else: tup[j])
-        addToVarSection(c, result, n, a)
-        continue
-      var v = semIdentDef(c, a[j], symkind, false)
-      styleCheckDef(c, v)
-      onDef(a[j].info, v)
-      if sfGenSym notin v.flags:
-        if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
-      else:
-        if v.owner == nil: v.owner = c.p.owner
-      when oKeepVariableNames:
-        if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
+      # generate new section from tuple unpacking and embed it into this one
+      let assignments = makeVarTupleSection(c, n, a, def, tup, symkind, result)
+      let resSection = semVarOrLet(c, assignments, symkind)
+      for resDef in resSection:
+        addToVarSection(c, result, n, resDef)
+    else:
+      if tup.kind == tyTuple and def.kind in {nkPar, nkTupleConstr} and
+          a.len > 3:
+        # var a, b = (1, 2)
+        message(c.config, a.info, warnEachIdentIsTuple)
+
+      for j in 0..<a.len-2:
+        if a[j].kind == nkDotExpr:
+          fillPartialObject(c, a[j], typ)
+          addToVarSection(c, result, n, a)
+          continue
+        var v = semIdentDef(c, a[j], symkind, false)
+        when defined(nimsuggest):
+          v.hasUserSpecifiedType = hasUserSpecifiedType
+        styleCheckDef(c, v)
+        onDef(a[j].info, v)
+        if sfGenSym notin v.flags:
+          if not isDiscardUnderscore(v): addInterfaceDecl(c, v)
         else:
-          let shadowed = findShadowedVar(c, v)
-          if shadowed != nil:
-            shadowed.flags.incl(sfShadowed)
-            if shadowed.kind == skResult and sfGenSym notin v.flags:
-              message(c.config, a.info, warnResultShadowed)
-      if a.kind != nkVarTuple:
+          if v.owner == nil: v.owner = c.p.owner
+        when oKeepVariableNames:
+          if c.inUnrolledContext > 0: v.flags.incl(sfShadowed)
+          else:
+            let shadowed = findShadowedVar(c, v)
+            if shadowed != nil:
+              shadowed.flags.incl(sfShadowed)
+              if shadowed.kind == skResult and sfGenSym notin v.flags:
+                message(c.config, a.info, warnResultShadowed)
         if def.kind != nkEmpty:
           if sfThread in v.flags: localError(c.config, def.info, errThreadvarCannotInit)
         setVarType(c, v, typ)
+        # this is needed for the evaluation pass, guard checking
+        #  and custom pragmas:
         b = newNodeI(nkIdentDefs, a.info)
         if importantComments(c.config):
           # keep documentation information:
           b.comment = a.comment
-        b.add newSymNode(v)
-        # keep type desc for doc generator
-        b.add a[^2]
-        b.add copyTree(def)
-        addToVarSection(c, result, n, b)
-        # this is needed for the evaluation pass, guard checking
-        #  and custom pragmas:
-        var ast = newNodeI(nkIdentDefs, a.info)
+        # postfix not generated here (to generate, get rid of it in transf)
         if a[j].kind == nkPragmaExpr:
           var p = newNodeI(nkPragmaExpr, a.info)
           p.add newSymNode(v)
-          p.add a[j][1].copyTree
-          ast.add p
-        else:
-          ast.add newSymNode(v)
-        ast.add a[^2].copyTree
-        ast.add def
-        v.ast = ast
-      else:
-        if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j]
-        # bug #7663, for 'nim check' this can be a non-tuple:
-        if tup.kind == tyTuple: setVarType(c, v, tup[j])
-        else: v.typ = tup
-        b[j] = newSymNode(v)
-      if def.kind == nkEmpty:
-        let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
-                                          tyUserTypeClassInst})
-        if actualType.kind in {tyObject, tyDistinct} and
-           actualType.requiresInit:
-          defaultConstructionError(c, v.typ, v.info)
+          p.add a[j][1]
+          b.add p
         else:
-          checkNilable(c, v)
-        # allow let to not be initialised if imported from C:
-        if v.kind == skLet and sfImportc notin v.flags:
-          localError(c.config, a.info, errLetNeedsInit)
-      if sfCompileTime in v.flags:
-        var x = newNodeI(result.kind, v.info)
-        x.add result[i]
-        vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x)
-      if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
-        message(c.config, v.info, hintGlobalVar)
-
-      suggestSym(c.graph, v.info, v, c.graph.usageSym)
+          b.add newSymNode(v)
+        # keep type desc for doc generator
+        b.add a[^2]
+        b.add copyTree(def)
+        addToVarSection(c, result, n, b)
+        v.ast = b
+        if def.kind == nkEmpty:
+          let actualType = v.typ.skipTypes({tyGenericInst, tyAlias,
+                                            tyUserTypeClassInst})
+          if actualType.kind in {tyObject, tyDistinct} and
+            actualType.requiresInit:
+            defaultConstructionError(c, v.typ, v.info)
+          else:
+            checkNilable(c, v)
+          # allow let to not be initialised if imported from C:
+          if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)):
+            localError(c.config, a.info, errLetNeedsInit)
+        if sfCompileTime in v.flags:
+          var x = newNodeI(result.kind, v.info)
+          x.add result[i]
+          vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x)
+        if v.flags * {sfGlobal, sfThread} == {sfGlobal}:
+          message(c.config, v.info, hintGlobalVar)
+        if {sfGlobal, sfPure} <= v.flags:
+          globalVarInitCheck(c, def)
+        suggestSym(c.graph, v.info, v, c.graph.usageSym)
 
 proc semConst(c: PContext, n: PNode): PNode =
   result = copyNode(n)
@@ -758,14 +976,17 @@ proc semConst(c: PContext, n: PNode): PNode =
       addToVarSection(c, result, b)
       continue
 
+    var hasUserSpecifiedType = false
     var typ: PType = nil
     if a[^2].kind != nkEmpty:
       typ = semTypeNode(c, a[^2], nil)
+      hasUserSpecifiedType = true
 
-    var typFlags: TTypeAllowedFlags
+    var typFlags: TTypeAllowedFlags = {}
 
     # don't evaluate here since the type compatibility check below may add a converter
-    var def = semExprWithType(c, a[^1], {}, typ)
+    openScope(c)
+    var def = semExprWithType(c, a[^1], {efTypeAllowed}, typ)
 
     if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}:
       typFlags.incl taIsTemplateOrMacro
@@ -791,38 +1012,46 @@ proc semConst(c: PContext, n: PNode): PNode =
       if c.matchedConcept != nil:
         typFlags.incl taConcept
       typeAllowedCheck(c, a.info, typ, skConst, typFlags)
+    closeScope(c)
 
     if a.kind == nkVarTuple:
-      if typ.kind != tyTuple:
-        localError(c.config, a.info, errXExpected, "tuple")
-      elif a.len-2 != typ.len:
-        localError(c.config, a.info, errWrongNumberOfVariables)
-      b = newNodeI(nkVarTuple, a.info)
-      newSons(b, a.len)
-      b[^2] = a[^2]
-      b[^1] = def
-
-    for j in 0..<a.len-2:
-      var v = semIdentDef(c, a[j], skConst)
-      if sfGenSym notin v.flags: addInterfaceDecl(c, v)
-      elif v.owner == nil: v.owner = getCurrOwner(c)
-      styleCheckDef(c, v)
-      onDef(a[j].info, v)
-
-      if a.kind != nkVarTuple:
-        setVarType(c, v, typ)
-        v.ast = def               # no need to copy
+      # generate new section from tuple unpacking and embed it into this one
+      let assignments = makeVarTupleSection(c, n, a, def, typ, skConst, result)
+      let resSection = semConst(c, assignments)
+      for resDef in resSection:
+        addToVarSection(c, result, n, resDef)
+    else:
+      for j in 0..<a.len-2:
+        var v = semIdentDef(c, a[j], skConst)
+        when defined(nimsuggest):
+          v.hasUserSpecifiedType = hasUserSpecifiedType
+        if sfGenSym notin v.flags: addInterfaceDecl(c, v)
+        elif v.owner == nil: v.owner = getCurrOwner(c)
+        styleCheckDef(c, v)
+        onDef(a[j].info, v)
+
+        var fillSymbol = true
+        if v.typ != nil:
+          # symbol already has type and probably value
+          # don't mutate
+          fillSymbol = false
+        else:
+          setVarType(c, v, typ)
         b = newNodeI(nkConstDef, a.info)
         if importantComments(c.config): b.comment = a.comment
-        b.add newSymNode(v)
+        # postfix not generated here (to generate, get rid of it in transf)
+        if a[j].kind == nkPragmaExpr:
+          var p = newNodeI(nkPragmaExpr, a.info)
+          p.add newSymNode(v)
+          p.add a[j][1].copyTree
+          b.add p
+        else:
+          b.add newSymNode(v)
         b.add a[1]
         b.add copyTree(def)
-      else:
-        setVarType(c, v, typ[j])
-        v.ast = if def[j].kind != nkExprColonExpr: def[j]
-                else: def[j][1]
-        b[j] = newSymNode(v)
-    addToVarSection(c, result, n, b)
+        if fillSymbol:
+          v.ast = b
+        addToVarSection(c, result, n, b)
   dec c.inStaticContext
 
 include semfields
@@ -843,10 +1072,16 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
   var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar})
   # n.len == 3 means that there is one for loop variable
   # and thus no tuple unpacking:
+  if iterAfterVarLent.kind == tyEmpty:
+    localError(c.config, n[^2].info, "cannot infer element type of $1" %
+               renderTree(n[^2], {renderNoComments}))
   if iterAfterVarLent.kind != tyTuple or n.len == 3:
     if n.len == 3:
       if n[0].kind == nkVarTuple:
-        if n[0].len-1 != iterAfterVarLent.len:
+        if iterAfterVarLent.kind != tyTuple:
+          return localErrorNode(c, n, n[0].info, errTupleUnpackingTupleExpected %
+              [typeToString(n[1].typ, preferDesc)])
+        elif n[0].len-1 != iterAfterVarLent.len:
           return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables)
 
         for i in 0..<n[0].len-1:
@@ -923,11 +1158,14 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode =
           if not isDiscardUnderscore(v): addDecl(c, v)
         elif v.owner == nil: v.owner = getCurrOwner(c)
   inc(c.p.nestedLoopCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = true
   openScope(c)
   n[^1] = semExprBranch(c, n[^1], flags)
   if efInTypeof notin flags:
     discardCheck(c, n[^1], flags)
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedLoopCounter)
 
 proc implicitIterator(c: PContext, it: string, arg: PNode): PNode =
@@ -956,12 +1194,12 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string;
     if maType == nil: return
 
     let headSymbol = selector[0]
-    var o: TOverloadIter
+    var o: TOverloadIter = default(TOverloadIter)
     var match: PSym = nil
     var symx = initOverloadIter(o, c, headSymbol)
     while symx != nil:
       if symx.kind in {skTemplate, skMacro}:
-        if symx.typ.len == 2 and symx.typ[1] == maType.typ:
+        if symx.typ.len == 2 and symx.typ.firstParamType == maType.typ:
           if match == nil:
             match = symx
           else:
@@ -978,6 +1216,8 @@ proc handleStmtMacro(c: PContext; n, selector: PNode; magicType: string;
     of skMacro: result = semMacroExpr(c, callExpr, callExpr, match, flags)
     of skTemplate: result = semTemplateExpr(c, callExpr, match, flags)
     else: result = nil
+  else:
+    result = nil
 
 proc handleForLoopMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
   result = handleStmtMacro(c, n, n[^2], "ForLoopStmt", flags)
@@ -990,8 +1230,8 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
   toResolve.add newIdentNode(getIdent(c.cache, "case"), n.info)
   toResolve.add n[0]
 
-  var errors: CandidateErrors
-  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {},
+  var errors: CandidateErrors = @[]
+  var r = resolveOverloads(c, toResolve, toResolve, {skTemplate, skMacro}, {efNoUndeclared},
                            errors, false)
   if r.state == csMatch:
     var match = r.calleeSym
@@ -1004,7 +1244,11 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode =
     case match.kind
     of skMacro: result = semMacroExpr(c, toExpand, toExpand, match, flags)
     of skTemplate: result = semTemplateExpr(c, toExpand, match, flags)
-    else: result = nil
+    else: result = errorNode(c, n[0])
+  else:
+    result = errorNode(c, n[0])
+  if result.kind == nkEmpty:
+    localError(c.config, n[0].info, errSelectorMustBeOfCertainTypes)
   # this would be the perfectly consistent solution with 'for loop macros',
   # but it kinda sucks for pattern matching as the matcher is not attached to
   # a type then:
@@ -1019,7 +1263,8 @@ proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode =
   result = n
   n[^2] = semExprNoDeref(c, n[^2], {efWantIterator})
   var call = n[^2]
-  if call.kind == nkStmtListExpr and isTrivalStmtExpr(call):
+
+  if call.kind == nkStmtListExpr and (isTrivalStmtExpr(call) or (call.lastSon.kind in nkCallKinds and call.lastSon[0].sym.kind == skIterator)):
     call = call.lastSon
     n[^2] = call
   let isCallExpr = call.kind in nkCallKinds
@@ -1057,30 +1302,23 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
   openScope(c)
   pushCaseContext(c, n)
   n[0] = semExprWithType(c, n[0])
-  var chckCovered = false
   var covered: Int128 = toInt128(0)
   var typ = commonTypeBegin
   var expectedType = expectedType
   var hasElse = false
   let caseTyp = skipTypes(n[0].typ, abstractVar-{tyTypeDesc})
-  const shouldChckCovered = {tyInt..tyInt64, tyChar, tyEnum, tyUInt..tyUInt64, tyBool}
+  var chckCovered = caseTyp.shouldCheckCaseCovered()
   case caseTyp.kind
-  of shouldChckCovered:
-    chckCovered = true
-  of tyRange:
-    if skipTypes(caseTyp[0], abstractInst).kind in shouldChckCovered:
-      chckCovered = true
-  of tyFloat..tyFloat128, tyString, tyCstring, tyError:
+  of tyFloat..tyFloat128, tyString, tyCstring, tyError, shouldChckCovered, tyRange:
     discard
   else:
     popCaseContext(c)
     closeScope(c)
-    #if caseStmtMacros in c.features:
-    result = handleCaseStmtMacro(c, n, flags)
-    if result != nil:
-      return result
-    localError(c.config, n[0].info, errSelectorMustBeOfCertainTypes)
-    return
+    return handleCaseStmtMacro(c, n, flags)
+  template invalidOrderOfBranches(n: PNode) =
+    localError(c.config, n.info, "invalid order of case branches")
+    break
+
   for i in 1..<n.len:
     setCaseContextIdx(c, i)
     var x = n[i]
@@ -1089,26 +1327,31 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil
         suggestEnum(c, x, caseTyp)
     case x.kind
     of nkOfBranch:
+      if hasElse: invalidOrderOfBranches(x)
       checkMinSonsLen(x, 2, c.config)
       semCaseBranch(c, n, x, i, covered)
       var last = x.len-1
       x[last] = semExprBranchScope(c, x[last], expectedType)
       typ = commonType(c, typ, x[last])
-      expectedType = typ
+      if not endsInNoReturn(x[last]):
+        expectedType = typ
     of nkElifBranch:
+      if hasElse: invalidOrderOfBranches(x)
       chckCovered = false
       checkSonsLen(x, 2, c.config)
       openScope(c)
       x[0] = forceBool(c, semExprWithType(c, x[0], expectedType = getSysType(c.graph, n.info, tyBool)))
       x[1] = semExprBranch(c, x[1], expectedType = expectedType)
       typ = commonType(c, typ, x[1])
-      expectedType = typ
+      if not endsInNoReturn(x[1]):
+        expectedType = typ
       closeScope(c)
     of nkElse:
       checkSonsLen(x, 1, c.config)
       x[0] = semExprBranchScope(c, x[0], expectedType)
       typ = commonType(c, typ, x[0])
-      expectedType = typ
+      if not endsInNoReturn(x[0]):
+        expectedType = typ
       if (chckCovered and covered == toCover(c, n[0].typ)) or hasElse:
         message(c.config, x.info, warnUnreachableElse)
       hasElse = true
@@ -1149,7 +1392,7 @@ proc semRaise(c: PContext, n: PNode): PNode =
       typ = typ.skipTypes({tyAlias, tyGenericInst, tyOwned})
       if typ.kind != tyRef:
         localError(c.config, n.info, errExprCannotBeRaised)
-      if typ.len > 0 and not isException(typ.lastSon):
+      if typ.len > 0 and not isException(typ.elementType):
         localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ))
 
 proc addGenericParamListToScope(c: PContext, n: PNode) =
@@ -1165,13 +1408,16 @@ proc typeSectionTypeName(c: PContext; n: PNode): PNode =
     result = n[0]
   else:
     result = n
+  if result.kind == nkPostfix:
+    if result.len != 2: illFormedAst(n, c.config)
+    result = result[1]
   if result.kind != nkSym: illFormedAst(n, c.config)
 
 proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
   let typeDef = typeSection[i]
   checkSonsLen(typeDef, 3, c.config)
   var name = typeDef[0]
-  var s: PSym
+  var s: PSym = nil
   if name.kind == nkDotExpr and typeDef[2].kind == nkObjectTy:
     let pkgName = considerQuotedIdent(c, name[0])
     let typName = considerQuotedIdent(c, name[1])
@@ -1232,9 +1478,15 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) =
     elif s.owner == nil: s.owner = getCurrOwner(c)
 
   if name.kind == nkPragmaExpr:
-    typeDef[0][0] = newSymNode(s)
+    if name[0].kind == nkPostfix:
+      typeDef[0][0][1] = newSymNode(s)
+    else:
+      typeDef[0][0] = newSymNode(s)
   else:
-    typeDef[0] = newSymNode(s)
+    if name.kind == nkPostfix:
+      typeDef[0][1] = newSymNode(s)
+    else:
+      typeDef[0] = newSymNode(s)
 
 proc typeSectionLeftSidePass(c: PContext, n: PNode) =
   # process the symbols on the left side for the whole type section, before
@@ -1254,7 +1506,7 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     inc i
 
 proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
-  var body = genericType[^1]
+  var body = genericType.typeBodyImpl
 
   proc traverseSubTypes(c: PContext; t: PType): bool =
     template error(msg) = localError(c.config, genericType.sym.info, msg)
@@ -1271,17 +1523,17 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
       for field in t.n:
         subresult traverseSubTypes(c, field.typ)
     of tyArray:
-      return traverseSubTypes(c, t[1])
+      return traverseSubTypes(c, t.elementType)
     of tyProc:
-      for subType in t.sons:
+      for subType in t.signature:
         if subType != nil:
           subresult traverseSubTypes(c, subType)
       if result:
         error("non-invariant type param used in a proc type: " & $t)
     of tySequence:
-      return traverseSubTypes(c, t[0])
+      return traverseSubTypes(c, t.elementType)
     of tyGenericInvocation:
-      let targetBody = t[0]
+      let targetBody = t.genericHead
       for i in 1..<t.len:
         let param = t[i]
         if param.kind == tyGenericParam:
@@ -1306,13 +1558,13 @@ proc checkCovariantParamsUsages(c: PContext; genericType: PType) =
     of tyUserTypeClass, tyUserTypeClassInst:
       error("non-invariant type parameters are not supported in concepts")
     of tyTuple:
-      for fieldType in t.sons:
+      for fieldType in t.kids:
         subresult traverseSubTypes(c, fieldType)
     of tyPtr, tyRef, tyVar, tyLent:
-      if t.base.kind == tyGenericParam: return true
-      return traverseSubTypes(c, t.base)
+      if t.elementType.kind == tyGenericParam: return true
+      return traverseSubTypes(c, t.elementType)
     of tyDistinct, tyAlias, tySink, tyOwned:
-      return traverseSubTypes(c, t.lastSon)
+      return traverseSubTypes(c, t.skipModifier)
     of tyGenericInst:
       internalAssert c.config, false
     else:
@@ -1350,10 +1602,14 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       # we fill it out later. For magic generics like 'seq', it won't be filled
       # so we use tyNone instead of nil to not crash for strange conversions
       # like: mydata.seq
-      rawAddSon(s.typ, newTypeS(tyNone, c))
+      if s.typ.kind in {tyOpenArray, tyVarargs} and s.typ.len == 1:
+        # XXX investigate why `tySequence` cannot be added here for now.
+        discard
+      else:
+        rawAddSon(s.typ, newTypeS(tyNone, c))
       s.ast = a
       inc c.inGenericContext
-      var body = semTypeNode(c, a[2], nil)
+      var body = semTypeNode(c, a[2], s.typ)
       dec c.inGenericContext
       if body != nil:
         body.sym = s
@@ -1377,7 +1633,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
           # possibilities such as instantiating C++ generic types with
           # garbage collected Nim types.
           if sfImportc in s.flags:
-            var body = s.typ.lastSon
+            var body = s.typ.last
             if body.kind == tyObject:
               # erases all declared fields
               body.n.sons = @[]
@@ -1401,22 +1657,30 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       # final pass
       if a[2].kind in nkCallKinds:
         incl a[2].flags, nfSem # bug #10548
-    if sfExportc in s.flags and s.typ.kind == tyAlias:
-      localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
-
-    if tfBorrowDot in s.typ.flags and s.typ.skipTypes({tyGenericBody}).kind != tyDistinct:
-      excl s.typ.flags, tfBorrowDot
-      localError(c.config, name.info, "only a 'distinct' type can borrow `.`")
+    if sfExportc in s.flags:
+      if s.typ.kind == tyAlias:
+        localError(c.config, name.info, "{.exportc.} not allowed for type aliases")
+      elif s.typ.kind == tyGenericBody:
+        localError(c.config, name.info, "{.exportc.} not allowed for generic types")
+
+    if tfBorrowDot in s.typ.flags:
+      let body = s.typ.skipTypes({tyGenericBody})
+      if body.kind != tyDistinct:
+        # flag might be copied from alias/instantiation:
+        let t = body.skipTypes({tyAlias, tyGenericInst})
+        if not (t.kind == tyDistinct and tfBorrowDot in t.flags):
+          excl s.typ.flags, tfBorrowDot
+          localError(c.config, name.info, "only a 'distinct' type can borrow `.`")
     let aa = a[2]
     if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and
        aa[0].kind == nkObjectTy:
       # give anonymous object a dummy symbol:
       var st = s.typ
-      if st.kind == tyGenericBody: st = st.lastSon
+      if st.kind == tyGenericBody: st = st.typeBodyImpl
       internalAssert c.config, st.kind in {tyPtr, tyRef}
-      internalAssert c.config, st.lastSon.sym == nil
+      internalAssert c.config, st.last.sym == nil
       incl st.flags, tfRefsAnonObj
-      let objTy = st.lastSon
+      let objTy = st.last
       # add flags for `ref object` etc to underlying `object`
       incl(objTy.flags, oldFlags)
       # {.inheritable, final.} is already disallowed, but
@@ -1424,39 +1688,54 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
       if tfInheritable in oldFlags and tfFinal in objTy.flags:
         excl(objTy.flags, tfFinal)
       let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"),
-                       nextSymId c.idgen, getCurrOwner(c), s.info)
+                       c.idgen, getCurrOwner(c), s.info)
       obj.flags.incl sfGeneratedType
       let symNode = newSymNode(obj)
       obj.ast = a.shallowCopy
       case a[0].kind
-        of nkSym: obj.ast[0] = symNode
-        of nkPragmaExpr:
-          obj.ast[0] = a[0].shallowCopy
+      of nkSym: obj.ast[0] = symNode
+      of nkPragmaExpr:
+        obj.ast[0] = a[0].shallowCopy
+        if a[0][0].kind == nkPostfix:
+          obj.ast[0][0] = a[0][0].shallowCopy
+          obj.ast[0][0][1] = symNode
+        else:
           obj.ast[0][0] = symNode
-          obj.ast[0][1] = a[0][1]
-        else: assert(false)
+        obj.ast[0][1] = a[0][1]
+      of nkPostfix:
+        obj.ast[0] = a[0].shallowCopy
+        obj.ast[0][1] = symNode
+      else: assert(false)
       obj.ast[1] = a[1]
       obj.ast[2] = a[2][0]
       if sfPure in s.flags:
         obj.flags.incl sfPure
       obj.typ = objTy
       objTy.sym = obj
-
-proc checkForMetaFields(c: PContext; n: PNode) =
-  proc checkMeta(c: PContext; n: PNode; t: PType) =
-    if t != nil and t.isMetaType and tfGenericTypeParam notin t.flags:
-      if t.kind == tyBuiltInTypeClass and t.len == 1 and t[0].kind == tyProc:
+  for sk in c.skipTypes:
+    discard semTypeNode(c, sk, nil)
+  c.skipTypes = @[]
+
+proc checkForMetaFields(c: PContext; n: PNode; hasError: var bool) =
+  proc checkMeta(c: PContext; n: PNode; t: PType; hasError: var bool; parent: PType) =
+    if t != nil and (t.isMetaType or t.kind == tyNone) and tfGenericTypeParam notin t.flags:
+      if t.kind == tyBuiltInTypeClass and t.len == 1 and t.elementType.kind == tyProc:
         localError(c.config, n.info, ("'$1' is not a concrete type; " &
           "for a callback without parameters use 'proc()'") % t.typeToString)
+      elif t.kind == tyNone and parent != nil:
+        # TODO: openarray has the `tfGenericTypeParam` flag & generics
+        # TODO: handle special cases (sink etc.) and views
+        localError(c.config, n.info, errTIsNotAConcreteType % parent.typeToString)
       else:
         localError(c.config, n.info, errTIsNotAConcreteType % t.typeToString)
+      hasError = true
 
   if n.isNil: return
   case n.kind
   of nkRecList, nkRecCase:
-    for s in n: checkForMetaFields(c, s)
+    for s in n: checkForMetaFields(c, s, hasError)
   of nkOfBranch, nkElse:
-    checkForMetaFields(c, n.lastSon)
+    checkForMetaFields(c, n.lastSon, hasError)
   of nkSym:
     let t = n.sym.typ
     case t.kind
@@ -1464,9 +1743,9 @@ proc checkForMetaFields(c: PContext; n: PNode) =
        tyProc, tyGenericInvocation, tyGenericInst, tyAlias, tySink, tyOwned:
       let start = ord(t.kind in {tyGenericInvocation, tyGenericInst})
       for i in start..<t.len:
-        checkMeta(c, n, t[i])
+        checkMeta(c, n, t[i], hasError, t)
     else:
-      checkMeta(c, n, t)
+      checkMeta(c, n, t, hasError, nil)
   else:
     internalAssert c.config, false
 
@@ -1501,14 +1780,16 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
               assert s.typ != nil
               assignType(s.typ, t)
               s.typ.itemId = t.itemId     # same id
-        checkConstructedType(c.config, s.info, s.typ)
-        if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil:
-          checkForMetaFields(c, s.typ.n)
-
-        # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name:
-        if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c):
-          incl(s.flags, sfGenSym)
-
+        var hasError = false
+        let baseType = s.typ.safeSkipTypes(abstractPtrs)
+        if baseType.kind in {tyObject, tyTuple} and not baseType.n.isNil and
+          (x.kind in {nkObjectTy, nkTupleTy} or
+           (x.kind in {nkRefTy, nkPtrTy} and x.len == 1 and
+           x[0].kind in {nkObjectTy, nkTupleTy})
+          ):
+          checkForMetaFields(c, baseType.n, hasError)
+        if not hasError:
+          checkConstructedType(c.config, s.info, s.typ)
   #instAllTypeBoundOp(c, n.info)
 
 
@@ -1582,8 +1863,9 @@ proc addParams(c: PContext, n: PNode, kind: TSymKind) =
 
 proc semBorrow(c: PContext, n: PNode, s: PSym) =
   # search for the correct alias:
-  var b = searchForBorrowProc(c, c.currentScope.parent, s)
-  if b != nil:
+  var (b, state) = searchForBorrowProc(c, c.currentScope.parent, s)
+  case state
+  of bsMatch:
     # store the alias:
     n[bodyPos] = newSymNode(b)
     # Carry over the original symbol magic, this is necessary in order to ensure
@@ -1592,6 +1874,14 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) =
     if b.typ != nil and b.typ.len > 0:
       s.typ.n[0] = b.typ.n[0]
     s.typ.flags = b.typ.flags
+  of bsNoDistinct:
+    localError(c.config, n.info, "borrow proc without distinct type parameter is meaningless")
+  of bsReturnNotMatch:
+    localError(c.config, n.info, "borrow from proc return type mismatch: '$1'" % typeToString(b.typ.returnType))
+  of bsGeneric:
+    localError(c.config, n.info, "borrow with generic parameter is not supported")
+  of bsNotSupported:
+    localError(c.config, n.info, "borrow from '$1' is not supported" % $b.name.s)
   else:
     localError(c.config, n.info, errNoSymbolToBorrowFromFound)
 
@@ -1604,7 +1894,7 @@ proc swapResult(n: PNode, sRes: PSym, dNode: PNode) =
 
 proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
   template genResSym(s) =
-    var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId c.idgen,
+    var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen,
                    getCurrOwner(c), n.info)
     s.typ = t
     incl(s.flags, sfUsed)
@@ -1629,12 +1919,15 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) =
 proc semProcAnnotation(c: PContext, prc: PNode;
                        validPragmas: TSpecialWords): PNode =
   # Mirrored with semVarMacroPragma
+  result = nil
   var n = prc[pragmasPos]
   if n == nil or n.kind == nkEmpty: return
   for i in 0..<n.len:
     let it = n[i]
     let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it
 
+    trySuggestPragmas(c, key)
+
     if isPossibleMacroPragma(c, it, key):
       # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and
       # let the semantic checker deal with it:
@@ -1674,15 +1967,9 @@ proc semProcAnnotation(c: PContext, prc: PNode;
 
       doAssert result != nil
 
-      # since a proc annotation can set pragmas, we process these here again.
-      # This is required for SqueakNim-like export pragmas.
-      if false and result.kind in procDefs and result[namePos].kind == nkSym and
-          result[pragmasPos].kind != nkEmpty:
-        pragma(c, result[namePos].sym, result[pragmasPos], validPragmas)
-
       return result
 
-proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
+proc semInferredLambda(c: PContext, pt: TypeMapping, n: PNode): PNode =
   ## used for resolving 'auto' in lambdas based on their callsite
   var n = n
   let original = n[namePos].sym
@@ -1697,7 +1984,6 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
   n[genericParamsPos] = c.graph.emptyNode
   # for LL we need to avoid wrong aliasing
   let params = copyTree n.typ.n
-  n[paramsPos] = params
   s.typ = n.typ
   for i in 1..<params.len:
     if params[i].typ.kind in {tyTypeDesc, tyGenericParam,
@@ -1709,8 +1995,8 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} =
   pushOwner(c, s)
   addParams(c, params, skProc)
   pushProcCon(c, s)
-  addResult(c, n, n.typ[0], skProc)
-  s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ[0]))
+  addResult(c, n, n.typ.returnType, skProc)
+  s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], n.typ.returnType))
   trackProc(c, s, s.ast[bodyPos])
   popProcCon(c)
   popOwner(c)
@@ -1740,8 +2026,8 @@ proc maybeAddResult(c: PContext, s: PSym, n: PNode) =
   if s.kind == skMacro:
     let resultType = sysTypeFromName(c.graph, n.info, "NimNode")
     addResult(c, n, resultType, s.kind)
-  elif s.typ[0] != nil and not isInlineIterator(s.typ):
-    addResult(c, n, s.typ[0], s.kind)
+  elif s.typ.returnType != nil and not isInlineIterator(s.typ):
+    addResult(c, n, s.typ.returnType, s.kind)
 
 proc canonType(c: PContext, t: PType): PType =
   if t.kind == tySequence:
@@ -1751,7 +2037,7 @@ proc canonType(c: PContext, t: PType): PType =
 
 proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
   var msg = "cannot bind another '" & prevOp.name.s & "' to: " & typeToString(obj)
-  if sfOverriden notin prevOp.flags:
+  if sfOverridden notin prevOp.flags:
     msg.add "; previous declaration was constructed here implicitly: " & (c.config $ prevOp.info)
   else:
     msg.add "; previous declaration was here: " & (c.config $ prevOp.info)
@@ -1760,30 +2046,79 @@ proc prevDestructor(c: PContext; prevOp: PSym; obj: PType; info: TLineInfo) =
 proc whereToBindTypeHook(c: PContext; t: PType): PType =
   result = t
   while true:
-    if result.kind in {tyGenericBody, tyGenericInst}: result = result.lastSon
+    if result.kind in {tyGenericBody, tyGenericInst}: result = result.skipModifier
     elif result.kind == tyGenericInvocation: result = result[0]
     else: break
   if result.kind in {tyObject, tyDistinct, tySequence, tyString}:
     result = canonType(c, result)
 
-proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
+proc bindDupHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
+  let t = s.typ
+  var noError = false
+  let cond = t.len == 2 and t.returnType != nil
+
+  if cond:
+    var obj = t.firstParamType
+    while true:
+      incl(obj.flags, tfHasAsgn)
+      if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
+      elif obj.kind == tyGenericInvocation: obj = obj.genericHead
+      else: break
+
+    var res = t.returnType
+    while true:
+      if res.kind in {tyGenericBody, tyGenericInst}: res = res.skipModifier
+      elif res.kind == tyGenericInvocation: res = res.genericHead
+      else: break
+
+    if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, res):
+      obj = canonType(c, obj)
+      let ao = getAttachedOp(c.graph, obj, op)
+      if ao == s:
+        discard "forward declared destructor"
+      elif ao.isNil and tfCheckedForDestructor notin obj.flags:
+        setAttachedOp(c.graph, c.module.position, obj, op, s)
+      else:
+        prevDestructor(c, ao, obj, n.info)
+      noError = true
+      if obj.owner.getModule != s.getModule:
+        localError(c.config, n.info, errGenerated,
+          "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
+
+  if not noError and sfSystemModule notin s.owner.flags:
+    localError(c.config, n.info, errGenerated,
+      "signature for '=dup' must be proc[T: object](x: T): T")
+
+  incl(s.flags, sfUsed)
+  incl(s.flags, sfOverridden)
+
+proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp; suppressVarDestructorWarning = false) =
   let t = s.typ
   var noError = false
-  let cond = if op == attachedDestructor:
-               t.len == 2 and t[0] == nil and t[1].kind == tyVar
-             elif op == attachedTrace:
-               t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer
+  let cond = case op
+             of attachedWasMoved:
+               t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
+             of attachedTrace:
+               t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar and t[2].kind == tyPointer
+             of attachedDestructor:
+               if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+                 t.len == 2 and t.returnType == nil
+               else:
+                 t.len == 2 and t.returnType == nil and t.firstParamType.kind == tyVar
              else:
-               t.len >= 2 and t[0] == nil
+               t.len >= 2 and t.returnType == nil
 
   if cond:
-    var obj = t[1].skipTypes({tyVar})
+    var obj = t.firstParamType.skipTypes({tyVar})
     while true:
       incl(obj.flags, tfHasAsgn)
-      if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.lastSon
-      elif obj.kind == tyGenericInvocation: obj = obj[0]
+      if obj.kind in {tyGenericBody, tyGenericInst}: obj = obj.skipModifier
+      elif obj.kind == tyGenericInvocation: obj = obj.genericHead
       else: break
     if obj.kind in {tyObject, tyDistinct, tySequence, tyString}:
+      if (not suppressVarDestructorWarning) and op == attachedDestructor and t.firstParamType.kind == tyVar and
+          c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+        message(c.config, n.info, warnDeprecated, "A custom '=destroy' hook which takes a 'var T' parameter is deprecated; it should take a 'T' parameter")
       obj = canonType(c, obj)
       let ao = getAttachedOp(c.graph, obj, op)
       if ao == s:
@@ -1797,30 +2132,43 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) =
         localError(c.config, n.info, errGenerated,
           "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")")
   if not noError and sfSystemModule notin s.owner.flags:
-    if op == attachedTrace:
+    case op
+    of attachedTrace:
       localError(c.config, n.info, errGenerated,
         "signature for '=trace' must be proc[T: object](x: var T; env: pointer)")
+    of attachedDestructor:
+      if c.config.selectedGC in {gcArc, gcAtomicArc, gcOrc}:
+        localError(c.config, n.info, errGenerated,
+          "signature for '=destroy' must be proc[T: object](x: var T) or proc[T: object](x: T)")
+      else:
+        localError(c.config, n.info, errGenerated,
+          "signature for '=destroy' must be proc[T: object](x: var T)")
     else:
       localError(c.config, n.info, errGenerated,
         "signature for '" & s.name.s & "' must be proc[T: object](x: var T)")
   incl(s.flags, sfUsed)
-  incl(s.flags, sfOverriden)
+  incl(s.flags, sfOverridden)
 
 proc semOverride(c: PContext, s: PSym, n: PNode) =
   let name = s.name.s.normalize
   case name
   of "=destroy":
     bindTypeHook(c, s, n, attachedDestructor)
+    if s.ast != nil:
+      if s.ast[pragmasPos].kind == nkEmpty:
+        s.ast[pragmasPos] = newNodeI(nkPragma, s.info)
+      s.ast[pragmasPos].add newTree(nkExprColonExpr,
+          newIdentNode(c.cache.getIdent("raises"),  s.info), newNodeI(nkBracket, s.info))
   of "deepcopy", "=deepcopy":
     if s.typ.len == 2 and
-        s.typ[1].skipTypes(abstractInst).kind in {tyRef, tyPtr} and
-        sameType(s.typ[1], s.typ[0]):
+        s.typ.firstParamType.skipTypes(abstractInst).kind in {tyRef, tyPtr} and
+        sameType(s.typ.firstParamType, s.typ.returnType):
       # Note: we store the deepCopy in the base of the pointer to mitigate
       # the problem that pointers are structural types:
-      var t = s.typ[1].skipTypes(abstractInst).lastSon.skipTypes(abstractInst)
+      var t = s.typ.firstParamType.skipTypes(abstractInst).elementType.skipTypes(abstractInst)
       while true:
-        if t.kind == tyGenericBody: t = t.lastSon
-        elif t.kind == tyGenericInvocation: t = t[0]
+        if t.kind == tyGenericBody: t = t.typeBodyImpl
+        elif t.kind == tyGenericInvocation: t = t.genericHead
         else: break
       if t.kind in {tyObject, tyDistinct, tyEnum, tySequence, tyString}:
         if getAttachedOp(c.graph, t, attachedDeepCopy).isNil:
@@ -1840,24 +2188,26 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       localError(c.config, n.info, errGenerated,
                  "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T")
     incl(s.flags, sfUsed)
-    incl(s.flags, sfOverriden)
+    incl(s.flags, sfOverridden)
   of "=", "=copy", "=sink":
     if s.magic == mAsgn: return
     incl(s.flags, sfUsed)
-    incl(s.flags, sfOverriden)
+    incl(s.flags, sfOverridden)
+    if name == "=":
+      message(c.config, n.info, warnDeprecated, "Overriding `=` hook is deprecated; Override `=copy` hook instead")
     let t = s.typ
-    if t.len == 3 and t[0] == nil and t[1].kind == tyVar:
-      var obj = t[1][0]
+    if t.len == 3 and t.returnType == nil and t.firstParamType.kind == tyVar:
+      var obj = t.firstParamType.elementType
       while true:
         incl(obj.flags, tfHasAsgn)
-        if obj.kind == tyGenericBody: obj = obj.lastSon
-        elif obj.kind == tyGenericInvocation: obj = obj[0]
+        if obj.kind == tyGenericBody: obj = obj.skipModifier
+        elif obj.kind == tyGenericInvocation: obj = obj.genericHead
         else: break
       var objB = t[2]
       while true:
-        if objB.kind == tyGenericBody: objB = objB.lastSon
+        if objB.kind == tyGenericBody: objB = objB.skipModifier
         elif objB.kind in {tyGenericInvocation, tyGenericInst}:
-          objB = objB[0]
+          objB = objB.genericHead
         else: break
       if obj.kind in {tyObject, tyDistinct, tySequence, tyString} and sameType(obj, objB):
         # attach these ops to the canonical tySequence
@@ -1882,12 +2232,19 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
   of "=trace":
     if s.magic != mTrace:
       bindTypeHook(c, s, n, attachedTrace)
+  of "=wasmoved":
+    if s.magic != mWasMoved:
+      bindTypeHook(c, s, n, attachedWasMoved)
+  of "=dup":
+    if s.magic != mDup:
+      bindDupHook(c, s, n, attachedDup)
   else:
-    if sfOverriden in s.flags:
+    if sfOverridden in s.flags:
       localError(c.config, n.info, errGenerated,
                  "'destroy' or 'deepCopy' expected for 'override'")
 
 proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
+  result = false
   if inCheckpoint(n.info, conf.m.trackPos) != cpNone: return true
   for i in 0..<n.safeLen:
     if cursorInProcAux(conf, n[i]): return true
@@ -1895,8 +2252,11 @@ proc cursorInProcAux(conf: ConfigRef; n: PNode): bool =
 proc cursorInProc(conf: ConfigRef; n: PNode): bool =
   if n.info.fileIndex == conf.m.trackPos.fileIndex:
     result = cursorInProcAux(conf, n)
+  else:
+    result = false
 
 proc hasObjParam(s: PSym): bool =
+  result = false
   var t = s.typ
   for col in 1..<t.len:
     if skipTypes(t[col], skipPtrs).kind == tyObject:
@@ -1906,6 +2266,55 @@ proc finishMethod(c: PContext, s: PSym) =
   if hasObjParam(s):
     methodDef(c.graph, c.idgen, s)
 
+proc semCppMember(c: PContext; s: PSym; n: PNode) =
+  if sfImportc notin s.flags:
+    let isVirtual = sfVirtual in s.flags
+    let isCtor = sfConstructor in s.flags
+    let pragmaName = if isVirtual: "virtual" elif isCtor: "constructor" else: "member"
+    if c.config.backend == backendCpp:
+      if s.typ.len < 2 and not isCtor:
+        localError(c.config, n.info, pragmaName & " must have at least one parameter")
+      for son in s.typ.signature:
+        if son!=nil and son.isMetaType:
+          localError(c.config, n.info, pragmaName & " unsupported for generic routine")
+      var typ: PType
+      if isCtor:
+        typ = s.typ.returnType
+        if typ == nil or typ.kind != tyObject:
+          localError(c.config, n.info, "constructor must return an object")
+        if sfImportc in typ.sym.flags:
+          localError(c.config, n.info, "constructor in an imported type needs importcpp pragma")
+      else:
+        typ = s.typ.firstParamType
+      if typ.kind == tyPtr and not isCtor:
+        typ = typ.elementType
+      if typ.kind != tyObject:
+        localError(c.config, n.info, pragmaName & " must be either ptr to object or object type.")
+      if typ.owner.id == s.owner.id and c.module.id == s.owner.id:
+        c.graph.memberProcsPerType.mgetOrPut(typ.itemId, @[]).add s
+      else:
+        localError(c.config, n.info,
+          pragmaName & " procs must be defined in the same scope as the type they are virtual for and it must be a top level scope")
+    else:
+      localError(c.config, n.info, pragmaName & " procs are only supported in C++")
+  else:
+    var typ = s.typ.returnType
+    if typ != nil and typ.kind == tyObject and typ.itemId notin c.graph.initializersPerType:
+      var initializerCall = newTree(nkCall, newSymNode(s))
+      var isInitializer = n[paramsPos].len > 1
+      for i in  1..<n[paramsPos].len:
+        let p = n[paramsPos][i]
+        let val = p[^1]
+        if val.kind == nkEmpty:
+          isInitializer = false
+          break
+        var j = 0
+        while p[j].sym.kind == skParam:
+          initializerCall.add val
+          inc j
+      if isInitializer:
+        c.graph.initializersPerType[typ.itemId] = initializerCall
+
 proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
   if s.isGenericRoutine:
     let tt = s.typ
@@ -1915,7 +2324,7 @@ proc semMethodPrototype(c: PContext; s: PSym; n: PNode) =
     for col in 1..<tt.len:
       let t = tt[col]
       if t != nil and t.kind == tyGenericInvocation:
-        var x = skipTypes(t[0], {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
+        var x = skipTypes(t.genericHead, {tyVar, tyLent, tyPtr, tyRef, tyGenericInst,
                                  tyGenericInvocation, tyGenericBody,
                                  tyAlias, tySink, tyOwned})
         if x.kind == tyObject and t.len-1 == n[genericParamsPos].len:
@@ -1940,20 +2349,25 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   result = n
   checkMinSonsLen(n, bodyPos + 1, c.config)
 
-  let isAnon = n[namePos].kind == nkEmpty
+  let
+    isAnon = n[namePos].kind == nkEmpty
+    isHighlight = c.config.ideCmd == ideHighlight
 
   var s: PSym
 
   case n[namePos].kind
   of nkEmpty:
-    s = newSym(kind, c.cache.idAnon, nextSymId c.idgen, c.getCurrOwner, n.info)
+    s = newSym(kind, c.cache.idAnon, c.idgen, c.getCurrOwner, n.info)
     s.flags.incl sfUsed
     n[namePos] = newSymNode(s)
   of nkSym:
     s = n[namePos].sym
     s.owner = c.getCurrOwner
   else:
-    s = semIdentDef(c, n[namePos], kind)
+    # Highlighting needs to be done early so the position for
+    # name isn't changed (see taccent_highlight). We don't want to check if this is the
+    # defintion yet since we are missing some info (comments, side effects)
+    s = semIdentDef(c, n[namePos], kind, reportToNimsuggest=isHighlight)
     n[namePos] = newSymNode(s)
     when false:
       # disable for now
@@ -1968,6 +2382,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   s.ast = n
   s.options = c.config.options
   #s.scope = c.currentScope
+  if s.kind in {skMacro, skTemplate}:
+    # push noalias flag at first to prevent unwanted recursive calls:
+    incl(s.flags, sfNoalias)
 
   # before compiling the proc params & body, set as current the scope
   # where the proc was declared
@@ -2108,7 +2525,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     pushOwner(c, s)
 
   if not isAnon:
-    if sfOverriden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
+    if sfOverridden in s.flags or s.name.s[0] == '=': semOverride(c, s, n)
     elif s.name.s[0] in {'.', '('}:
       if s.name.s in [".", ".()", ".="] and {Feature.destructor, dotOperators} * c.features == {}:
         localError(c.config, n.info, "the overloaded " & s.name.s &
@@ -2117,10 +2534,14 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         localError(c.config, n.info, "the overloaded " & s.name.s &
           " operator has to be enabled with {.experimental: \"callOperator\".}")
 
+  if sfBorrow in s.flags and c.config.cmd notin cmdDocLike:
+    result[bodyPos] = c.graph.emptyNode
+
+  if sfCppMember * s.flags != {} and sfWasForwarded notin s.flags:
+    semCppMember(c, s, n)
+
   if n[bodyPos].kind != nkEmpty and sfError notin s.flags:
     # for DLL generation we allow sfImportc to have a body, for use in VM
-    if sfBorrow in s.flags:
-      localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
     if c.config.ideCmd in {ideSug, ideCon} and s.kind notin {skMacro, skTemplate} and not
         cursorInProc(c.config, n[bodyPos]):
       # speed up nimsuggest
@@ -2131,8 +2552,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # absolutely no generics (empty) or a single generic return type are
         # allowed, everything else, including a nullary generic is an error.
         pushProcCon(c, s)
-        addResult(c, n, s.typ[0], skProc)
-        s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ[0]))
+        addResult(c, n, s.typ.returnType, skProc)
+        s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos], s.typ.returnType))
         trackProc(c, s, s.ast[bodyPos])
         popProcCon(c)
       elif efOperand notin flags:
@@ -2143,13 +2564,12 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # Macros and Templates can have generic parameters, but they are only
         # used for overload resolution (there is no instantiation of the symbol)
         if s.kind notin {skMacro, skTemplate} and s.magic == mNone: paramsTypeCheck(c, s.typ)
-
         maybeAddResult(c, s, n)
-        let resultType = 
+        let resultType =
           if s.kind == skMacro:
             sysTypeFromName(c.graph, n.info, "NimNode")
           elif not isInlineIterator(s.typ):
-            s.typ[0]
+            s.typ.returnType
           else:
             nil
         # semantic checking also needed with importc in case used in VM
@@ -2158,8 +2578,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
         # context as it may even be evaluated in 'system.compiles':
         trackProc(c, s, s.ast[bodyPos])
       else:
-        if (s.typ[0] != nil and s.kind != skIterator):
-          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), nextSymId c.idgen, s, n.info))
+        if (s.typ.returnType != nil and s.kind != skIterator):
+          addDecl(c, newSym(skUnknown, getIdent(c.cache, "result"), c.idgen, s, n.info))
 
         openScope(c)
         n[bodyPos] = semGenericStmt(c, n[bodyPos])
@@ -2173,8 +2593,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
     if hasProto: localError(c.config, n.info, errImplOfXexpected % proto.name.s)
     if {sfImportc, sfBorrow, sfError} * s.flags == {} and s.magic == mNone:
       # this is a forward declaration and we're building the prototype
-      if s.kind in {skProc, skFunc} and s.typ[0] != nil and s.typ[0].kind == tyUntyped:
-        # `auto` is represented as `tyUntyped` at this point in compilation.
+      if s.kind in {skProc, skFunc} and s.typ.returnType != nil and s.typ.returnType.kind == tyAnything:
         localError(c.config, n[paramsPos][0].info, "return type 'auto' cannot be used in forward declarations")
 
       incl(s.flags, sfForward)
@@ -2195,6 +2614,11 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind,
   elif isTopLevel(c) and s.kind != skIterator and s.typ.callConv == ccClosure:
     localError(c.config, s.info, "'.closure' calling convention for top level routines is invalid")
 
+  # Prevent double highlights. We already highlighted before.
+  # When not highlighting we still need to allow for suggestions though
+  if not isHighlight:
+    suggestSym(c.graph, s.info, s, c.graph.usageSym)
+
 proc determineType(c: PContext, s: PSym) =
   if s.typ != nil: return
   #if s.magic != mNone: return
@@ -2214,7 +2638,7 @@ proc semIterator(c: PContext, n: PNode): PNode =
   if result.kind != n.kind: return
   var s = result[namePos].sym
   var t = s.typ
-  if t[0] == nil and s.typ.callConv != ccClosure:
+  if t.returnType == nil and s.typ.callConv != ccClosure:
     localError(c.config, n.info, "iterator needs a return type")
   # iterators are either 'inline' or 'closure'; for backwards compatibility,
   # we require first class iterators to be marked with 'closure' explicitly
@@ -2251,16 +2675,15 @@ proc semMethod(c: PContext, n: PNode): PNode =
   # test case):
   let disp = getDispatcher(s)
   # auto return type?
-  if disp != nil and disp.typ[0] != nil and disp.typ[0].kind == tyUntyped:
-    let ret = s.typ[0]
-    disp.typ[0] = ret
+  if disp != nil and disp.typ.returnType != nil and disp.typ.returnType.kind == tyUntyped:
+    let ret = s.typ.returnType
+    disp.typ.setReturnType ret
     if disp.ast[resultPos].kind == nkSym:
       if isEmptyType(ret): disp.ast[resultPos] = c.graph.emptyNode
       else: disp.ast[resultPos].sym.typ = ret
 
 proc semConverterDef(c: PContext, n: PNode): PNode =
   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
@@ -2270,12 +2693,11 @@ proc semConverterDef(c: PContext, n: PNode): PNode =
   if result.kind != nkConverterDef: return
   var s = result[namePos].sym
   var t = s.typ
-  if t[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
+  if t.returnType == nil: localError(c.config, n.info, errXNeedsReturnType % "converter")
   if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument")
   addConverterDef(c, LazySym(sym: s))
 
 proc semMacroDef(c: PContext, n: PNode): PNode =
-  checkSonsLen(n, bodyPos + 1, c.config)
   result = semProcAux(c, n, skMacro, macroPragmas)
   # macros can transform macros to nothing:
   if namePos >= result.safeLen: return result
@@ -2286,10 +2708,16 @@ proc semMacroDef(c: PContext, n: PNode): PNode =
   var s = result[namePos].sym
   var t = s.typ
   var allUntyped = true
+  var nullary = true
   for i in 1..<t.n.len:
     let param = t.n[i].sym
     if param.typ.kind != tyUntyped: allUntyped = false
+    # no default value, parameters required in call
+    if param.ast == nil: nullary = false
   if allUntyped: incl(s.flags, sfAllUntyped)
+  if nullary and n[genericParamsPos].kind == nkEmpty:
+    # macro can be called with alias syntax, remove pushed noalias flag
+    excl(s.flags, sfNoalias)
   if n[bodyPos].kind == nkEmpty:
     localError(c.config, n.info, errImplOfXexpected % s.name.s)
 
@@ -2307,28 +2735,31 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) =
 proc evalInclude(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
   result.add n
+  template checkAs(it: PNode) =
+    if it.kind == nkInfix and it.len == 3:
+      let op = it[0].getPIdent
+      if op != nil and op.id == ord(wAs):
+        localError(c.config, it.info, "Cannot use '" & it[0].renderTree & "' in 'include'.")
   for i in 0..<n.len:
-    var imp: PNode
     let it = n[i]
-    if it.kind == nkInfix and it.len == 3 and it[0].ident.s != "/":
-      localError(c.config, it.info, "Cannot use '" & it[0].ident.s & "' in 'include'.")
-    if it.kind == nkInfix and it.len == 3 and it[2].kind == nkBracket:
-      let sep = it[0]
-      let dir = it[1]
-      imp = newNodeI(nkInfix, it.info)
-      imp.add sep
-      imp.add dir
-      imp.add sep # dummy entry, replaced in the loop
-      for x in it[2]:
-        imp[2] = x
+    checkAs(it)
+    if it.kind in {nkInfix, nkPrefix} and it[^1].kind == nkBracket:
+      let lastPos = it.len - 1
+      var imp = copyNode(it)
+      newSons(imp, it.len)
+      for i in 0 ..< lastPos: imp[i] = it[i]
+      imp[lastPos] = imp[0] # dummy entry, replaced in the loop
+      for x in it[lastPos]:
+        checkAs(x)
+        imp[lastPos] = x
         incMod(c, n, imp, result)
     else:
       incMod(c, n, it, result)
 
-proc setLine(n: PNode, info: TLineInfo) =
+proc recursiveSetFlag(n: PNode, flag: TNodeFlag) =
   if n != nil:
-    for i in 0..<n.safeLen: setLine(n[i], info)
-    n.info = info
+    for i in 0..<n.safeLen: recursiveSetFlag(n[i], flag)
+    incl(n.flags, flag)
 
 proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   checkSonsLen(n, 2, c.config)
@@ -2353,8 +2784,8 @@ proc semPragmaBlock(c: PContext, n: PNode; expectedType: PType = nil): PNode =
   result.typ = n[1].typ
   for i in 0..<pragmaList.len:
     case whichPragma(pragmaList[i])
-    of wLine: setLine(result, pragmaList[i].info)
-    of wNoRewrite: incl(result.flags, nfNoRewrite)
+    of wLine: setInfoRecursive(result, pragmaList[i].info)
+    of wNoRewrite: recursiveSetFlag(result, nfNoRewrite)
     else: discard
 
 proc semStaticStmt(c: PContext, n: PNode): PNode =
@@ -2384,8 +2815,11 @@ proc usesResult(n: PNode): bool =
     elif n.kind == nkReturnStmt:
       result = true
     else:
+      result = false
       for c in n:
         if usesResult(c): return true
+  else:
+    result = false
 
 proc inferConceptStaticParam(c: PContext, inferred, n: PNode) =
   var typ = inferred.typ
@@ -2428,7 +2862,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
         let verdict = semConstExpr(c, n[i])
         if verdict == nil or verdict.kind != nkIntLit or verdict.intVal == 0:
           localError(c.config, result.info, "concept predicate failed")
-      of tyUnknown: continue
+      of tyFromExpr: continue
       else: discard
     if n[i].typ == c.enforceVoidContext: #or usesResult(n[i]):
       voidContext = true
@@ -2441,9 +2875,10 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
     else:
       n.typ = n[i].typ
       if not isEmptyType(n.typ): n.transitionSonsKind(nkStmtListExpr)
-    if n[i].kind in nkLastBlockStmts or
-        n[i].kind in nkCallKinds and n[i][0].kind == nkSym and
-        sfNoReturn in n[i][0].sym.flags:
+    var m = n[i]
+    while m.kind in {nkStmtListExpr, nkStmtList} and m.len > 0: # from templates
+      m = m.lastSon
+    if endsInNoReturn(m):
       for j in i + 1..<n.len:
         case n[j].kind
         of nkPragma, nkCommentStmt, nkNilLit, nkEmpty, nkState: discard
@@ -2459,12 +2894,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags, expectedType: PType =
      result[0].kind != nkDefer:
     result = result[0]
 
-  when defined(nimfix):
-    if result.kind == nkCommentStmt and not result.comment.isNil and
-        not (result.comment[0] == '#' and result.comment[1] == '#'):
-      # it is an old-style comment statement: we replace it with 'discard ""':
-      prettybase.replaceComment(result.info)
-
 proc semStmt(c: PContext, n: PNode; flags: TExprFlags): PNode =
   if efInTypeof notin flags:
     result = semExprNoType(c, n)
diff --git a/compiler/semstrictfuncs.nim b/compiler/semstrictfuncs.nim
new file mode 100644
index 000000000..c54196283
--- /dev/null
+++ b/compiler/semstrictfuncs.nim
@@ -0,0 +1,55 @@
+#

+#

+#           The Nim Compiler

+#        (c) Copyright 2022 Andreas Rumpf

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## New "strict funcs" checking. Much simpler and hopefully easier to teach than

+## the old but more advanced algorithm that can/could be found in `varpartitions.nim`.

+

+import ast, typeallowed, renderer

+from aliasanalysis import PathKinds0, PathKinds1

+from trees import getMagic

+

+proc isDangerousLocation*(n: PNode; owner: PSym): bool =

+  var n = n

+  var hasDeref = false

+  while true:

+    case n.kind

+    of nkDerefExpr, nkHiddenDeref:

+      if n[0].typ.kind != tyVar:

+        hasDeref = true

+      n = n[0]

+    of PathKinds0 - {nkDerefExpr, nkHiddenDeref}:

+      n = n[0]

+    of PathKinds1:

+      n = n[1]

+    of nkCallKinds:

+      if n.len > 1:

+        if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice:

+          # borrow from first parameter:

+          n = n[1]

+        else:

+          break

+      else:

+        break

+    else:

+      break

+  if n.kind == nkSym:

+    # dangerous if contains a pointer deref or if it doesn't belong to us:

+    result = hasDeref or n.sym.owner != owner

+    when false:

+      # store to something that belongs to a `var` parameter is fine:

+      let s = n.sym

+      if s.kind == skParam:

+        # dangerous unless a `var T` parameter:

+        result = s.typ.kind != tyVar

+      else:

+        # dangerous if contains a pointer deref or if it doesn't belong to us:

+        result = hasDeref or s.owner != owner

+  else:

+    # dangerous if it contains a pointer deref

+    result = hasDeref

diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim
index 2d5f077f9..817cb6249 100644
--- a/compiler/semtempl.nim
+++ b/compiler/semtempl.nim
@@ -34,6 +34,7 @@ type
     spNone, spGenSym, spInject
 
 proc symBinding(n: PNode): TSymBinding =
+  result = spNone
   for i in 0..<n.len:
     var it = n[i]
     var key = if it.kind == nkExprColonExpr: it[0] else: it
@@ -51,7 +52,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
                isField = false): PNode =
   var
     a: PSym
-    o: TOverloadIter
+    o: TOverloadIter = default(TOverloadIter)
   var i = 0
   a = initOverloadIter(o, c, n)
   while a != nil:
@@ -70,6 +71,9 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule;
       onUse(info, s)
     else:
       result = n
+  elif i == 0:
+    # forced open but symbol not in scope, retain information
+    result = n
   else:
     # semantic checking requires a type; ``fitNode`` deals with it
     # appropriately
@@ -110,14 +114,10 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode =
 
 proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode =
   result = copyNode(n)
-  var count = 0
   for i in 0..<n.len:
     toMixin.incl(considerQuotedIdent(c, n[i]).id)
     let x = symChoice(c, n[i], nil, scForceOpen)
-    inc count, x.len
     result.add x
-  if count == 0:
-    result = newNodeI(nkEmpty, n.info)
 
 proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) =
   case n.kind
@@ -136,24 +136,32 @@ type
     noGenSym: int
     inTemplateHeader: int
 
-proc getIdentNode(c: var TemplCtx, n: PNode): PNode =
+proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} =
+  result = s.kind == skParam and
+           s.owner == c.owner and sfTemplateParam in s.flags
+
+proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] =
   case n.kind
-  of nkPostfix: result = getIdentNode(c, n[1])
-  of nkPragmaExpr: result = getIdentNode(c, n[0])
+  of nkPostfix: result = getIdentReplaceParams(c, n[1])
+  of nkPragmaExpr: result = getIdentReplaceParams(c, n[0])
   of nkIdent:
-    result = n
+    result = (n, false)
     let s = qualifiedLookUp(c.c, n, {})
-    if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
-        result = newSymNode(s, n.info)
-  of nkAccQuoted, nkSym: result = n
+    if s != nil and isTemplParam(c, s):
+      n = newSymNode(s, n.info)
+      result = (n, true)
+  of nkSym:
+    result = (n, isTemplParam(c, n.sym))
+  of nkAccQuoted:
+    result = (n, false)
+    for i in 0..<n.safeLen:
+      let (ident, hasParam) = getIdentReplaceParams(c, n[i])
+      if hasParam:
+        result.node[i] = ident
+        result.hasParam = true
   else:
     illFormedAst(n, c.c.config)
-    result = n
-
-proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} =
-  result = n.kind == nkSym and n.sym.kind == skParam and
-           n.sym.owner == c.owner and sfTemplateParam in n.sym.flags
+    result = (n, false)
 
 proc semTemplBody(c: var TemplCtx, n: PNode): PNode
 
@@ -168,47 +176,21 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode =
   result = semTemplBody(c, n)
   closeScope(c)
 
-proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode =
-  result = n
-  if n.kind == nkIdent:
-    let s = qualifiedLookUp(c.c, n, {})
-    if s != nil:
-      if s.owner == c.owner and s.kind == skParam:
-        incl(s.flags, sfUsed)
-        result = newSymNode(s, n.info)
-        onUse(n.info, s)
-  else:
-    for i in 0..<n.safeLen:
-      result[i] = onlyReplaceParams(c, n[i])
-
 proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym =
-  result = newSym(kind, considerQuotedIdent(c.c, n), nextSymId c.c.idgen, c.owner, n.info)
+  result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info)
   incl(result.flags, sfGenSym)
   incl(result.flags, sfShadowed)
 
 proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
-  # locals default to 'gensym':
-  if n.kind == nkPragmaExpr and symBinding(n[1]) == spInject:
+  # locals default to 'gensym', fields default to 'inject':
+  if (n.kind == nkPragmaExpr and symBinding(n[1]) == spInject) or
+      k == skField:
     # even if injected, don't produce a sym choice here:
     #n = semTemplBody(c, n)
-    var x = n[0]
-    while true:
-      case x.kind
-      of nkPostfix: x = x[1]
-      of nkPragmaExpr: x = x[0]
-      of nkIdent: break
-      of nkAccQuoted:
-        # consider:  type `T TemplParam` {.inject.}
-        # it suffices to return to treat it like 'inject':
-        n = onlyReplaceParams(c, n)
-        return
-      else:
-        illFormedAst(x, c.c.config)
-    let ident = getIdentNode(c, x)
-    if not isTemplParam(c, ident):
-      c.toInject.incl(x.ident.id)
-    else:
-      replaceIdentBySym(c.c, n, ident)
+    let (ident, hasParam) = getIdentReplaceParams(c, n)
+    if not hasParam:
+      if k != skField:
+        c.toInject.incl(considerQuotedIdent(c.c, ident).id)
   else:
     if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma):
       let pragmaNode = n[1]
@@ -218,16 +200,16 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
         var found = false
         if ni.kind == nkIdent:
           for a in templatePragmas:
-            if ni.ident == getIdent(c.c.cache, $a):
+            if ni.ident.id == ord(a):
               found = true
               break
         if not found:
           openScope(c)
           pragmaNode[i] = semTemplBody(c, pragmaNode[i])
           closeScope(c)
-    let ident = getIdentNode(c, n)
-    if not isTemplParam(c, ident):
-      if n.kind != nkSym:
+    let (ident, hasParam) = getIdentReplaceParams(c, n)
+    if not hasParam:
+      if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)):
         let local = newGenSym(k, ident, c)
         addPrelimDecl(c.c, local)
         styleCheckDef(c.c, n.info, local)
@@ -235,77 +217,111 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) =
         replaceIdentBySym(c.c, n, newSymNode(local, n.info))
         if k == skParam and c.inTemplateHeader > 0:
           local.flags.incl sfTemplateParam
-    else:
-      replaceIdentBySym(c.c, n, ident)
 
-proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode =
+proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode =
   incl(s.flags, sfUsed)
   # bug #12885; ideally sem'checking is performed again afterwards marking
   # the symbol as used properly, but the nfSem mechanism currently prevents
   # that from happening, so we mark the module as used here already:
-  markOwnerModuleAsUsed(c, s)
+  markOwnerModuleAsUsed(c.c, s)
   # we do not call onUse here, as the identifier is not really
   # resolved here. We will fixup the used identifiers later.
   case s.kind
   of skUnknown:
     # Introduced in this pass! Leave it as an identifier.
     result = n
-  of OverloadableSyms-{skTemplate,skMacro}:
-    result = symChoice(c, n, s, scOpen, isField)
-  of skTemplate, skMacro:
-    result = symChoice(c, n, s, scOpen, isField)
-    if result.kind == nkSym:
-      # template/macro symbols might need to be semchecked again
-      # prepareOperand etc don't do this without setting the type to nil
-      result.typ = nil
+  of OverloadableSyms:
+    result = symChoice(c.c, n, s, scOpen, isField)
+    if not isField and result.kind in {nkSym, nkOpenSymChoice}:
+      if openSym in c.c.features:
+        if result.kind == nkSym:
+          result = newOpenSym(result)
+        else:
+          result.typ = nil
+      else:
+        result.flags.incl nfDisabledOpenSym
+        result.typ = nil
   of skGenericParam:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
+    else:
+      result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and s.owner != c.owner:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   of skParam:
     result = n
   of skType:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNodeTypeDesc(s, c.idgen, n.info)
+    else:
+      if isAmbiguous:
+        # ambiguous types should be symchoices since lookup behaves
+        # differently for them in regular expressions
+        result = symChoice(c.c, n, s, scOpen, isField)
+      else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info)
+      if not isField and not (s.owner == c.owner and
+          s.typ != nil and s.typ.kind == tyGenericParam) and
+          result.kind in {nkSym, nkOpenSymChoice}:
+        if openSym in c.c.features:
+          if result.kind == nkSym:
+            result = newOpenSym(result)
+          else:
+            result.typ = nil
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
   else:
     if isField and sfGenSym in s.flags: result = n
-    else: result = newSymNode(s, n.info)
+    else:
+      result = newSymNode(s, n.info)
+      if not isField:
+        if openSym in c.c.features:
+          result = newOpenSym(result)
+        else:
+          result.flags.incl nfDisabledOpenSym
+          result.typ = nil
     # Issue #12832
     when defined(nimsuggest):
-      suggestSym(c.graph, n.info, s, c.graph.usageSym, false)
+      suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false)
     # field access (dot expr) will be handled by builtinFieldAccess
     if not isField:
-      styleCheckUse(c, n.info, s)
+      styleCheckUse(c.c, n.info, s)
 
-proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode =
+proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode =
   result = n
   if n.kind == nkIdent:
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
-      if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags):
+      if s.owner == c.owner and (s.kind == skParam or
+          (sfGenSym in s.flags and not explicitInject)):
         incl(s.flags, sfUsed)
         result = newSymNode(s, n.info)
         onUse(n.info, s)
   else:
     for i in 0..<n.safeLen:
-      result[i] = semRoutineInTemplName(c, n[i])
+      result[i] = semRoutineInTemplName(c, n[i], explicitInject)
 
 proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   result = n
   checkSonsLen(n, bodyPos + 1, c.c.config)
-  # routines default to 'inject':
-  if n.kind notin nkLambdaKinds and symBinding(n[pragmasPos]) == spGenSym:
-    let ident = getIdentNode(c, n[namePos])
-    if not isTemplParam(c, ident):
-      var s = newGenSym(k, ident, c)
-      s.ast = n
-      addPrelimDecl(c.c, s)
-      styleCheckDef(c.c, n.info, s)
-      onDef(n.info, s)
-      n[namePos] = newSymNode(s, n[namePos].info)
+  if n.kind notin nkLambdaKinds:
+    # routines default to 'inject':
+    let binding = symBinding(n[pragmasPos])
+    if binding == spGenSym:
+      let (ident, hasParam) = getIdentReplaceParams(c, n[namePos])
+      if not hasParam:
+        var s = newGenSym(k, ident, c)
+        s.ast = n
+        addPrelimDecl(c.c, s)
+        styleCheckDef(c.c, n.info, s)
+        onDef(n.info, s)
+        n[namePos] = newSymNode(s, n[namePos].info)
+      else:
+        n[namePos] = ident
     else:
-      n[namePos] = ident
-  else:
-    n[namePos] = semRoutineInTemplName(c, n[namePos])
+      n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject)
   # open scope for parameters
   openScope(c)
   for i in patternPos..paramsPos-1:
@@ -327,21 +343,24 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode =
   # close scope for parameters
   closeScope(c)
 
+proc semTemplIdentDef(c: var TemplCtx, a: PNode, symKind: TSymKind) =
+  checkMinSonsLen(a, 3, c.c.config)
+  when defined(nimsuggest):
+    inc c.c.inTypeContext
+  a[^2] = semTemplBody(c, a[^2])
+  when defined(nimsuggest):
+    dec c.c.inTypeContext
+  a[^1] = semTemplBody(c, a[^1])
+  for j in 0..<a.len-2:
+    addLocalDecl(c, a[j], symKind)
+
 proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) =
   for i in start..<n.len:
     var a = n[i]
     case a.kind:
     of nkCommentStmt: continue
     of nkIdentDefs, nkVarTuple, nkConstDef:
-      checkMinSonsLen(a, 3, c.c.config)
-      when defined(nimsuggest):
-        inc c.c.inTypeContext
-      a[^2] = semTemplBody(c, a[^2])
-      when defined(nimsuggest):
-        dec c.c.inTypeContext
-      a[^1] = semTemplBody(c, a[^1])
-      for j in 0..<a.len-2:
-        addLocalDecl(c, a[j], symKind)
+      semTemplIdentDef(c, a, symKind)
     else:
       illFormedAst(a, c.c.config)
 
@@ -359,6 +378,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   case n.kind
   of nkIdent:
     if n.ident.id in c.toInject: return n
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
       if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags:
@@ -376,7 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         result = newSymNode(s, n.info)
         onUse(n.info, s)
       else:
-        result = semTemplSymbol(c.c, n, s, c.noGenSym > 0)
+        if s.kind in {skVar, skLet, skConst}:
+          discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
+        result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
   of nkBind:
     result = semTemplBody(c, n[0])
   of nkBindStmt:
@@ -479,6 +501,25 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
         closeScope(c)
       else:
         a[2] = semTemplBody(c, a[2])
+  of nkObjectTy:
+    openScope(c)
+    result = semTemplBodySons(c, n)
+    closeScope(c)
+  of nkRecList:
+    for i in 0..<n.len:
+      var a = n[i]
+      case a.kind:
+      of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue
+      of nkIdentDefs:
+        semTemplIdentDef(c, a, skField)
+      of nkRecCase, nkRecWhen:
+        n[i] = semTemplBody(c, a)
+      else:
+        illFormedAst(a, c.c.config)
+  of nkRecCase:
+    semTemplIdentDef(c, n[0], skField)
+    for i in 1..<n.len:
+      n[i] = semTemplBody(c, n[i])
   of nkProcDef, nkLambdaKinds:
     result = semRoutineInTemplBody(c, n, skProc)
   of nkFuncDef:
@@ -502,16 +543,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
       if x.kind == nkExprColonExpr:
         x[1] = semTemplBody(c, x[1])
   of nkBracketExpr:
-    result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
-    for i in 0..<n.len: result.add(n[i])
+    if n.typ == nil:
+      # if a[b] is nested inside a typed expression, don't convert it
+      # back to `[]`(a, b), prepareOperand will not typecheck it again
+      # and so `[]` will not be resolved
+      # checking if a[b] is typed should be enough to cover this case
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info)
+      for i in 0..<n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
   of nkCurlyExpr:
-    result = newNodeI(nkCall, n.info)
-    result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
-    for i in 0..<n.len: result.add(n[i])
+    if n.typ == nil:
+      # see nkBracketExpr case for explanation
+      result = newNodeI(nkCall, n.info)
+      result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info)
+      for i in 0..<n.len: result.add(n[i])
     result = semTemplBodySons(c, result)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     checkSonsLen(n, 2, c.c.config)
     let a = n[0]
     let b = n[1]
@@ -519,17 +567,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
     let k = a.kind
     case k
     of nkBracketExpr:
-      result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
-      for i in 0..<a.len: result.add(a[i])
-      result.add(b)
+      if a.typ == nil:
+        # see nkBracketExpr case above for explanation
+        result = newNodeI(nkCall, n.info)
+        result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info)
+        for i in 0..<a.len: result.add(a[i])
+        result.add(b)
       let a0 = semTemplBody(c, a[0])
       result = semTemplBodySons(c, result)
     of nkCurlyExpr:
-      result = newNodeI(nkCall, n.info)
-      result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
-      for i in 0..<a.len: result.add(a[i])
-      result.add(b)
+      if a.typ == nil:
+        # see nkBracketExpr case above for explanation
+        result = newNodeI(nkCall, n.info)
+        result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info)
+        for i in 0..<a.len: result.add(a[i])
+        result.add(b)
       result = semTemplBodySons(c, result)
     else:
       result = semTemplBodySons(c, n)
@@ -540,8 +592,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
   of nkDotExpr, nkAccQuoted:
     # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam',
     # so we use the generic code for nkDotExpr too
+    c.c.isAmbiguous = false
     let s = qualifiedLookUp(c.c, n, {})
     if s != nil:
+      # mirror the nkIdent case
       # do not symchoice a quoted template parameter (bug #2390):
       if s.owner == c.owner and s.kind == skParam and
           n.kind == nkAccQuoted and n.len == 1:
@@ -553,13 +607,23 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode =
       elif contains(c.toMixin, s.name.id):
         return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0)
       else:
-        return symChoice(c.c, n, s, scOpen, c.noGenSym > 0)
+        if s.kind in {skVar, skLet, skConst}:
+          discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule})
+        return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous)
     if n.kind == nkDotExpr:
       result = n
       result[0] = semTemplBody(c, n[0])
       inc c.noGenSym
       result[1] = semTemplBody(c, n[1])
       dec c.noGenSym
+      if result[1].kind == nkSym and result[1].sym.kind in routineKinds:
+        # prevent `dotTransformation` from rewriting this node to `nkIdent`
+        # by making it a symchoice
+        # in generics this becomes `nkClosedSymChoice` but this breaks code
+        # as the old behavior here was that this became `nkIdent`
+        var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c))
+        choice.add result[1]
+        result[1] = choice
     else:
       result = semTemplBodySons(c, n)
   of nkExprColonExpr, nkExprEqExpr:
@@ -620,56 +684,77 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
     s = semIdentVis(c, skTemplate, n[namePos], {})
   assert s.kind == skTemplate
 
-  if s.owner != nil:
-    const names = ["!=", ">=", ">", "incl", "excl", "in", "notin", "isnot"]
-    if sfSystemModule in s.owner.flags and s.name.s in names or
-       s.owner.name.s == "vm" and s.name.s == "stackTrace":
-      incl(s.flags, sfCallsite)
-
   styleCheckDef(c, s)
   onDef(n[namePos].info, s)
   # check parameter list:
   #s.scope = c.currentScope
+  # push noalias flag at first to prevent unwanted recursive calls:
+  incl(s.flags, sfNoalias)
   pushOwner(c, s)
   openScope(c)
   n[namePos] = newSymNode(s)
+  s.ast = n # for implicitPragmas to use
   pragmaCallable(c, s, n, templatePragmas)
   implicitPragmas(c, s, n.info, templatePragmas)
 
   setGenericParamsMisc(c, n)
   # process parameters:
   var allUntyped = true
+  var nullary = true
   if n[paramsPos].kind != nkEmpty:
     semParamList(c, n[paramsPos], n[genericParamsPos], s)
     # a template's parameters are not gensym'ed even if that was originally the
     # case as we determine whether it's a template parameter in the template
     # body by the absence of the sfGenSym flag:
+    let retType = s.typ.returnType
+    if retType != nil and retType.kind != tyUntyped:
+      allUntyped = false
     for i in 1..<s.typ.n.len:
       let param = s.typ.n[i].sym
-      param.flags.incl sfTemplateParam
-      param.flags.excl sfGenSym
+      if param.name.id != ord(wUnderscore):
+        param.flags.incl sfTemplateParam
+        param.flags.excl sfGenSym
       if param.typ.kind != tyUntyped: allUntyped = false
+      # no default value, parameters required in call
+      if param.ast == nil: nullary = false
   else:
     s.typ = newTypeS(tyProc, c)
     # XXX why do we need tyTyped as a return type again?
     s.typ.n = newNodeI(nkFormalParams, n.info)
     rawAddSon(s.typ, newTypeS(tyTyped, c))
-    s.typ.n.add newNodeIT(nkType, n.info, s.typ[0])
+    s.typ.n.add newNodeIT(nkType, n.info, s.typ.returnType)
   if n[genericParamsPos].safeLen == 0:
     # restore original generic type params as no explicit or implicit were found
     n[genericParamsPos] = n[miscPos][1]
     n[miscPos] = c.graph.emptyNode
   if allUntyped: incl(s.flags, sfAllUntyped)
+  if nullary and
+      n[genericParamsPos].kind == nkEmpty and
+      n[bodyPos].kind != nkEmpty:
+    # template can be called with alias syntax, remove pushed noalias flag
+    excl(s.flags, sfNoalias)
 
   if n[patternPos].kind != nkEmpty:
     n[patternPos] = semPattern(c, n[patternPos], s)
 
-  var ctx: TemplCtx
-  ctx.toBind = initIntSet()
-  ctx.toMixin = initIntSet()
-  ctx.toInject = initIntSet()
-  ctx.c = c
-  ctx.owner = s
+  var ctx = TemplCtx(
+    toBind: initIntSet(),
+    toMixin: initIntSet(),
+    toInject: initIntSet(),
+    c: c,
+    owner: s
+  )
+  # handle default params:
+  for i in 1..<s.typ.n.len:
+    let param = s.typ.n[i].sym
+    if param.ast != nil:
+      # param default values need to be treated like template body:
+      if sfDirty in s.flags:
+        param.ast = semTemplBodyDirty(ctx, param.ast)
+      else:
+        param.ast = semTemplBody(ctx, param.ast)
+      if param.ast.referencesAnotherParam(s):
+        param.ast.flags.incl nfDefaultRefsParam
   if sfDirty in s.flags:
     n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos])
   else:
@@ -679,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   closeScope(c)
   popOwner(c)
 
-  # set the symbol AST after pragmas, at least. This stops pragma that have
-  # been pushed (implicit) to be explicitly added to the template definition
-  # and misapplied to the body. see #18113
-  s.ast = n
-
   if sfCustomPragma in s.flags:
     if n[bodyPos].kind != nkEmpty:
       localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s)
@@ -695,7 +775,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode =
   elif not comesFromShadowscope:
     if {sfTemplateRedefinition, sfGenSym} * s.flags == {}:
       #wrongRedefinition(c, n.info, proto.name.s, proto.info)
-      message(c.config, n.info, warnTemplateRedefinition, s.name.s)
+      message(c.config, n.info, warnImplicitTemplateRedefinition, s.name.s)
     symTabReplace(c.currentScope.symbols, proto, s)
   if n[patternPos].kind != nkEmpty:
     c.patterns.add(s)
@@ -820,12 +900,13 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode =
 
 proc semPattern(c: PContext, n: PNode; s: PSym): PNode =
   openScope(c)
-  var ctx: TemplCtx
-  ctx.toBind = initIntSet()
-  ctx.toMixin = initIntSet()
-  ctx.toInject = initIntSet()
-  ctx.c = c
-  ctx.owner = getCurrOwner(c)
+  var ctx = TemplCtx(
+    toBind: initIntSet(),
+    toMixin: initIntSet(),
+    toInject: initIntSet(),
+    c: c,
+    owner: getCurrOwner(c)
+  )
   result = flattenStmts(semPatternBody(ctx, n))
   if result.kind in {nkStmtList, nkStmtListExpr}:
     if result.len == 1:
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 0a823f20a..113946fef 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -15,7 +15,8 @@ const
   errStringLiteralExpected = "string literal expected"
   errIntLiteralExpected = "integer literal expected"
   errWrongNumberOfVariables = "wrong number of variables"
-  errInvalidOrderInEnumX = "invalid order in enum '$1'"
+  errDuplicateAliasInEnumX = "duplicate value in enum '$1'"
+  errOverflowInEnumX = "The enum '$1' exceeds its maximum value ($2)"
   errOrdinalTypeExpected = "ordinal type expected; given: $1"
   errSetTooBig = "set is too large; use `std/sets` for ordinal types with more than 2^16 elements"
   errBaseTypeMustBeOrdinal = "base type of a set must be an ordinal"
@@ -23,7 +24,6 @@ const
   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"
@@ -38,13 +38,21 @@ const
   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, son: sink PType): PType =
+  if prev == nil or prev.kind == tyGenericBody:
+    result = newTypeS(kind, c, son)
+  else:
+    result = prev
+    result.setSon(son)
+    if result.kind == tyForward: result.kind = kind
+  #if kind == tyError: result.flags.incl tfCheckedForDestructor
+
 proc newOrPrevType(kind: TTypeKind, prev: PType, c: PContext): PType =
-  if prev == nil:
+  if prev == nil or prev.kind == tyGenericBody:
     result = newTypeS(kind, c)
   else:
     result = prev
     if result.kind == tyForward: result.kind = kind
-  #if kind == tyError: result.flags.incl tfCheckedForDestructor
 
 proc newConstraint(c: PContext, k: TTypeKind): PType =
   result = newTypeS(tyBuiltInTypeClass, c)
@@ -57,10 +65,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     # don't create an empty tyEnum; fixes #3052
     return errorType(c)
   var
-    counter, x: BiggestInt
-    e: PSym
-    base: PType
-    identToReplace: ptr PNode
+    counter, x: BiggestInt = 0
+    e: PSym = nil
+    base: PType = nil
+    identToReplace: ptr PNode = nil
+    counterSet = initPackedSet[BiggestInt]()
   counter = 0
   base = nil
   result = newOrPrevType(tyEnum, prev, c)
@@ -73,11 +82,11 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     counter = toInt64(lastOrd(c.config, base)) + 1
   rawAddSon(result, base)
   let isPure = result.sym != nil and sfPure in result.sym.flags
-  var symbols: TStrTable
-  if isPure: initStrTable(symbols)
+  var symbols: TStrTable = initStrTable()
   var hasNull = false
   for i in 1..<n.len:
     if n[i].kind == nkEmpty: continue
+    var useAutoCounter = false
     case n[i].kind
     of nkEnumFieldDef:
       if n[i][0].kind == nkPragmaExpr:
@@ -105,34 +114,44 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
       of tyString, tyCstring:
         strVal = v
         x = counter
+        useAutoCounter = true
       else:
-        if not isOrdinalType(v.typ, allowEnumWithHoles=true):
+        if isOrdinalType(v.typ, allowEnumWithHoles=true):
+          x = toInt64(getOrdValue(v))
+          n[i][1] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
+        else:
           localError(c.config, v.info, errOrdinalTypeExpected % typeToString(v.typ, preferDesc))
-        x = toInt64(getOrdValue(v))
-        n[i][1] = newIntTypeNode(x, getSysType(c.graph, unknownLineInfo, tyInt))
       if i != 1:
         if x != counter: incl(result.flags, tfEnumHasHoles)
-        if x < counter:
-          localError(c.config, n[i].info, errInvalidOrderInEnumX % e.name.s)
-          x = counter
       e.ast = strVal # might be nil
       counter = x
     of nkSym:
       e = n[i].sym
+      useAutoCounter = true
     of nkIdent, nkAccQuoted:
       e = newSymS(skEnumField, n[i], c)
       identToReplace = addr n[i]
+      useAutoCounter = true
     of nkPragmaExpr:
       e = newSymS(skEnumField, n[i][0], c)
       pragma(c, e, n[i][1], enumFieldPragmas)
       identToReplace = addr n[i][0]
+      useAutoCounter = true
     else:
       illFormedAst(n[i], c.config)
+
+    if useAutoCounter:
+      while counter in counterSet and counter != high(typeof(counter)):
+        inc counter
+      counterSet.incl counter
+    elif counterSet.containsOrIncl(counter):
+      localError(c.config, n[i].info, errDuplicateAliasInEnumX % e.name.s)
+
     e.typ = result
     e.position = int(counter)
     let symNode = newSymNode(e)
-    if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil and
-        c.config.cmd notin cmdDocLike: # A hack to produce documentation for enum fields.
+    if identToReplace != nil and c.config.cmd notin cmdDocLike:
+      # A hack to produce documentation for enum fields.
       identToReplace[] = symNode
     if e.position == 0: hasNull = true
     if result.sym != nil and sfExported in result.sym.flags:
@@ -141,14 +160,19 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType =
     result.n.add symNode
     styleCheckDef(c, e)
     onDef(e.info, e)
+    suggestSym(c.graph, e.info, e, c.graph.usageSym)
     if sfGenSym notin e.flags:
       if not isPure:
         addInterfaceOverloadableSymAt(c, c.currentScope, e)
       else:
         declarePureEnumField(c, e)
-    if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
+    if (let conflict = strTableInclReportConflict(symbols, e); conflict != nil):
       wrongRedefinition(c, e.info, e.name.s, conflict.info)
-    inc(counter)
+    if counter == high(typeof(counter)):
+      if i > 1 and result.n[i-2].sym.position == high(int):
+        localError(c.config, n[i].info, errOverflowInEnumX % [e.name.s, $high(typeof(counter))])
+    else:
+      inc(counter)
   if isPure and sfExported in result.sym.flags:
     addPureEnum(c, LazySym(sym: result.sym))
   if tfNotNil in e.typ.flags and not hasNull:
@@ -160,9 +184,11 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 2 and n[1].kind != nkEmpty:
     var base = semTypeNode(c, n[1], nil)
     addSonSkipIntLit(result, base, c.idgen)
-    if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base)
+    if base.kind in {tyGenericInst, tyAlias, tySink}: base = skipModifier(base)
     if base.kind notin {tyGenericParam, tyGenericInvocation}:
-      if not isOrdinalType(base, allowEnumWithHoles = true):
+      if base.kind == tyForward:
+        c.skipTypes.add n
+      elif not isOrdinalType(base, allowEnumWithHoles = true):
         localError(c.config, n.info, errOrdinalTypeExpected % typeToString(base, preferDesc))
       elif lengthOrd(c.config, base) > MaxSetElements:
         localError(c.config, n.info, errSetTooBig)
@@ -196,9 +222,10 @@ proc semVarargs(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, errXExpectsOneTypeParam % "varargs")
     addSonSkipIntLit(result, errorType(c), c.idgen)
 
-proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
+proc semVarOutType(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
   if n.len == 1:
-    result = newOrPrevType(kind, prev, c)
+    result = newOrPrevType(tyVar, prev, c)
+    result.flags = flags
     var base = semTypeNode(c, n[0], nil)
     if base.kind == tyTypeDesc and not isSelf(base):
       base = base[0]
@@ -207,7 +234,7 @@ proc semVarOutType(c: PContext, n: PNode, prev: PType; kind: TTypeKind): PType =
       base = base[0]
     addSonSkipIntLit(result, base, c.idgen)
   else:
-    result = newConstraint(c, kind)
+    result = newConstraint(c, tyVar)
 
 proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
   if t == nil:
@@ -216,10 +243,33 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool =
     return true
   case t.kind
   of tyAlias, tyGenericInst, tyDistinct:
-    return isRecursiveType(t.lastSon, cycleDetector)
+    return isRecursiveType(t.skipModifier, cycleDetector)
   else:
     return false
 
+proc fitDefaultNode(c: PContext, n: PNode): PType =
+  inc c.inStaticContext
+  let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil
+  n[^1] = semConstExpr(c, n[^1], expectedType = expectedType)
+  let oldType = n[^1].typ
+  n[^1].flags.incl nfSem
+  if n[^2].kind != nkEmpty:
+    if expectedType != nil and oldType != expectedType:
+      n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info)
+      changeType(c, n[^1], expectedType, true) # infer types for default fields value
+        # bug #22926; be cautious that it uses `semConstExpr` to
+        # evaulate the default fields; it's only natural to use
+        # `changeType` to infer types for constant values
+        # that's also the reason why we don't use `semExpr` to check
+        # the type since two overlapping error messages might be produced
+    result = n[^1].typ
+  else:
+    result = n[^1].typ
+  # xxx any troubles related to defaults fields, consult `semConst` for a potential answer
+  if n[^1].kind != nkNilLit:
+    typeAllowedCheck(c, n.info, result, skConst, {taProcContextIsNotMacro, taIsDefaultField})
+  dec c.inStaticContext
+
 proc isRecursiveType*(t: PType): bool =
   # handle simple recusive types before typeFinalPass
   var cycleDetector = initIntSet()
@@ -227,7 +277,7 @@ proc isRecursiveType*(t: PType): bool =
 
 proc addSonSkipIntLitChecked(c: PContext; father, son: PType; it: PNode, id: IdGenerator) =
   let s = son.skipIntLit(id)
-  father.sons.add(s)
+  father.add(s)
   if isRecursiveType(s):
     localError(c.config, it.info, "illegal recursion in type '" & typeToString(s) & "'")
   else:
@@ -252,15 +302,17 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, "range is empty")
 
   var range: array[2, PNode]
+  # XXX this is still a hard compilation in a generic context, this can
+  # result in unresolved generic parameters being treated like real types
   range[0] = semExprWithType(c, n[1], {efDetermineType})
   range[1] = semExprWithType(c, n[2], {efDetermineType})
 
-  var rangeT: array[2, PType]
+  var rangeT: array[2, PType] = default(array[2, PType])
   for i in 0..1:
     rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen)
 
   let hasUnknownTypes = c.inGenericContext > 0 and
-    rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr
+    (rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr)
 
   if not hasUnknownTypes:
     if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})):
@@ -268,7 +320,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
 
     elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or
         rangeT[0].kind == tyBool:
-      localError(c.config, n.info, "ordinal or float type expected")
+      localError(c.config, n.info, "ordinal or float type expected, but got " & typeToString(rangeT[0]))
     elif enumHasHoles(rangeT[0]):
       localError(c.config, n.info, "enum '$1' has holes" % typeToString(rangeT[0]))
 
@@ -279,9 +331,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType =
     else:
       result.n.add semConstExpr(c, range[i])
 
-  if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and result.n[0].floatVal.isNaN) or
-      (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and result.n[1].floatVal.isNaN):
-    localError(c.config, n.info, "NaN is not a valid start or end for a range")
+    if result.n[i].kind in {nkFloatLit..nkFloat64Lit} and result.n[i].floatVal.isNaN:
+      localError(c.config, n.info, "NaN is not a valid range " & (if i == 0: "start" else: "end"))
 
   if weakLeValue(result.n[0], result.n[1]) == impNo:
     localError(c.config, n.info, "range is empty")
@@ -293,17 +344,18 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 2:
     if isRange(n[1]):
       result = semRangeAux(c, n[1], prev)
-      let n = result.n
-      if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
-        incl(result.flags, tfRequiresInit)
-      elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
-        incl(result.flags, tfRequiresInit)
-      elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
-          n[0].floatVal > 0.0:
-        incl(result.flags, tfRequiresInit)
-      elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
-          n[1].floatVal < 0.0:
-        incl(result.flags, tfRequiresInit)
+      if not isDefined(c.config, "nimPreviewRangeDefault"):
+        let n = result.n
+        if n[0].kind in {nkCharLit..nkUInt64Lit} and n[0].intVal > 0:
+          incl(result.flags, tfRequiresInit)
+        elif n[1].kind in {nkCharLit..nkUInt64Lit} and n[1].intVal < 0:
+          incl(result.flags, tfRequiresInit)
+        elif n[0].kind in {nkFloatLit..nkFloat64Lit} and
+            n[0].floatVal > 0.0:
+          incl(result.flags, tfRequiresInit)
+        elif n[1].kind in {nkFloatLit..nkFloat64Lit} and
+            n[1].floatVal < 0.0:
+          incl(result.flags, tfRequiresInit)
     else:
       if n[1].kind == nkInfix and considerQuotedIdent(c, n[1][0]).s == "..<":
         localError(c.config, n[0].info, "range types need to be constructed with '..', '..<' is not supported")
@@ -314,10 +366,22 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, errXExpectsOneTypeParam % "range")
     result = newOrPrevType(tyError, prev, c)
 
+proc semArrayIndexConst(c: PContext, e: PNode, info: TLineInfo): PType =
+  let x = semConstExpr(c, e)
+  if x.kind in {nkIntLit..nkUInt64Lit}:
+    result = makeRangeType(c, 0, x.intVal-1, info,
+                        x.typ.skipTypes({tyTypeDesc}))
+  else:
+    result = x.typ.skipTypes({tyTypeDesc})
+
 proc semArrayIndex(c: PContext, n: PNode): PType =
   if isRange(n):
     result = semRangeAux(c, n, nil)
+  elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<":
+    result = errorType(c)
   else:
+    # XXX this is still a hard compilation in a generic context, this can
+    # result in unresolved generic parameters being treated like real types
     let e = semExprWithType(c, n, {efDetermineType})
     if e.typ.kind == tyFromExpr:
       result = makeRangeWithStaticExpr(c, e.typ.n)
@@ -326,19 +390,23 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
         localError(c.config, n.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:
-      if e.sym.ast != nil:
-        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(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
-      result = makeRangeWithStaticExpr(c, e)
-      if c.inGenericContext > 0: result.flags.incl tfUnresolved
+    elif e.kind == nkSym and (e.typ.kind == tyStatic or e.typ.kind == tyTypeDesc):
+      if e.typ.kind == tyStatic:
+        if e.sym.ast != nil:
+          return semArrayIndex(c, e.sym.ast)
+        if e.typ.skipModifier.kind != tyGenericParam and not isOrdinalType(e.typ.skipModifier):
+          let info = if n.safeLen > 1: n[1].info else: n.info
+          localError(c.config, info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
+        result = makeRangeWithStaticExpr(c, e)
+        if c.inGenericContext > 0: result.flags.incl tfUnresolved
+      else:
+        result = e.typ.skipTypes({tyTypeDesc})
+        result.flags.incl tfImplicitStatic
     elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e):
       if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})):
         localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(e.typ, preferDesc))
       # This is an int returning call, depending on an
-      # yet unknown generic param (see tgenericshardcases).
+      # yet unknown generic param (see tuninstantiatedgenericcalls).
       # We are going to construct a range type that will be
       # properly filled-out in semtypinst (see how tyStaticExpr
       # is handled there).
@@ -346,12 +414,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
     elif e.kind == nkIdent:
       result = e.typ.skipTypes({tyTypeDesc})
     else:
-      let x = semConstExpr(c, e)
-      if x.kind in {nkIntLit..nkUInt64Lit}:
-        result = makeRangeType(c, 0, x.intVal-1, n.info,
-                             x.typ.skipTypes({tyTypeDesc}))
-      else:
-        result = x.typ.skipTypes({tyTypeDesc})
+      result = semArrayIndexConst(c, e, n.info)
         #localError(c.config, n[1].info, errConstExprExpected)
 
 proc semArray(c: PContext, n: PNode, prev: PType): PType =
@@ -360,21 +423,24 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType =
     # 3 = length(array indx base)
     let indx = semArrayIndex(c, n[1])
     var indxB = indx
-    if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = lastSon(indxB)
-    if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr}:
-      if indxB.skipTypes({tyRange}).kind in {tyUInt, tyUInt64}:
-        discard
-      elif not isOrdinalType(indxB):
+    if indxB.kind in {tyGenericInst, tyAlias, tySink}: indxB = skipModifier(indxB)
+    if indxB.kind notin {tyGenericParam, tyStatic, tyFromExpr} and
+        tfUnresolved notin indxB.flags:
+      if not isOrdinalType(indxB):
         localError(c.config, n[1].info, errOrdinalTypeExpected % typeToString(indxB, preferDesc))
       elif enumHasHoles(indxB):
         localError(c.config, n[1].info, "enum '$1' has holes" %
                    typeToString(indxB.skipTypes({tyRange})))
+      elif indxB.kind != tyRange and
+          lengthOrd(c.config, indxB) > high(uint16).int:
+        # assume range type is intentional
+        localError(c.config, n[1].info,
+          "index type '$1' for array is too large" % typeToString(indxB))
     base = semTypeNode(c, n[2], nil)
     # ensure we only construct a tyArray when there was no error (bug #3048):
-    result = newOrPrevType(tyArray, prev, c)
     # bug #6682: Do not propagate initialization requirements etc for the
     # index type:
-    rawAddSonNoPropagationOfTypeFlags(result, indx)
+    result = newOrPrevType(tyArray, prev, c, indx)
     addSonSkipIntLit(result, base, c.idgen)
   else:
     localError(c.config, n.info, errArrayExpectsTwoTypeParams)
@@ -401,68 +467,6 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
     localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal")
     result = newOrPrevType(tyError, prev, c)
 
-proc semTypeIdent(c: PContext, n: PNode): PSym =
-  if n.kind == nkSym:
-    result = getGenSym(c, n.sym)
-  else:
-    result = pickSym(c, n, {skType, skGenericParam, skParam})
-    if result.isNil:
-      result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
-    if result != nil:
-      markUsed(c, n.info, result)
-      onUse(n.info, result)
-
-      if result.kind == skParam and result.typ.kind == tyTypeDesc:
-        # This is a typedesc param. is it already bound?
-        # it's not bound when it's used multiple times in the
-        # proc signature for example
-        if c.inGenericInst > 0:
-          let bound = result.typ[0].sym
-          if bound != nil: return bound
-          return result
-        if result.typ.sym == nil:
-          localError(c.config, n.info, errTypeExpected)
-          return errorSym(c, n)
-        result = result.typ.sym.copySym(nextSymId c.idgen)
-        result.typ = exactReplica(result.typ)
-        result.typ.flags.incl tfUnresolved
-
-      if result.kind == skGenericParam:
-        if result.typ.kind == tyGenericParam and result.typ.len == 0 and
-           tfWildcard in result.typ.flags:
-          # collapse the wild-card param to a type
-          result.transitionGenericParamToType()
-          result.typ.flags.excl tfWildcard
-          return
-        else:
-          localError(c.config, n.info, errTypeExpected)
-          return errorSym(c, n)
-      if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
-        # this implements the wanted ``var v: V, x: V`` feature ...
-        var ov: TOverloadIter
-        var amb = initOverloadIter(ov, c, n)
-        while amb != nil and amb.kind != skType:
-          amb = nextOverloadIter(ov, c, n)
-        if amb != nil: result = amb
-        else:
-          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!
-        var oldInfo = n.info
-        when defined(useNodeIds):
-          let oldId = n.id
-        reset(n[])
-        when defined(useNodeIds):
-          n.id = oldId
-        n.transitionNoneToSym()
-        n.sym = result
-        n.info = oldInfo
-        n.typ = result.typ
-    else:
-      localError(c.config, n.info, "identifier expected")
-      result = errorSym(c, n)
-
 proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
   if n.len == 0:
     localError(c.config, n.info, errTypeExpected)
@@ -471,6 +475,13 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType =
     let t = semTypeNode(c, it, nil)
     addSonSkipIntLitChecked(c, result, t, it, c.idgen)
 
+proc firstRange(config: ConfigRef, t: PType): PNode =
+  if t.skipModifier().kind in tyFloat..tyFloat64:
+    result = newFloatNode(nkFloatLit, firstFloat(t))
+  else:
+    result = newIntNode(nkIntLit, firstOrd(config, t))
+  result.typ = t
+
 proc semTuple(c: PContext, n: PNode, prev: PType): PType =
   var typ: PType
   result = newOrPrevType(tyTuple, prev, c)
@@ -481,13 +492,17 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
     var a = n[i]
     if (a.kind != nkIdentDefs): illFormedAst(a, c.config)
     checkMinSonsLen(a, 3, c.config)
-    if a[^2].kind != nkEmpty:
+    var hasDefaultField = a[^1].kind != nkEmpty
+    if hasDefaultField:
+      typ = fitDefaultNode(c, a)
+    elif a[^2].kind != nkEmpty:
       typ = semTypeNode(c, a[^2], nil)
+      if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
+        a[^1] = firstRange(c.config, typ)
+        hasDefaultField = true
     else:
       localError(c.config, a.info, errTypeExpected)
       typ = errorType(c)
-    if a[^1].kind != nkEmpty:
-      localError(c.config, a[^1].info, errInitHereNotAllowed)
     for j in 0..<a.len - 2:
       var field = newSymG(skField, a[j], c)
       field.typ = typ
@@ -496,7 +511,11 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType =
       if containsOrIncl(check, field.name.id):
         localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'")
       else:
-        result.n.add newSymNode(field)
+        let fSym = newSymNode(field)
+        if hasDefaultField:
+          fSym.sym.ast = a[^1]
+          fSym.sym.ast.flags.incl nfSkipFieldChecking
+        result.n.add fSym
         addSonSkipIntLit(result, typ, c.idgen)
       styleCheckDef(c, a[j].info, field)
       onDef(field.info, field)
@@ -521,12 +540,13 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode,
         else:
           localError(c.config, n[0].info, errInvalidVisibilityX % renderTree(n[0]))
     else:
+      result = nil
       illFormedAst(n, c.config)
   else:
     result = newSymG(kind, n, c)
 
 proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
-                        allowed: TSymFlags): PSym =
+                        allowed: TSymFlags, fromTopLevel = false): PSym =
   if n.kind == nkPragmaExpr:
     checkSonsLen(n, 2, c.config)
     result = semIdentVis(c, kind, n[0], allowed)
@@ -541,6 +561,16 @@ proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode,
     else: discard
   else:
     result = semIdentVis(c, kind, n, allowed)
+    let invalidPragmasForPush = if fromTopLevel and sfWasGenSym notin result.flags:
+      {}
+    else:
+      {wExportc, wExportCpp, wDynlib}
+    case kind
+    of skField: implicitPragmas(c, result, n.info, fieldPragmas)
+    of skVar:   implicitPragmas(c, result, n.info, varPragmas-invalidPragmasForPush)
+    of skLet:   implicitPragmas(c, result, n.info, letPragmas-invalidPragmasForPush)
+    of skConst: implicitPragmas(c, result, n.info, constPragmas-invalidPragmasForPush)
+    else: discard
 
 proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
   let ex = t[branchIndex][currentEx].skipConv
@@ -550,13 +580,20 @@ proc checkForOverlap(c: PContext, t: PNode, currentEx, branchIndex: int) =
       if overlap(t[i][j].skipConv, ex):
         localError(c.config, ex.info, errDuplicateCaseLabel)
 
-proc semBranchRange(c: PContext, t, a, b: PNode, covered: var Int128): PNode =
-  checkMinSonsLen(t, 1, c.config)
+proc semBranchRange(c: PContext, n, a, b: PNode, covered: var Int128): PNode =
+  checkMinSonsLen(n, 1, c.config)
   let ac = semConstExpr(c, a)
   let bc = semConstExpr(c, b)
-  let at = fitNode(c, t[0].typ, ac, ac.info).skipConvTakeType
-  let bt = fitNode(c, t[0].typ, bc, bc.info).skipConvTakeType
-
+  if ac.kind in {nkStrLit..nkTripleStrLit} or bc.kind in {nkStrLit..nkTripleStrLit}:
+    localError(c.config, b.info, "range of string is invalid")
+  var at = fitNode(c, n[0].typ, ac, ac.info).skipConvTakeType
+  var bt = fitNode(c, n[0].typ, bc, bc.info).skipConvTakeType
+  # the calls to fitNode may introduce calls to converters
+  # mirrored with semCaseBranch for single elements
+  if at.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    at = semConstExpr(c, at)
+  if bt.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
+    bt = semConstExpr(c, bt)
   result = newNodeI(nkRange, a.info)
   result.add(at)
   result.add(bt)
@@ -568,40 +605,44 @@ proc semCaseBranchRange(c: PContext, t, b: PNode,
   checkSonsLen(b, 3, c.config)
   result = semBranchRange(c, t, b[1], b[2], covered)
 
-proc semCaseBranchSetElem(c: PContext, t, b: PNode,
+proc semCaseBranchSetElem(c: PContext, n, b: PNode,
                           covered: var Int128): PNode =
   if isRange(b):
     checkSonsLen(b, 3, c.config)
-    result = semBranchRange(c, t, b[1], b[2], covered)
+    result = semBranchRange(c, n, b[1], b[2], covered)
   elif b.kind == nkRange:
     checkSonsLen(b, 2, c.config)
-    result = semBranchRange(c, t, b[0], b[1], covered)
+    result = semBranchRange(c, n, b[0], b[1], covered)
   else:
-    result = fitNode(c, t[0].typ, b, b.info)
+    result = fitNode(c, n[0].typ, b, b.info)
     inc(covered)
 
-proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
+proc semCaseBranch(c: PContext, n, branch: PNode, branchIndex: int,
                    covered: var Int128) =
   let lastIndex = branch.len - 2
   for i in 0..lastIndex:
     var b = branch[i]
     if b.kind == nkRange:
       branch[i] = b
+      # same check as in semBranchRange for exhaustiveness
+      covered = covered + getOrdValue(b[1]) + 1 - getOrdValue(b[0])
     elif isRange(b):
-      branch[i] = semCaseBranchRange(c, t, b, covered)
+      branch[i] = semCaseBranchRange(c, n, b, covered)
     else:
       # constant sets and arrays are allowed:
-      var r = semConstExpr(c, b)
+      # set expected type to selector type for type inference
+      # even if it can be a different type like a set or array
+      var r = semConstExpr(c, b, expectedType = n[0].typ)
       if r.kind in {nkCurly, nkBracket} and r.len == 0 and branch.len == 2:
         # discarding ``{}`` and ``[]`` branches silently
         delSon(branch, 0)
         return
       elif r.kind notin {nkCurly, nkBracket} or r.len == 0:
-        checkMinSonsLen(t, 1, c.config)
-        var tmp = fitNode(c, t[0].typ, r, r.info)
+        checkMinSonsLen(n, 1, c.config)
+        var tmp = fitNode(c, n[0].typ, r, r.info)
         # the call to fitNode may introduce a call to a converter
-        if tmp.kind == nkHiddenCallConv or
-            (tmp.kind == nkHiddenStdConv and t[0].typ.kind == tyCstring):
+        # mirrored with semBranchRange
+        if tmp.kind in {nkHiddenCallConv, nkHiddenStdConv, nkHiddenSubConv}:
           tmp = semConstExpr(c, tmp)
         branch[i] = skipConv(tmp)
         inc(covered)
@@ -610,18 +651,18 @@ proc semCaseBranch(c: PContext, t, branch: PNode, branchIndex: int,
           r = deduplicate(c.config, r)
 
         # first element is special and will overwrite: branch[i]:
-        branch[i] = semCaseBranchSetElem(c, t, r[0], covered)
+        branch[i] = semCaseBranchSetElem(c, n, r[0], covered)
 
         # other elements have to be added to ``branch``
         for j in 1..<r.len:
-          branch.add(semCaseBranchSetElem(c, t, r[j], covered))
+          branch.add(semCaseBranchSetElem(c, n, r[j], covered))
           # caution! last son of branch must be the actions to execute:
           swap(branch[^2], branch[^1])
-    checkForOverlap(c, t, i, branchIndex)
+    checkForOverlap(c, n, i, branchIndex)
 
   # Elements added above needs to be checked for overlaps.
   for i in lastIndex.succ..<branch.len - 1:
-    checkForOverlap(c, t, i, branchIndex)
+    checkForOverlap(c, n, i, branchIndex)
 
 proc toCover(c: PContext, t: PType): Int128 =
   let t2 = skipTypes(t, abstractVarRange-{tyTypeDesc})
@@ -713,7 +754,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int,
   of tyFloat..tyFloat128, tyError:
     discard
   of tyRange:
-    if skipTypes(typ[0], abstractInst).kind in shouldChckCovered:
+    if skipTypes(typ.elementType, abstractInst).kind in shouldChckCovered:
       chckCovered = true
   of tyForward:
     errorUndeclaredIdentifier(c, n[0].info, typ.sym.name.s)
@@ -753,9 +794,11 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
   if n == nil: return
   case n.kind
   of nkRecWhen:
+    var a = copyTree(n)
     var branch: PNode = nil   # the branch to take
-    for i in 0..<n.len:
-      var it = n[i]
+    var cannotResolve = false # no branch should be taken
+    for i in 0..<a.len:
+      var it = a[i]
       if it == nil: illFormedAst(n, c.config)
       var idx = 1
       case it.kind
@@ -766,24 +809,35 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
           if e.kind != nkIntLit: discard "don't report followup error"
           elif e.intVal != 0 and branch == nil: branch = it[1]
         else:
-          it[0] = forceBool(c, semExprWithType(c, it[0]))
+          # XXX this is still a hard compilation in a generic context, this can
+          # result in unresolved generic parameters being treated like real types
+          let e = semExprWithType(c, it[0], {efDetermineType})
+          if e.typ.kind == tyFromExpr:
+            it[0] = makeStaticExpr(c, e)
+            cannotResolve = true
+          else:
+            it[0] = forceBool(c, e)
+            let val = getConstExpr(c.module, it[0], c.idgen, c.graph)
+            if val == nil or val.kind != nkIntLit:
+              cannotResolve = true
+            elif not cannotResolve and val.intVal != 0 and branch == nil:
+              branch = it[1]
       of nkElse:
         checkSonsLen(it, 1, c.config)
-        if branch == nil: branch = it[0]
+        if branch == nil and not cannotResolve: branch = it[0]
         idx = 0
       else: illFormedAst(n, c.config)
-      if c.inGenericContext > 0:
+      if c.inGenericContext > 0 and cannotResolve:
         # use a new check intset here for each branch:
-        var newCheck: IntSet
-        assign(newCheck, check)
+        var newCheck: IntSet = check
         var newPos = pos
         var newf = newNodeI(nkRecList, n.info)
         semRecordNodeAux(c, it[idx], newCheck, newPos, newf, rectype, hasCaseFields)
         it[idx] = if newf.len == 1: newf[0] else: newf
-    if c.inGenericContext > 0:
-      father.add n
-    elif branch != nil:
+    if branch != nil:
       semRecordNodeAux(c, branch, check, pos, father, rectype, hasCaseFields)
+    elif cannotResolve:
+      father.add a
     elif father.kind in {nkElse, nkOfBranch}:
       father.add newNodeI(nkRecList, n.info)
   of nkRecCase:
@@ -801,14 +855,19 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
     var a: PNode
     if father.kind != nkRecList and n.len >= 4: a = newNodeI(nkRecList, n.info)
     else: a = newNodeI(nkEmpty, n.info)
-    if n[^1].kind != nkEmpty:
-      localError(c.config, n[^1].info, errInitHereNotAllowed)
     var typ: PType
-    if n[^2].kind == nkEmpty:
+    var hasDefaultField = n[^1].kind != nkEmpty
+    if hasDefaultField:
+      typ = fitDefaultNode(c, n)
+      propagateToOwner(rectype, typ)
+    elif n[^2].kind == nkEmpty:
       localError(c.config, n.info, errTypeExpected)
       typ = errorType(c)
     else:
       typ = semTypeNode(c, n[^2], nil)
+      if c.graph.config.isDefined("nimPreviewRangeDefault") and typ.skipTypes(abstractInst).kind == tyRange:
+        n[^1] = firstRange(c.config, typ)
+        hasDefaultField = true
       propagateToOwner(rectype, typ)
     var fieldOwner = if c.inGenericContext > 0: c.getCurrOwner
                      else: rectype.sym
@@ -824,14 +883,18 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int,
       f.options = c.config.options
       if fieldOwner != nil and
          {sfImportc, sfExportc} * fieldOwner.flags != {} and
-         not hasCaseFields and f.loc.r == "":
-        f.loc.r = rope(f.name.s)
+         not hasCaseFields and f.loc.snippet == "":
+        f.loc.snippet = rope(f.name.s)
         f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags
       inc(pos)
       if containsOrIncl(check, f.name.id):
         localError(c.config, info, "attempt to redefine: '" & f.name.s & "'")
-      if a.kind == nkEmpty: father.add newSymNode(f)
-      else: a.add newSymNode(f)
+      let fSym = newSymNode(f)
+      if hasDefaultField:
+        fSym.sym.ast = n[^1]
+        fSym.sym.ast.flags.incl nfSkipFieldChecking
+      if a.kind == nkEmpty: father.add fSym
+      else: a.add fSym
       styleCheckDef(c, f)
       onDef(f.info, f)
     if a.kind != nkEmpty: father.add a
@@ -871,16 +934,24 @@ proc skipGenericInvocation(t: PType): PType {.inline.} =
   if result.kind == tyGenericInvocation:
     result = result[0]
   while result.kind in {tyGenericInst, tyGenericBody, tyRef, tyPtr, tyAlias, tySink, tyOwned}:
-    result = lastSon(result)
-
-proc addInheritedFields(c: PContext, check: var IntSet, pos: var int,
-                        obj: PType) =
-  assert obj.kind == tyObject
-  if (obj.len > 0) and (obj[0] != nil):
-    addInheritedFields(c, check, pos, obj[0].skipGenericInvocation)
-  addInheritedFieldsAux(c, check, pos, obj.n)
+    result = skipModifier(result)
+
+proc tryAddInheritedFields(c: PContext, check: var IntSet, pos: var int,
+                        obj: PType, n: PNode, isPartial = false, innerObj: PType = nil): bool =
+  if ((not isPartial) and (obj.kind notin {tyObject, tyGenericParam} or tfFinal in obj.flags)) or
+    (innerObj != nil and obj.sym.id == innerObj.sym.id):
+    localError(c.config, n.info, "Cannot inherit from: '" & $obj & "'")
+    result = false
+  elif obj.kind == tyObject:
+    result = true
+    if (obj.len > 0) and (obj[0] != nil):
+      result = result and tryAddInheritedFields(c, check, pos, obj[0].skipGenericInvocation, n, false, obj)
+    addInheritedFieldsAux(c, check, pos, obj.n)
+  else:
+    result = true
 
 proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType =
+  result = nil
   if n.len == 0:
     return newConstraint(c, tyObject)
   var check = initIntSet()
@@ -905,7 +976,11 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
           if concreteBase.sym != nil and concreteBase.sym.magic == mException and
               sfSystemModule notin c.module.flags:
             message(c.config, n.info, warnInheritFromException, "")
-          addInheritedFields(c, check, pos, concreteBase)
+          if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+            return newType(tyError, c.idgen, result.owner)
+
+      elif concreteBase.kind == tyForward:
+        c.skipTypes.add n #we retry in the final pass
       else:
         if concreteBase.kind != tyError:
           localError(c.config, n[1].info, "inheritance only works with non-final objects; " &
@@ -923,7 +998,9 @@ proc semObjectNode(c: PContext, n: PNode, prev: PType; flags: TTypeFlags): PType
     result.n = newNodeI(nkRecList, n.info)
   else:
     # partial object so add things to the check
-    addInheritedFields(c, check, pos, result)
+    if not tryAddInheritedFields(c, check, pos, result, n, isPartial = true):
+      return newType(tyError, c.idgen, result.owner)
+
   semRecordNodeAux(c, n[2], check, pos, result.n, result)
   if n[0].kind != nkEmpty:
     # dummy symbol for `pragma`:
@@ -943,7 +1020,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
     let n = if n[0].kind == nkBracket: n[0] else: n
     checkMinSonsLen(n, 1, c.config)
     let body = n.lastSon
-    var t = if prev != nil and body.kind == nkObjectTy:
+    var t = if prev != nil and prev.kind != tyGenericBody and body.kind == nkObjectTy:
               semObjectNode(c, body, nil, prev.flags)
             else:
               semTypeNode(c, body, nil)
@@ -973,31 +1050,30 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
           addSonSkipIntLit(result, region, c.idgen)
     addSonSkipIntLit(result, t, c.idgen)
     if tfPartial in result.flags:
-      if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial)
+      if result.elementType.kind == tyObject: incl(result.elementType.flags, tfPartial)
     # if not isNilable: result.flags.incl tfNotNil
     case wrapperKind
     of tyOwned:
       if optOwnedRefs in c.config.globalOptions:
-        let t = newTypeS(tyOwned, c)
+        let t = newTypeS(tyOwned, c, result)
         t.flags.incl tfHasOwned
-        t.rawAddSonNoPropagationOfTypeFlags result
         result = t
     of tySink:
-      let t = newTypeS(tySink, c)
-      t.rawAddSonNoPropagationOfTypeFlags result
+      let t = newTypeS(tySink, c, result)
       result = t
     else: discard
-    if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc}:
+    if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
       result.flags.incl tfHasAsgn
 
 proc findEnforcedStaticType(t: PType): PType =
   # This handles types such as `static[T] and Foo`,
   # which are subset of `static[T]`, hence they could
   # be treated in the same way
+  result = nil
   if t == nil: return nil
   if t.kind == tyStatic: return t
   if t.kind == tyAnd:
-    for s in t.sons:
+    for s in t.kids:
       let t = findEnforcedStaticType(s)
       if t != nil: return t
 
@@ -1005,7 +1081,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
   if kind == skMacro:
     let staticType = findEnforcedStaticType(param.typ)
     if staticType != nil:
-      var a = copySym(param, nextSymId c.idgen)
+      var a = copySym(param, c.idgen)
       a.typ = staticType.base
       addDecl(c, a)
       #elif param.typ != nil and param.typ.kind == tyTypeDesc:
@@ -1013,7 +1089,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) =
     else:
       # within a macro, every param has the type NimNode!
       let nn = getSysSym(c.graph, param.info, "NimNode")
-      var a = copySym(param, nextSymId c.idgen)
+      var a = copySym(param, c.idgen)
       a.typ = nn.typ
       addDecl(c, a)
   else:
@@ -1043,7 +1119,7 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent;
 
   let owner = if typeClass.sym != nil: typeClass.sym
               else: getCurrOwner(c)
-  var s = newSym(skType, finalTypId, nextSymId c.idgen, owner, info)
+  var s = newSym(skType, finalTypId, c.idgen, owner, info)
   if sfExplain in owner.flags: s.flags.incl sfExplain
   if typId == nil: s.flags.incl(sfAnon)
   s.linkTo(typeClass)
@@ -1078,7 +1154,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
     let base = (if lifted != nil: lifted else: paramType.base)
     if base.isMetaType and procKind == skMacro:
       localError(c.config, info, errMacroBodyDependsOnGenericTypes % paramName)
-    result = addImplicitGeneric(c, c.newTypeWithSons(tyStatic, @[base]),
+    result = addImplicitGeneric(c, newTypeS(tyStatic, c, base),
         paramTypId, info, genericParams, paramName)
     if result != nil: result.flags.incl({tfHasStatic, tfUnresolved})
 
@@ -1090,38 +1166,42 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
           paramTypId.id == getIdent(c.cache, "type").id):
         # XXX Why doesn't this check for tyTypeDesc instead?
         paramTypId = nil
-      let t = c.newTypeWithSons(tyTypeDesc, @[paramType.base])
+      let t = newTypeS(tyTypeDesc, c, paramType.base)
       incl t.flags, tfCheckedForDestructor
       result = addImplicitGeneric(c, t, paramTypId, info, genericParams, paramName)
-
+    else:
+      result = nil
   of tyDistinct:
     if paramType.len == 1:
       # disable the bindOnce behavior for the type class
       result = recurse(paramType.base, true)
-
+    else:
+      result = nil
   of tyTuple:
+    result = nil
     for i in 0..<paramType.len:
       let t = recurse(paramType[i])
       if t != nil:
         paramType[i] = t
         result = paramType
 
-  of tyAlias, tyOwned, tySink:
+  of tyAlias, tyOwned:
     result = recurse(paramType.base)
 
   of tySequence, tySet, tyArray, tyOpenArray,
-     tyVar, tyLent, tyPtr, tyRef, tyProc:
+     tyVar, tyLent, tyPtr, tyRef, tyProc, tySink:
     # XXX: this is a bit strange, but proc(s: seq)
     # produces tySequence(tyGenericParam, tyNone).
     # This also seems to be true when creating aliases
     # like: type myseq = distinct seq.
     # Maybe there is another better place to associate
     # the seq type class with the seq identifier.
-    if paramType.kind == tySequence and paramType.lastSon.kind == tyNone:
-      let typ = c.newTypeWithSons(tyBuiltInTypeClass,
-                                  @[newTypeS(paramType.kind, c)])
+    if paramType.kind == tySequence and paramType.elementType.kind == tyNone:
+      let typ = newTypeS(tyBuiltInTypeClass, c,
+                         newTypeS(paramType.kind, c))
       result = addImplicitGeneric(c, typ, paramTypId, info, genericParams, paramName)
     else:
+      result = nil
       for i in 0..<paramType.len:
         if paramType[i] == paramType:
           globalError(c.config, info, errIllegalRecursionInTypeX % typeToString(paramType))
@@ -1142,21 +1222,22 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       else:
         result.rawAddSon newTypeS(tyAnything, c)
 
-    if paramType.lastSon.kind == tyUserTypeClass:
+    if paramType.typeBodyImpl.kind == tyUserTypeClass:
       result.kind = tyUserTypeClassInst
-      result.rawAddSon paramType.lastSon
+      result.rawAddSon paramType.typeBodyImpl
       return addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
 
     let x = instGenericContainer(c, paramType.sym.info, result,
                                   allowMetaTypes = true)
-    result = newTypeWithSons(c, tyCompositeTypeClass, @[paramType, x])
-    #result = newTypeS(tyCompositeTypeClass, c)
-    #for i in 0..<x.len: result.rawAddSon(x[i])
+    result = newTypeS(tyCompositeTypeClass, c)
+    result.rawAddSon paramType
+    result.rawAddSon x
     result = addImplicitGeneric(c, result, paramTypId, info, genericParams, paramName)
 
   of tyGenericInst:
-    if paramType.lastSon.kind == tyUserTypeClass:
-      var cp = copyType(paramType, nextTypeId c.idgen, getCurrOwner(c))
+    result = nil
+    if paramType.skipModifier.kind == tyUserTypeClass:
+      var cp = copyType(paramType, c.idgen, getCurrOwner(c))
       copyTypeProps(c.graph, c.idgen.module, cp, paramType)
 
       cp.kind = tyUserTypeClassInst
@@ -1167,15 +1248,16 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       if lifted != nil:
         paramType[i] = lifted
         result = paramType
-        result.lastSon.shouldHaveMeta
+        result.last.shouldHaveMeta
 
-    let liftBody = recurse(paramType.lastSon, true)
+    let liftBody = recurse(paramType.skipModifier, true)
     if liftBody != nil:
       result = liftBody
       result.flags.incl tfHasMeta
       #result.shouldHaveMeta
 
   of tyGenericInvocation:
+    result = nil
     for i in 1..<paramType.len:
       #if paramType[i].kind != tyTypeDesc:
       let lifted = recurse(paramType[i])
@@ -1187,7 +1269,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       # before one of its param types
       return
 
-    if body.lastSon.kind == tyUserTypeClass:
+    if body.last.kind == tyUserTypeClass:
       let expanded = instGenericContainer(c, info, paramType,
                                           allowMetaTypes = true)
       result = recurse(expanded, true)
@@ -1195,17 +1277,18 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
      tyAnd, tyOr, tyNot, tyConcept:
     result = addImplicitGeneric(c,
-        copyType(paramType, nextTypeId c.idgen, getCurrOwner(c)), paramTypId,
+        copyType(paramType, c.idgen, getCurrOwner(c)), paramTypId,
         info, genericParams, paramName)
 
   of tyGenericParam:
+    result = nil
     markUsed(c, paramType.sym.info, paramType.sym)
     onUse(paramType.sym.info, paramType.sym)
     if tfWildcard in paramType.flags:
       paramType.flags.excl tfWildcard
       paramType.sym.transitionGenericParamToType()
 
-  else: discard
+  else: result = nil
 
 proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
   ## Semchecks the type of parameters.
@@ -1213,7 +1296,7 @@ proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType =
     result = semTypeNode(c, n[0], nil)
     constraint = semNodeKindConstraints(n, c.config, 1)
   elif n.kind == nkCall and
-      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice} and
+      n[0].kind in {nkIdent, nkSym, nkOpenSymChoice, nkClosedSymChoice, nkOpenSym} and
       considerQuotedIdent(c, n[0]).s == "{}":
     result = semTypeNode(c, n[1], nil)
     constraint = semNodeKindConstraints(n, c.config, 2)
@@ -1231,6 +1314,7 @@ proc newProcType(c: PContext; info: TLineInfo; prev: PType = nil): PType =
   result.n.add newNodeI(nkEffectList, info)
 
 proc isMagic(sym: PSym): bool =
+  if sym.ast == nil: return false
   let nPragmas = sym.ast[pragmasPos]
   return hasPragma(nPragmas, wMagic)
 
@@ -1242,6 +1326,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
   result = newProcType(c, n.info, prev)
   var check = initIntSet()
   var counter = 0
+  template isCurrentlyGeneric: bool =
+    # genericParams might update as implicit generic params are added
+    genericParams != nil and genericParams.len > 0
 
   for i in 1..<n.len:
     var a = n[i]
@@ -1262,7 +1349,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
       hasDefault = a[^1].kind != nkEmpty
 
     if hasType:
+      let isGeneric = isCurrentlyGeneric()
+      inc c.inGenericContext, ord(isGeneric)
       typ = semParamType(c, a[^2], constraint)
+      dec c.inGenericContext, ord(isGeneric)
       # TODO: Disallow typed/untyped in procs in the compiler/stdlib
       if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped):
         if not isMagic(getCurrOwner(c)):
@@ -1271,14 +1361,37 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
 
     if hasDefault:
       def = a[^1]
+      if a.len > 3:
+        var msg = ""
+        for j in 0 ..< a.len - 2:
+          if msg.len != 0: msg.add(", ")
+          msg.add($a[j])
+        msg.add(" all have default value '")
+        msg.add(def.renderTree)
+        msg.add("', this may be unintentional, " &
+          "either use ';' (semicolon) or explicitly write each default value")
+        message(c.config, a.info, warnImplicitDefaultValue, msg)
       block determineType:
-        if genericParams.isGenericParams:
-          def = semGenericStmt(c, def)
-          if hasUnresolvedArgs(c, def):
+        var canBeVoid = false
+        if kind == skTemplate:
+          if typ != nil and typ.kind == tyUntyped:
+            # don't do any typechecking or assign a type for
+            # `untyped` parameter default value
+            break determineType
+          elif hasUnresolvedArgs(c, def):
+            # template default value depends on other parameter
+            # don't do any typechecking
             def.typ = makeTypeFromExpr(c, def.copyTree)
             break determineType
-
-        def = semExprWithType(c, def, {efDetermineType})
+          elif typ != nil and typ.kind == tyTyped:
+            canBeVoid = true
+        let isGeneric = isCurrentlyGeneric()
+        inc c.inGenericContext, ord(isGeneric)
+        if canBeVoid:
+          def = semExpr(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        else:
+          def = semExprWithType(c, def, {efDetermineType, efAllowSymChoice}, typ)
+        dec c.inGenericContext, ord(isGeneric)
         if def.referencesAnotherParam(getCurrOwner(c)):
           def.flags.incl nfDefaultRefsParam
 
@@ -1294,10 +1407,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           # which will prevent other types from matching - clearly a very
           # surprising behavior. We must instead fix the expected type of
           # the proc to be the unbound typedesc type:
-          typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+          typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
           typ.flags.incl tfCheckedForDestructor
 
-      else:
+      elif def.typ != nil and def.typ.kind != tyFromExpr: # def.typ can be void
         # if def.typ != nil and def.typ.kind != tyNone:
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
@@ -1317,6 +1430,10 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
 
     for j in 0..<a.len-2:
       var arg = newSymG(skParam, if a[j].kind == nkPragmaExpr: a[j][0] else: a[j], c)
+      if arg.name.id == ord(wUnderscore):
+        arg.flags.incl(sfGenSym)
+      elif containsOrIncl(check, arg.name.id):
+        localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
       if a[j].kind == nkPragmaExpr:
         pragma(c, arg, a[j][1], paramPragmas)
       if not hasType and not hasDefault and kind notin {skTemplate, skMacro}:
@@ -1325,28 +1442,33 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
         else:
           localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s)
           typ = errorType(c)
+      var nameForLift = arg.name.s
+      if sfGenSym in arg.flags:
+        nameForLift.add("`gensym" & $arg.id)
       let lifted = liftParamType(c, kind, genericParams, typ,
-                                 arg.name.s, arg.info)
+                                 nameForLift, arg.info)
       let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen)
       arg.typ = finalType
       arg.position = counter
-      arg.constraint = constraint
+      if constraint != nil:
+        #only replace the constraint when it has been set as arg could contain codegenDecl
+        arg.constraint = constraint
       inc(counter)
       if def != nil and def.kind != nkEmpty:
         arg.ast = copyTree(def)
-      if containsOrIncl(check, arg.name.id):
-        localError(c.config, a[j].info, "attempt to redefine: '" & arg.name.s & "'")
       result.n.add newSymNode(arg)
       rawAddSon(result, finalType)
       addParamOrResult(c, arg, kind)
       styleCheckDef(c, a[j].info, arg)
       onDef(a[j].info, arg)
-      if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}:
-        a[j] = newSymNode(arg)
+      a[j] = newSymNode(arg)
 
-  var r: PType
+  var r: PType = nil
   if n[0].kind != nkEmpty:
+    let isGeneric = isCurrentlyGeneric()
+    inc c.inGenericContext, ord(isGeneric)
     r = semTypeNode(c, n[0], nil)
+    dec c.inGenericContext, ord(isGeneric)
 
   if r != nil and kind in {skMacro, skTemplate} and r.kind == tyTyped:
     # XXX: To implement the proposed change in the warning, just
@@ -1369,9 +1491,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
             "' is only valid for macros and templates")
       # 'auto' as a return type does not imply a generic:
       elif r.kind == tyAnything:
-        # 'p(): auto' and 'p(): untyped' are equivalent, but the rest of the
-        # compiler is hardly aware of 'auto':
-        r = newTypeS(tyUntyped, c)
+        r = copyType(r, c.idgen, r.owner)
+        r.flags.incl tfRetType
       elif r.kind == tyStatic:
         # type allowed should forbid this type
         discard
@@ -1400,7 +1521,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode,
           result.flags.excl tfHasMeta
       result.n.typ = r
 
-  if genericParams.isGenericParams:
+  if isCurrentlyGeneric():
     for n in genericParams:
       if {sfUsed, sfAnon} * n.sym.flags == {}:
         result.flags.incl tfUnresolved
@@ -1422,6 +1543,8 @@ proc semStmtListType(c: PContext, n: PNode, prev: PType): PType =
 
 proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
   inc(c.p.nestedBlockCounter)
+  let oldBreakInLoop = c.p.breakInLoop
+  c.p.breakInLoop = false
   checkSonsLen(n, 2, c.config)
   openScope(c)
   if n[0].kind notin {nkEmpty, nkSym}:
@@ -1430,31 +1553,42 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
   n[1].typ = result
   n.typ = result
   closeScope(c)
+  c.p.breakInLoop = oldBreakInLoop
   dec(c.p.nestedBlockCounter)
 
 proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
   result = semTypeNode(c, n, nil)
   n.typ = makeTypeDesc(c, result)
 
-proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
+proc trySemObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType): bool =
   var
     check = initIntSet()
     pos = 0
   let
-    realBase = t[0]
+    realBase = t.baseClass
     base = skipTypesOrNil(realBase, skipPtrs)
+  result = true
   if base.isNil:
     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)
+      if not tryAddInheritedFields(c, check, pos, concreteBase, n):
+        return false
     else:
       if concreteBase.kind != tyError:
         localError(c.config, n.info, errInheritanceOnlyWithNonFinalObjects)
   var newf = newNodeI(nkRecList, n.info)
   semRecordNodeAux(c, t.n, check, pos, newf, t)
 
+proc containsGenericInvocationWithForward(n: PNode): bool =
+  if n.kind == nkSym and n.sym.ast != nil and n.sym.ast.len > 1 and n.sym.ast[2].kind == nkObjectTy:
+    for p in n.sym.ast[2][^1]:
+      if p.kind == nkIdentDefs and p[1].typ != nil and p[1].typ.kind == tyGenericInvocation and
+        p[1][0].kind == nkSym and p[1][0].typ.kind == tyForward:
+          return true
+  return false
+
 proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   if s.typ == nil:
     localError(c.config, n.info, "cannot instantiate the '$1' $2" %
@@ -1464,20 +1598,24 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
   var t = s.typ.skipTypes({tyAlias})
   if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody:
     t = t.base
-
   result = newOrPrevType(tyGenericInvocation, prev, c)
   addSonSkipIntLit(result, t, c.idgen)
 
-  template addToResult(typ) =
+  template addToResult(typ, skip) =
+
     if typ.isNil:
       internalAssert c.config, false
       rawAddSon(result, typ)
-    else: addSonSkipIntLit(result, typ, c.idgen)
+    else:
+      if skip:
+        addSonSkipIntLit(result, typ, c.idgen)
+      else:
+        rawAddSon(result, makeRangeWithStaticExpr(c, typ.n))
 
   if t.kind == tyForward:
     for i in 1..<n.len:
       var elem = semGenericParamInInvocation(c, n[i])
-      addToResult(elem)
+      addToResult(elem, true)
     return
   elif t.kind != tyGenericBody:
     # we likely got code of the form TypeA[TypeB] where TypeA is
@@ -1497,7 +1635,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
       return newOrPrevType(tyError, prev, c)
 
     var isConcrete = true
-
+    let rType = m.call[0].typ
+    let mIndex = if rType != nil: rType.len - 1 else: -1
     for i in 1..<m.call.len:
       var typ = m.call[i].typ
       # is this a 'typedesc' *parameter*? If so, use the typedesc type,
@@ -1505,17 +1644,22 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
       if m.call[i].kind == nkSym and m.call[i].sym.kind == skParam and
           typ.kind == tyTypeDesc and containsGenericType(typ):
         isConcrete = false
-        addToResult(typ)
+        addToResult(typ, true)
       else:
         typ = typ.skipTypes({tyTypeDesc})
         if containsGenericType(typ): isConcrete = false
-        addToResult(typ)
+        var skip = true
+        if mIndex >= i - 1 and tfImplicitStatic in rType[i - 1].flags and isIntLit(typ):
+          skip = false
+        addToResult(typ, skip)
 
     if isConcrete:
       if s.ast == nil and s.typ.kind != tyCompositeTypeClass:
         # XXX: What kind of error is this? is it still relevant?
         localError(c.config, n.info, errCannotInstantiateX % s.name.s)
         result = newOrPrevType(tyError, prev, c)
+      elif containsGenericInvocationWithForward(n[0]):
+        c.skipTypes.add n #fixes 1500
       else:
         result = instGenericContainer(c, n.info, result,
                                       allowMetaTypes = false)
@@ -1528,30 +1672,36 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType =
     return errorType(c)
   if tx != result and tx.kind == tyObject:
     if tx[0] != nil:
-      semObjectTypeForInheritedGenericInst(c, n, tx)
+      if not trySemObjectTypeForInheritedGenericInst(c, n, tx):
+        return newOrPrevType(tyError, prev, c)
     var position = 0
     recomputeFieldPositions(tx, tx.n, position)
 
 proc maybeAliasType(c: PContext; typeExpr, prev: PType): PType =
-  if typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody} and prev != nil:
+  if prev != nil and (prev.kind == tyGenericBody or
+      typeExpr.kind in {tyObject, tyEnum, tyDistinct, tyForward, tyGenericBody}):
     result = newTypeS(tyAlias, c)
     result.rawAddSon typeExpr
     result.sym = prev.sym
-    assignType(prev, result)
+    if prev.kind != tyGenericBody:
+      assignType(prev, result)
+  else:
+    result = nil
 
 proc fixupTypeOf(c: PContext, prev: PType, typExpr: PNode) =
   if prev != nil:
     let result = newTypeS(tyAlias, c)
     result.rawAddSon typExpr.typ
     result.sym = prev.sym
-    assignType(prev, result)
+    if prev.kind != tyGenericBody:
+      assignType(prev, result)
 
 proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
   var n = semExprWithType(c, n, {efDetermineType})
   if n.typ.kind == tyTypeDesc:
     result = n.typ.base
     # fix types constructed by macros/template:
-    if prev != nil and prev.sym != nil:
+    if prev != nil and prev.kind != tyGenericBody and prev.sym != nil:
       if result.sym.isNil:
         # Behold! you're witnessing enormous power yielded
         # by macros. Only macros can summon unnamed types
@@ -1567,13 +1717,17 @@ proc semTypeExpr(c: PContext, n: PNode; prev: PType): PType =
         # unnecessary new type creation
         let alias = maybeAliasType(c, result, prev)
         if alias != nil: result = alias
+  elif n.typ.kind == tyFromExpr and c.inGenericContext > 0:
+    # sometimes not possible to distinguish type from value in generic body,
+    # for example `T.Foo`, so both are handled under `tyFromExpr`
+    result = n.typ
   else:
     localError(c.config, n.info, "expected type, but got: " & n.renderTree)
     result = errorType(c)
 
 proc freshType(c: PContext; res, prev: PType): PType {.inline.} =
-  if prev.isNil:
-    result = copyType(res, nextTypeId c.idgen, res.owner)
+  if prev.isNil or prev.kind == tyGenericBody:
+    result = copyType(res, c.idgen, res.owner)
     copyTypeProps(c.graph, c.idgen.module, result, res)
   else:
     result = res
@@ -1599,11 +1753,10 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
     pragmas = n[1]
     inherited = n[2]
 
-  result = newOrPrevType(tyUserTypeClass, prev, c)
-  result.flags.incl tfCheckedForDestructor
   var owner = getCurrOwner(c)
-  var candidateTypeSlot = newTypeWithSons(owner, tyAlias, @[c.errorType], c.idgen)
-  result.sons = @[candidateTypeSlot]
+  var candidateTypeSlot = newTypeS(tyAlias, c, c.errorType)
+  result = newOrPrevType(tyUserTypeClass, prev, c, son = candidateTypeSlot)
+  result.flags.incl tfCheckedForDestructor
   result.n = n
 
   if inherited.kind != nkEmpty:
@@ -1637,7 +1790,7 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
 
     internalAssert c.config, dummyName.kind == nkIdent
     var dummyParam = newSym(if modifier == tyTypeDesc: skType else: skVar,
-                            dummyName.ident, nextSymId c.idgen, owner, param.info)
+                            dummyName.ident, c.idgen, owner, param.info)
     dummyParam.typ = dummyType
     incl dummyParam.flags, sfUsed
     addDecl(c, dummyParam)
@@ -1646,18 +1799,22 @@ proc semTypeClass(c: PContext, n: PNode, prev: PType): PType =
   closeScope(c)
 
 proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode =
+  result = nil
   for p in pragmas:
     let key = if p.kind in nkPragmaCallKinds and p.len >= 1: p[0] else: p
-
     if p.kind == nkEmpty or whichPragma(p) != wInvalid:
       discard "builtin pragma"
     else:
-      let ident = considerQuotedIdent(c, key)
-      if strTableGet(c.userPragmas, ident) != nil:
+      trySuggestPragmas(c, key)
+      let ident =
+        if key.kind in nkIdentKinds:
+          considerQuotedIdent(c, key)
+        else:
+          nil
+      if ident != nil and strTableGet(c.userPragmas, ident) != nil:
         discard "User-defined pragma"
       else:
-        var amb = false
-        let sym = searchInScopes(c, ident, amb)
+        let sym = qualifiedLookUp(c, key, {})
         # XXX: What to do here if amb is true?
         if sym != nil and sfCustomPragma in sym.flags:
           discard "Custom user pragma"
@@ -1701,7 +1858,7 @@ proc semProcTypeWithScope(c: PContext, n: PNode,
   if n[1].kind != nkEmpty and n[1].len > 0:
     pragma(c, s, n[1], procTypePragmas)
     when useEffectSystem: setEffectsForProcType(c.graph, result, n[1])
-  elif c.optionStack.len > 0 and optNimV1Emulation notin c.config.globalOptions:
+  elif c.optionStack.len > 0:
     # we construct a fake 'nkProcDef' for the 'mergePragmas' inside 'implicitPragmas'...
     s.ast = newTree(nkProcDef, newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info),
         newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info), newNodeI(nkEmpty, n.info))
@@ -1724,10 +1881,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType =
 
 proc semTypeOf(c: PContext; n: PNode; prev: PType): PType =
   openScope(c)
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let t = semExprWithType(c, n, {efInTypeof})
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
 
 proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
   openScope(c)
@@ -1738,10 +1899,81 @@ proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType =
       localError(c.config, n.info, "typeof: cannot evaluate 'mode' parameter at compile-time")
     else:
       m = mode.intVal
+  inc c.inTypeofContext
+  defer: dec c.inTypeofContext # compiles can raise an exception
   let t = semExprWithType(c, n[1], if m == 1: {efInTypeof} else: {})
   closeScope(c)
   fixupTypeOf(c, prev, t)
   result = t.typ
+  if result.kind == tyFromExpr:
+    result.flags.incl tfNonConstExpr
+
+proc semTypeIdent(c: PContext, n: PNode): PSym =
+  if n.kind == nkSym:
+    result = getGenSym(c, n.sym)
+  else:
+    result = pickSym(c, n, {skType, skGenericParam, skParam})
+    if result.isNil:
+      result = qualifiedLookUp(c, n, {checkAmbiguity, checkUndeclared})
+    if result != nil:
+      markUsed(c, n.info, result)
+      onUse(n.info, result)
+
+      # alias syntax, see semSym for skTemplate, skMacro
+      if result.kind in {skTemplate, skMacro} and sfNoalias notin result.flags:
+        let t = semTypeExpr(c, n, nil)
+        result = symFromType(c, t, n.info)
+
+      if result.kind == skParam and result.typ.kind == tyTypeDesc:
+        # This is a typedesc param. is it already bound?
+        # it's not bound when it's used multiple times in the
+        # proc signature for example
+        if c.inGenericInst > 0:
+          let bound = result.typ.elementType.sym
+          if bound != nil: return bound
+          return result
+        if result.typ.sym == nil:
+          localError(c.config, n.info, errTypeExpected)
+          return errorSym(c, n)
+        result = result.typ.sym.copySym(c.idgen)
+        result.typ = exactReplica(result.typ)
+        result.typ.flags.incl tfUnresolved
+
+      if result.kind == skGenericParam:
+        if result.typ.kind == tyGenericParam and result.typ.len == 0 and
+           tfWildcard in result.typ.flags:
+          # collapse the wild-card param to a type
+          result.transitionGenericParamToType()
+          result.typ.flags.excl tfWildcard
+          return
+        else:
+          localError(c.config, n.info, errTypeExpected)
+          return errorSym(c, n)
+      if result.kind != skType and result.magic notin {mStatic, mType, mTypeOf}:
+        # this implements the wanted ``var v: V, x: V`` feature ...
+        var ov: TOverloadIter = default(TOverloadIter)
+        var amb = initOverloadIter(ov, c, n)
+        while amb != nil and amb.kind != skType:
+          amb = nextOverloadIter(ov, c, n)
+        if amb != nil: result = amb
+        else:
+          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!
+        var oldInfo = n.info
+        when defined(useNodeIds):
+          let oldId = n.id
+        reset(n[])
+        when defined(useNodeIds):
+          n.id = oldId
+        n.transitionNoneToSym()
+        n.sym = result
+        n.info = oldInfo
+        n.typ = result.typ
+    else:
+      localError(c.config, n.info, "identifier expected")
+      result = errorSym(c, n)
 
 proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   result = nil
@@ -1762,11 +1994,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkTupleConstr: result = semAnonTuple(c, n, prev)
   of nkCallKinds:
     let x = n[0]
-    let ident = case x.kind
-                of nkIdent: x.ident
-                of nkSym: x.sym.name
-                of nkClosedSymChoice, nkOpenSymChoice: x[0].sym.name
-                else: nil
+    let ident = x.getPIdent
     if ident != nil and ident.s == "[]":
       let b = newNodeI(nkBracketExpr, n.info)
       for i in 1..<n.len: b.add(n[i])
@@ -1785,7 +2013,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = semTypeExpr(c, n, prev)
     else:
       let op = considerQuotedIdent(c, n[0])
-      if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
+      if op.id == ord(wAnd) or op.id == ord(wOr) or op.s == "|":
         checkSonsLen(n, 3, c.config)
         var
           t1 = semTypeNode(c, n[1], nil)
@@ -1856,19 +2084,21 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       elif op.id == ord(wType):
         checkSonsLen(n, 2, c.config)
         result = semTypeOf(c, n[1], prev)
-      elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf:
+      elif op.s == "typeof" and (
+          (n[0].kind == nkSym and n[0].sym.magic == mTypeOf) or
+          (n[0].kind == nkOpenSym and n[0][0].sym.magic == mTypeOf)):
         result = semTypeOf2(c, n, prev)
       elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2:
         result = semTypeExpr(c, n[1], prev)
       else:
-        if c.inGenericContext > 0 and n.kind == nkCall:
-          result = makeTypeFromExpr(c, n.copyTree)
-        else:
-          result = semTypeExpr(c, n, prev)
+        result = semTypeExpr(c, n, prev)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.transitionSonsKind(nkStmtListType)
-    result = semTypeNode(c, whenResult, prev)
+    if whenResult.kind == nkWhenStmt:
+      result = whenResult.typ
+    else:
+      result = semTypeNode(c, whenResult, prev)
   of nkBracketExpr:
     checkMinSonsLen(n, 2, c.config)
     var head = n[0]
@@ -1896,7 +2126,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       result = semTypeNode(c, n[0], nil)
       if result != nil:
         let old = result
-        result = copyType(result, nextTypeId c.idgen, getCurrOwner(c))
+        result = copyType(result, c.idgen, getCurrOwner(c))
         copyTypeProps(c.graph, c.idgen.module, result, old)
         for i in 1..<n.len:
           result.rawAddSon(semTypeNode(c, n[i], nil))
@@ -1913,6 +2143,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
     of mRef: result = semAnyRef(c, n, tyRef, prev)
     of mPtr: result = semAnyRef(c, n, tyPtr, prev)
     of mTuple: result = semTuple(c, n, prev)
+    of mBuiltinType:
+      case s.name.s
+      of "lent": result = semAnyRef(c, n, tyLent, prev)
+      of "sink": result = semAnyRef(c, n, tySink, prev)
+      of "owned": result = semAnyRef(c, n, tyOwned, prev)
+      else: result = semGeneric(c, n, s, prev)
     else: result = semGeneric(c, n, s, prev)
   of nkDotExpr:
     let typeExpr = semExpr(c, n)
@@ -1942,7 +2178,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       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 c.config, s.typ.base.kind != tyNone and prev == nil
+      internalAssert c.config, s.typ.base.kind != tyNone
       result = s.typ.base
     elif prev == nil:
       result = s.typ
@@ -1950,6 +2186,8 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
       let alias = maybeAliasType(c, s.typ, prev)
       if alias != nil:
         result = alias
+      elif prev.kind == tyGenericBody:
+        result = s.typ
       else:
         assignType(prev, s.typ)
         # bugfix: keep the fresh id for aliases to integral types:
@@ -1964,12 +2202,12 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         if s.kind == skType:
           s.typ
         else:
-          internalAssert c.config, s.typ.base.kind != tyNone and prev == nil
+          internalAssert c.config, s.typ.base.kind != tyNone
           s.typ.base
       let alias = maybeAliasType(c, t, prev)
       if alias != nil:
         result = alias
-      elif prev == nil:
+      elif prev == nil or prev.kind == tyGenericBody:
         result = t
       else:
         assignType(prev, t)
@@ -1990,32 +2228,42 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkTypeClassTy: result = semTypeClass(c, n, prev)
   of nkRefTy: result = semAnyRef(c, n, tyRef, prev)
   of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev)
-  of nkVarTy: result = semVarOutType(c, n, prev, tyVar)
+  of nkVarTy: result = semVarOutType(c, n, prev, {})
+  of nkOutTy: result = semVarOutType(c, n, prev, {tfIsOutParam})
   of nkDistinctTy: result = semDistinct(c, n, prev)
   of nkStaticTy: result = semStaticType(c, n[0], prev)
-  of nkIteratorTy:
-    if n.len == 0:
+  of nkProcTy, nkIteratorTy:
+    if n.len == 0 or n[0].kind == nkEmpty:
+      # 0 length or empty param list with possible pragmas imply typeclass
       result = newTypeS(tyBuiltInTypeClass, c)
       let child = newTypeS(tyProc, c)
-      child.flags.incl tfIterator
+      if n.kind == nkIteratorTy:
+        child.flags.incl tfIterator
+      if n.len > 0 and n[1].kind != nkEmpty and n[1].len > 0:
+        # typeclass with pragma
+        let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
+        # dummy symbol for `pragma`:
+        var s = newSymS(symKind, newIdentNode(getIdent(c.cache, "dummy"), n.info), c)
+        s.typ = child
+        # for now only call convention pragmas supported in proc typeclass
+        pragma(c, s, n[1], {FirstCallConv..LastCallConv})
       result.addSonSkipIntLit(child, c.idgen)
     else:
-      result = semProcTypeWithScope(c, n, prev, skIterator)
-      if result.kind == tyProc:
+      let symKind = if n.kind == nkIteratorTy: skIterator else: skProc
+      result = semProcTypeWithScope(c, n, prev, symKind)
+      if result == nil:
+        localError(c.config, n.info, "type expected, but got: " & renderTree(n))
+        result = newOrPrevType(tyError, prev, c)
+
+      if n.kind == nkIteratorTy and result.kind == tyProc:
         result.flags.incl(tfIterator)
-        if n.lastSon.kind == nkPragma and hasPragma(n.lastSon, wInline):
-          result.callConv = ccInline
-        else:
-          result.callConv = ccClosure
-  of nkProcTy:
-    if n.len == 0:
-      result = newConstraint(c, tyProc)
-    else:
-      result = semProcTypeWithScope(c, n, prev, skProc)
+      if result.callConv == ccClosure and c.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
+        result.flags.incl tfHasAsgn
   of nkEnumTy: result = semEnum(c, n, prev)
   of nkType: result = n.typ
   of nkStmtListType: result = semStmtListType(c, n, prev)
   of nkBlockType: result = semBlockType(c, n, prev)
+  of nkOpenSym: result = semTypeNode(c, n[0], prev)
   else:
     result = semTypeExpr(c, n, prev)
     when false:
@@ -2128,7 +2376,7 @@ proc processMagicType(c: PContext, m: PSym) =
   else: localError(c.config, m.info, errTypeExpected)
 
 proc semGenericConstraints(c: PContext, x: PType): PType =
-  result = newTypeWithSons(c, tyGenericParam, @[x])
+  result = newTypeS(tyGenericParam, c, x)
 
 proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
 
@@ -2148,14 +2396,14 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
     of nkIdentDefs:
       var def = a[^1]
       let constraint = a[^2]
-      var typ: PType
+      var typ: PType = nil
 
       if constraint.kind != nkEmpty:
         typ = semTypeNode(c, constraint, nil)
         if typ.kind != tyStatic or typ.len == 0:
           if typ.kind == tyTypeDesc:
-            if typ[0].kind == tyNone:
-              typ = newTypeWithSons(c, tyTypeDesc, @[newTypeS(tyNone, c)])
+            if typ.elementType.kind == tyNone:
+              typ = newTypeS(tyTypeDesc, c, newTypeS(tyNone, c))
               incl typ.flags, tfCheckedForDestructor
           else:
             typ = semGenericConstraints(c, typ)
@@ -2164,7 +2412,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
         def = semConstExpr(c, def)
         if typ == nil:
           if def.typ.kind != tyTypeDesc:
-            typ = newTypeWithSons(c, tyStatic, @[def.typ])
+            typ = newTypeS(tyStatic, c, def.typ)
         else:
           # the following line fixes ``TV2*[T:SomeNumber=TR] = array[0..1, T]``
           # from manyloc/named_argument_bug/triengine:
@@ -2183,7 +2431,7 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
         if j == 0:
           finalType = typ
         else:
-          finalType = copyType(typ, nextTypeId c.idgen, typ.owner)
+          finalType = copyType(typ, c.idgen, typ.owner)
           copyTypeProps(c.graph, c.idgen.module, finalType, typ)
         # it's important the we create an unique
         # type for each generic param. the index
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 945667a81..759e8e6ab 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -9,49 +9,47 @@
 
 # This module does the instantiation of generic types.
 
+import std / tables
+
 import ast, astalgo, msgs, types, magicsys, semdata, renderer, options,
   lineinfos, modulegraphs
 
-from concepts import makeTypeDesc
-
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 const tfInstClearedFlags = {tfHasMeta, tfUnresolved}
 
 proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) =
-  if t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}:
+  if t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
 
 proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) =
   var t = typ.skipTypes({tyDistinct})
   if t.kind in tyTypeClasses: discard
-  elif t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}:
+  elif t.kind in {tyVar, tyLent} and t.elementType.kind in {tyVar, tyLent}:
     localError(conf, info, "type 'var var' is not allowed")
   elif computeSize(conf, t) == szIllegalRecursion or isTupleRecursive(t):
     localError(conf, info, "illegal recursion in type '" & typeToString(t) & "'")
-  when false:
-    if t.kind == tyObject and t[0] != nil:
-      if t[0].kind != tyObject or tfFinal in t[0].flags:
-        localError(info, errInheritanceOnlyWithNonFinalObjects)
 
 proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
+  result = nil
   let genericTyp = key[0]
   if not (genericTyp.kind == tyGenericBody and
       genericTyp.sym != nil): return
 
   for inst in typeInstCacheItems(g, genericTyp.sym):
     if inst.id == key.id: return inst
-    if inst.len < key.len:
+    if inst.kidsLen < key.kidsLen:
       # XXX: This happens for prematurely cached
       # types such as Channel[empty]. Why?
       # See the notes for PActor in handleGenericInvocation
-      return
+      # if this is return the same type gets cached more than it needs to
+      continue
     if not sameFlags(inst, key):
       continue
 
     block matchType:
-      for j in 1..high(key.sons):
+      for j in FirstGenericParamAt..<key.kidsLen:
         # XXX sameType is not really correct for nested generics?
         if not compareTypes(inst[j], key[j],
                             flags = {ExactGenericParams, PickyCAliases}):
@@ -61,21 +59,21 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType =
 
 proc cacheTypeInst(c: PContext; inst: PType) =
   let gt = inst[0]
-  let t = if gt.kind == tyGenericBody: gt.lastSon else: gt
+  let t = if gt.kind == tyGenericBody: gt.typeBodyImpl else: gt
   if t.kind in {tyStatic, tyError, tyGenericParam} + tyTypeClasses:
     return
   addToGenericCache(c, gt.sym, inst)
 
 type
   LayeredIdTable* {.acyclic.} = ref object
-    topLayer*: TIdTable
+    topLayer*: TypeMapping
     nextLayer*: LayeredIdTable
 
   TReplTypeVars* = object
     c*: PContext
     typeMap*: LayeredIdTable  # map PType to PType
-    symMap*: TIdTable         # map PSym to PSym
-    localCache*: TIdTable     # local cache for remembering already replaced
+    symMap*: SymMapping         # map PSym to PSym
+    localCache*: TypeMapping     # local cache for remembering already replaced
                               # types during instantiation of meta types
                               # (they are not stored in the global cache)
     info*: TLineInfo
@@ -87,27 +85,26 @@ type
     recursionLimit: int
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
-proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
-proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode
+proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym
+proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode
 
-proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable =
+proc initLayeredTypeMap*(pt: sink TypeMapping): LayeredIdTable =
   result = LayeredIdTable()
-  copyIdTable(result.topLayer, pt)
+  result.topLayer = pt
 
 proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable =
-  result = LayeredIdTable()
-  result.nextLayer = cl.typeMap
-  initIdTable(result.topLayer)
+  result = LayeredIdTable(nextLayer: cl.typeMap, topLayer: initTable[ItemId, PType]())
 
 proc lookup(typeMap: LayeredIdTable, key: PType): PType =
+  result = nil
   var tm = typeMap
   while tm != nil:
-    result = PType(idTableGet(tm.topLayer, key))
+    result = getOrDefault(tm.topLayer, key.itemId)
     if result != nil: return
     tm = tm.nextLayer
 
 template put(typeMap: LayeredIdTable, key, value: PType) =
-  idTablePut(typeMap.topLayer, key, value)
+  typeMap.topLayer[key.itemId] = value
 
 template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code
   when false:
@@ -121,19 +118,86 @@ proc replaceTypeVarsT*(cl: var TReplTypeVars, t: PType): PType =
   result = replaceTypeVarsTAux(cl, t)
   checkMetaInvariants(cl, result)
 
-proc prepareNode(cl: var TReplTypeVars, n: PNode): PNode =
+proc prepareNode*(cl: var TReplTypeVars, n: PNode): PNode =
+  ## instantiates a given generic expression, not a type node
+  if n.kind == nkSym and n.sym.kind == skType and
+      n.sym.typ != nil and n.sym.typ.kind == tyGenericBody:
+    # generic body types are allowed as user expressions, see #24090
+    return n
   let t = replaceTypeVarsT(cl, n.typ)
   if t != nil and t.kind == tyStatic and t.n != nil:
     return if tfUnresolved in t.flags: prepareNode(cl, t.n)
            else: t.n
   result = copyNode(n)
   result.typ = t
-  if result.kind == nkSym: result.sym = replaceTypeVarsS(cl, n.sym)
-  let isCall = result.kind in nkCallKinds
-  for i in 0..<n.safeLen:
-    # XXX HACK: ``f(a, b)``, avoid to instantiate `f`
-    if isCall and i == 0: result.add(n[i])
-    else: result.add(prepareNode(cl, n[i]))
+  if result.kind == nkSym:
+    result.sym =
+      if n.typ != nil and n.typ == n.sym.typ:
+        replaceTypeVarsS(cl, n.sym, result.typ)
+      else:
+        replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
+  # we need to avoid trying to instantiate nodes that can have uninstantiated
+  # types, like generic proc symbols or raw generic type symbols
+  case n.kind
+  of nkSymChoices:
+    # don't try to instantiate symchoice symbols, they can be
+    # generic procs which the compiler will think are uninstantiated
+    # because their type will contain uninstantiated params
+    for i in 0..<n.len:
+      result.add(n[i])
+  of nkCallKinds:
+    # don't try to instantiate call names since they may be generic proc syms
+    # also bracket expressions can turn into calls with symchoice [] and
+    # we need to not instantiate the Generic in Generic[int]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    # avoid instantiating generic proc symbols, refine condition if needed:
+    let ignoreFirst = n[0].kind notin {nkDotExpr, nkBracketExpr} + nkCallKinds
+    let name = n[0].getPIdent
+    let ignoreSecond = name != nil and name.s == "[]" and n.len > 1 and
+      # generic type instantiation:
+      ((n[1].typ != nil and n[1].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[1].kind == nkSym and n[1].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    if n.len > 1:
+      if ignoreSecond:
+        result.add(n[1])
+      else:
+        result.add(prepareNode(cl, n[1]))
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkBracketExpr:
+    # don't instantiate Generic body type in expression like Generic[T]
+    # exception exists for the call name being a dot expression since
+    # dot expressions need their LHS instantiated
+    assert n.len != 0
+    let ignoreFirst = n[0].kind != nkDotExpr and
+      # generic type instantiation:
+      ((n[0].typ != nil and n[0].typ.kind == tyTypeDesc) or
+        # generic proc instantiation:
+        (n[0].kind == nkSym and n[0].sym.isGenericRoutineStrict))
+    if ignoreFirst:
+      result.add(n[0])
+    else:
+      result.add(prepareNode(cl, n[0]))
+    for i in 1..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  of nkDotExpr:
+    # don't try to instantiate RHS of dot expression, it can outright be
+    # undeclared, but definitely instantiate LHS
+    assert n.len >= 2
+    result.add(prepareNode(cl, n[0]))
+    result.add(n[1])
+    for i in 2..<n.len:
+      result.add(prepareNode(cl, n[i]))
+  else:
+    for i in 0..<n.safeLen:
+      result.add(prepareNode(cl, n[i]))
 
 proc isTypeParam(n: PNode): bool =
   # XXX: generic params should use skGenericParam instead of skType
@@ -141,27 +205,28 @@ proc isTypeParam(n: PNode): bool =
          (n.sym.kind == skGenericParam or
            (n.sym.kind == skType and sfFromGeneric in n.sym.flags))
 
-proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
-  # This is needed for tgenericshardcases
-  # It's possible that a generic param will be used in a proc call to a
-  # typedesc accepting proc. After generic param substitution, such procs
-  # should be optionally instantiated with the correct type. In order to
-  # perform this instantiation, we need to re-run the generateInstance path
-  # in the compiler, but it's quite complicated to do so at the moment so we
-  # resort to a mild hack; the head symbol of the call is temporary reset and
-  # overload resolution is executed again (which may trigger generateInstance).
-  if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
-    var needsFixing = false
-    for i in 1..<n.safeLen:
-      if isTypeParam(n[i]): needsFixing = true
-    if needsFixing:
-      n[0] = newSymNode(n[0].sym.owner)
-      return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
-
-  for i in 0..<n.safeLen:
-    n[i] = reResolveCallsWithTypedescParams(cl, n[i])
-
-  return n
+when false: # old workaround
+  proc reResolveCallsWithTypedescParams(cl: var TReplTypeVars, n: PNode): PNode =
+    # This is needed for tuninstantiatedgenericcalls
+    # It's possible that a generic param will be used in a proc call to a
+    # typedesc accepting proc. After generic param substitution, such procs
+    # should be optionally instantiated with the correct type. In order to
+    # perform this instantiation, we need to re-run the generateInstance path
+    # in the compiler, but it's quite complicated to do so at the moment so we
+    # resort to a mild hack; the head symbol of the call is temporary reset and
+    # overload resolution is executed again (which may trigger generateInstance).
+    if n.kind in nkCallKinds and sfFromGeneric in n[0].sym.flags:
+      var needsFixing = false
+      for i in 1..<n.safeLen:
+        if isTypeParam(n[i]): needsFixing = true
+      if needsFixing:
+        n[0] = newSymNode(n[0].sym.owner)
+        return cl.c.semOverloadedCall(cl.c, n, n, {skProc, skFunc}, {})
+
+    for i in 0..<n.safeLen:
+      n[i] = reResolveCallsWithTypedescParams(cl, n[i])
+
+    return n
 
 proc replaceObjBranches(cl: TReplTypeVars, n: PNode): PNode =
   result = n
@@ -193,10 +258,31 @@ proc replaceObjBranches(cl: TReplTypeVars, n: PNode): PNode =
     for i in 0..<n.len:
       n[i] = replaceObjBranches(cl, n[i])
 
-proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
+proc hasValuelessStatics(n: PNode): bool =
+  # We should only attempt to call an expression that has no tyStatics
+  # As those are unresolved generic parameters, which means in the following
+  # The compiler attempts to do `T == 300` which errors since the typeclass `MyThing` lacks a parameter
+  #[
+    type MyThing[T: static int] = object
+      when T == 300:
+        a
+    proc doThing(_: MyThing)
+  ]#
+  if n.safeLen == 0 and n.kind != nkEmpty: # Some empty nodes can get in here
+    n.typ == nil or n.typ.kind == tyStatic
+  else:
+    for x in n:
+      if hasValuelessStatics(x):
+        return true
+    false
+
+proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0; expectedType: PType = nil): PNode =
   if n == nil: return
   result = copyNode(n)
   if n.typ != nil:
+    if n.typ.kind == tyFromExpr:
+      # type of node should not be evaluated as a static value
+      n.typ.flags.incl tfNonConstExpr
     result.typ = replaceTypeVarsT(cl, n.typ)
     checkMetaInvariants(cl, result.typ)
   case n.kind
@@ -204,8 +290,13 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
     discard
   of nkOpenSymChoice, nkClosedSymChoice: result = n
   of nkSym:
-    result.sym = replaceTypeVarsS(cl, n.sym)
-    if result.sym.typ.kind == tyVoid:
+    result.sym =
+      if n.typ != nil and n.typ == n.sym.typ:
+        replaceTypeVarsS(cl, n.sym, result.typ)
+      else:
+        replaceTypeVarsS(cl, n.sym, replaceTypeVarsT(cl, n.sym.typ))
+    # sym type can be nil if was gensym created by macro, see #24048
+    if result.sym.typ != nil and result.sym.typ.kind == tyVoid:
       # don't add the 'void' field
       result = newNodeI(nkRecList, n.info)
   of nkRecWhen:
@@ -217,10 +308,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
       of nkElifBranch:
         checkSonsLen(it, 2, cl.c.config)
         var cond = prepareNode(cl, it[0])
-        var e = cl.c.semConstExpr(cl.c, cond)
-        if e.kind != nkIntLit:
-          internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
-        if e.intVal != 0 and branch == nil: branch = it[1]
+        if not cond.hasValuelessStatics:
+          var e = cl.c.semConstExpr(cl.c, cond)
+          if e.kind != nkIntLit:
+            internalError(cl.c.config, e.info, "ReplaceTypeVarsN: when condition not a bool")
+          if e.intVal != 0 and branch == nil: branch = it[1]
       of nkElse:
         checkSonsLen(it, 1, cl.c.config)
         if branch == nil: branch = it[0]
@@ -231,10 +323,11 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
       result = newNodeI(nkRecList, n.info)
   of nkStaticExpr:
     var n = prepareNode(cl, n)
-    n = reResolveCallsWithTypedescParams(cl, n)
+    when false:
+      n = reResolveCallsWithTypedescParams(cl, n)
     result = if cl.allowMetaTypes: n
-             else: cl.c.semExpr(cl.c, n)
-    if not cl.allowMetaTypes:
+             else: cl.c.semExpr(cl.c, n, {}, expectedType)
+    if not cl.allowMetaTypes and expectedType != nil:
       assert result.kind notin nkCallKinds
   else:
     if n.len > 0:
@@ -244,7 +337,7 @@ proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode; start=0): PNode =
       for i in start..<n.len:
         result[i] = replaceTypeVarsN(cl, n[i])
 
-proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
+proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym, t: PType): PSym =
   if s == nil: return nil
   # symbol is not our business:
   if cl.owner != nil and s.owner != cl.owner:
@@ -281,15 +374,18 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym =
       var g: G[string]
 
   ]#
-  result = copySym(s, nextSymId cl.c.idgen)
+  result = copySym(s, cl.c.idgen)
   incl(result.flags, sfFromGeneric)
   #idTablePut(cl.symMap, s, result)
   result.owner = s.owner
-  result.typ = replaceTypeVarsT(cl, s.typ)
+  result.typ = t
   if result.kind != skType:
     result.ast = replaceTypeVarsN(cl, s.ast)
 
 proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
+  if tfRetType in t.flags and t.kind == tyAnything:
+    # don't bind `auto` return type to a previous binding of `auto`
+    return nil
   result = cl.typeMap.lookup(t)
   if result == nil:
     if cl.allowMetaTypes or tfRetType in t.flags: return
@@ -307,7 +403,7 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   if cl.allowMetaTypes:
     result = t.exactReplica
   else:
-    result = copyType(t, nextTypeId(cl.c.idgen), t.owner)
+    result = copyType(t, cl.c.idgen, t.owner)
     copyTypeProps(cl.c.graph, cl.c.idgen.module, result, t)
     #cl.typeMap.topLayer.idTablePut(result, t)
 
@@ -327,13 +423,13 @@ proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
 proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # tyGenericInvocation[A, tyGenericInvocation[A, B]]
   # is difficult to handle:
-  var body = t[0]
+  var body = t.genericHead
   if body.kind != tyGenericBody:
     internalError(cl.c.config, cl.info, "no generic body")
   var header = t
   # search for some instantiation here:
   if cl.allowMetaTypes:
-    result = PType(idTableGet(cl.localCache, t))
+    result = getOrDefault(cl.localCache, t.itemId)
   else:
     result = searchInstTypes(cl.c.graph, t)
 
@@ -341,7 +437,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     when defined(reportCacheHits):
       echo "Generic instantiation cached ", typeToString(result), " for ", typeToString(t)
     return
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     var x = t[i]
     if x.kind in {tyGenericParam}:
       x = lookupTypeVar(cl, x)
@@ -363,24 +459,23 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   else:
     header = instCopyType(cl, t)
 
-  result = newType(tyGenericInst, nextTypeId(cl.c.idgen), t[0].owner)
+  result = newType(tyGenericInst, cl.c.idgen, t.genericHead.owner, son = header.genericHead)
   result.flags = header.flags
   # be careful not to propagate unnecessary flags here (don't use rawAddSon)
-  result.sons = @[header[0]]
   # ugh need another pass for deeply recursive generic types (e.g. PActor)
   # we need to add the candidate here, before it's fully instantiated for
   # recursive instantions:
   if not cl.allowMetaTypes:
     cacheTypeInst(cl.c, result)
   else:
-    idTablePut(cl.localCache, t, result)
+    cl.localCache[t.itemId] = result
 
   let oldSkipTypedesc = cl.skipTypedesc
   cl.skipTypedesc = true
 
   cl.typeMap = newTypeMapLayer(cl)
 
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     var x = replaceTypeVarsT(cl):
       if header[i].kind == tyGenericInst:
         t[i]
@@ -391,7 +486,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     propagateToOwner(header, x)
     cl.typeMap.put(body[i-1], x)
 
-  for i in 1..<t.len:
+  for i in FirstGenericParamAt..<t.kidsLen:
     # if one of the params is not concrete, we cannot do anything
     # but we already raised an error!
     rawAddSon(result, header[i], propagateHasAsgn = false)
@@ -399,7 +494,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   if body.kind == tyError:
     return
 
-  let bbody = lastSon body
+  let bbody = last body
   var newbody = replaceTypeVarsT(cl, bbody)
   cl.skipTypedesc = oldSkipTypedesc
   newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags)
@@ -413,6 +508,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   # One step is enough, because the recursive nature of
   # handleGenericInvocation will handle the alias-to-alias-to-alias case
   if newbody.isGenericAlias: newbody = newbody.skipGenericAlias
+
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.c.config, cl.info, newbody)
   if not cl.allowMetaTypes:
@@ -429,11 +525,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
         # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
         # need to look into this issue later
         assert newbody.kind in {tyRef, tyPtr}
-        if newbody.lastSon.typeInst != nil:
+        if newbody.last.typeInst != nil:
           #internalError(cl.c.config, cl.info, "ref already has a 'typeInst' field")
           discard
         else:
-          newbody.lastSon.typeInst = result
+          newbody.last.typeInst = result
     # DESTROY: adding object|opt for opt[topttree.Tree]
     # sigmatch: Formal opt[=destroy.T] real opt[topttree.Tree]
     # adding myseq for myseq[system.int]
@@ -453,25 +549,24 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
   # don't deal with '(): void':
-  if t[0] != nil and t[0].kind == tyVoid:
-    t[0] = nil
+  if t.returnType != nil and t.returnType.kind == tyVoid:
+    t.setReturnType nil
 
-  for i in 1..<t.len:
+  for i in FirstParamAt..<t.signatureLen:
     # don't touch any memory unless necessary
     if t[i].kind == tyVoid:
       var pos = i
-      for j in i+1..<t.len:
+      for j in i+1..<t.signatureLen:
         if t[j].kind != tyVoid:
           t[pos] = t[j]
           t.n[pos] = t.n[j]
           inc pos
-      setLen t.sons, pos
+      newSons t, pos
       setLen t.n.sons, pos
       break
 
 proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
-  for i in 0..<t.len:
-    let p = t[i]
+  for i, p in t.ikids:
     if p == nil: continue
     let skipped = p.skipIntLit(idgen)
     if skipped != p:
@@ -480,8 +575,8 @@ proc skipIntLiteralParams*(t: PType; idgen: IdGenerator) =
 
   # when the typeof operator is used on a static input
   # param, the results gets infected with static as well:
-  if t[0] != nil and t[0].kind == tyStatic:
-    t[0] = t[0].base
+  if t.returnType != nil and t.returnType.kind == tyStatic:
+    t.setReturnType t.returnType.skipModifier
 
 proc propagateFieldFlags(t: PType, n: PNode) =
   # This is meant for objects and tuples
@@ -499,10 +594,20 @@ proc propagateFieldFlags(t: PType, n: PNode) =
 
 proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
   template bailout =
-    if t.sym != nil and sfGeneratedType in t.sym.flags:
-      # Only consider the recursion limit if the symbol is a type with generic
-      # parameters that have not been explicitly supplied, typechecking should
-      # terminate when generic parameters are explicitly supplied.
+    if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags):
+      # In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see
+      # issue https://github.com/nim-lang/Nim/issues/20416 for more details.
+      # Fortunately for us this works for now because partial ref/ptr types are
+      # not allowed in object construction, eg.
+      #   type
+      #     Container[T] = ...
+      #     O = object
+      #      val: ref Container
+      #
+      # In the second case only consider the recursion limit if the symbol is a
+      # type with generic parameters that have not been explicitly supplied,
+      # typechecking should terminate when generic parameters are explicitly
+      # supplied.
       if cl.recursionLimit > 100:
         # bail out, see bug #2509. But note this caching is in general wrong,
         # look at this example where TwoVectors should not share the generic
@@ -510,24 +615,27 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         # type
         #   Vector[N: static[int]] = array[N, float64]
         #   TwoVectors[Na, Nb: static[int]] = (Vector[Na], Vector[Nb])
-        result = PType(idTableGet(cl.localCache, t))
+        result = getOrDefault(cl.localCache, t.itemId)
         if result != nil: return result
       inc cl.recursionLimit
 
   result = t
   if t == nil: return
 
-  if t.kind in {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses:
+  const lookupMetas = {tyStatic, tyGenericParam, tyConcept} + tyTypeClasses - {tyAnything}
+  if t.kind in lookupMetas or
+      (t.kind == tyAnything and tfRetType notin t.flags):
     let lookup = cl.typeMap.lookup(t)
     if lookup != nil: return lookup
 
   case t.kind
   of tyGenericInvocation:
     result = handleGenericInvocation(cl, t)
-    if result.lastSon.kind == tyUserTypeClass:
+    if result.last.kind == tyUserTypeClass:
       result.kind = tyUserTypeClassInst
 
   of tyGenericBody:
+    if cl.allowMetaTypes: return
     localError(
       cl.c.config,
       cl.info,
@@ -546,21 +654,25 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
     assert t.n.typ != t
     var n = prepareNode(cl, t.n)
     if n.kind != nkEmpty:
-      n = cl.c.semConstExpr(cl.c, n)
+      if tfNonConstExpr in t.flags:
+        n = cl.c.semExprWithType(cl.c, n, flags = {efInTypeof})
+      else:
+        n = cl.c.semConstExpr(cl.c, n)
     if n.typ.kind == tyTypeDesc:
       # XXX: sometimes, chained typedescs enter here.
       # It may be worth investigating why this is happening,
       # because it may cause other bugs elsewhere.
       result = n.typ.skipTypes({tyTypeDesc})
       # result = n.typ.base
+    elif tfNonConstExpr in t.flags:
+      result = n.typ
     else:
       if n.typ.kind != tyStatic and n.kind != nkType:
         # XXX: In the future, semConstExpr should
         # return tyStatic values to let anyone make
         # use of this knowledge. The patching here
         # won't be necessary then.
-        result = newTypeS(tyStatic, cl.c)
-        result.sons = @[n.typ]
+        result = newTypeS(tyStatic, cl.c, son = n.typ)
         result.n = n
       else:
         result = n.typ
@@ -576,19 +688,42 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         result = makeTypeDesc(cl.c, result)
       elif tfUnresolved in t.flags or cl.skipTypedesc:
         result = result.base
-    elif t[0].kind != tyNone:
-      result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t[0]))
+    elif t.elementType.kind != tyNone:
+      result = makeTypeDesc(cl.c, replaceTypeVarsT(cl, t.elementType))
 
-  of tyUserTypeClass, tyStatic:
+  of tyUserTypeClass:
     result = t
+  
+  of tyStatic:
+    if cl.c.matchedConcept != nil:
+      # allow concepts to not instantiate statics for now
+      # they can't always infer them
+      return
+    if not containsGenericType(t) and (t.n == nil or t.n.kind in nkLiterals):
+      # no need to instantiate
+      return
+    bailout()
+    result = instCopyType(cl, t)
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
+      var r = result[i]
+      if r != nil:
+        r = replaceTypeVarsT(cl, r)
+        result[i] = r
+        propagateToOwner(result, r)
+    result.n = replaceTypeVarsN(cl, result.n)
+    if not cl.allowMetaTypes and result.n != nil and
+        result.base.kind != tyNone:
+      result.n = cl.c.semConstExpr(cl.c, result.n)
+      result.n.typ = result.base
 
   of tyGenericInst, tyUserTypeClassInst:
     bailout()
     result = instCopyType(cl, t)
-    idTablePut(cl.localCache, t, result)
-    for i in 1..<result.len:
+    cl.localCache[t.itemId] = result
+    for i in FirstGenericParamAt..<result.kidsLen:
       result[i] = replaceTypeVarsT(cl, result[i])
-    propagateToOwner(result, result.lastSon)
+    propagateToOwner(result, result.last)
 
   else:
     if containsGenericType(t):
@@ -597,17 +732,17 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result = instCopyType(cl, t)
       result.size = -1 # needs to be recomputed
       #if not cl.allowMetaTypes:
-      idTablePut(cl.localCache, t, result)
+      cl.localCache[t.itemId] = result
 
-      for i in 0..<result.len:
-        if result[i] != nil:
-          if result[i].kind == tyGenericBody:
+      for i, resulti in result.ikids:
+        if resulti != nil:
+          if resulti.kind == tyGenericBody and not cl.allowMetaTypes:
             localError(cl.c.config, if t.sym != nil: t.sym.info else: cl.info,
               "cannot instantiate '" &
               typeToString(result[i], preferDesc) &
               "' inside of type definition: '" &
               t.owner.name.s & "'; Maybe generic arguments are missing?")
-          var r = replaceTypeVarsT(cl, result[i])
+          var r = replaceTypeVarsT(cl, resulti)
           if result.kind == tyObject:
             # carefully coded to not skip the precious tyGenericInst:
             let r2 = r.skipTypes({tyAlias, tySink, tyOwned})
@@ -620,7 +755,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result.n = replaceTypeVarsN(cl, result.n, ord(result.kind==tyProc))
       case result.kind
       of tyArray:
-        let idx = result[0]
+        let idx = result.indexType
         internalAssert cl.c.config, idx.kind != tyStatic
 
       of tyObject, tyTuple:
@@ -633,7 +768,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
         skipIntLiteralParams(result, cl.c.idgen)
 
       of tyRange:
-        result[0] = result[0].skipTypes({tyStatic, tyDistinct})
+        result.setIndexType result.indexType.skipTypes({tyStatic, tyDistinct})
 
       else: discard
     else:
@@ -642,8 +777,8 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
       result = t
 
       # Slow path, we have some work to do
-      if t.kind == tyRef and t.len > 0 and t[0].kind == tyObject and t[0].n != nil:
-        discard replaceObjBranches(cl, t[0].n)
+      if t.kind == tyRef and t.hasElementType and t.elementType.kind == tyObject and t.elementType.n != nil:
+        discard replaceObjBranches(cl, t.elementType.n)
 
       elif result.n != nil and t.kind == tyObject:
         # Invalidate the type size as we may alter its structure
@@ -652,20 +787,26 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
 
 proc initTypeVars*(p: PContext, typeMap: LayeredIdTable, info: TLineInfo;
                    owner: PSym): TReplTypeVars =
-  initIdTable(result.symMap)
-  initIdTable(result.localCache)
-  result.typeMap = typeMap
-  result.info = info
-  result.c = p
-  result.owner = owner
-
-proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode;
-                         owner: PSym, allowMetaTypes = false): PNode =
+  result = TReplTypeVars(symMap: initSymMapping(),
+            localCache: initTypeMapping(), typeMap: typeMap,
+            info: info, c: p, owner: owner)
+
+proc replaceTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
+                         owner: PSym, allowMetaTypes = false,
+                         fromStaticExpr = false, expectedType: PType = nil): PNode =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, typeMap, n.info, owner)
   cl.allowMetaTypes = allowMetaTypes
   pushInfoContext(p.config, n.info)
-  result = replaceTypeVarsN(cl, n)
+  result = replaceTypeVarsN(cl, n, expectedType = expectedType)
+  popInfoContext(p.config)
+
+proc prepareTypesInBody*(p: PContext, pt: TypeMapping, n: PNode;
+                         owner: PSym = nil): PNode =
+  var typeMap = initLayeredTypeMap(pt)
+  var cl = initTypeVars(p, typeMap, n.info, owner)
+  pushInfoContext(p.config, n.info)
+  result = prepareNode(cl, n)
   popInfoContext(p.config)
 
 when false:
@@ -680,8 +821,8 @@ when false:
     popInfoContext(p.config)
 
 proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
-  if t != nil and t.len > 0 and t[0] != nil:
-    let b = skipTypes(t[0], skipPtrs)
+  if t != nil and t.baseClass != nil:
+    let b = skipTypes(t.baseClass, skipPtrs)
     recomputeFieldPositions(b, b.n, currPosition)
   case obj.kind
   of nkRecList:
@@ -695,7 +836,7 @@ proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) =
     inc currPosition
   else: discard "cannot happen"
 
-proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
+proc generateTypeInstance*(p: PContext, pt: TypeMapping, info: TLineInfo,
                            t: PType): PType =
   # Given `t` like Foo[T]
   # pt: Table with type mappings: T -> int
@@ -711,7 +852,7 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
     var position = 0
     recomputeFieldPositions(objType, objType.n, position)
 
-proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
+proc prepareMetatypeForSigmatch*(p: PContext, pt: TypeMapping, info: TLineInfo,
                                  t: PType): PType =
   var typeMap = initLayeredTypeMap(pt)
   var cl = initTypeVars(p, typeMap, info, nil)
@@ -720,6 +861,6 @@ proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
   result = replaceTypeVarsT(cl, t)
   popInfoContext(p.config)
 
-template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
+template generateTypeInstance*(p: PContext, pt: TypeMapping, arg: PNode,
                                t: PType): untyped =
   generateTypeInstance(p, pt, arg.info, t)
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index f58c9c3ef..d8dfe1828 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -9,9 +9,12 @@
 
 ## Computes hash values for routine (proc, method etc) signatures.
 
-import ast, tables, ropes, md5_old, modulegraphs
-from hashes import Hash
+import ast, ropes, modulegraphs, options, msgs, pathutils
+from std/hashes import Hash
+import std/tables
 import types
+import ../dist/checksums/src/checksums/md5
+
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -20,7 +23,7 @@ when defined(nimPreviewSlimSystem):
 proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
 proc `&=`(c: var MD5Context, ch: char) =
   # XXX suspicious code here; relies on ch being zero terminated?
-  md5Update(c, unsafeAddr ch, 1)
+  md5Update(c, cast[cstring](unsafeAddr ch), 1)
 
 proc `&=`(c: var MD5Context, i: BiggestInt) =
   md5Update(c, cast[cstring](unsafeAddr i), sizeof(i))
@@ -42,8 +45,7 @@ type
     CoDistinct
     CoHashTypeInsideNode
 
-proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag])
-
+proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef)
 proc hashSym(c: var MD5Context, s: PSym) =
   if sfAnon in s.flags or s.kind == skGenericParam:
     c &= ":anon"
@@ -53,21 +55,26 @@ proc hashSym(c: var MD5Context, s: PSym) =
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
-proc hashTypeSym(c: var MD5Context, s: PSym) =
+proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) =
   if sfAnon in s.flags or s.kind == skGenericParam:
     c &= ":anon"
   else:
     var it = s
+    c &= customPath(conf.toFullPath(s.info))
     while it != nil:
       if sfFromGeneric in it.flags and it.kind in routineKinds and
           it.typ != nil:
-        hashType c, it.typ, {CoProc}
+        hashType c, it.typ, {CoProc}, conf
       c &= it.name.s
       c &= "."
       it = it.owner
+    c &= "#"
+    c &= s.disamb
 
-proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) =
+proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) =
   if n == nil:
     c &= "\255"
     return
@@ -82,7 +89,7 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) =
   of nkSym:
     hashSym(c, n.sym)
     if CoHashTypeInsideNode in flags and n.sym.typ != nil:
-      hashType(c, n.sym.typ, flags)
+      hashType(c, n.sym.typ, flags, conf)
   of nkCharLit..nkUInt64Lit:
     let v = n.intVal
     lowlevel v
@@ -92,24 +99,24 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) =
   of nkStrLit..nkTripleStrLit:
     c &= n.strVal
   else:
-    for i in 0..<n.len: hashTree(c, n[i], flags)
+    for i in 0..<n.len: hashTree(c, n[i], flags, conf)
 
-proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
+proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef) =
   if t == nil:
     c &= "\254"
     return
 
   case t.kind
   of tyGenericInvocation:
-    for i in 0..<t.len:
-      c.hashType t[i], flags
+    for a in t.kids:
+      c.hashType a, flags, conf
   of tyDistinct:
     if CoDistinct in flags:
       if t.sym != nil: c.hashSym(t.sym)
       if t.sym == nil or tfFromGeneric in t.flags:
-        c.hashType t.lastSon, flags
+        c.hashType t.elementType, flags, conf
     elif CoType in flags or t.sym == nil:
-      c.hashType t.lastSon, flags
+      c.hashType t.elementType, flags, conf
     else:
       c.hashSym(t.sym)
   of tyGenericInst:
@@ -118,16 +125,17 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
       # We cannot trust the `lastSon` to hold a properly populated and unique
       # value for each instantiation, so we hash the generic parameters here:
       let normalizedType = t.skipGenericAlias
-      for i in 0..<normalizedType.len - 1:
-        c.hashType t[i], flags
+      c.hashType normalizedType.genericHead, flags, conf
+      for _, a in normalizedType.genericInstParams:
+        c.hashType a, flags, conf
     else:
-      c.hashType t.lastSon, flags
+      c.hashType t.skipModifier, flags, conf
   of tyAlias, tySink, tyUserTypeClasses, tyInferred:
-    c.hashType t.lastSon, flags
+    c.hashType t.skipModifier, flags, conf
   of tyOwned:
     if CoConsiderOwned in flags:
       c &= char(t.kind)
-    c.hashType t.lastSon, flags
+    c.hashType t.skipModifier, flags, conf
   of tyBool, tyChar, tyInt..tyUInt64:
     # no canonicalization for integral types, so that e.g. ``pid_t`` is
     # produced instead of ``NI``:
@@ -140,8 +148,9 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
       let inst = t.typeInst
       t.typeInst = nil
       assert inst.kind == tyGenericInst
-      for i in 0..<inst.len - 1:
-        c.hashType inst[i], flags
+      c.hashType inst.genericHead, flags, conf
+      for _, a in inst.genericInstParams:
+        c.hashType a, flags, conf
       t.typeInst = inst
       return
     c &= char(t.kind)
@@ -149,15 +158,15 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
     # is actually safe without an infinite recursion check:
     if t.sym != nil:
       if {sfCompilerProc} * t.sym.flags != {}:
-        doAssert t.sym.loc.r != ""
+        doAssert t.sym.loc.snippet != ""
         # The user has set a specific name for this type
-        c &= t.sym.loc.r
+        c &= t.sym.loc.snippet
       elif CoOwnerSig in flags:
-        c.hashTypeSym(t.sym)
+        c.hashTypeSym(t.sym, conf)
       else:
         c.hashSym(t.sym)
 
-      var symWithFlags: PSym
+      var symWithFlags: PSym = nil
       template hasFlag(sym): bool =
         let ret = {sfAnon, sfGenSym} * sym.flags != {}
         if ret: symWithFlags = sym
@@ -172,7 +181,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
           # xxx instead, use a hash table to indicate we've already visited a type, which
           # would also be more efficient.
           symWithFlags.flags.excl {sfAnon, sfGenSym}
-          hashTree(c, t.n, flags + {CoHashTypeInsideNode})
+          hashTree(c, t.n, flags + {CoHashTypeInsideNode}, conf)
           symWithFlags.flags = oldFlags
         else:
           # The object has no fields: we _must_ add something here in order to
@@ -181,36 +190,40 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
           c &= ".empty"
     else:
       c &= t.id
-    if t.len > 0 and t[0] != nil:
-      hashType c, t[0], flags
-  of tyRef, tyPtr, tyGenericBody, tyVar:
+    if t.hasElementType and t.baseClass != nil:
+      hashType c, t.baseClass, flags, conf
+  of tyRef, tyPtr, tyVar:
     c &= char(t.kind)
-    c.hashType t.lastSon, flags
+    if t.hasElementType:
+      c.hashType t.elementType, flags, conf
     if tfVarIsPtr in t.flags: c &= ".varisptr"
+  of tyGenericBody:
+    c &= char(t.kind)
+    if t.hasElementType:
+      c.hashType t.typeBodyImpl, flags, conf
   of tyFromExpr:
     c &= char(t.kind)
-    c.hashTree(t.n, {})
+    c.hashTree(t.n, {}, conf)
   of tyTuple:
     c &= char(t.kind)
     if t.n != nil and CoType notin flags:
-      assert(t.n.len == t.len)
       for i in 0..<t.n.len:
         assert(t.n[i].kind == nkSym)
         c &= t.n[i].sym.name.s
         c &= ':'
-        c.hashType(t[i], flags+{CoIgnoreRange})
+        c.hashType(t.n[i].sym.typ, flags+{CoIgnoreRange}, conf)
         c &= ','
     else:
-      for i in 0..<t.len: c.hashType t[i], flags+{CoIgnoreRange}
+      for a in t.kids: c.hashType a, flags+{CoIgnoreRange}, conf
   of tyRange:
     if CoIgnoreRange notin flags:
       c &= char(t.kind)
-      c.hashTree(t.n, {})
-    c.hashType(t[0], flags)
+      c.hashTree(t.n, {}, conf)
+    c.hashType(t.elementType, flags, conf)
   of tyStatic:
     c &= char(t.kind)
-    c.hashTree(t.n, {})
-    c.hashType(t[0], flags)
+    c.hashTree(t.n, {}, conf)
+    c.hashType(t.skipModifier, flags, conf)
   of tyProc:
     c &= char(t.kind)
     c &= (if tfIterator in t.flags: "iterator " else: "proc ")
@@ -220,11 +233,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
         let param = params[i].sym
         c &= param.name.s
         c &= ':'
-        c.hashType(param.typ, flags)
+        c.hashType(param.typ, flags, conf)
         c &= ','
-      c.hashType(t[0], flags)
+      c.hashType(t.returnType, flags, conf)
     else:
-      for i in 0..<t.len: c.hashType(t[i], flags)
+      for a in t.signature: c.hashType(a, flags, conf)
     c &= char(t.callConv)
     # purity of functions doesn't have to affect the mangling (which is in fact
     # problematic for HCR - someone could have cached a pointer to another
@@ -236,10 +249,11 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
     if tfVarargs in t.flags: c &= ".varargs"
   of tyArray:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t[i], flags-{CoIgnoreRange})
+    c.hashType(t.indexType, flags-{CoIgnoreRange}, conf)
+    c.hashType(t.elementType, flags-{CoIgnoreRange}, conf)
   else:
     c &= char(t.kind)
-    for i in 0..<t.len: c.hashType(t[i], flags)
+    for a in t.kids: c.hashType(a, flags, conf)
   if tfNotNil in t.flags and CoType notin flags: c &= "not nil"
 
 when defined(debugSigHashes):
@@ -256,19 +270,21 @@ when defined(debugSigHashes):
   #  select hash, type from sighashes where hash in
   # (select hash from sighashes group by hash having count(*) > 1) order by hash;
 
-proc hashType*(t: PType; flags: set[ConsiderFlag] = {CoType}): SigHash =
-  var c: MD5Context
+proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash =
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
-  hashType c, t, flags+{CoOwnerSig}
+  hashType c, t, flags+{CoOwnerSig}, conf
   md5Final c, result.MD5Digest
   when defined(debugSigHashes):
     db.exec(sql"INSERT OR IGNORE INTO sighashes(type, hash) VALUES (?, ?)",
             typeToString(t), $result)
 
-proc hashProc*(s: PSym): SigHash =
-  var c: MD5Context
+proc hashProc(s: PSym; conf: ConfigRef): SigHash =
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
-  hashType c, s.typ, {CoProc}
+  hashType c, s.typ, {CoProc}, conf
 
   var m = s
   while m.kind != skModule: m = m.owner
@@ -286,7 +302,8 @@ proc hashProc*(s: PSym): SigHash =
   md5Final c, result.MD5Digest
 
 proc hashNonProc*(s: PSym): SigHash =
-  var c: MD5Context
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
   hashSym(c, s)
   var it = s
@@ -302,7 +319,8 @@ proc hashNonProc*(s: PSym): SigHash =
   md5Final c, result.MD5Digest
 
 proc hashOwner*(s: PSym): SigHash =
-  var c: MD5Context
+  result = default(SigHash)
+  var c: MD5Context = default(MD5Context)
   md5Init c
   var m = s
   while m.kind != skModule: m = m.owner
@@ -314,9 +332,9 @@ proc hashOwner*(s: PSym): SigHash =
 
   md5Final c, result.MD5Digest
 
-proc sigHash*(s: PSym): SigHash =
+proc sigHash*(s: PSym; conf: ConfigRef): SigHash =
   if s.kind in routineKinds and s.typ != nil:
-    result = hashProc(s)
+    result = hashProc(s, conf)
   else:
     result = hashNonProc(s)
 
@@ -333,7 +351,7 @@ proc hashVarSymBody(graph: ModuleGraph, c: var MD5Context, s: PSym) =
     c &= hashNonProc(s)
     # this one works for let and const but not for var. True variables can change value
     # later on. it is user resposibility to hash his global state if required
-    if s.ast != nil and s.ast.kind == nkIdentDefs:
+    if s.ast != nil and s.ast.kind in {nkIdentDefs, nkConstDef}:
       hashBodyTree(graph, c, s.ast[^1])
     else:
       hashBodyTree(graph, c, s.ast)
@@ -371,13 +389,13 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
   ## compute unique digest of the proc/func/method symbols
   ## recursing into invoked symbols as well
   assert(sym.kind in skProcKinds, $sym.kind)
-
+  result = default(SigHash)
   graph.symBodyHashes.withValue(sym.id, value):
     return value[]
 
-  var c: MD5Context
+  var c: MD5Context = default(MD5Context)
   md5Init(c)
-  c.hashType(sym.typ, {CoProc})
+  c.hashType(sym.typ, {CoProc}, graph.config)
   c &= char(sym.kind)
   c.md5Final(result.MD5Digest)
   graph.symBodyHashes[sym.id] = result # protect from recursion in the body
@@ -390,12 +408,12 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash =
     graph.symBodyHashes[sym.id] = result
 
 proc idOrSig*(s: PSym, currentModule: string,
-              sigCollisions: var CountTable[SigHash]): Rope =
+              sigCollisions: var CountTable[SigHash]; conf: ConfigRef): Rope =
   if s.kind in routineKinds and s.typ != nil:
     # signatures for exported routines are reliable enough to
     # produce a unique name and this means produced C++ is more stable regarding
     # Nim changes:
-    let sig = hashProc(s)
+    let sig = hashProc(s, conf)
     result = rope($sig)
     #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m
     let counter = sigCollisions.getOrDefault(sig)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 290b9c8db..6ea2c7bb5 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -11,17 +11,20 @@
 ## the call to overloaded procs, generic procs and operators.
 
 import
-  intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
-  magicsys, idents, lexer, options, parampatterns, strutils, trees,
+  ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
+  magicsys, idents, lexer, options, parampatterns, trees,
   linter, lineinfos, lowerings, modulegraphs, concepts
 
+import std/[intsets, strutils, tables]
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 type
   MismatchKind* = enum
     kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
-    kMissingParam, kExtraArg, kPositionalAlreadyGiven
+    kMissingParam, kExtraArg, kPositionalAlreadyGiven,
+    kGenericParamTypeMismatch, kMissingGenericParam, kExtraGenericParam
 
   MismatchInfo* = object
     kind*: MismatchKind # reason for mismatch
@@ -53,12 +56,12 @@ type
     calleeScope*: int        # scope depth:
                              # is this a top-level symbol or a nested proc?
     call*: PNode             # modified call
-    bindings*: TIdTable      # maps types to types
+    bindings*: TypeMapping   # maps types to types
     magic*: TMagic           # magic of operation
     baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                              # for example
-    fauxMatch*: TTypeKind    # the match was successful only due to the use
-                             # of error or wildcard (unknown) types.
+    matchedErrorType*: bool  # match is considered successful after matching
+                             # error type to avoid cascading errors
                              # this is used to prevent instantiations.
     genericConverter*: bool  # true if a generic converter needs to
                              # be instantiated
@@ -78,7 +81,8 @@ type
                               # or when the explain pragma is used. may be
                               # triggered with an idetools command in the
                               # future.
-    inheritancePenalty: int   # to prefer closest father object type
+                              # to prefer closest father object type
+    inheritancePenalty: int
     firstMismatch*: MismatchInfo # mismatch info for better error messages
     diagnosticsEnabled*: bool
 
@@ -86,45 +90,38 @@ type
     trDontBind
     trNoCovariance
     trBindGenericParam  # bind tyGenericParam even with trDontBind
+    trIsOutParam
 
   TTypeRelFlags* = set[TTypeRelFlag]
 
 
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
+  maxInheritancePenalty = high(int) div 2
 
-proc markUsed*(c: PContext; info: TLineInfo, s: PSym)
+proc markUsed*(c: PContext; info: TLineInfo, s: PSym; checkStyle = true)
 proc markOwnerModuleAsUsed*(c: PContext; s: PSym)
 
-template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
-
 proc initCandidateAux(ctx: PContext,
-                      c: var TCandidate, callee: PType) {.inline.} =
-  c.c = ctx
-  c.exactMatches = 0
-  c.subtypeMatches = 0
-  c.convMatches = 0
-  c.intConvMatches = 0
-  c.genericMatches = 0
-  c.state = csEmpty
-  c.firstMismatch = MismatchInfo()
-  c.callee = callee
-  c.call = nil
-  c.baseTypeMatch = false
-  c.genericConverter = false
-  c.inheritancePenalty = 0
-
-proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
-  initCandidateAux(ctx, c, callee)
-  c.calleeSym = nil
-  initIdTable(c.bindings)
+                      callee: PType): TCandidate {.inline.} =
+  result = TCandidate(c: ctx, exactMatches: 0, subtypeMatches: 0,
+                      convMatches: 0, intConvMatches: 0, genericMatches: 0,
+                      state: csEmpty, firstMismatch: MismatchInfo(),
+                      callee: callee, call: nil, baseTypeMatch: false,
+                      genericConverter: false, inheritancePenalty: -1
+  )
+
+proc initCandidate*(ctx: PContext, callee: PType): TCandidate =
+  result = initCandidateAux(ctx, callee)
+  result.calleeSym = nil
+  result.bindings = initTypeMapping()
 
 proc put(c: var TCandidate, key, val: PType) {.inline.} =
   ## Given: proc foo[T](x: T); foo(4)
   ## key: 'T'
   ## val: 'int' (typeof(4))
   when false:
-    let old = PType(idTableGet(c.bindings, key))
+    let old = idTableGet(c.bindings, key)
     if old != nil:
       echo "Putting ", typeToString(key), " ", typeToString(val), " and old is ", typeToString(old)
       if typeToString(old) == "float32":
@@ -133,78 +130,174 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} =
       echo "binding ", key, " -> ", val
   idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen))
 
-proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
+proc typeRel*(c: var TCandidate, f, aOrig: PType,
+              flags: TTypeRelFlags = {}): TTypeRelation
+
+proc matchGenericParam(m: var TCandidate, formal: PType, n: PNode) =
+  var arg = n.typ
+  if m.c.inGenericContext > 0:
+    # don't match yet-unresolved generic instantiations
+    while arg != nil and arg.kind == tyGenericParam:
+      arg = idTableGet(m.bindings, arg)
+    if arg == nil or arg.containsUnresolvedType:
+      m.state = csNoMatch
+      return
+  # fix up the type to get ready to match formal:
+  var formalBase = formal
+  while formalBase.kind == tyGenericParam and
+      formalBase.genericParamHasConstraints:
+    formalBase = formalBase.genericConstraint
+  if formalBase.kind == tyStatic and arg.kind != tyStatic:
+    # maybe call `paramTypesMatch` here, for now be conservative
+    if n.kind in nkSymChoices: n.flags.excl nfSem
+    let evaluated = m.c.semTryConstExpr(m.c, n, formalBase.skipTypes({tyStatic}))
+    if evaluated != nil:
+      arg = newTypeS(tyStatic, m.c, son = evaluated.typ)
+      arg.n = evaluated
+  elif formalBase.kind == tyTypeDesc:
+    if arg.kind != tyTypeDesc:
+      arg = makeTypeDesc(m.c, arg)
+  else:
+    arg = arg.skipTypes({tyTypeDesc})
+  let tm = typeRel(m, formal, arg)
+  if tm in {isNone, isConvertible}:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kGenericParamTypeMismatch
+    return
+
+proc matchGenericParams*(m: var TCandidate, binding: PNode, callee: PSym) =
+  ## matches explicit generic instantiation `binding` against generic params of
+  ## proc symbol `callee`
+  ## state is set to `csMatch` if all generic params match, `csEmpty` if
+  ## implicit generic parameters are missing (matches but cannot instantiate),
+  ## `csNoMatch` if a constraint fails or param count doesn't match
+  let c = m.c
+  let typeParams = callee.ast[genericParamsPos]
+  let paramCount = typeParams.len
+  let bindingCount = binding.len-1
+  if bindingCount > paramCount:
+    m.state = csNoMatch
+    m.firstMismatch.kind = kExtraGenericParam
+    m.firstMismatch.arg = paramCount + 1
+    return
+  for i in 1..bindingCount:
+    matchGenericParam(m, typeParams[i-1].typ, binding[i])
+    if m.state == csNoMatch:
+      m.firstMismatch.arg = i
+      m.firstMismatch.formal = typeParams[i-1].sym
+      return
+  # not enough generic params given, check if remaining have defaults:
+  for i in bindingCount ..< paramCount:
+    let param = typeParams[i]
+    assert param.kind == nkSym
+    let paramSym = param.sym
+    if paramSym.ast != nil:
+      matchGenericParam(m, param.typ, paramSym.ast)
+      if m.state == csNoMatch:
+        m.firstMismatch.arg = i + 1
+        m.firstMismatch.formal = paramSym
+        return
+    elif tfImplicitTypeParam in paramSym.typ.flags:
+      # not a mismatch, but can't create sym
+      m.state = csEmpty
+      return
+    else:
+      m.state = csNoMatch
+      m.firstMismatch.kind = kMissingGenericParam
+      m.firstMismatch.arg = i + 1
+      m.firstMismatch.formal = paramSym
+      return
+  m.state = csMatch
+
+proc copyingEraseVoidParams(m: TCandidate, t: var PType) =
+  ## if `t` is a proc type with void parameters, copies it and erases them
+  assert t.kind == tyProc
+  let original = t
+  var copied = false
+  for i in 1 ..< original.len:
+    var f = original[i]
+    var isVoidParam = f.kind == tyVoid
+    if not isVoidParam:
+      let prev = idTableGet(m.bindings, f)
+      if prev != nil: f = prev
+      isVoidParam = f.kind == tyVoid
+    if isVoidParam:
+      if not copied:
+        # keep first i children
+        t = copyType(original, m.c.idgen, t.owner)
+        t.setSonsLen(i)
+        t.n = copyNode(original.n)
+        t.n.sons = original.n.sons
+        t.n.sons.setLen(i)
+        copied = true
+    elif copied:
+      t.add(f)
+      t.n.add(original.n[i])
+
+proc initCandidate*(ctx: PContext, callee: PSym,
                     binding: PNode, calleeScope = -1,
-                    diagnosticsEnabled = false) =
-  initCandidateAux(ctx, c, callee.typ)
-  c.calleeSym = callee
+                    diagnosticsEnabled = false): TCandidate =
+  result = initCandidateAux(ctx, callee.typ)
+  result.calleeSym = callee
   if callee.kind in skProcKinds and calleeScope == -1:
-    if callee.originatingModule == ctx.module:
-      c.calleeScope = 2
-      var owner = callee
-      while true:
-        owner = owner.skipGenericOwner
-        if owner.kind == skModule: break
-        inc c.calleeScope
-    else:
-      c.calleeScope = 1
+    result.calleeScope = cmpScopes(ctx, callee)
   else:
-    c.calleeScope = calleeScope
-  c.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
-  c.diagnosticsEnabled = diagnosticsEnabled
-  c.magic = c.calleeSym.magic
-  initIdTable(c.bindings)
+    result.calleeScope = calleeScope
+  result.diagnostics = @[] # if diagnosticsEnabled: @[] else: nil
+  result.diagnosticsEnabled = diagnosticsEnabled
+  result.magic = result.calleeSym.magic
+  result.bindings = initTypeMapping()
   if binding != nil and callee.kind in routineKinds:
-    var typeParams = callee.ast[genericParamsPos]
-    for i in 1..min(typeParams.len, binding.len-1):
-      var formalTypeParam = typeParams[i-1].typ
-      var bound = binding[i].typ
-      if bound != nil:
-        if formalTypeParam.kind == tyTypeDesc:
-          if bound.kind != tyTypeDesc:
-            bound = makeTypeDesc(ctx, bound)
-        else:
-          bound = bound.skipTypes({tyTypeDesc})
-        put(c, formalTypeParam, bound)
+    matchGenericParams(result, binding, callee)
+    let genericMatch = result.state
+    if genericMatch != csNoMatch:
+      result.state = csEmpty
+      if genericMatch == csMatch: # csEmpty if not fully instantiated
+        # instantiate the type, emulates old compiler behavior
+        # wouldn't be needed if sigmatch could handle complex cases,
+        # examples are in texplicitgenerics
+        # might be buggy, see rest of generateInstance if problems occur
+        let typ = ctx.instantiateOnlyProcType(ctx, result.bindings, callee, binding.info)
+        result.callee = typ
+      else:
+        # createThread[void] requires this if the above branch is removed:
+        copyingEraseVoidParams(result, result.callee)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
                    binding: PNode, calleeScope = -1): TCandidate =
-  initCandidate(ctx, result, callee, binding, calleeScope)
+  result = initCandidate(ctx, callee, binding, calleeScope)
 
 proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
-  initCandidate(ctx, result, callee)
-
-proc copyCandidate(a: var TCandidate, b: TCandidate) =
-  a.c = b.c
-  a.exactMatches = b.exactMatches
-  a.subtypeMatches = b.subtypeMatches
-  a.convMatches = b.convMatches
-  a.intConvMatches = b.intConvMatches
-  a.genericMatches = b.genericMatches
-  a.state = b.state
-  a.callee = b.callee
-  a.calleeSym = b.calleeSym
-  a.call = copyTree(b.call)
-  a.baseTypeMatch = b.baseTypeMatch
-  copyIdTable(a.bindings, b.bindings)
-
-proc typeRel*(c: var TCandidate, f, aOrig: PType,
-              flags: TTypeRelFlags = {}): TTypeRelation
+  result = initCandidate(ctx, callee)
+
+proc copyCandidate(dest: var TCandidate, src: TCandidate) =
+  dest.c = src.c
+  dest.exactMatches = src.exactMatches
+  dest.subtypeMatches = src.subtypeMatches
+  dest.convMatches = src.convMatches
+  dest.intConvMatches = src.intConvMatches
+  dest.genericMatches = src.genericMatches
+  dest.state = src.state
+  dest.callee = src.callee
+  dest.calleeSym = src.calleeSym
+  dest.call = copyTree(src.call)
+  dest.baseTypeMatch = src.baseTypeMatch
+  dest.bindings = src.bindings
 
 proc checkGeneric(a, b: TCandidate): int =
   let c = a.c
   let aa = a.callee
   let bb = b.callee
   var winner = 0
-  for i in 1..<min(aa.len, bb.len):
-    var ma = newCandidate(c, bb[i])
-    let tra = typeRel(ma, bb[i], aa[i], {trDontBind})
-    var mb = newCandidate(c, aa[i])
-    let trb = typeRel(mb, aa[i], bb[i], {trDontBind})
-    if tra == isGeneric and trb == isNone:
+  for aai, bbi in underspecifiedPairs(aa, bb, 1):
+    var ma = newCandidate(c, bbi)
+    let tra = typeRel(ma, bbi, aai, {trDontBind})
+    var mb = newCandidate(c, aai)
+    let trb = typeRel(mb, aai, bbi, {trDontBind})
+    if tra == isGeneric and trb in {isNone, isInferred, isInferredConvertible}:
       if winner == -1: return 0
       winner = 1
-    if trb == isGeneric and tra == isNone:
+    if trb == isGeneric and tra in {isNone, isInferred, isInferredConvertible}:
       if winner == 1: return 0
       winner = -1
   result = winner
@@ -213,53 +306,65 @@ proc sumGeneric(t: PType): int =
   # count the "genericness" so that Foo[Foo[T]] has the value 3
   # and Foo[T] has the value 2 so that we know Foo[Foo[T]] is more
   # specific than Foo[T].
+  result = 0
   var t = t
-  var isvar = 1
   while true:
     case t.kind
-    of tyGenericInst, tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
-        tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody,
-        tyLent, tyOwned:
-      t = t.lastSon
+    of tyAlias, tySink, tyNot: t = t.skipModifier
+    of tyArray, tyRef, tyPtr, tyDistinct, tyUncheckedArray,
+        tyOpenArray, tyVarargs, tySet, tyRange, tySequence,
+        tyLent, tyOwned, tyVar:
+      t = t.elementType
+      inc result
+    of tyBool, tyChar, tyEnum, tyObject, tyPointer, tyVoid,
+        tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
+        tyUInt..tyUInt64, tyCompositeTypeClass, tyBuiltInTypeClass:
+      inc result
+      break
+    of tyGenericBody:
+      t = t.typeBodyImpl
+    of tyGenericInst, tyStatic:
+      t = t.skipModifier
       inc result
     of tyOr:
       var maxBranch = 0
-      for branch in t.sons:
+      for branch in t.kids:
         let branchSum = sumGeneric(branch)
         if branchSum > maxBranch: maxBranch = branchSum
       inc result, maxBranch
       break
-    of tyVar:
-      t = t[0]
-      inc result
-      inc isvar
     of tyTypeDesc:
-      t = t.lastSon
+      t = t.elementType
       if t.kind == tyEmpty: break
       inc result
-    of tyGenericInvocation, tyTuple, tyProc, tyAnd:
-      result += ord(t.kind in {tyGenericInvocation, tyAnd})
-      for i in 0..<t.len:
-        if t[i] != nil:
-          result += sumGeneric(t[i])
+    of tyGenericParam:
+      if t.len > 0:
+        t = t.skipModifier
+      else:
+        inc result
+        break
+    of tyUntyped, tyTyped: break
+    of tyGenericInvocation, tyTuple, tyAnd:
+      result += ord(t.kind == tyAnd)
+      for a in t.kids:
+        if a != nil:
+          result += sumGeneric(a)
+      break
+    of tyProc:
+      if t.returnType != nil:
+        result += sumGeneric(t.returnType)
+      for _, a in t.paramTypes:
+        result += sumGeneric(a)
       break
-    of tyStatic:
-      return sumGeneric(t[0]) + 1
-    of tyGenericParam, tyUntyped, tyTyped: break
-    of tyAlias, tySink: t = t.lastSon
-    of tyBool, tyChar, tyEnum, tyObject, tyPointer,
-        tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128,
-        tyUInt..tyUInt64, tyCompositeTypeClass:
-      return isvar
     else:
-      return 0
+      break
 
 proc complexDisambiguation(a, b: PType): int =
   # 'a' matches better if *every* argument matches better or equal than 'b'.
   var winner = 0
-  for i in 1..<min(a.len, b.len):
-    let x = a[i].sumGeneric
-    let y = b[i].sumGeneric
+  for ai, bi in underspecifiedPairs(a, b, 1):
+    let x = ai.sumGeneric
+    let y = bi.sumGeneric
     if x != y:
       if winner == 0:
         if x > y: winner = 1
@@ -274,8 +379,8 @@ proc complexDisambiguation(a, b: PType): int =
   result = winner
   when false:
     var x, y: int
-    for i in 1..<a.len: x += a[i].sumGeneric
-    for i in 1..<b.len: y += b[i].sumGeneric
+    for i in 1..<a.len: x += ai.sumGeneric
+    for i in 1..<b.len: y += bi.sumGeneric
     result = x - y
 
 proc writeMatches*(c: TCandidate) =
@@ -287,7 +392,16 @@ proc writeMatches*(c: TCandidate) =
   echo "  conv matches: ", c.convMatches
   echo "  inheritance: ", c.inheritancePenalty
 
-proc cmpCandidates*(a, b: TCandidate): int =
+proc cmpInheritancePenalty(a, b: int): int =
+  var eb = b
+  var ea = a
+  if b < 0:
+    eb = maxInheritancePenalty  # defacto max penalty
+  if a < 0:
+    ea = maxInheritancePenalty
+  eb - ea
+
+proc cmpCandidates*(a, b: TCandidate, isFormal=true): int =
   result = a.exactMatches - b.exactMatches
   if result != 0: return
   result = a.genericMatches - b.genericMatches
@@ -298,16 +412,16 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.convMatches - b.convMatches
   if result != 0: return
-  # the other way round because of other semantics:
-  result = b.inheritancePenalty - a.inheritancePenalty
+  result = cmpInheritancePenalty(a.inheritancePenalty, b.inheritancePenalty)
   if result != 0: return
-  # check for generic subclass relation
-  result = checkGeneric(a, b)
+  if isFormal:
+    # check for generic subclass relation
+    result = checkGeneric(a, b)
+    if result != 0: return
+    # prefer more specialized generic over more general generic:
+    result = complexDisambiguation(a.callee, b.callee)
   if result != 0: return
-  # prefer more specialized generic over more general generic:
-  result = complexDisambiguation(a.callee, b.callee)
   # only as a last resort, consider scoping:
-  if result != 0: return
   result = a.calleeScope - b.calleeScope
 
 proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
@@ -321,26 +435,44 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   else:
     result = arg.typ.typeToString(prefer)
 
-proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
-  result = ""
-  for i in startIdx..<n.len:
-    var arg = n[i]
-    if n[i].kind == nkExprEqExpr:
-      result.add renderTree(n[i][0])
-      result.add ": "
-      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
-        # XXX we really need to 'tryExpr' here!
-        arg = c.semOperand(c, n[i][1])
+template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) =
+  var arg = n[i]
+  if n[i].kind == nkExprEqExpr:
+    result.add renderTree(n[i][0])
+    result.add ": "
+    if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}:
+      arg = c.semTryExpr(c, n[i][1])
+      if arg == nil:
+        arg = n[i][1]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
         n[i].typ = arg.typ
         n[i][1] = arg
-    else:
-      if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
-                                           nkOfBranch, nkElifBranch,
-                                           nkExceptBranch}:
-        arg = c.semOperand(c, n[i])
+  else:
+    if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse,
+                                          nkOfBranch, nkElifBranch,
+                                          nkExceptBranch}:
+      arg = c.semTryExpr(c, n[i])
+      if arg == nil:
+        arg = n[i]
+        arg.typ = newTypeS(tyUntyped, c)
+      else:
+        if arg.typ == nil:
+          arg.typ = newTypeS(tyVoid, c)
         n[i] = arg
-    if arg.typ != nil and arg.typ.kind == tyError: return
-    result.add argTypeToString(arg, prefer)
+  if arg.typ != nil and arg.typ.kind == tyError: return
+  result.add argTypeToString(arg, prefer)
+
+proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string =
+  result = ""
+  describeArgImpl(c, n, i, startIdx, prefer)
+
+proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string =
+  result = ""
+  for i in startIdx..<n.len:
+    describeArgImpl(c, n, i, startIdx, prefer)
     if i != n.len - 1: result.add ", "
 
 proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
@@ -349,12 +481,13 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
     if c.isNoCall: result = t
     else: result = nil
   of tySequence, tySet:
-    if t[0].kind == tyEmpty: result = nil
+    if t.elementType.kind == tyEmpty: result = nil
     else: result = t
   of tyGenericParam, tyAnything, tyConcept:
     result = t
+    if c.isNoCall: return
     while true:
-      result = PType(idTableGet(c.bindings, t))
+      result = idTableGet(c.bindings, t)
       if result == nil:
         break # it's ok, no match
         # example code that triggers it:
@@ -365,22 +498,31 @@ proc concreteType(c: TCandidate, t: PType; f: PType = nil): PType =
   of tyOwned:
     # bug #11257: the comparison system.`==`[T: proc](x, y: T) works
     # better without the 'owned' type:
-    if f != nil and f.len > 0 and f[0].skipTypes({tyBuiltInTypeClass}).kind == tyProc:
-      result = t.lastSon
+    if f != nil and f.hasElementType and f.elementType.skipTypes({tyBuiltInTypeClass, tyOr}).kind == tyProc:
+      result = t.skipModifier
     else:
       result = t
   else:
     result = t                # Note: empty is valid here
 
-proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
+proc handleRange(c: PContext, f, a: PType, min, max: TTypeKind): TTypeRelation =
   if a.kind == f.kind:
     result = isEqual
   else:
     let ab = skipTypes(a, {tyRange})
     let k = ab.kind
-    if k == f.kind: result = isSubrange
-    elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64,
-                                   tyUInt..tyUInt64} and
+    let nf = c.config.normalizeKind(f.kind)
+    let na = c.config.normalizeKind(k)
+    if k == f.kind:
+      # `a` is a range type matching its base type
+      # see very bottom for range types matching different types
+      if isIntLit(ab):
+        # range type can only give isFromIntLit for base type
+        result = isFromIntLit
+      else:
+        result = isSubrange
+    elif a.kind == tyInt and f.kind in {tyRange, tyInt..tyInt64,
+                                        tyUInt..tyUInt64} and
         isIntLit(ab) and getInt(ab.n) >= firstOrd(nil, f) and
                          getInt(ab.n) <= lastOrd(nil, f):
       # passing 'nil' to firstOrd/lastOrd here as type checking rules should
@@ -388,9 +530,13 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
       # integer literal in the proper range; we want ``i16 + 4`` to stay an
       # ``int16`` operation so we declare the ``4`` pseudo-equal to int16
       result = isFromIntLit
-    elif f.kind == tyInt and k in {tyInt8..tyInt32}:
+    elif a.kind == tyInt and nf == c.config.targetSizeSignedToKind:
+      result = isIntConv
+    elif a.kind == tyUInt and nf == c.config.targetSizeUnsignedToKind:
+      result = isIntConv
+    elif f.kind == tyInt and na in {tyInt8 .. pred(c.config.targetSizeSignedToKind)}:
       result = isIntConv
-    elif f.kind == tyUInt and k in {tyUInt8..tyUInt32}:
+    elif f.kind == tyUInt and na in {tyUInt8 .. pred(c.config.targetSizeUnsignedToKind)}:
       result = isIntConv
     elif k >= min and k <= max:
       result = isConvertible
@@ -404,7 +550,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
       result = isConvertible
     else: result = isNone
 
-proc isConvertibleToRange(f, a: PType): bool =
+proc isConvertibleToRange(c: PContext, f, a: PType): bool =
   if f.kind in {tyInt..tyInt64, tyUInt..tyUInt64} and
      a.kind in {tyInt..tyInt64, tyUInt..tyUInt64}:
     case f.kind
@@ -412,18 +558,21 @@ proc isConvertibleToRange(f, a: PType): bool =
     of tyInt16: result = isIntLit(a) or a.kind in {tyInt8, tyInt16}
     of tyInt32: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32}
     # This is wrong, but seems like there's a lot of code that relies on it :(
-    of tyInt, tyUInt, tyUInt64: result = true
+    of tyInt, tyUInt: result = true
+    # of tyInt: result = isIntLit(a) or a.kind in {tyInt8 .. c.config.targetSizeSignedToKind}
     of tyInt64: result = isIntLit(a) or a.kind in {tyInt8, tyInt16, tyInt32, tyInt, tyInt64}
     of tyUInt8: result = isIntLit(a) or a.kind in {tyUInt8}
     of tyUInt16: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16}
     of tyUInt32: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32}
-    #of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt}
-    #of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt, tyUInt64}
+    # of tyUInt: result = isIntLit(a) or a.kind in {tyUInt8 .. c.config.targetSizeUnsignedToKind}
+    of tyUInt64: result = isIntLit(a) or a.kind in {tyUInt8, tyUInt16, tyUInt32, tyUInt64}
     else: result = false
   elif f.kind in {tyFloat..tyFloat128}:
     # `isIntLit` is correct and should be used above as well, see PR:
     # https://github.com/nim-lang/Nim/pull/11197
     result = isIntLit(a) or a.kind in {tyFloat..tyFloat128}
+  else:
+    result = false
 
 proc handleFloatRange(f, a: PType): TTypeRelation =
   if a.kind == f.kind:
@@ -440,11 +589,50 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
       else: result = isIntConv
     else: result = isNone
 
+proc reduceToBase(f: PType): PType =
+  #[
+    Returns the lowest order (most general) type that that is compatible with the input.
+    E.g.
+    A[T] = ptr object ... A -> ptr object
+    A[N: static[int]] = array[N, int] ... A -> array
+  ]#
+  case f.kind:
+  of tyGenericParam:
+    if f.len <= 0 or f.skipModifier == nil:
+      result = f
+    else:
+      result = reduceToBase(f.skipModifier)
+  of tyGenericInvocation:
+    result = reduceToBase(f.baseClass)
+  of tyCompositeTypeClass, tyAlias:
+    if not f.hasElementType or f.elementType == nil:
+      result = f
+    else:
+      result = reduceToBase(f.elementType)
+  of tyGenericInst:
+    result = reduceToBase(f.skipModifier)
+  of tyGenericBody:
+    result = reduceToBase(f.typeBodyImpl)
+  of tyUserTypeClass:
+    if f.isResolvedUserTypeClass:
+      result = f.base  # ?? idk if this is right
+    else:
+      result = f.skipModifier
+  of tyStatic, tyOwned, tyVar, tyLent, tySink:
+    result = reduceToBase(f.base)
+  of tyInferred:
+    # This is not true "After a candidate type is selected"
+    result = reduceToBase(f.base)
+  of tyRange:
+    result = f.elementType
+  else:
+    result = f
+
 proc genericParamPut(c: var TCandidate; last, fGenericOrigin: PType) =
   if fGenericOrigin != nil and last.kind == tyGenericInst and
-     last.len-1 == fGenericOrigin.len:
-    for i in 1..<fGenericOrigin.len:
-      let x = PType(idTableGet(c.bindings, fGenericOrigin[i]))
+     last.kidsLen-1 == fGenericOrigin.kidsLen:
+    for i in FirstGenericParamAt..<fGenericOrigin.kidsLen:
+      let x = idTableGet(c.bindings, fGenericOrigin[i])
       if x == nil:
         put(c, fGenericOrigin[i], last[i])
 
@@ -454,8 +642,9 @@ proc isObjectSubtype(c: var TCandidate; a, f, fGenericOrigin: PType): int =
   var depth = 0
   var last = a
   while t != nil and not sameObjectTypes(f, t):
-    assert t.kind == tyObject
-    t = t[0]
+    if t.kind != tyObject:  # avoid entering generic params etc
+      return -1
+    t = t.baseClass
     if t == nil: break
     last = t
     t = skipTypes(t, skipPtrs)
@@ -476,20 +665,23 @@ proc skipToObject(t: PType; skipped: var SkippedPtr): PType =
   while r != nil:
     case r.kind
     of tyGenericInvocation:
-      r = r[0]
+      r = r.genericHead
     of tyRef:
       inc ptrs
       skipped = skippedRef
-      r = r.lastSon
+      r = r.elementType
     of tyPtr:
       inc ptrs
       skipped = skippedPtr
-      r = r.lastSon
-    of tyGenericBody, tyGenericInst, tyAlias, tySink, tyOwned:
-      r = r.lastSon
+      r = r.elementType
+    of tyGenericInst, tyAlias, tySink, tyOwned:
+      r = r.elementType
+    of tyGenericBody:
+      r = r.typeBodyImpl
     else:
       break
   if r.kind == tyObject and ptrs <= 1: result = r
+  else: result = nil
 
 proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin: PType): bool =
   assert f.kind in {tyGenericInst, tyGenericInvocation, tyGenericBody}
@@ -503,7 +695,7 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin
   # XXX sameObjectType can return false here. Need to investigate
   # why that is but sameObjectType does way too much work here anyway.
   while t != nil and r.sym != t.sym and askip == fskip:
-    t = t[0]
+    t = t.baseClass
     if t == nil: break
     last = t
     t = t.skipToObject(askip)
@@ -512,22 +704,29 @@ proc isGenericSubtype(c: var TCandidate; a, f: PType, d: var int, fGenericOrigin
     genericParamPut(c, last, fGenericOrigin)
     d = depth
     result = true
+  else:
+    result = false
 
 proc minRel(a, b: TTypeRelation): TTypeRelation =
   if a <= b: result = a
   else: result = b
 
-proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
+proc recordRel(c: var TCandidate, f, a: PType, flags: TTypeRelFlags): TTypeRelation =
   result = isNone
   if sameType(f, a):
     result = isEqual
-  elif a.len == f.len:
+  elif sameTupleLengths(a, f):
     result = isEqual
     let firstField = if f.kind == tyTuple: 0
                      else: 1
-    for i in firstField..<f.len:
-      var m = typeRel(c, f[i], a[i])
+    for _, ff, aa in tupleTypePairs(f, a):
+      var m = typeRel(c, ff, aa, flags)
       if m < isSubtype: return isNone
+      if m == isSubtype and aa.kind != tyNil and c.inheritancePenalty > -1:
+        # we can't process individual element type conversions from a
+        # type conversion for the whole tuple
+        # subtype relations need type conversions when inheritance is used
+        return isNone
       result = minRel(result, m)
     if f.n != nil and a.n != nil:
       for i in 0..<f.n.len:
@@ -537,7 +736,7 @@ proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
         else:
           var x = f.n[i].sym
           var y = a.n[i].sym
-          if f.kind == tyObject and typeRel(c, x.typ, y.typ) < isSubtype:
+          if f.kind == tyObject and typeRel(c, x.typ, y.typ, flags) < isSubtype:
             return isNone
           if x.name.id != y.name.id: return isNone
 
@@ -545,12 +744,13 @@ proc allowsNil(f: PType): TTypeRelation {.inline.} =
   result = if tfNotNil notin f.flags: isSubtype else: isNone
 
 proc inconsistentVarTypes(f, a: PType): bool {.inline.} =
-  result = f.kind != a.kind and
-    (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})
+  result = (f.kind != a.kind and
+    (f.kind in {tyVar, tyLent, tySink} or a.kind in {tyVar, tyLent, tySink})) or
+    isOutParam(f) != isOutParam(a)
 
-proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
+proc procParamTypeRel(c: var TCandidate; f, a: PType): TTypeRelation =
   ## For example we have:
-  ##   ```
+  ##   ```nim
   ##   proc myMap[T,S](sIn: seq[T], f: proc(x: T): S): seq[S] = ...
   ##   proc innerProc[Q,W](q: Q): W = ...
   ##   ```
@@ -570,7 +770,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     a = a
 
   if a.isMetaType:
-    let aResolved = PType(idTableGet(c.bindings, a))
+    let aResolved = idTableGet(c.bindings, a)
     if aResolved != nil:
       a = aResolved
   if a.isMetaType:
@@ -592,6 +792,8 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     if reverseRel >= isGeneric:
       result = isInferred
       #inc c.genericMatches
+    else:
+      result = isNone
   else:
     # Note that this typeRel call will save f's resolved type into c.bindings
     # if f is metatype.
@@ -606,10 +808,15 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   case a.kind
   of tyProc:
-    if f.len != a.len: return
+    var f = f
+    copyingEraseVoidParams(c, f)
+    if f.signatureLen != a.signatureLen: return
     result = isEqual      # start with maximum; also correct for no
                           # params at all
 
+    if f.flags * {tfIterator} != a.flags * {tfIterator}:
+      return isNone
+
     template checkParam(f, a) =
       result = minRel(result, procParamTypeRel(c, f, a))
       if result == isNone: return
@@ -636,7 +843,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 
   of tyNil:
     result = f.allowsNil
-  else: discard
+  else: result = isNone
 
 proc typeRangeRel(f, a: PType): TTypeRelation {.noinline.} =
   template checkRange[T](a0, a1, f0, f1: T): TTypeRelation =
@@ -661,7 +868,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     c = m.c
     typeClass = ff.skipTypes({tyUserTypeClassInst})
     body = typeClass.n[3]
-    matchedConceptContext: TMatchedConcept
+    matchedConceptContext = TMatchedConcept()
     prevMatchedConcept = c.matchedConcept
     prevCandidateType = typeClass[0][0]
 
@@ -681,20 +888,20 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     typeClass[0][0] = prevCandidateType
     closeScope(c)
 
-  var typeParams: seq[(PSym, PType)]
+  var typeParams: seq[(PSym, PType)] = @[]
 
   if ff.kind == tyUserTypeClassInst:
     for i in 1..<(ff.len - 1):
       var
         typeParamName = ff.base[i-1].sym.name
         typ = ff[i]
-        param: PSym
-        alreadyBound = PType(idTableGet(m.bindings, typ))
+        param: PSym = nil
+        alreadyBound = idTableGet(m.bindings, typ)
 
       if alreadyBound != nil: typ = alreadyBound
 
       template paramSym(kind): untyped =
-        newSym(kind, typeParamName, nextSymId(c.idgen), typeClass.sym, typeClass.sym.info, {})
+        newSym(kind, typeParamName, c.idgen, typeClass.sym, typeClass.sym.info, {})
 
       block addTypeParam:
         for prev in typeParams:
@@ -707,19 +914,19 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
         of tyStatic:
           param = paramSym skConst
           param.typ = typ.exactReplica
-          #copyType(typ, nextTypeId(c.idgen), typ.owner)
+          #copyType(typ, c.idgen, typ.owner)
           if typ.n == nil:
             param.typ.flags.incl tfInferrableStatic
           else:
             param.ast = typ.n
-        of tyUnknown:
+        of tyFromExpr:
           param = paramSym skVar
           param.typ = typ.exactReplica
-          #copyType(typ, nextTypeId(c.idgen), typ.owner)
+          #copyType(typ, c.idgen, typ.owner)
         else:
           param = paramSym skType
           param.typ = if typ.isMetaType:
-                        c.newTypeWithSons(tyInferred, @[typ])
+                        newTypeS(tyInferred, c, typ)
                       else:
                         makeTypeDesc(c, typ)
 
@@ -728,8 +935,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
       addDecl(c, param)
 
   var
-    oldWriteHook: typeof(m.c.config.writelnHook)
-    diagnostics: seq[string]
+    oldWriteHook = default typeof(m.c.config.writelnHook)
+    diagnostics: seq[string] = @[]
     errorPrefix: string
     flags: TExprFlags = {}
     collectDiagnostics = m.diagnosticsEnabled or
@@ -767,7 +974,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType =
     result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff)
   else:
     result = ff.exactReplica
-    #copyType(ff, nextTypeId(c.idgen), ff.owner)
+    #copyType(ff, c.idgen, ff.owner)
 
   result.n = checkedBody
 
@@ -790,7 +997,9 @@ proc maybeSkipDistinct(m: TCandidate; t: PType, callee: PSym): PType =
     result = t
 
 proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
-                            allowUnresolved = false): PNode =
+                            allowUnresolved = false,
+                            allowCalls = false,
+                            expectedType: PType = nil): PNode =
   # Consider this example:
   #   type Value[N: static[int]] = object
   #   proc foo[N](a: Value[N], r: range[0..(N-1)])
@@ -799,6 +1008,8 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
   # This proc is used to evaluate such static expressions.
   let instantiated = replaceTypesInBody(c.c, c.bindings, n, nil,
                                         allowMetaTypes = allowUnresolved)
+  if not allowCalls and instantiated.kind in nkCallKinds:
+    return nil
   result = c.c.semExpr(c.c, instantiated)
 
 proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
@@ -869,8 +1080,9 @@ proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
 
     else: discard
 
-  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
-    var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons)
+  elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and
+      (lhs.typ.n == nil or idTableGet(c.bindings, lhs.typ) == nil):
+    var inferred = newTypeS(tyStatic, c.c, lhs.typ.elementType)
     inferred.n = newIntNode(nkIntLit, rhs)
     put(c, lhs.typ, inferred)
     if c.c.matchedConcept != nil:
@@ -901,6 +1113,7 @@ proc inferStaticsInRange(c: var TCandidate,
     else:
       failureToInferStaticParam(c.c.config, exp)
 
+  result = isNone
   if lowerBound.kind == nkIntLit:
     if upperBound.kind == nkIntLit:
       if lengthOrd(c.c.config, concrete) == upperBound.intVal - lowerBound.intVal + 1:
@@ -912,9 +1125,21 @@ proc inferStaticsInRange(c: var TCandidate,
     doInferStatic(lowerBound, getInt(upperBound) + 1 - lengthOrd(c.c.config, concrete))
 
 template subtypeCheck() =
-  if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {
-      tyRef, tyPtr, tyVar, tyLent, tyOwned}:
+  case result
+  of isIntConv:
     result = isNone
+  of isSubrange:
+    discard # XXX should be isNone with preview define, warnings
+  of isConvertible:
+    if f.last.skipTypes(abstractInst).kind != tyOpenArray:
+      # exclude var openarray which compiler supports
+      result = isNone
+  of isSubtype:
+    if f.last.skipTypes(abstractInst).kind in {
+        tyRef, tyPtr, tyVar, tyLent, tyOwned}:
+      # compiler can't handle subtype conversions with pointer indirection
+      result = isNone
+  else: discard
 
 proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
   # this proc is always called for a pair of matching types
@@ -971,8 +1196,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
   # of the designated type class.
   #
   # 3) When used with two type classes, it will check whether the types
-  # matching the first type class are a strict subset of the types matching
-  # the other. This allows us to compare the signatures of generic procs in
+  # matching the first type class (aOrig) are a strict subset of the types matching
+  # the other (f). This allows us to compare the signatures of generic procs in
   # order to give preferrence to the most specific one:
   #
   # seq[seq[any]] is a strict subset of seq[any] and hence more specific.
@@ -1012,7 +1237,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
 
       case f.kind
       of tyGenericParam:
-        var prev = PType(idTableGet(c.bindings, f))
+        var prev = idTableGet(c.bindings, f)
         if prev != nil: candidate = prev
       of tyFromExpr:
         let computedType = tryResolvingStaticExpr(c, f.n).typ
@@ -1044,16 +1269,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
   # situation when nkDotExpr are rotated to nkDotCalls
 
   if aOrig.kind in {tyAlias, tySink}:
-    return typeRel(c, f, lastSon(aOrig), flags)
+    return typeRel(c, f, skipModifier(aOrig), flags)
 
   if a.kind == tyGenericInst and
       skipTypes(f, {tyStatic, tyVar, tyLent, tySink}).kind notin {
         tyGenericBody, tyGenericInvocation,
         tyGenericInst, tyGenericParam} + tyTypeClasses:
-    return typeRel(c, f, lastSon(a), flags)
+    return typeRel(c, f, skipModifier(a), flags)
 
   if a.isResolvedUserTypeClass:
-    return typeRel(c, f, a.lastSon, flags)
+    return typeRel(c, f, a.skipModifier, flags)
 
   template bindingRet(res) =
     if doBind:
@@ -1062,10 +1287,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     return res
 
   template considerPreviousT(body: untyped) =
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
     if prev == nil: body
     else: return typeRel(c, prev, a, flags)
 
+  if c.c.inGenericContext > 0 and not c.isNoCall and
+      (tfUnresolved in a.flags or a.kind in tyTypeClasses):
+    # cheap check for unresolved arg, not nested
+    return isNone
+
   case a.kind
   of tyOr:
     # XXX: deal with the current dual meaning of tyGenericParam
@@ -1074,23 +1304,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     # both int and string must match against number
     # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
     result = isGeneric
-    for branch in a.sons:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x == isNone: return isNone
       if x < result: result = x
     return result
-
   of tyAnd:
     # XXX: deal with the current dual meaning of tyGenericParam
     c.typedescMatched = true
     # seq[Sortable and Iterable] vs seq[Sortable]
     # only one match is enough
-    for branch in a.sons:
+    for branch in a.kids:
       let x = typeRel(c, f, branch, flags + {trDontBind})
       if x != isNone:
         return if x >= isGeneric: isGeneric else: x
     return isNone
-
   of tyIterable:
     if f.kind != tyIterable: return isNone
   of tyNot:
@@ -1100,18 +1328,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       # seq[float] matches the first, but not the second
       # we must turn the problem around:
       # is number a subset of int?
-      return typeRel(c, a.lastSon, f.lastSon, flags)
+      return typeRel(c, a.elementType, f.elementType, flags)
 
     else:
       # negative type classes are essentially infinite,
       # so only the `any` type class is their superset
       return if f.kind == tyAnything: isGeneric
              else: isNone
-
   of tyAnything:
     if f.kind == tyAnything: return isGeneric
     else: return isNone
-
   of tyUserTypeClass, tyUserTypeClassInst:
     if c.c.matchedConcept != nil and c.c.matchedConcept.depth <= 4:
       # consider this: 'var g: Node' *within* a concept where 'Node'
@@ -1120,6 +1346,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       let x = typeRel(c, a, f, flags + {trDontBind})
       if x >= isGeneric:
         return isGeneric
+  of tyFromExpr:
+    if c.c.inGenericContext > 0:
+      if not c.isNoCall:
+        # generic type bodies can sometimes compile call expressions
+        # prevent expressions with unresolved types from
+        # being passed as parameters
+        return isNone
+      else:
+        # Foo[templateCall(T)] shouldn't fail early if Foo has a constraint
+        # and we can't evaluate `templateCall(T)` yet
+        return isGeneric
   else: discard
 
   case f.kind
@@ -1138,43 +1375,57 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       if result notin {isNone, isGeneric}:
         # resolve any late-bound static expressions
         # that may appear in the range:
+        let expectedType = base(f)
         for i in 0..1:
           if f.n[i].kind == nkStaticExpr:
-            f.n[i] = tryResolvingStaticExpr(c, f.n[i])
+            let r = tryResolvingStaticExpr(c, f.n[i], expectedType = expectedType)
+            if r != nil:
+              f.n[i] = r
         result = typeRangeRel(f, a)
     else:
       let f = skipTypes(f, {tyRange})
       if f.kind == a.kind and (f.kind != tyEnum or sameEnumTypes(f, a)):
         result = isIntConv
-      elif isConvertibleToRange(f, a):
+      elif isConvertibleToRange(c.c, f, a):
         result = isConvertible  # a convertible to f
-  of tyInt:      result = handleRange(f, a, tyInt8, tyInt32)
-  of tyInt8:     result = handleRange(f, a, tyInt8, tyInt8)
-  of tyInt16:    result = handleRange(f, a, tyInt8, tyInt16)
-  of tyInt32:    result = handleRange(f, a, tyInt8, tyInt32)
-  of tyInt64:    result = handleRange(f, a, tyInt, tyInt64)
-  of tyUInt:     result = handleRange(f, a, tyUInt8, tyUInt32)
-  of tyUInt8:    result = handleRange(f, a, tyUInt8, tyUInt8)
-  of tyUInt16:   result = handleRange(f, a, tyUInt8, tyUInt16)
-  of tyUInt32:   result = handleRange(f, a, tyUInt8, tyUInt32)
-  of tyUInt64:   result = handleRange(f, a, tyUInt, tyUInt64)
+  of tyInt:      result = handleRange(c.c, f, a, tyInt8, c.c.config.targetSizeSignedToKind)
+  of tyInt8:     result = handleRange(c.c, f, a, tyInt8, tyInt8)
+  of tyInt16:    result = handleRange(c.c, f, a, tyInt8, tyInt16)
+  of tyInt32:    result = handleRange(c.c, f, a, tyInt8, tyInt32)
+  of tyInt64:    result = handleRange(c.c, f, a, tyInt, tyInt64)
+  of tyUInt:     result = handleRange(c.c, f, a, tyUInt8, c.c.config.targetSizeUnsignedToKind)
+  of tyUInt8:    result = handleRange(c.c, f, a, tyUInt8, tyUInt8)
+  of tyUInt16:   result = handleRange(c.c, f, a, tyUInt8, tyUInt16)
+  of tyUInt32:   result = handleRange(c.c, f, a, tyUInt8, tyUInt32)
+  of tyUInt64:   result = handleRange(c.c, f, a, tyUInt, tyUInt64)
   of tyFloat:    result = handleFloatRange(f, a)
   of tyFloat32:  result = handleFloatRange(f, a)
   of tyFloat64:  result = handleFloatRange(f, a)
   of tyFloat128: result = handleFloatRange(f, a)
-  of tyVar, tyLent:
-    if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base, flags)
-    else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
+  of tyVar:
+    let flags = if isOutParam(f): flags + {trIsOutParam} else: flags
+    if aOrig.kind == f.kind and (isOutParam(aOrig) == isOutParam(f)):
+      result = typeRel(c, f.base, aOrig.base, flags)
+    else:
+      result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
+    subtypeCheck()
+  of tyLent:
+    if aOrig.kind == f.kind:
+      result = typeRel(c, f.base, aOrig.base, flags)
+    else:
+      result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
     subtypeCheck()
   of tyArray:
-    case a.kind
-    of tyArray:
-      var fRange = f[0]
-      var aRange = a[0]
-      if fRange.kind == tyGenericParam:
-        var prev = PType(idTableGet(c.bindings, fRange))
+    a = reduceToBase(a)
+    if a.kind == tyArray:
+      var fRange = f.indexType
+      var aRange = a.indexType
+      if fRange.kind in {tyGenericParam, tyAnything}:
+        var prev = idTableGet(c.bindings, fRange)
         if prev == nil:
-          put(c, fRange, a[0])
+          if typeRel(c, fRange, aRange) == isNone:
+            return isNone
+          put(c, fRange, a.indexType)
           fRange = a
         else:
           fRange = prev
@@ -1182,11 +1433,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       # This typeDesc rule is wrong, see bug #7331
       let aa = a[1] #.skipTypes({tyTypeDesc})
 
-      if f[0].kind != tyGenericParam and aa.kind == tyEmpty:
+      if f.indexType.kind != tyGenericParam and aa.kind == tyEmpty:
         result = isGeneric
       else:
         result = typeRel(c, ff, aa, flags)
-
       if result < isGeneric:
         if nimEnableCovariance and
            trNoCovariance notin flags and
@@ -1197,24 +1447,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           return isNone
 
       if fRange.rangeHasUnresolvedStatic:
+        if aRange.kind in {tyGenericParam} and aRange.reduceToBase() == aRange:
+          return
         return inferStaticsInRange(c, fRange, a)
       elif c.c.matchedConcept != nil and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
+      elif result == isGeneric and concreteType(c, aa, ff) == nil:
+        return isNone
       else:
         if lengthOrd(c.c.config, fRange) != lengthOrd(c.c.config, aRange):
           result = isNone
-    else: discard
-  of tyUncheckedArray:
-    if a.kind == tyUncheckedArray:
-      result = typeRel(c, base(f), base(a), flags)
-      if result < isGeneric: result = isNone
-    else: discard
   of tyOpenArray, tyVarargs:
     # varargs[untyped] is special too but handled earlier. So we only need to
     # handle varargs[typed]:
     if f.kind == tyVarargs:
       if tfVarargs in a.flags:
-        return typeRel(c, f.base, a.lastSon, flags)
+        return typeRel(c, f.base, a.elementType, flags)
       if f[0].kind == tyTyped: return
 
     template matchArrayOrSeq(aBase: PType) =
@@ -1234,13 +1482,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       result = typeRel(c, base(f), base(a), flags)
       if result < isGeneric: result = isNone
     of tyArray:
-      if (f[0].kind != tyGenericParam) and (a[1].kind == tyEmpty):
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         return isSubtype
-      matchArrayOrSeq(a[1])
+      matchArrayOrSeq(a.elementType)
     of tySequence:
-      if (f[0].kind != tyGenericParam) and (a[0].kind == tyEmpty):
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         return isConvertible
-      matchArrayOrSeq(a[0])
+      matchArrayOrSeq(a.elementType)
     of tyString:
       if f.kind == tyOpenArray:
         if f[0].kind == tyChar:
@@ -1249,14 +1497,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             typeRel(c, base(f), base(a), flags) >= isGeneric:
           result = isConvertible
     else: discard
-  of tySequence:
-    case a.kind
-    of tySequence:
-      if (f[0].kind != tyGenericParam) and (a[0].kind == tyEmpty):
+  of tySequence, tyUncheckedArray:
+    if a.kind == f.kind:
+      if (f[0].kind != tyGenericParam) and (a.elementType.kind == tyEmpty):
         result = isSubtype
       else:
         let ff = f[0]
-        let aa = a[0]
+        let aa = a.elementType
         result = typeRel(c, ff, aa, flags)
         if result < isGeneric:
           if nimEnableCovariance and
@@ -1266,13 +1513,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             result = isSubtype
           else:
             result = isNone
-        elif tfNotNil in f.flags and tfNotNil notin a.flags:
-          result = isNilConversion
-    of tyNil: result = isNone
-    else: discard
+    elif a.kind == tyNil:
+      result = isNone
   of tyOrdinal:
-    if isOrdinalType(a, allowEnumWithHoles = optNimV1Emulation in c.c.config.globalOptions):
-      var x = if a.kind == tyOrdinal: a[0] else: a
+    if isOrdinalType(a):
+      var x = if a.kind == tyOrdinal: a.elementType else: a
       if f[0].kind == tyNone:
         result = isGeneric
       else:
@@ -1287,16 +1532,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     skipOwned(a)
     if a.kind == f.kind: result = isEqual
   of tyTuple:
-    if a.kind == tyTuple: result = recordRel(c, f, a)
+    if a.kind == tyTuple: result = recordRel(c, f, a, flags)
   of tyObject:
-    if a.kind == tyObject:
-      if sameObjectTypes(f, a):
+    let effectiveArgType = if useTypeLoweringRuleInTypeClass:
+        a
+      else:
+        reduceToBase(a)
+    if effectiveArgType.kind == tyObject:
+      if sameObjectTypes(f, effectiveArgType):
+        c.inheritancePenalty = if tfFinal in f.flags: -1 else: 0
         result = isEqual
         # elif tfHasMeta in f.flags: result = recordRel(c, f, a)
-      else:
-        var depth = isObjectSubtype(c, a, f, nil)
-        if depth > 0:
-          inc(c.inheritancePenalty, depth)
+      elif trIsOutParam notin flags:
+        c.inheritancePenalty = isObjectSubtype(c, effectiveArgType, f, nil)
+        if c.inheritancePenalty > 0:
           result = isSubtype
   of tyDistinct:
     a = a.skipTypes({tyOwned, tyGenericInst, tyRange})
@@ -1312,20 +1561,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       else:
         result = typeRel(c, f[0], a[0], flags)
         if result < isGeneric:
-          if result <= isConvertible:
+          if tfIsConstructor notin a.flags:
+            # set['a'..'z'] and set[char] have different representations
             result = isNone
-          elif tfIsConstructor notin a.flags:
-            # set constructors are a bit special...
-            result = isNone
-
+          else:
+            # but we can convert individual elements of the constructor
+            result = isConvertible
   of tyPtr, tyRef:
-    skipOwned(a)
+    a = reduceToBase(a)
     if a.kind == f.kind:
       # ptr[R, T] can be passed to ptr[T], but not the other way round:
       if a.len < f.len: return isNone
       for i in 0..<f.len-1:
         if typeRel(c, f[i], a[i], flags) == isNone: return isNone
-      result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance})
+      result = typeRel(c, f.elementType, a.elementType, flags + {trNoCovariance})
       subtypeCheck()
       if result <= isIntConv: result = isNone
       elif tfNotNil in f.flags and tfNotNil notin a.flags:
@@ -1340,7 +1589,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
   of tyOwned:
     case a.kind
     of tyOwned:
-      result = typeRel(c, lastSon(f), lastSon(a), flags)
+      result = typeRel(c, skipModifier(f), skipModifier(a), flags)
     of tyNil: result = f.allowsNil
     else: discard
   of tyPointer:
@@ -1353,7 +1602,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         result = isEqual
     of tyNil: result = f.allowsNil
     of tyProc:
-      if a.callConv != ccClosure: result = isConvertible
+      if isDefined(c.c.config, "nimPreviewProcConversion"):
+        result = isNone
+      else:
+        if a.callConv != ccClosure: result = isConvertible
     of tyPtr:
       # 'pointer' is NOT compatible to regionized pointers
       # so 'dealloc(regionPtr)' fails:
@@ -1362,11 +1614,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     else: discard
   of tyString:
     case a.kind
-    of tyString:
-      if tfNotNil in f.flags and tfNotNil notin a.flags:
-        result = isNilConversion
-      else:
-        result = isEqual
+    of tyString: result = isEqual
     of tyNil: result = isNone
     else: discard
   of tyCstring:
@@ -1380,40 +1628,43 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     of tyNil: result = f.allowsNil
     of tyString: result = isConvertible
     of tyPtr:
-      # ptr[Tag, char] is not convertible to 'cstring' for now:
-      if a.len == 1:
-        let pointsTo = a[0].skipTypes(abstractInst)
-        if pointsTo.kind == tyChar: result = isConvertible
-        elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar:
-          result = isConvertible
-        elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and
-            skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and
-            pointsTo[1].kind == tyChar:
-          result = isConvertible
+      if isDefined(c.c.config, "nimPreviewCstringConversion"):
+        result = isNone
+      else:
+        if a.len == 1:
+          let pointsTo = a[0].skipTypes(abstractInst)
+          if pointsTo.kind == tyChar: result = isConvertible
+          elif pointsTo.kind == tyUncheckedArray and pointsTo[0].kind == tyChar:
+            result = isConvertible
+          elif pointsTo.kind == tyArray and firstOrd(nil, pointsTo[0]) == 0 and
+              skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} and
+              pointsTo[1].kind == tyChar:
+            result = isConvertible
     else: discard
-
   of tyEmpty, tyVoid:
     if a.kind == f.kind: result = isEqual
-
   of tyAlias, tySink:
-    result = typeRel(c, lastSon(f), a, flags)
-
+    result = typeRel(c, skipModifier(f), a, flags)
   of tyIterable:
     if a.kind == tyIterable:
       if f.len == 1:
-        result = typeRel(c, lastSon(f), lastSon(a), flags)
+        result = typeRel(c, skipModifier(f), skipModifier(a), flags)
       else:
         # f.len = 3, for some reason
         result = isGeneric
     else:
       result = isNone
   of tyGenericInst:
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
     let origF = f
     var f = if prev == nil: f else: prev
 
-    let roota = a.skipGenericAlias
-    let rootf = f.skipGenericAlias
+    let deptha = a.genericAliasDepth()
+    let depthf = f.genericAliasDepth()
+    let skipBoth = deptha == depthf and (a.len > 0 and f.len > 0 and a.base != f.base)
+
+    let roota = if skipBoth or deptha > depthf: a.skipGenericAlias else: a
+    let rootf = if skipBoth or depthf > deptha: f.skipGenericAlias else: f
 
     if a.kind == tyGenericInst:
       if roota.base == rootf.base:
@@ -1445,13 +1696,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             return isNone
         if prev == nil: put(c, f, a)
       else:
-        let fKind = rootf.lastSon.kind
+        let fKind = rootf.last.kind
         if fKind in {tyAnd, tyOr}:
-          result = typeRel(c, lastSon(f), a, flags)
+          result = typeRel(c, last(f), a, flags)
           if result != isNone: put(c, f, a)
           return
 
-        var aAsObject = roota.lastSon
+        var aAsObject = roota.last
 
         if fKind in {tyRef, tyPtr}:
           if aAsObject.kind == tyObject:
@@ -1461,46 +1712,47 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           elif aAsObject.kind == fKind:
             aAsObject = aAsObject.base
 
-        if aAsObject.kind == tyObject:
+        if aAsObject.kind == tyObject and trIsOutParam notin flags:
           let baseType = aAsObject.base
           if baseType != nil:
-            c.inheritancePenalty += 1
+            inc c.inheritancePenalty, 1 + int(c.inheritancePenalty < 0)
             let ret = typeRel(c, f, baseType, flags)
             return if ret in {isEqual,isGeneric}: isSubtype else: ret
 
         result = isNone
     else:
-      assert lastSon(origF) != nil
-      result = typeRel(c, lastSon(origF), a, flags)
+      assert last(origF) != nil
+      result = typeRel(c, last(origF), a, flags)
       if result != isNone and a.kind != tyNil:
         put(c, f, a)
-
   of tyGenericBody:
     considerPreviousT:
       if a == f or a.kind == tyGenericInst and a.skipGenericAlias[0] == f:
         bindingRet isGeneric
-      let ff = lastSon(f)
+      let ff = last(f)
       if ff != nil:
         result = typeRel(c, ff, a, flags)
-
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    if x.kind == tyGenericParam and x.len > 0:
+      x = x.last
     let concpt = f[0].skipTypes({tyGenericBody})
     var preventHack = concpt.kind == tyConcept
     if x.kind == tyOwned and f[0].kind != tyOwned:
       preventHack = true
-      x = x.lastSon
+      x = x.last
     # XXX: This is very hacky. It should be moved back into liftTypeParam
     if x.kind in {tyGenericInst, tyArray} and
       c.calleeSym != nil and
       c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack:
       let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
-      #echo "inferred ", typeToString(inst), " for ", f
       return typeRel(c, inst, a, flags)
 
     if x.kind == tyGenericInvocation:
       if f[0] == x[0]:
         for i in 1..<f.len:
+          # Handle when checking against a generic that isn't fully instantiated
+          if i >= x.len: return
           let tr = typeRel(c, f[i], x[i], flags)
           if tr <= isSubtype: return
         result = isGeneric
@@ -1521,10 +1773,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       var askip = skippedNone
       var fskip = skippedNone
       let aobj = x.skipToObject(askip)
-      let fobj = genericBody.lastSon.skipToObject(fskip)
-      var depth = -1
-      if fobj != nil and aobj != nil and askip == fskip:
-        depth = isObjectSubtype(c, aobj, fobj, f)
+      let fobj = genericBody.last.skipToObject(fskip)
       result = typeRel(c, genericBody, x, flags)
       if result != isNone:
         # see tests/generics/tgeneric3.nim for an example that triggers this
@@ -1538,18 +1787,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         #
         # we steal the generic parameters from the tyGenericBody:
         for i in 1..<f.len:
-          let x = PType(idTableGet(c.bindings, genericBody[i-1]))
+          let x = idTableGet(c.bindings, genericBody[i-1])
           if x == nil:
             discard "maybe fine (for e.g. a==tyNil)"
           elif x.kind in {tyGenericInvocation, tyGenericParam}:
             internalError(c.c.graph.config, "wrong instantiated type!")
           else:
             let key = f[i]
-            let old = PType(idTableGet(c.bindings, key))
+            let old = idTableGet(c.bindings, key)
             if old == nil:
               put(c, key, x)
             elif typeRel(c, old, x, flags + {trDontBind}) == isNone:
               return isNone
+      var depth = -1
+      if fobj != nil and aobj != nil and askip == fskip:
+        depth = isObjectSubtype(c, aobj, fobj, f)
 
       if result == isNone:
         # Here object inheriting from generic/specialized generic object
@@ -1560,72 +1812,73 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           depth = -1
 
       if depth >= 0:
-        c.inheritancePenalty += depth
+        inc c.inheritancePenalty, depth + int(c.inheritancePenalty < 0)
         # bug #4863: We still need to bind generic alias crap, so
         # we cannot return immediately:
         result = if depth == 0: isGeneric else: isSubtype
   of tyAnd:
     considerPreviousT:
       result = isEqual
-      for branch in f.sons:
+      for branch in f.kids:
         let x = typeRel(c, branch, aOrig, flags)
         if x < isSubtype: return isNone
         # 'and' implies minimum matching result:
         if x < result: result = x
       if result > isGeneric: result = isGeneric
       bindingRet result
-
   of tyOr:
     considerPreviousT:
       result = isNone
       let oldInheritancePenalty = c.inheritancePenalty
-      var maxInheritance = 0
-      for branch in f.sons:
-        c.inheritancePenalty = 0
+      var minInheritance = maxInheritancePenalty
+      for branch in f.kids:
+        c.inheritancePenalty = -1
         let x = typeRel(c, branch, aOrig, flags)
-        maxInheritance = max(maxInheritance, c.inheritancePenalty)
-
-        # 'or' implies maximum matching result:
-        if x > result: result = x
-      if result >= isSubtype:
+        if x >= result:
+          if  c.inheritancePenalty > -1:
+            minInheritance = min(minInheritance, c.inheritancePenalty)
+          result = x
+      if result >= isIntConv:
+        if minInheritance < maxInheritancePenalty:
+          c.inheritancePenalty = oldInheritancePenalty + minInheritance
         if result > isGeneric: result = isGeneric
         bindingRet result
       else:
         result = isNone
-      c.inheritancePenalty = oldInheritancePenalty + maxInheritance
-
   of tyNot:
     considerPreviousT:
-      for branch in f.sons:
-        if typeRel(c, branch, aOrig, flags) != isNone:
-          return isNone
+      if typeRel(c, f.elementType, aOrig, flags) != isNone:
+        return isNone
 
       bindingRet isGeneric
-
   of tyAnything:
     considerPreviousT:
       var concrete = concreteType(c, a)
       if concrete != nil and doBind:
         put(c, f, concrete)
       return isGeneric
-
   of tyBuiltInTypeClass:
     considerPreviousT:
-      let targetKind = f[0].kind
-      let effectiveArgType = a.skipTypes({tyRange, tyGenericInst,
-                                          tyBuiltInTypeClass, tyAlias, tySink, tyOwned})
-      let typeClassMatches = targetKind == effectiveArgType.kind and
-                             not effectiveArgType.isEmptyContainer
-      if typeClassMatches or
-        (targetKind in {tyProc, tyPointer} and effectiveArgType.kind == tyNil):
-        put(c, f, a)
+      let target = f.genericHead
+      let targetKind = target.kind
+      var effectiveArgType = reduceToBase(a)
+      effectiveArgType = effectiveArgType.skipTypes({tyBuiltInTypeClass})
+      if targetKind == effectiveArgType.kind:
+        if effectiveArgType.isEmptyContainer:
+          return isNone
+        if targetKind == tyProc:
+          if target.flags * {tfIterator} != effectiveArgType.flags * {tfIterator}:
+            return isNone
+          if tfExplicitCallConv in target.flags and
+              target.callConv != effectiveArgType.callConv:
+            return isNone
+        if doBind: put(c, f, a)
         return isGeneric
       else:
         return isNone
-
   of tyUserTypeClassInst, tyUserTypeClass:
     if f.isResolvedUserTypeClass:
-      result = typeRel(c, f.lastSon, a, flags)
+      result = typeRel(c, f.last, a, flags)
     else:
       considerPreviousT:
         if aOrig == f: return isEqual
@@ -1634,10 +1887,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           bindConcreteTypeToUserTypeClass(matched, a)
           if doBind: put(c, f, matched)
           result = isGeneric
-        elif a.len > 0 and a.lastSon == f:
+        elif a.len > 0 and a.last == f:
           # Needed for checking `Y` == `Addable` in the following
           #[
-            type 
+            type
               Addable = concept a, type A
                 a + a is A
               MyType[T: Addable; Y: static T] = object
@@ -1645,15 +1898,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           result = isGeneric
         else:
           result = isNone
-
   of tyConcept:
     result = if concepts.conceptMatch(c.c, f, a, c.bindings, nil): isGeneric
              else: isNone
-
   of tyCompositeTypeClass:
     considerPreviousT:
       let roota = a.skipGenericAlias
-      let rootf = f.lastSon.skipGenericAlias
+      let rootf = f.last.skipGenericAlias
       if a.kind == tyGenericInst and roota.base == rootf.base:
         for i in 1..<rootf.len-1:
           let ff = rootf[i]
@@ -1662,14 +1913,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
           if result == isNone: return
           if ff.kind == tyRange and result != isEqual: return isNone
       else:
-        result = typeRel(c, rootf.lastSon, a, flags)
+        result = typeRel(c, rootf.last, a, flags)
       if result != isNone:
         put(c, f, a)
         result = isGeneric
-
   of tyGenericParam:
     let doBindGP = doBind or trBindGenericParam in flags
-    var x = PType(idTableGet(c.bindings, f))
+    var x = idTableGet(c.bindings, f)
     if x == nil:
       if c.callee.kind == tyGenericBody and not c.typedescMatched:
         # XXX: The fact that generic types currently use tyGenericParam for
@@ -1687,7 +1937,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
             c.typedescMatched = true
             var aa = a
             while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
-              aa = lastSon(aa)
+              aa = last(aa)
             if aa.kind in {tyGenericParam} + tyTypeClasses:
               # If the constraint is a genericParam or typeClass this isGeneric
               return isGeneric
@@ -1704,18 +1954,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       else:
         # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)'
         if f.len > 0 and f[0].kind != tyNone:
-          let oldInheritancePenalty = c.inheritancePenalty
-          result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam})
+          result = typeRel(c, f[0], a, flags + {trDontBind, trBindGenericParam})
           if doBindGP and result notin {isNone, isGeneric}:
             let concrete = concreteType(c, a, f)
             if concrete == nil: return isNone
             put(c, f, concrete)
-          # bug #6526
           if result in {isEqual, isSubtype}:
-            # 'T: Class' is a *better* match than just 'T'
-            # but 'T: Subclass' is even better:
-            c.inheritancePenalty = oldInheritancePenalty - c.inheritancePenalty -
-                                  100 * ord(result == isEqual)
             result = isGeneric
         elif a.kind == tyTypeDesc:
           # somewhat special typing rule, the following is illegal:
@@ -1730,7 +1974,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         if tfWildcard in a.flags:
           a.sym.transitionGenericParamToType()
           a.flags.excl tfWildcard
-        else:
+        elif doBind:
+          # careful: `trDontDont` (set by `checkGeneric`) is not always respected in this call graph.
+          # typRel having two different modes (binding and non-binding) can make things harder to
+          # reason about and maintain. Refactoring typeRel to not be responsible for setting, or
+          # at least validating, bindings can have multiple benefits. This is debatable. I'm not 100% sure.
+          # A design that allows a proper complexity analysis of types like `tyOr` would be ideal.
           concrete = concreteType(c, a, f)
           if concrete == nil:
             return isNone
@@ -1743,21 +1992,34 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
     elif x.kind == tyGenericParam:
       result = isGeneric
     else:
+      # This is the bound type - can't benifit from these tallies
+      let
+        inheritancePenaltyOld = c.inheritancePenalty
       result = typeRel(c, x, a, flags) # check if it fits
+      c.inheritancePenalty = inheritancePenaltyOld
       if result > isGeneric: result = isGeneric
   of tyStatic:
-    let prev = PType(idTableGet(c.bindings, f))
+    let prev = idTableGet(c.bindings, f)
     if prev == nil:
       if aOrig.kind == tyStatic:
-        if f.base.kind notin {tyNone, tyGenericParam}:
+        if c.c.inGenericContext > 0 and aOrig.n == nil and not c.isNoCall:
+          # don't match unresolved static value to static param to avoid
+          # faulty instantiations in calls in generic bodies
+          # but not for generic invocations as they only check constraints
+          result = isNone
+        elif f.base.kind notin {tyNone, tyGenericParam}:
           result = typeRel(c, f.base, a, flags)
           if result != isNone and f.n != nil:
-            if not exprStructuralEquivalent(f.n, aOrig.n):
+            var r = tryResolvingStaticExpr(c, f.n)
+            if r == nil: r = f.n
+            if not exprStructuralEquivalent(r, aOrig.n) and
+                not (aOrig.n != nil and aOrig.n.kind == nkIntLit and
+                  inferStaticParam(c, r, aOrig.n.intVal)):
               result = isNone
         elif f.base.kind == tyGenericParam:
           # Handling things like `type A[T; Y: static T] = object`
           if f.base.len > 0: # There is a constraint, handle it
-            result = typeRel(c, f.base.lastSon, a, flags)
+            result = typeRel(c, f.base.last, a, flags)
           else:
             # No constraint
             if tfGenericTypeParam in f.flags:
@@ -1770,17 +2032,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         if result != isNone: put(c, f, aOrig)
       elif aOrig.n != nil and aOrig.n.typ != nil:
         result = if f.base.kind != tyNone:
-                   typeRel(c, f.lastSon, aOrig.n.typ, flags)
+                   typeRel(c, f.last, aOrig.n.typ, flags)
                  else: isGeneric
         if result != isNone:
-          var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ])
+          var boundType = newTypeS(tyStatic, c.c, aOrig.n.typ)
           boundType.n = aOrig.n
           put(c, f, boundType)
       else:
         result = isNone
     elif prev.kind == tyStatic:
       if aOrig.kind == tyStatic:
-        result = typeRel(c, prev.lastSon, a, flags)
+        result = typeRel(c, prev.last, a, flags)
         if result != isNone and prev.n != nil:
           if not exprStructuralEquivalent(prev.n, aOrig.n):
             result = isNone
@@ -1789,7 +2051,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       # XXX endless recursion?
       #result = typeRel(c, prev, aOrig, flags)
       result = isNone
-
   of tyInferred:
     let prev = f.previouslyInferred
     if prev != nil:
@@ -1799,14 +2060,18 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
       if result != isNone:
         c.inferredTypes.add f
         f.add a
-
   of tyTypeDesc:
-    var prev = PType(idTableGet(c.bindings, f))
+    var prev = idTableGet(c.bindings, f)
     if prev == nil:
       # proc foo(T: typedesc, x: T)
       # when `f` is an unresolved typedesc, `a` could be any
       # type, so we should not perform this check earlier
-      if a.kind != tyTypeDesc:
+      if c.c.inGenericContext > 0 and a.containsUnresolvedType:
+        # generic type bodies can sometimes compile call expressions
+        # prevent unresolved generic parameters from being passed to procs as
+        # typedesc parameters
+        result = isNone
+      elif a.kind != tyTypeDesc:
         if a.kind == tyGenericParam and tfWildcard in a.flags:
           # TODO: prevent `a` from matching as a wildcard again
           result = isGeneric
@@ -1826,32 +2091,40 @@ proc typeRel(c: var TCandidate, f, aOrig: PType,
         result = typeRel(c, prev.base, a.base, flags)
       else:
         result = isNone
-
   of tyTyped:
     if aOrig != nil:
       put(c, f, aOrig)
     result = isGeneric
-
-  of tyProxy:
+  of tyError:
     result = isEqual
-
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
     if f.n == nil or f.n.kind == nkEmpty: return isGeneric
-    let reevaluated = tryResolvingStaticExpr(c, f.n)
-    case reevaluated.typ.kind
+    if c.c.inGenericContext > 0:
+      # need to delay until instantiation
+      # also prevent infinite recursion below
+      return isNone
+    inc c.c.inGenericContext # to generate tyFromExpr again if unresolved
+    # use prepareNode for consistency with other tyFromExpr in semtypinst:
+    let instantiated = prepareTypesInBody(c.c, c.bindings, f.n)
+    let reevaluated = c.c.semExpr(c.c, instantiated).typ
+    dec c.c.inGenericContext
+    case reevaluated.kind
+    of tyFromExpr:
+      # not resolved
+      result = isNone
     of tyTypeDesc:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
+      result = typeRel(c, reevaluated.base, a, flags)
     of tyStatic:
-      result = typeRel(c, a, reevaluated.typ.base, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, reevaluated.base, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
     else:
       # bug #14136: other types are just like 'tyStatic' here:
-      result = typeRel(c, a, reevaluated.typ, flags)
-      if result != isNone and reevaluated.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
+      result = typeRel(c, reevaluated, a, flags)
+      if result != isNone and reevaluated.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.n):
           result = isNone
   of tyNone:
     if a.kind == tyNone: result = isEqual
@@ -1879,7 +2152,7 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
 
 proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
                          f: PType): PType =
-  result = PType(idTableGet(m.bindings, f))
+  result = idTableGet(m.bindings, f)
   if result == nil:
     result = generateTypeInstance(c, m.bindings, arg, f)
   if result == nil:
@@ -1890,23 +2163,112 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
                   c: PContext): PNode =
   result = newNodeI(kind, arg.info)
   if containsGenericType(f):
-    if not m.hasFauxMatch:
+    if not m.matchedErrorType:
       result.typ = getInstantiatedType(c, arg, m, f).skipTypes({tySink})
     else:
       result.typ = errorType(c)
   else:
     result.typ = f.skipTypes({tySink})
+  # keep varness
+  if arg.typ != nil and arg.typ.kind == tyVar:
+    result.typ = toVar(result.typ, tyVar, c.idgen)
+  else:
+    result.typ = result.typ.skipTypes({tyVar})
+
   if result.typ == nil: internalError(c.graph.config, arg.info, "implicitConv")
   result.add c.graph.emptyNode
-  result.add arg
+  if arg.typ != nil and arg.typ.kind == tyLent:
+    let a = newNodeIT(nkHiddenDeref, arg.info, arg.typ.elementType)
+    a.add arg
+    result.add a
+  else:
+    result.add arg
+
+proc convertLiteral(kind: TNodeKind, c: PContext, m: TCandidate; n: PNode, newType: PType): PNode =
+  # based off changeType but generates implicit conversions instead
+  template addConsiderNil(s, node) =
+    let val = node
+    if val.isNil: return nil
+    s.add(val)
+  case n.kind
+  of nkCurly:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      if n[i].kind == nkRange:
+        var x = copyNode(n[i])
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][0], elemType(newType))
+        x.addConsiderNil convertLiteral(kind, c, m, n[i][1], elemType(newType))
+        result.add x
+      else:
+        result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkBracket:
+    result = copyNode(n)
+    for i in 0..<n.len:
+      result.addConsiderNil convertLiteral(kind, c, m, n[i], elemType(newType))
+    result.typ = newType
+    return
+  of nkPar, nkTupleConstr:
+    let tup = newType.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct})
+    if tup.kind == tyTuple:
+      result = copyNode(n)
+      if n.len > 0 and n[0].kind == nkExprColonExpr:
+        # named tuple?
+        for i in 0..<n.len:
+          var name = n[i][0]
+          if name.kind != nkSym:
+            #globalError(c.config, name.info, "invalid tuple constructor")
+            return nil
+          if tup.n != nil:
+            var f = getSymFromList(tup.n, name.sym.name)
+            if f == nil:
+              #globalError(c.config, name.info, "unknown identifier: " & name.sym.name.s)
+              return nil
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], f.typ)
+          else:
+            result.addConsiderNil convertLiteral(kind, c, m, n[i][1], tup[i])
+      else:
+        for i in 0..<n.len:
+          result.addConsiderNil convertLiteral(kind, c, m, n[i], tup[i])
+      result.typ = newType
+      return
+  of nkCharLit..nkUInt64Lit:
+    if n.kind != nkUInt64Lit and not sameTypeOrNil(n.typ, newType) and isOrdinalType(newType):
+      let value = n.intVal
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkFloatLit..nkFloat64Lit:
+    if newType.skipTypes(abstractVarRange-{tyTypeDesc}).kind == tyFloat:
+      if not floatRangeCheck(n.floatVal, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  of nkSym:
+    if n.sym.kind == skEnumField and not sameTypeOrNil(n.sym.typ, newType) and isOrdinalType(newType):
+      let value = n.sym.position
+      if value < firstOrd(c.config, newType) or value > lastOrd(c.config, newType):
+        return nil
+      result = copyNode(n)
+      result.typ = newType
+      return
+  else: discard
+  return implicitConv(kind, newType, n, m, c)
 
-proc isLValue(c: PContext; n: PNode): bool {.inline.} =
+proc isLValue(c: PContext; n: PNode, isOutParam = false): bool {.inline.} =
   let aa = isAssignable(nil, n)
   case aa
   of arLValue, arLocalLValue, arStrange:
     result = true
   of arDiscriminant:
     result = c.inUncheckedAssignSection > 0
+  of arAddressableConst:
+    let sym = getRoot(n)
+    result = strictDefs in c.features and sym != nil and sym.kind == skLet and isOutParam
   else:
     result = false
 
@@ -1914,8 +2276,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
                    arg: PNode): PNode =
   result = nil
   for i in 0..<c.converters.len:
-    var src = c.converters[i].typ[1]
-    var dest = c.converters[i].typ[0]
+    var src = c.converters[i].typ.firstParamType
+    var dest = c.converters[i].typ.returnType
     # for generic type converters we need to check 'src <- a' before
     # 'f <- dest' in order to not break the unification:
     # see tests/tgenericconverter:
@@ -1947,8 +2309,8 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       if srca == isSubtype:
         param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c)
       elif src.kind in {tyVar}:
-        # Analyse the converter return type
-        param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1])
+        # Analyse the converter return type.
+        param = newNodeIT(nkHiddenAddr, arg.info, s.typ.firstParamType)
         param.add copyTree(arg)
       else:
         param = copyTree(arg)
@@ -1982,7 +2344,7 @@ proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
   if result != nil:
     if result.typ == nil: return nil
     # bug #13378, ensure we produce a real generic instantiation:
-    result = c.semExpr(c, call)
+    result = c.semExpr(c, call, {efNoSem2Check})
     # resulting type must be consistent with the other arguments:
     var r = typeRel(m, f[0], result.typ)
     if r < isGeneric: return nil
@@ -2004,11 +2366,12 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   of isNone: discard
 
 template matchesVoidProc(t: PType): bool =
-  (t.kind == tyProc and t.len == 1 and t[0] == nil) or
-    (t.kind == tyBuiltInTypeClass and t[0].kind == tyProc)
+  (t.kind == tyProc and t.len == 1 and t.returnType == nil) or
+    (t.kind == tyBuiltInTypeClass and t.elementType.kind == tyProc)
 
 proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
                         argSemantized, argOrig: PNode): PNode =
+  result = nil
   var
     fMaybeStatic = f.skipTypes({tyDistinct})
     arg = argSemantized
@@ -2033,13 +2396,15 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
          a.n == nil and
          tfGenericTypeParam notin a.flags:
         return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
-    else:
+    elif a.kind == tyFromExpr and c.inGenericContext > 0:
+      # don't try to evaluate
+      discard
+    elif arg.kind != nkEmpty:
       var evaluated = c.semTryConstExpr(c, arg)
       if evaluated != nil:
         # Don't build the type in-place because `evaluated` and `arg` may point
         # to the same object and we'd end up creating recursive types (#9255)
-        let typ = newTypeS(tyStatic, c)
-        typ.sons = @[evaluated.typ]
+        let typ = newTypeS(tyStatic, c, son = evaluated.typ)
         typ.n = evaluated
         arg = copyTree(arg) # fix #12864
         arg.typ = typ
@@ -2072,36 +2437,58 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     else:
       return argSemantized # argOrig
 
-  # If r == isBothMetaConvertible then we rerun typeRel.
-  # bothMetaCounter is for safety to avoid any infinite loop,
-  #  I don't have any example when it is needed.
-  # lastBindingsLenth is used to check whether m.bindings remains the same,
-  #  because in that case there is no point in continuing.
-  var bothMetaCounter = 0
-  var lastBindingsLength = -1
-  while r == isBothMetaConvertible and
-      lastBindingsLength != m.bindings.counter and
-      bothMetaCounter < 100:
-    lastBindingsLength = m.bindings.counter
-    inc(bothMetaCounter)
-    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
-      result = c.semInferredLambda(c, m.bindings, arg)
-    elif arg.kind != nkSym:
-      return nil
-    else:
-      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
-      result = newSymNode(inferred, arg.info)
-    inc(m.convMatches)
-    arg = result
-    r = typeRel(m, f, arg.typ)
+  block instantiateGenericRoutine:
+    # In the case where the matched value is a generic proc, we need to
+    # fully instantiate it and then rerun typeRel to make sure it matches.
+    # instantiationCounter is for safety to avoid any infinite loop,
+    #  I don't have any example when it is needed.
+    # lastBindingCount is used to check whether m.bindings remains the same,
+    #  because in that case there is no point in continuing.
+    var instantiationCounter = 0
+    var lastBindingCount = -1
+    while r in {isBothMetaConvertible, isInferred, isInferredConvertible} and
+        lastBindingCount != m.bindings.len and
+        instantiationCounter < 100:
+      lastBindingCount = m.bindings.len
+      inc(instantiationCounter)
+      if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
+        result = c.semInferredLambda(c, m.bindings, arg)
+      elif arg.kind != nkSym:
+        return nil
+      elif arg.sym.kind in {skMacro, skTemplate}:
+        return nil
+      else:
+        if arg.sym.ast == nil:
+          return nil
+        let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
+        result = newSymNode(inferred, arg.info)
+      arg = result
+      r = typeRel(m, f, arg.typ)
 
   case r
   of isConvertible:
+    if f.skipTypes({tyRange}).kind in {tyInt, tyUInt}:
+      inc(m.convMatches)
     inc(m.convMatches)
-    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
+    if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tySet:
+      if tfIsConstructor in a.flags and arg.kind == nkCurly:
+        # we marked the set as convertible only because the arg is a literal
+        # in which case we individually convert each element
+        let t =
+          if containsGenericType(f):
+            getInstantiatedType(c, arg, m, f).skipTypes({tySink})
+          else:
+            f.skipTypes({tySink})
+        result = convertLiteral(nkHiddenStdConv, c, m, arg, t)
+      else:
+        result = nil
+    else:
+      result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isIntConv:
     # I'm too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
+    if f.skipTypes({tyRange}).kind notin {tyInt, tyUInt}:
+      inc(m.intConvMatches)
     inc(m.intConvMatches)
     result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isSubtype:
@@ -2116,25 +2503,20 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       result = arg
     else:
       result = implicitConv(nkHiddenStdConv, f, arg, m, c)
-  of isInferred, isInferredConvertible:
-    if arg.kind in {nkProcDef, nkFuncDef, nkIteratorDef} + nkLambdaKinds:
-      result = c.semInferredLambda(c, m.bindings, arg)
-    elif arg.kind != nkSym:
-      return nil
-    else:
-      let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info)
-      result = newSymNode(inferred, arg.info)
-    if r == isInferredConvertible:
-      inc(m.convMatches)
-      result = implicitConv(nkHiddenStdConv, f, result, m, c)
-    else:
-      inc(m.genericMatches)
+  of isInferred:
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.genericMatches)
+  of isInferredConvertible:
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.convMatches)
+    result = implicitConv(nkHiddenStdConv, f, result, m, c)
   of isGeneric:
     inc(m.genericMatches)
     if arg.typ == nil:
       result = arg
-    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or
-         m.inheritancePenalty > oldInheritancePenalty:
+    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple or cmpInheritancePenalty(oldInheritancePenalty, m.inheritancePenalty) > 0:
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
     elif arg.typ.isEmptyContainer:
       result = arg.copyTree
@@ -2142,8 +2524,10 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
     else:
       result = arg
   of isBothMetaConvertible:
-    # This is the result for the 101th time.
-    result = nil
+    # result should be set in above while loop:
+    assert result != nil
+    inc(m.convMatches)
+    result = arg
   of isFromIntLit:
     # too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
@@ -2152,17 +2536,21 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
   of isEqual:
     inc(m.exactMatches)
     result = arg
-    if skipTypes(f, abstractVar-{tyTypeDesc}).kind == tyTuple or
+    let ff = skipTypes(f, abstractVar-{tyTypeDesc})
+    if ff.kind == tyTuple or
       (arg.typ != nil and skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple):
       result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isNone:
     # do not do this in ``typeRel`` as it then can't infer T in ``ref T``:
-    if a.kind in {tyProxy, tyUnknown}:
+    if a.kind == tyFromExpr: return nil
+    elif a.kind == tyError:
       inc(m.genericMatches)
-      m.fauxMatch = a.kind
+      m.matchedErrorType = true
       return arg
     elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
       # lift do blocks without params to lambdas
+      # now deprecated
+      message(c.config, argOrig.info, warnStmtListLambda)
       let p = c.graph
       let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, body = argOrig,
           params = nkFormalParams.newTree(p.emptyNode), name = p.emptyNode, pattern = p.emptyNode,
@@ -2179,6 +2567,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       if f.n != nil:
         # Forward to the varargs converter
         result = localConvMatch(c, m, f, a, arg)
+      elif f[0].kind == tyTyped:
+        inc m.genericMatches
+        result = arg
       else:
         r = typeRel(m, base(f), a)
         case r
@@ -2206,61 +2597,103 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
           result = userConvMatch(c, m, base(f), a, arg)
           if result != nil: m.baseTypeMatch = true
 
+proc staticAwareTypeRel(m: var TCandidate, f: PType, arg: var PNode): TTypeRelation =
+  if f.kind == tyStatic and f.base.kind == tyProc:
+    # The ast of the type does not point to the symbol.
+    # Without this we will never resolve a `static proc` with overloads
+    let copiedNode = copyNode(arg)
+    copiedNode.typ = exactReplica(copiedNode.typ)
+    copiedNode.typ.n = arg
+    arg = copiedNode
+  typeRel(m, f, arg.typ)
+
+
 proc paramTypesMatch*(m: var TCandidate, f, a: PType,
                       arg, argOrig: PNode): PNode =
   if arg == nil or arg.kind notin nkSymChoices:
     result = paramTypesMatchAux(m, f, a, arg, argOrig)
   else:
-    # CAUTION: The order depends on the used hashing scheme. Thus it is
-    # incorrect to simply use the first fitting match. However, to implement
-    # this correctly is inefficient. We have to copy `m` here to be able to
-    # roll back the side effects of the unification algorithm.
-    let c = m.c
-    var
-      x = newCandidate(c, m.callee)
-      y = newCandidate(c, m.callee)
-      z = newCandidate(c, m.callee)
-    x.calleeSym = m.calleeSym
-    y.calleeSym = m.calleeSym
-    z.calleeSym = m.calleeSym
+    # symbol kinds that don't participate in symchoice type disambiguation:
+    let matchSet = {low(TSymKind)..high(TSymKind)} - {skModule, skPackage}
+
     var best = -1
-    for i in 0..<arg.len:
-      if arg[i].sym.kind in {skProc, skFunc, skMethod, skConverter,
-                             skIterator, skMacro, skTemplate, skEnumField}:
-        copyCandidate(z, m)
-        z.callee = arg[i].typ
-        if tfUnresolved in z.callee.flags: continue
-        z.calleeSym = arg[i].sym
-        # XXX this is still all wrong: (T, T) should be 2 generic matches
-        # and  (int, int) 2 exact matches, etc. Essentially you cannot call
-        # typeRel here and expect things to work!
-        let r = typeRel(z, f, arg[i].typ)
-        incMatches(z, r, 2)
-        if r != isNone:
-          z.state = csMatch
-          case x.state
-          of csEmpty, csNoMatch:
-            x = z
+    result = arg
+
+    var actingF = f
+    if f.kind == tyVarargs:
+      if m.calleeSym.kind in {skTemplate, skMacro}:
+        actingF = f[0]
+    if actingF.kind in {tyTyped, tyUntyped}:
+      var
+        bestScope = -1
+        counts = 0
+      for i in 0..<arg.len:
+        if arg[i].sym.kind in matchSet:
+          let thisScope = cmpScopes(m.c, arg[i].sym)
+          if thisScope > bestScope:
             best = i
-          of csMatch:
-            let cmp = cmpCandidates(x, z)
-            if cmp < 0:
-              best = i
-              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(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 == tyUntyped' should match
-      # anyway:
-      if f.kind in {tyUntyped, tyTyped}: result = arg
-      else: result = nil
+            bestScope = thisScope
+            counts = 0
+          elif thisScope == bestScope:
+            inc counts
+      if best == -1:
+        result = nil
+      elif counts > 0:
+        m.genericMatches = 1
+        best = -1
     else:
+      # CAUTION: The order depends on the used hashing scheme. Thus it is
+      # incorrect to simply use the first fitting match. However, to implement
+      # this correctly is inefficient. We have to copy `m` here to be able to
+      # roll back the side effects of the unification algorithm.
+      let c = m.c
+      var
+        x = newCandidate(c, m.callee)  # potential "best"
+        y = newCandidate(c, m.callee)  # potential competitor with x
+        z = newCandidate(c, m.callee)  # buffer for copies of m
+      x.calleeSym = m.calleeSym
+      y.calleeSym = m.calleeSym
+      z.calleeSym = m.calleeSym
+
+      for i in 0..<arg.len:
+        if arg[i].sym.kind in matchSet:
+          copyCandidate(z, m)
+          z.callee = arg[i].typ
+          if arg[i].sym.kind == skType and z.callee.kind != tyTypeDesc:
+            # creating the symchoice with the type sym having typedesc type
+            # breaks a lot of stuff, so we make the typedesc type here
+            # mirrored from `newSymNodeTypeDesc`
+            z.callee = newType(tyTypeDesc, c.idgen, arg[i].sym.owner)
+            z.callee.addSonSkipIntLit(arg[i].sym.typ, c.idgen)
+          if tfUnresolved in z.callee.flags: continue
+          z.calleeSym = arg[i].sym
+          z.calleeScope = cmpScopes(m.c, arg[i].sym)
+          # XXX this is still all wrong: (T, T) should be 2 generic matches
+          # and  (int, int) 2 exact matches, etc. Essentially you cannot call
+          # typeRel here and expect things to work!
+          let r = staticAwareTypeRel(z, f, arg[i])
+          incMatches(z, r, 2)
+          if r != isNone:
+            z.state = csMatch
+            case x.state
+            of csEmpty, csNoMatch:
+              x = z
+              best = i
+            of csMatch:
+              let cmp = cmpCandidates(x, z, isFormal=false)
+              if cmp < 0:
+                best = i
+                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, isFormal=false) == 0:
+        if x.state != csMatch:
+          internalError(m.c.graph.config, arg.info, "x.state is not csMatch")
+        result = nil
+    if best > -1 and result != nil:
       # only one valid interpretation found:
       markUsed(m.c, arg.info, arg[best].sym)
       onUse(arg.info, arg[best].sym)
@@ -2282,7 +2715,7 @@ proc setSon(father: PNode, at: int, son: PNode) =
 # we are allowed to modify the calling node in the 'prepare*' procs:
 proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
   if formal.kind == tyUntyped and formal.len != 1:
-    # {tyTypeDesc, tyUntyped, tyTyped, tyProxy}:
+    # {tyTypeDesc, tyUntyped, tyTyped, tyError}:
     # a.typ == nil is valid
     result = a
   elif a.typ.isNil:
@@ -2320,7 +2753,7 @@ proc arrayConstr(c: PContext, n: PNode): PType =
   result = newTypeS(tyArray, c)
   rawAddSon(result, makeRangeType(c, 0, 0, n.info))
   addSonSkipIntLit(result, skipTypes(n.typ,
-      {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen)
+      {tyVar, tyLent, tyOrdinal}), c.idgen)
 
 proc arrayConstr(c: PContext, info: TLineInfo): PType =
   result = newTypeS(tyArray, c)
@@ -2329,11 +2762,14 @@ proc arrayConstr(c: PContext, info: TLineInfo): PType =
 
 proc incrIndexType(t: PType) =
   assert t.kind == tyArray
-  inc t[0].n[1].intVal
+  inc t.indexType.n[1].intVal
 
 template isVarargsUntyped(x): untyped =
   x.kind == tyVarargs and x[0].kind == tyUntyped
 
+template isVarargsTyped(x): untyped =
+  x.kind == tyVarargs and x[0].kind == tyTyped
+
 proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
   # see https://github.com/nim-lang/RFCs/issues/405
   result = int.high
@@ -2341,7 +2777,9 @@ proc findFirstArgBlock(m: var TCandidate, n: PNode): int =
     # checking `nfBlockArg in n[a2].flags` wouldn't work inside templates
     if n[a2].kind != nkStmtList: break
     let formalLast = m.callee.n[m.callee.n.len - (n.len - a2)]
-    if formalLast.kind == nkSym and formalLast.sym.ast == nil:
+    # parameter has to occupy space (no default value, not void or varargs)
+    if formalLast.kind == nkSym and formalLast.sym.ast == nil and
+        formalLast.sym.typ.kind notin {tyVoid, tyVarargs}:
       result = a2
     else: break
 
@@ -2355,7 +2793,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
     return
 
   template checkConstraint(n: untyped) {.dirty.} =
-    if not formal.constraint.isNil:
+    if not formal.constraint.isNil and sfCodegenDecl notin formal.flags:
       if matchNodeKinds(formal.constraint, n):
         # better match over other routines with no such restriction:
         inc(m.genericMatches, 100)
@@ -2368,7 +2806,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
         if argConverter.typ.kind notin {tyVar}:
           m.firstMismatch.kind = kVarNeeded
           noMatch()
-      elif not isLValue(c, n):
+      elif not (isLValue(c, n, isOutParam(formal.typ))):
         m.firstMismatch.kind = kVarNeeded
         noMatch()
 
@@ -2381,7 +2819,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
     a = 1 # iterates over the actual given arguments
     f = if m.callee.kind != tyGenericBody: 1
         else: 0 # iterates over formal parameters
-    arg: PNode # current prepared argument
+    arg: PNode = nil # current prepared argument
     formalLen = m.callee.n.len
     formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter
     container: PNode = nil # constructed container
@@ -2506,7 +2944,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
                                     n[a], nOrig[a])
           if arg == nil:
             noMatch()
-          if m.baseTypeMatch:
+          if formal.typ.isVarargsTyped and m.calleeSym.kind in {skTemplate, skMacro}:
+            if container.isNil:
+              container = newNodeIT(nkBracket, n[a].info, arrayConstr(c, n.info))
+              setSon(m.call, formal.position + 1, implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
+            else:
+              incrIndexType(container.typ)
+            container.add n[a]
+            f = max(f, formalLen - n.len + a + 1)
+          elif m.baseTypeMatch:
             assert formal.typ.kind == tyVarargs
             #assert(container == nil)
             if container.isNil:
@@ -2546,12 +2992,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int
   m.firstMismatch.arg = a
   m.firstMismatch.formal = formal
 
-proc semFinishOperands*(c: PContext, n: PNode) =
-  # this needs to be called to ensure that after overloading resolution every
-  # argument has been sem'checked:
-  for i in 1..<n.len:
-    n[i] = prepareOperand(c, n[i])
-
 proc partialMatch*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   # for 'suggest' support:
   var marker = initIntSet()
@@ -2567,6 +3007,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
       inc m.genericMatches
       inc m.exactMatches
     return
+  # initCandidate may have given csNoMatch if generic params didn't match:
+  if m.state == csNoMatch: return
   var marker = initIntSet()
   matchesAux(c, n, nOrig, m, marker)
   if m.state == csNoMatch: return
@@ -2589,14 +3031,16 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
           m.firstMismatch.formal = formal
           break
       else:
+        # mirrored with updateDefaultParams:
         if formal.ast.kind == nkEmpty:
           # The default param value is set to empty in `instantiateProcType`
           # when the type of the default expression doesn't match the type
           # of the instantiated proc param:
-          localError(c.config, m.call.info,
-                     ("The default parameter '$1' has incompatible type " &
-                      "with the explicitly requested proc instantiation") %
-                      formal.name.s)
+          pushInfoContext(c.config, m.call.info,
+            if m.calleeSym != nil: m.calleeSym.detailedInfo else: "")
+          typeMismatch(c.config, formal.ast.info, formal.typ, formal.ast.typ, formal.ast)
+          popInfoContext(c.config)
+          formal.ast.typ = errorType(c)
         if nfDefaultRefsParam in formal.ast.flags:
           m.call.flags.incl nfDefaultRefsParam
         var defaultValue = copyTree(formal.ast)
@@ -2605,7 +3049,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
         # proc foo(x: T = 0.0)
         # foo()
         if {tfImplicitTypeParam, tfGenericTypeParam} * formal.typ.flags != {}:
-          let existing = PType(idTableGet(m.bindings, formal.typ))
+          let existing = idTableGet(m.bindings, formal.typ)
           if existing == nil or existing.kind == tyTypeDesc:
             # see bug #11600:
             put(m, formal.typ, defaultValue.typ)
@@ -2614,7 +3058,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
   # forget all inferred types if the overload matching failed
   if m.state == csNoMatch:
     for t in m.inferredTypes:
-      if t.len > 1: t.sons.setLen 1
+      if t.len > 1: t.newSons 1
 
 proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool =
   var m = newCandidate(c, f)
@@ -2628,11 +3072,9 @@ proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool =
     # pattern templates do not allow for conversions except from int literal
     res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256]
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
 
 proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
-                      op: TTypeAttachedOp; col: int): PSym {.nosinks.} =
+                      op: TTypeAttachedOp; col: int): PSym =
   var m = newCandidate(c, dc.typ)
   if col >= dc.typ.len:
     localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
@@ -2640,10 +3082,11 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
   var f = dc.typ[col]
 
   if op == attachedDeepCopy:
-    if f.kind in {tyRef, tyPtr}: f = f.lastSon
+    if f.kind in {tyRef, tyPtr}: f = f.elementType
   else:
-    if f.kind in {tyVar}: f = f.lastSon
+    if f.kind in {tyVar}: f = f.elementType
   if typeRel(m, f, t) == isNone:
+    result = nil
     localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'")
   else:
     result = c.semGenerateInstance(c, dc, m.bindings, info)
diff --git a/compiler/sinkparameter_inference.nim b/compiler/sinkparameter_inference.nim
index fa9f2b445..09d54ec79 100644
--- a/compiler/sinkparameter_inference.nim
+++ b/compiler/sinkparameter_inference.nim
@@ -19,7 +19,6 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
     var local = p # sink parameter?
     passToSink(local)
   ]#
-  if optSinkInference notin config.options: return
   case arg.kind
   of nkSym:
     if arg.sym.kind == skParam and
@@ -32,7 +31,7 @@ proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNod
       if sfWasForwarded notin owner.flags:
         let argType = arg.sym.typ
 
-        let sinkType = newType(tySink, nextTypeId(idgen), owner)
+        let sinkType = newType(tySink, idgen, owner)
         sinkType.size = argType.size
         sinkType.align = argType.align
         sinkType.paddingAtEnd = argType.paddingAtEnd
diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim
index 009acf6eb..1dd481ec0 100644
--- a/compiler/sizealignoffsetimpl.nim
+++ b/compiler/sizealignoffsetimpl.nim
@@ -12,7 +12,7 @@
 proc align(address, alignment: BiggestInt): BiggestInt =
   result = (address + (alignment - 1)) and not (alignment - 1)
 
-proc align(address, alignment: int): int =
+proc align(address, alignment: int32): int32 =
   result = (address + (alignment - 1)) and not (alignment - 1)
 
 const
@@ -29,25 +29,25 @@ proc raiseIllegalTypeRecursion() =
   raise newException(IllegalTypeRecursionError, "illegal type recursion")
 
 type
-  OffsetAccum = object
-    maxAlign: int
-    offset: int
+  OffsetAccum* = object
+    maxAlign*: int32
+    offset*: int32
 
-proc inc(arg: var OffsetAccum; value: int) =
+proc inc*(arg: var OffsetAccum; value: int32) =
   if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
   if value == szUnknownSize or arg.offset == szUnknownSize:
     arg.offset = szUnknownSize
   else:
     arg.offset += value
 
-proc alignmentMax(a, b: int): int =
+proc alignmentMax(a, b: int32): int32 =
   if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion()
   if a == szUnknownSize or b == szUnknownSize:
     szUnknownSize
   else:
     max(a, b)
 
-proc align(arg: var OffsetAccum; value: int) =
+proc align*(arg: var OffsetAccum; value: int32) =
   if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion()
   if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
     arg.maxAlign = szUnknownSize
@@ -65,7 +65,7 @@ proc mergeBranch(arg: var OffsetAccum; value: OffsetAccum) =
     arg.offset = max(arg.offset, value.offset)
     arg.maxAlign = max(arg.maxAlign, value.maxAlign)
 
-proc finish(arg: var OffsetAccum): int =
+proc finish(arg: var OffsetAccum): int32 =
   if arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize:
     result = szUnknownSize
     arg.offset = szUnknownSize
@@ -73,7 +73,7 @@ proc finish(arg: var OffsetAccum): int =
     result = align(arg.offset, arg.maxAlign) - arg.offset
     arg.offset += result
 
-proc computeSizeAlign(conf: ConfigRef; typ: PType)
+proc computeSizeAlign*(conf: ConfigRef; typ: PType)
 
 proc computeSubObjectAlign(conf: ConfigRef; n: PNode): BiggestInt =
   ## returns object alignment
@@ -122,20 +122,20 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; a
   of nkRecCase:
     assert(n[0].kind == nkSym)
     computeObjectOffsetsFoldFunction(conf, n[0], packed, accum)
-    var maxChildAlign: int = if accum.offset == szUnknownSize: szUnknownSize else: 1
+    var maxChildAlign = if accum.offset == szUnknownSize: szUnknownSize.int32 else: 1'i32
     if not packed:
       for i in 1..<n.len:
         let child = n[i]
         case child.kind
         of nkOfBranch, nkElse:
           # offset parameter cannot be known yet, it needs to know the alignment first
-          let align = int(computeSubObjectAlign(conf, n[i].lastSon))
+          let align = int32(computeSubObjectAlign(conf, n[i].lastSon))
           maxChildAlign = alignmentMax(maxChildAlign, align)
         else:
           internalError(conf, "computeObjectOffsetsFoldFunction(record case branch)")
     if maxChildAlign == szUnknownSize:
       setOffsetsToUnknown(n)
-      accum.offset  = szUnknownSize
+      accum.offset = szUnknownSize
       accum.maxAlign = szUnknownSize
     else:
       # the union needs to be aligned first, before the offsets can be assigned
@@ -150,15 +150,15 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; a
     for i, child in n.sons:
       computeObjectOffsetsFoldFunction(conf, child, packed, accum)
   of nkSym:
-    var size = szUnknownSize
-    var align = szUnknownSize
+    var size = szUnknownSize.int32
+    var align = szUnknownSize.int32
     if n.sym.bitsize == 0: # 0 represents bitsize not set
       computeSizeAlign(conf, n.sym.typ)
-      size = n.sym.typ.size.int
-      align = if packed: 1 else: n.sym.typ.align.int
+      size = n.sym.typ.size.int32
+      align = if packed: 1 else: n.sym.typ.align.int32
     accum.align(align)
     if n.sym.alignment > 0:
-      accum.align(n.sym.alignment)
+      accum.align(n.sym.alignment.int32)
     n.sym.offset = accum.offset
     accum.inc(size)
   else:
@@ -180,15 +180,15 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bo
       discard finish(branchAccum)
       accum.mergeBranch(branchAccum)
   of nkSym:
-    var size = szUnknownSize
-    var align = szUnknownSize
+    var size = szUnknownSize.int32
+    var align = szUnknownSize.int32
     if n.sym.bitsize == 0: # 0 represents bitsize not set
       computeSizeAlign(conf, n.sym.typ)
-      size = n.sym.typ.size.int
-      align = if packed: 1 else: n.sym.typ.align.int
+      size = n.sym.typ.size.int32
+      align = if packed: 1 else: n.sym.typ.align.int32
     accum.align(align)
     if n.sym.alignment > 0:
-      accum.align(n.sym.alignment)
+      accum.align(n.sym.alignment.int32)
     n.sym.offset = accum.offset
     accum.inc(size)
   else:
@@ -248,7 +248,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.size = conf.target.ptrSize
     typ.align = int16(conf.target.ptrSize)
   of tyCstring, tySequence, tyPtr, tyRef, tyVar, tyLent:
-    let base = typ.lastSon
+    let base = typ.last
     if base == typ:
       # this is not the correct location to detect ``type A = ptr A``
       typ.size = szIllegalRecursion
@@ -262,9 +262,9 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.size = conf.target.ptrSize
 
   of tyArray:
-    computeSizeAlign(conf, typ[1])
-    let elemSize = typ[1].size
-    let len = lengthOrd(conf, typ[0])
+    computeSizeAlign(conf, typ.elementType)
+    let elemSize = typ.elementType.size
+    let len = lengthOrd(conf, typ.indexType)
     if elemSize < 0:
       typ.size = elemSize
       typ.align = int16(elemSize)
@@ -273,10 +273,10 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.align = szUnknownSize
     else:
       typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize)
-      typ.align = typ[1].align
+      typ.align = typ.elementType.align
 
   of tyUncheckedArray:
-    let base = typ.lastSon
+    let base = typ.last
     computeSizeAlign(conf, base)
     typ.size = 0
     typ.align = base.align
@@ -300,11 +300,11 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
         typ.size = 8
         typ.align = int16(conf.floatInt64Align)
   of tySet:
-    if typ[0].kind == tyGenericParam:
+    if typ.elementType.kind == tyGenericParam:
       typ.size = szUncomputedSize
       typ.align = szUncomputedSize
     else:
-      let length = toInt64(lengthOrd(conf, typ[0]))
+      let length = toInt64(lengthOrd(conf, typ.elementType))
       if length <= 8:
         typ.size = 1
         typ.align = 1
@@ -319,27 +319,26 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
         typ.align = int16(conf.floatInt64Align)
       elif align(length, 8) mod 8 == 0:
         typ.size = align(length, 8) div 8
-        typ.align = int16(conf.floatInt64Align)
+        typ.align = 1
       else:
         typ.size = align(length, 8) div 8 + 1
-        typ.align = int16(conf.floatInt64Align)
+        typ.align = 1
   of tyRange:
-    computeSizeAlign(conf, typ[0])
-    typ.size = typ[0].size
-    typ.align = typ[0].align
-    typ.paddingAtEnd = typ[0].paddingAtEnd
+    computeSizeAlign(conf, typ.elementType)
+    typ.size = typ.elementType.size
+    typ.align = typ.elementType.align
+    typ.paddingAtEnd = typ.elementType.paddingAtEnd
 
   of tyTuple:
     try:
       var accum = OffsetAccum(maxAlign: 1)
-      for i in 0..<typ.len:
-        let child = typ[i]
+      for i, child in typ.ikids:
         computeSizeAlign(conf, child)
         accum.align(child.align)
         if typ.n != nil: # is named tuple (has field symbols)?
           let sym = typ.n[i].sym
           sym.offset = accum.offset
-        accum.inc(int(child.size))
+        accum.inc(int32(child.size))
       typ.paddingAtEnd = int16(accum.finish())
       typ.size = if accum.offset == 0: 1 else: accum.offset
       typ.align = int16(accum.maxAlign)
@@ -351,27 +350,27 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
   of tyObject:
     try:
       var accum =
-        if typ[0] != nil:
+        if typ.baseClass != nil:
           # compute header size
-          var st = typ[0]
+          var st = typ.baseClass
           while st.kind in skipPtrs:
-            st = st[^1]
+            st = st.skipModifier
           computeSizeAlign(conf, st)
           if conf.backend == backendCpp:
             OffsetAccum(
-              offset: int(st.size) - int(st.paddingAtEnd),
+              offset: int32(st.size) - int32(st.paddingAtEnd),
               maxAlign: st.align
             )
           else:
             OffsetAccum(
-              offset: int(st.size),
+              offset: int32(st.size),
               maxAlign: st.align
             )
         elif isObjectWithTypeFieldPredicate(typ):
           # this branch is taken for RootObj
           OffsetAccum(
-            offset: conf.target.intSize,
-            maxAlign: conf.target.intSize
+            offset: conf.target.intSize.int32,
+            maxAlign: conf.target.intSize.int32
           )
         else:
           OffsetAccum(maxAlign: 1)
@@ -386,6 +385,13 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
         accum.maxAlign = 1
         computeObjectOffsetsFoldFunction(conf, typ.n, true, accum)
       else:
+        if typ.baseClass == nil and lacksMTypeField(typ) and typ.n.len == 1 and
+            typ.n[0].kind == nkSym and
+            typ.n[0].sym.typ.skipTypes(abstractInst).kind == tyUncheckedArray:
+          # a dummy field is generated for an object with a single field
+          # with an UncheckedArray type
+          assert accum.offset == 0
+          accum.offset = 1
         computeObjectOffsetsFoldFunction(conf, typ.n, false, accum)
       let paddingAtEnd = int16(accum.finish())
       if typ.sym != nil and
@@ -403,24 +409,24 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
       typ.align = szIllegalRecursion
       typ.paddingAtEnd = szIllegalRecursion
   of tyInferred:
-    if typ.len > 1:
-      computeSizeAlign(conf, typ.lastSon)
-      typ.size = typ.lastSon.size
-      typ.align = typ.lastSon.align
-      typ.paddingAtEnd = typ.lastSon.paddingAtEnd
+    if typ.hasElementType:
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
 
   of tyGenericInst, tyDistinct, tyGenericBody, tyAlias, tySink, tyOwned:
-    computeSizeAlign(conf, typ.lastSon)
-    typ.size = typ.lastSon.size
-    typ.align = typ.lastSon.align
-    typ.paddingAtEnd = typ.lastSon.paddingAtEnd
+    computeSizeAlign(conf, typ.skipModifier)
+    typ.size = typ.skipModifier.size
+    typ.align = typ.skipModifier.align
+    typ.paddingAtEnd = typ.last.paddingAtEnd
 
   of tyTypeClasses:
     if typ.isResolvedUserTypeClass:
-      computeSizeAlign(conf, typ.lastSon)
-      typ.size = typ.lastSon.size
-      typ.align = typ.lastSon.align
-      typ.paddingAtEnd = typ.lastSon.paddingAtEnd
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
     else:
       typ.size = szUnknownSize
       typ.align = szUnknownSize
@@ -433,17 +439,16 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) =
     typ.paddingAtEnd = typ.base.paddingAtEnd
 
   of tyForward:
-    # is this really illegal recursion, or maybe just unknown?
-    typ.size = szIllegalRecursion
-    typ.align = szIllegalRecursion
-    typ.paddingAtEnd = szIllegalRecursion
+    typ.size = szUnknownSize
+    typ.align = szUnknownSize
+    typ.paddingAtEnd = szUnknownSize
 
   of tyStatic:
     if typ.n != nil:
-      computeSizeAlign(conf, typ.lastSon)
-      typ.size = typ.lastSon.size
-      typ.align = typ.lastSon.align
-      typ.paddingAtEnd = typ.lastSon.paddingAtEnd
+      computeSizeAlign(conf, typ.last)
+      typ.size = typ.last.size
+      typ.align = typ.last.align
+      typ.paddingAtEnd = typ.last.paddingAtEnd
     else:
       typ.size = szUnknownSize
       typ.align = szUnknownSize
@@ -503,6 +508,7 @@ template foldOffsetOf*(conf: ConfigRef; n: PNode; fallback: PNode): PNode =
     elif node[1].kind == nkCheckedFieldExpr:
       dotExpr = node[1][0]
     else:
+      dotExpr = nil
       localError(config, node.info, "can't compute offsetof on this ast")
 
   assert dotExpr != nil
diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim
index b87de75f3..1395168cd 100644
--- a/compiler/sourcemap.nim
+++ b/compiler/sourcemap.nim
@@ -1,383 +1,206 @@
-import os, strformat, strutils, tables, sets, ropes, json, algorithm
+import std/[strutils, strscans, parseutils, assertions]
 
 type
-  SourceNode* = ref object
-    line*:      int
-    column*:    int
-    source*:    string
-    name*:      string
-    children*:  seq[Child]
-
-  C = enum cSourceNode, cSourceString
-
-  Child* = ref object
-    case kind*: C:
-    of cSourceNode:
-      node*:  SourceNode
-    of cSourceString:
-      s*:     string
-
-  SourceMap* = ref object
+  Segment = object
+    ## Segment refers to a block of something in the JS output.
+    ## This could be a token or an entire line
+    original: int # Column in the Nim source
+    generated: int # Column in the generated JS
+    name: int # Index into names list (-1 for no name)
+
+  Mapping = object
+    ## Mapping refers to a line in the JS output.
+    ## It is made up of segments which refer to the tokens in the line
+    case inSource: bool # Whether the line in JS has Nim equivalent
+    of true:
+      file: int # Index into files list
+      line: int # 0 indexed line of code in the Nim source
+      segments: seq[Segment]
+    else: discard
+
+  SourceInfo = object
+    mappings: seq[Mapping]
+    names, files: seq[string]
+
+  SourceMap* = object
     version*:   int
     sources*:   seq[string]
     names*:     seq[string]
     mappings*:  string
     file*:      string
-    # sourceRoot*: string
-    # sourcesContent*: string
-
-  SourceMapGenerator = ref object
-    file:           string
-    sourceRoot:     string
-    skipValidation: bool
-    sources:        seq[string]
-    names:          seq[string]
-    mappings:       seq[Mapping]
-
-  Mapping* = ref object
-    source*:        string
-    original*:      tuple[line: int, column: int]
-    generated*:     tuple[line: int, column: int]
-    name*:          string
-    noSource*:      bool
-    noName*:        bool
-
-
-proc child*(s: string): Child =
-  Child(kind: cSourceString, s: s)
-
-
-proc child*(node: SourceNode): Child =
-  Child(kind: cSourceNode, node: node)
-
 
-proc newSourceNode(line: int, column: int, path: string, node: SourceNode, name: string = ""): SourceNode =
-  SourceNode(line: line, column: column, source: path, name: name, children: @[child(node)])
-
-
-proc newSourceNode(line: int, column: int, path: string, s: string, name: string = ""): SourceNode =
-  SourceNode(line: line, column: column, source: path, name: name, children: @[child(s)])
-
-
-proc newSourceNode(line: int, column: int, path: string, children: seq[Child], name: string = ""): SourceNode =
-  SourceNode(line: line, column: column, source: path, name: name, children: children)
-
-
-
-
-# debugging
-
-
-proc text*(sourceNode: SourceNode, depth: int): string =
-  let empty = "  "
-  result = &"{repeat(empty, depth)}SourceNode({sourceNode.source}:{sourceNode.line}:{sourceNode.column}):\n"
-  for child in sourceNode.children:
-    if child.kind == cSourceString:
-      result.add(&"{repeat(empty, depth + 1)}{child.s}\n")
-    else:
-      result.add(child.node.text(depth + 1))
-
-
-proc `$`*(sourceNode: SourceNode): string = text(sourceNode, 0)
+func addSegment(info: var SourceInfo, original, generated: int, name: string = "") {.raises: [].} =
+  ## Adds a new segment into the current line
+  assert info.mappings.len > 0, "No lines have been added yet"
+  var segment = Segment(original: original, generated: generated, name: -1)
+  if name != "":
+    # Make name be index into names list
+    segment.name = info.names.find(name)
+    if segment.name == -1:
+      segment.name = info.names.len
+      info.names &= name
+
+  assert info.mappings[^1].inSource, "Current line isn't in Nim source"
+  info.mappings[^1].segments &= segment
+
+func newLine(info: var SourceInfo) {.raises: [].} =
+  ## Add new mapping which doesn't appear in the Nim source
+  info.mappings &= Mapping(inSource: false)
+
+func newLine(info: var SourceInfo, file: string, line: int) {.raises: [].} =
+  ## Starts a new line in the mappings. Call addSegment after this to add
+  ## segments into the line
+  var mapping = Mapping(inSource: true, line: line)
+  # Set file to file position. Add in if needed
+  mapping.file = info.files.find(file)
+  if mapping.file == -1:
+    mapping.file = info.files.len
+    info.files &= file
+  info.mappings &= mapping
 
 
 # base64_VLQ
-
-
-let integers = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
-
-
-proc encode*(i: int): string =
+func encode*(values: seq[int]): string {.raises: [].} =
+  ## Encodes a series of integers into a VLQ base64 encoded string
+  # References:
+  #   - https://www.lucidchart.com/techblog/2019/08/22/decode-encoding-base64-vlqs-source-maps/
+  #   - https://github.com/rails/sprockets/blob/main/guides/source_maps.md#source-map-file
+  const
+    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+    shift = 5
+    continueBit = 1 shl 5
+    mask = continueBit - 1
   result = ""
-  var n = i
-  if n < 0:
-    n = (-n shl 1) or 1
-  else:
-    n = n shl 1
-
-  var z = 0
-  while z == 0 or n > 0:
-    var e = n and 31
-    n = n shr 5
-    if n > 0:
-      e = e or 32
-
-    result.add(integers[e])
-    z += 1
-
-
-type TokenState = enum Normal, String, Ident, Mangled
-
-iterator tokenize*(line: string): (bool, string) =
-  # result = @[]
-  var state = Normal
-  var token = ""
-  var isMangled = false
-  for z, ch in line:
-    if ch.isAlphaAscii:
-      if state == Normal:
-        state = Ident
-        if token.len > 0:
-          yield (isMangled, token)
-        token = $ch
-        isMangled = false
-      else:
-        token.add(ch)
-    elif ch == '_':
-      if state == Ident:
-        state = Mangled
-        isMangled = true
-      token.add($ch)
-    elif ch != '"' and not ch.isAlphaNumeric:
-      if state in {Ident, Mangled}:
-        state = Normal
-        if token.len > 0:
-          yield (isMangled, token)
-        token = $ch
-        isMangled = false
-      else:
-        token.add($ch)
-    elif ch == '"':
-      if state != String:
-        state = String
-        if token.len > 0:
-          yield (isMangled, token)
-        token = $ch
-        isMangled = false
-      else:
-        state = Normal
-        token.add($ch)
-        if token.len > 0:
-          yield (isMangled, token)
-        isMangled = false
-        token = ""
+  for val in values:
+    # Sign is stored in first bit
+    var newVal = abs(val) shl 1
+    if val < 0:
+      newVal = newVal or 1
+    # Now comes the variable length part
+    # This is how we are able to store large numbers
+    while true:
+      # We only encode 5 bits.
+      var masked = newVal and mask
+      newVal = newVal shr shift
+      # If there is still something left
+      # then signify with the continue bit that the
+      # decoder should keep decoding
+      if newVal > 0:
+        masked = masked or continueBit
+      result &= alphabet[masked]
+      # If the value is zero then we have nothing left to encode
+      if newVal == 0:
+        break
+
+iterator tokenize*(line: string): (int, string) =
+  ## Goes through a line and splits it into Nim identifiers and
+  ## normal JS code. This allows us to map mangled names back to Nim names.
+  ## Yields (column, name). Doesn't yield anything but identifiers.
+  ## See mangleName in compiler/jsgen.nim for how name mangling is done
+  var
+    col = 0
+    token = ""
+  while col < line.len:
+    var
+      token: string = ""
+      name: string = ""
+    # First we find the next identifier
+    col += line.skipWhitespace(col)
+    col += line.skipUntil(IdentStartChars, col)
+    let identStart = col
+    col += line.parseIdent(token, col)
+    # Idents will either be originalName_randomInt or HEXhexCode_randomInt
+    if token.startsWith("HEX"):
+      var hex: int = 0
+      # 3 = "HEX".len and we only want to parse the two integers after it
+      discard token[3 ..< 5].parseHex(hex)
+      name = $chr(hex)
+    elif not token.endsWith("_Idx"): # Ignore address indexes
+      # It might be in the form originalName_randomInt
+      let lastUnderscore = token.rfind('_')
+      if lastUnderscore != -1:
+        name = token[0..<lastUnderscore]
+    if name != "":
+      yield (identStart, name)
+
+func parse*(source: string): SourceInfo =
+  ## Parses the JS output for embedded line info
+  ## So it can convert those into a series of mappings
+  result = default(SourceInfo)
+  var
+    skipFirstLine = true
+    currColumn = 0
+    currLine = 0
+    currFile = ""
+  # Add each line as a node into the output
+  for line in source.splitLines():
+    var
+      lineNumber: int = 0
+      linePath: string = ""
+      column: int = 0
+    if line.strip().scanf("/* line $i:$i \"$+\" */", lineNumber, column, linePath):
+      # When we reach the first line mappinsegmentg then we can assume
+      # we can map the rest of the JS lines to Nim lines
+      currColumn = column # Column is already zero indexed
+      currLine = lineNumber - 1
+      currFile = linePath
+      # Lines are zero indexed
+      result.newLine(currFile, currLine)
+      # Skip whitespace to find the starting column
+      result.addSegment(currColumn, line.skipWhitespace())
+    elif currFile != "":
+      result.newLine(currFile, currLine)
+      # There mightn't be any tokens so add a starting segment
+      result.addSegment(currColumn, line.skipWhitespace())
+      for jsColumn, token in line.tokenize:
+        result.addSegment(currColumn, jsColumn, token)
     else:
-      token.add($ch)
-  if token.len > 0:
-    yield (isMangled, token)
-
-proc parse*(source: string, path: string): SourceNode =
-  let lines = source.splitLines()
-  var lastLocation: SourceNode = nil
-  result = newSourceNode(0, 0, path, @[])
-    
-  # we just use one single parent and add all nim lines
-  # as its children, I guess in typical codegen
-  # that happens recursively on ast level
-  # we also don't have column info, but I doubt more one nim lines can compile to one js
-  # maybe in macros?
-
-  for i, originalLine in lines:
-    let line = originalLine.strip
-    if line.len == 0:
-      continue
-      
-    # this shouldn't be a problem:
-    # jsgen doesn't generate comments
-    # and if you emit // line you probably know what you're doing
-    if line.startsWith("// line"):
-      if result.children.len > 0:
-        result.children[^1].node.children.add(child(line & "\n"))
-      let pos = line.find(" ", 8)
-      let lineNumber = line[8 .. pos - 1].parseInt
-      let linePath = line[pos + 2 .. ^2] # quotes
-      
-      lastLocation = newSourceNode(
-        lineNumber,
-        0,
-        linePath,
-        @[])
-      result.children.add(child(lastLocation))
-    else:
-      var last: SourceNode
-      for token in line.tokenize():
-        var name = ""
-        if token[0]:
-          name = token[1].split('_', 1)[0]
-        
-        
-        if result.children.len > 0:
-          result.children[^1].node.children.add(
-            child(
-              newSourceNode(
-                result.children[^1].node.line,
-                0,
-                result.children[^1].node.source,
-                token[1],
-                name)))
-          last = result.children[^1].node.children[^1].node
-        else:
-          result.children.add(
-            child(
-              newSourceNode(i + 1, 0, path, token[1], name)))
-          last = result.children[^1].node
-      let nl = "\n"
-      if not last.isNil:
-        last.source.add(nl)
-
-proc cmp(a: Mapping, b: Mapping): int =
-  var c = cmp(a.generated, b.generated)
-  if c != 0:
-    return c
-
-  c = cmp(a.source, b.source)
-  if c != 0:
-    return c
-
-  c = cmp(a.original, b.original)
-  if c != 0:
-    return c
-
-  return cmp(a.name, b.name)
-
-
-proc index*[T](elements: seq[T], element: T): int =
-  for z in 0 ..< elements.len:
-    if elements[z] == element:
-      return z
-  return -1
-
-
-proc serializeMappings(map: SourceMapGenerator, mappings: seq[Mapping]): string =
-  var previous = Mapping(generated: (line: 1, column: 0), original: (line: 0, column: 0), name: "", source: "")
-  var previousSourceId = 0
-  var previousNameId = 0
-  var next = ""
-  var nameId = 0
-  var sourceId = 0
-  result = ""
-
-  for z, mapping in mappings:
-    next = ""
-
-    if mapping.generated.line != previous.generated.line:
-      previous.generated.column = 0
-
-      while mapping.generated.line != previous.generated.line:
-        next.add(";")
-        previous.generated.line += 1
-
-    else:
-      if z > 0:
-        if cmp(mapping, mappings[z - 1]) == 0:
-          continue
-        next.add(",")
-
-    next.add(encode(mapping.generated.column - previous.generated.column))
-    previous.generated.column = mapping.generated.column
-
-    if not mapping.noSource and mapping.source.len > 0:
-      sourceId = map.sources.index(mapping.source)
-      next.add(encode(sourceId - previousSourceId))
-      previousSourceId = sourceId
-      next.add(encode(mapping.original.line - 1 - previous.original.line))
-      previous.original.line = mapping.original.line - 1
-      next.add(encode(mapping.original.column - previous.original.column))
-      previous.original.column = mapping.original.column
-
-      if not mapping.noName and mapping.name.len > 0:
-        nameId = map.names.index(mapping.name)
-        next.add(encode(nameId - previousNameId))
-        previousNameId = nameId
-
-    result.add(next)
-
-
-proc gen*(map: SourceMapGenerator): SourceMap =
-  var mappings = map.mappings.sorted do (a: Mapping, b: Mapping) -> int:
-    cmp(a, b)
-  result = SourceMap(
-    file: map.file,
-    version: 3,
-    sources: map.sources[0..^1],
-    names: map.names[0..^1],
-    mappings: map.serializeMappings(mappings))
-
-
-
-proc addMapping*(map: SourceMapGenerator, mapping: Mapping) =
-  if not mapping.noSource and mapping.source notin map.sources:
-    map.sources.add(mapping.source)
-
-  if not mapping.noName and mapping.name.len > 0 and mapping.name notin map.names:
-    map.names.add(mapping.name)
-
-  # echo "map ", mapping.source, " ", mapping.original, " ", mapping.generated, " ", mapping.name
-  map.mappings.add(mapping)
-
-
-proc walk*(node: SourceNode, fn: proc(line: string, original: SourceNode)) =
-  for child in node.children:
-    if child.kind == cSourceString and child.s.len > 0:
-      fn(child.s, node)
-    else:
-      child.node.walk(fn)
-
-
-proc toSourceMap*(node: SourceNode, file: string): SourceMapGenerator =
-  var map = SourceMapGenerator(file: file, sources: @[], names: @[], mappings: @[])
-
-  var generated = (line: 1, column: 0)
-  var sourceMappingActive = false
-  var lastOriginal = SourceNode(source: "", line: -1, column: 0, name: "", children: @[])
-
-  node.walk do (line: string, original: SourceNode):
-    if original.source.endsWith(".js"):
-      # ignore it
-      discard
-    else:
-      if original.line != -1:
-        if lastOriginal.source != original.source or
-           lastOriginal.line != original.line or
-           lastOriginal.column != original.column or
-           lastOriginal.name != original.name:
-          map.addMapping(
-            Mapping(
-              source: original.source,
-              original: (line: original.line, column: original.column),
-              generated: (line: generated.line, column: generated.column),
-              name: original.name))
-
-        lastOriginal = SourceNode(
-          source: original.source,
-          line: original.line,
-          column: original.column,
-          name: original.name,
-          children: lastOriginal.children)
-        sourceMappingActive = true
-      elif sourceMappingActive:
-        map.addMapping(
-          Mapping(
-            noSource: true,
-            noName: true,
-            generated: (line: generated.line, column: generated.column),
-            original: (line: -1, column: -1)))
-        lastOriginal.line = -1
-        sourceMappingActive = false
-
-    for z in 0 ..< line.len:
-      if line[z] in Newlines:
-        generated.line += 1
-        generated.column = 0
-
-        if z == line.len - 1:
-          lastOriginal.line = -1
-          sourceMappingActive = false
-        elif sourceMappingActive:
-          map.addMapping(
-            Mapping(
-              source: original.source,
-              original: (line: original.line, column: original.column),
-              generated: (line: generated.line, column: generated.column),
-              name: original.name))
-      else:
-        generated.column += 1
-    
-  map
-
-
-proc genSourceMap*(source: string, outFile: string): (Rope, SourceMap) =
-  let node = parse(source, outFile)
-  let map = node.toSourceMap(file = outFile)
-  ((&"{source}\n//# sourceMappingURL={outFile}.map").rope, map.gen)
+      result.newLine()
+
+func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} =
+  ## Convert from high level SourceInfo into the required SourceMap object
+  # Add basic info
+  result = SourceMap(version: 3, file: file, sources: info.files, names: info.names)
+  # Convert nodes into mappings.
+  # Mappings are split into blocks where each block referes to a line in the outputted JS.
+  # Blocks can be separated into statements which refere to tokens on the line.
+  # Since the mappings depend on previous values we need to
+  # keep track of previous file, name, etc
+  var
+    prevFile = 0
+    prevLine = 0
+    prevName = 0
+    prevNimCol = 0
+
+  for mapping in info.mappings:
+    # We know need to encode segments with the following fields
+    # All these fields are relative to their previous values
+    # - 0: Column in generated code
+    # - 1: Index of Nim file in source list
+    # - 2: Line in Nim source
+    # - 3: Column in Nim source
+    # - 4: Index in names list
+    if mapping.inSource:
+      # JS Column is special in that it is reset after every line
+      var prevJSCol = 0
+      for segment in mapping.segments:
+        var values = @[segment.generated - prevJSCol, mapping.file - prevFile, mapping.line - prevLine, segment.original - prevNimCol]
+        # Add name field if needed
+        if segment.name != -1:
+          values &= segment.name - prevName
+          prevName = segment.name
+        prevJSCol = segment.generated
+        prevNimCol = segment.original
+        prevFile = mapping.file
+        prevLine = mapping.line
+        result.mappings &= encode(values) & ","
+      # Remove trailing ,
+      if mapping.segments.len > 0:
+        result.mappings.setLen(result.mappings.len - 1)
+
+    result.mappings &= ";"
+
+proc genSourceMap*(source: string, outFile: string): SourceMap =
+  let node = parse(source)
+  result = node.toSourceMap(outFile)
 
diff --git a/compiler/spawn.nim b/compiler/spawn.nim
index 581f722d5..58d5a4928 100644
--- a/compiler/spawn.nim
+++ b/compiler/spawn.nim
@@ -16,7 +16,7 @@ from trees import getMagic, getRoot
 proc callProc(a: PNode): PNode =
   result = newNodeI(nkCall, a.info)
   result.add a
-  result.typ = a.typ[0]
+  result.typ = a.typ.returnType
 
 # we have 4 cases to consider:
 # - a void proc --> nothing to do
@@ -37,7 +37,7 @@ proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
   else: srFlowVar
 
 proc flowVarKind(c: ConfigRef, t: PType): TFlowVarKind =
-  if c.selectedGC in {gcArc, gcOrc}: fvBlob
+  if c.selectedGC in {gcArc, gcOrc, gcAtomicArc}: fvBlob
   elif t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
   elif containsGarbageCollectedRef(t): fvInvalid
   else: fvBlob
@@ -50,12 +50,12 @@ proc typeNeedsNoDeepCopy(t: PType): bool =
   # note that seq[T] is fine, but 'var seq[T]' is not, so we need to skip 'var'
   # for the stricter check and likewise we can skip 'seq' for a less
   # strict check:
-  if t.kind in {tyVar, tyLent, tySequence}: t = t.lastSon
+  if t.kind in {tyVar, tyLent, tySequence}: t = t.elementType
   result = not containsGarbageCollectedRef(t)
 
 proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator; owner: PSym; typ: PType;
                  v: PNode; useShallowCopy=false): PSym =
-  result = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId idgen, owner, varSection.info,
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), idgen, owner, varSection.info,
                   owner.options)
   result.typ = typ
   incl(result.flags, sfFromGeneric)
@@ -66,7 +66,7 @@ proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; idgen: IdGenerator;
   vpart[2] = if varInit.isNil: v else: vpart[1]
   varSection.add vpart
   if varInit != nil:
-    if g.config.selectedGC in {gcArc, gcOrc}:
+    if g.config.selectedGC in {gcArc, gcOrc, gcAtomicArc}:
       # inject destructors pass will do its own analysis
       varInit.add newFastMoveStmt(g, newSymNode(result), v)
     else:
@@ -109,12 +109,22 @@ stmtList:
 
 """
 
+proc castToVoidPointer(g: ModuleGraph, n: PNode, fvField: PNode): PNode =
+  if g.config.backend == backendCpp:
+    result = fvField
+  else:
+    let ptrType = getSysType(g, n.info, tyPointer)
+    result = newNodeI(nkCast, fvField.info)
+    result.add newNodeI(nkEmpty, fvField.info)
+    result.add fvField
+    result.typ = ptrType
+
 proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
                        varSection, varInit, call, barrier, fv: PNode;
                        idgen: IdGenerator;
                        spawnKind: TSpawnResult, result: PSym) =
   var body = newNodeI(nkStmtList, f.info)
-  var threadLocalBarrier: PSym
+  var threadLocalBarrier: PSym = nil
   if barrier != nil:
     var varSection2 = newNodeI(nkVarSection, barrier.info)
     threadLocalBarrier = addLocalVar(g, varSection2, nil, idgen, result,
@@ -122,7 +132,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     body.add varSection2
     body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
       threadLocalBarrier.newSymNode)
-  var threadLocalProm: PSym
+  var threadLocalProm: PSym = nil
   if spawnKind == srByVar:
     threadLocalProm = addLocalVar(g, varSection, nil, idgen, result, fv.typ, fv)
   elif fv != nil:
@@ -141,10 +151,10 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   if spawnKind == srByVar:
     body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
   elif fv != nil:
-    let fk = flowVarKind(g.config, fv.typ[1])
+    let fk = flowVarKind(g.config, fv.typ.firstGenericParam)
     if fk == fvInvalid:
       localError(g.config, f.info, "cannot create a flowVar of type: " &
-        typeToString(fv.typ[1]))
+        typeToString(fv.typ.firstGenericParam))
     body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
       if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
     if fk == fvGC:
@@ -156,8 +166,9 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
     if barrier == nil:
       # by now 'fv' is shared and thus might have beeen overwritten! we need
       # to use the thread-local view instead:
+      let castExpr = castToVoidPointer(g, f, threadLocalProm.newSymNode)
       body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
-        threadLocalProm.newSymNode)
+        castExpr)
   else:
     body.add call
   if barrier != nil:
@@ -169,7 +180,7 @@ proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
   params.add threadParam.newSymNode
   params.add argsParam.newSymNode
 
-  var t = newType(tyProc, nextTypeId idgen, threadParam.owner)
+  var t = newType(tyProc, idgen, threadParam.owner)
   t.rawAddSon nil
   t.rawAddSon threadParam.typ
   t.rawAddSon argsParam.typ
@@ -189,11 +200,11 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode
   result = newNodeI(nkCast, argsParam.info)
   result.add newNodeI(nkEmpty, argsParam.info)
   result.add newSymNode(argsParam)
-  result.typ = newType(tyPtr, nextTypeId idgen, objType.owner)
+  result.typ = newType(tyPtr, idgen, objType.owner)
   result.typ.rawAddSon(objType)
 
 template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) =
-  if (formal.typ.kind == tyVarargs and formal.typ[0].kind in {tyTyped, tyUntyped}) or
+  if (formal.typ.kind == tyVarargs and formal.typ.elementType.kind in {tyTyped, tyUntyped}) or
           formal.typ.kind in {tyTyped, tyUntyped}:
     localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter")
 
@@ -219,7 +230,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType;
     #  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, nextSymId idgen, objType.owner, n.info, g.config.options)
+    var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options)
     field.typ = argType
     discard objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
@@ -250,7 +261,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
     #  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, nextSymId idgen, objType.owner, n.info, g.config.options)
+    var field = newSym(skField, fieldname, idgen, objType.owner, n.info, g.config.options)
 
     if argType.kind in {tyVarargs, tyOpenArray}:
       # important special case: we always create a zero-copy slice:
@@ -258,7 +269,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
       slice.typ = n.typ
       slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice))
       slice[0].typ = getSysType(g, n.info, tyInt) # fake type
-      var fieldB = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options)
+      var fieldB = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
       fieldB.typ = getSysType(g, n.info, tyInt)
       discard objType.addField(fieldB, g.cache, idgen)
 
@@ -268,7 +279,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
         discard objType.addField(field, g.cache, idgen)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
 
-        var fieldA = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options)
+        var fieldA = newSym(skField, tmpName, idgen, objType.owner, n.info, g.config.options)
         fieldA.typ = getSysType(g, n.info, tyInt)
         discard objType.addField(fieldA, g.cache, idgen)
         result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
@@ -317,7 +328,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType;
       call.add(threadLocal.newSymNode)
 
 proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExpr: PNode; retType: PType;
-                       barrier, dest: PNode = nil): PNode =
+                       barrier: PNode = nil, dest: PNode = nil): PNode =
   # if 'barrier' != nil, then it is in a 'parallel' section and we
   # generate quite different code
   let n = spawnExpr[^2]
@@ -343,9 +354,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
   var fn = n[0]
   let
     name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper"
-    wrapperProc = newSym(skProc, getIdent(g.cache, name), nextSymId idgen, owner, fn.info, g.config.options)
-    threadParam = newSym(skParam, getIdent(g.cache, "thread"), nextSymId idgen, wrapperProc, n.info, g.config.options)
-    argsParam = newSym(skParam, getIdent(g.cache, "args"), nextSymId idgen, wrapperProc, n.info, g.config.options)
+    wrapperProc = newSym(skProc, getIdent(g.cache, name), idgen, owner, fn.info, g.config.options)
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), idgen, wrapperProc, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), idgen, wrapperProc, n.info, g.config.options)
 
   wrapperProc.flags.incl sfInjectDestructors
   block:
@@ -358,7 +369,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
   incl(objType.flags, tfFinal)
   let castExpr = createCastExpr(argsParam, objType, idgen)
 
-  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), nextSymId idgen, owner, n.info, g.config.options)
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), idgen, owner, n.info, g.config.options)
   block:
     scratchObj.typ = objType
     incl(scratchObj.flags, sfFromGeneric)
@@ -375,7 +386,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
                                                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(g.cache, "fn"), nextSymId idgen, owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fn"), idgen, owner, n.info, g.config.options)
     field.typ = argType
     discard objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
@@ -395,9 +406,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
 
   var barrierAsExpr: PNode = nil
   if barrier != nil:
-    let typ = newType(tyPtr, nextTypeId idgen, owner)
+    let typ = newType(tyPtr, idgen, owner)
     typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
-    var field = newSym(skField, getIdent(g.cache, "barrier"), nextSymId idgen, owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), idgen, owner, n.info, g.config.options)
     field.typ = typ
     discard objType.addField(field, g.cache, idgen)
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
@@ -405,7 +416,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
 
   var fvField, fvAsExpr: PNode = nil
   if spawnKind == srFlowVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options)
+    var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
     field.typ = retType
     discard objType.addField(field, g.cache, idgen)
     fvField = newDotExpr(scratchObj, field)
@@ -413,11 +424,12 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp
     # create flowVar:
     result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
     if barrier == nil:
-      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField)
+      let castExpr = castToVoidPointer(g, n, fvField)
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, castExpr)
 
   elif spawnKind == srByVar:
-    var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options)
-    field.typ = newType(tyPtr, nextTypeId idgen, objType.owner)
+    var field = newSym(skField, getIdent(g.cache, "fv"), idgen, owner, n.info, g.config.options)
+    field.typ = newType(tyPtr, idgen, objType.owner)
     field.typ.rawAddSon(retType)
     discard objType.addField(field, g.cache, idgen)
     fvAsExpr = indirectAccess(castExpr, field, n.info)
diff --git a/compiler/strutils2.nim b/compiler/strutils2.nim
deleted file mode 100644
index f44b811c7..000000000
--- a/compiler/strutils2.nim
+++ /dev/null
@@ -1,57 +0,0 @@
-##[
-internal API for now, subject to modifications and moving around
-
-string API's focusing on performance, that can be used as building blocks
-for other routines.
-
-Un-necessary allocations are avoided and appropriate algorithms are used at the
-expense of code clarity when justified.
-]##
-
-proc dataPointer*[T](a: T): pointer =
-  ## same as C++ `data` that works with std::string, std::vector etc.
-  ## Note: safe to use when a.len == 0 but whether the result is nil or not
-  ## is implementation defined for performance reasons.
-  # this could be improved with ocmpiler support to avoid the `if`, e.g. in C++
-  # `&a[0]` is well defined even if a.size() == 0
-  when T is string | seq:
-    if a.len == 0: nil else: cast[pointer](a[0].unsafeAddr)
-  elif T is array:
-    when a.len > 0: a.unsafeAddr
-    else: nil
-  elif T is cstring:
-    cast[pointer](a)
-  else: static: doAssert false, $T
-
-proc setLen*(result: var string, n: int, isInit: bool) =
-  ## when isInit = false, elements are left uninitialized, analog to `{.noinit.}`
-  ## else, there are 0-initialized.
-  # xxx placeholder until system.setLen supports this
-  # to distinguish between algorithms that need 0-initialization vs not; note
-  # that `setLen` for string is inconsistent with `setLen` for seq.
-  # likwise with `newString` vs `newSeq`. This should be fixed in `system`.
-  let n0 = result.len
-  result.setLen(n)
-  if isInit and n > n0:
-    zeroMem(result[n0].addr, n - n0)
-
-proc forceCopy*(result: var string, a: string) =
-  ## also forces a copy if `a` is shallow
-  # the naitve `result = a` would not work if `a` is shallow
-  let n = a.len
-  result.setLen n, isInit = false
-  copyMem(result.dataPointer, a.dataPointer, n)
-
-proc isUpperAscii(c: char): bool {.inline.} =
-  # avoids import strutils.isUpperAscii
-  c in {'A'..'Z'}
-
-proc toLowerAscii*(a: var string) =
-  ## optimized and inplace overload of strutils.toLowerAscii
-  # refs https://github.com/timotheecour/Nim/pull/54
-  # this is 10X faster than a naive implementation using a an optimization trick
-  # that can be adapted in similar contexts. Predictable writes avoid write
-  # hazards and lead to better machine code, compared to random writes arising
-  # from: `if c.isUpperAscii: c = ...`
-  for c in mitems(a):
-    c = chr(c.ord + (if c.isUpperAscii: (ord('a') - ord('A')) else: 0))
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 5e8d896bf..a5213086b 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,11 +32,13 @@
 
 # included from sigmatch.nim
 
-import algorithm, sets, prefixmatches, parseutils, tables
+import prefixmatches, suggestsymdb
 from wordrecg import wDeprecated, wError, wAddr, wYield
 
+import std/[algorithm, sets, parseutils, tables]
+
 when defined(nimsuggest):
-  import passes, tables, pathutils # importer
+  import pathutils # importer
 
 const
   sep = '\t'
@@ -53,8 +55,10 @@ proc findDocComment(n: PNode): PNode =
     if result != nil: return
     if n.len > 1:
       result = findDocComment(n[1])
-  elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2:
+  elif n.kind in {nkAsgn, nkFastAsgn, nkSinkAsgn} and n.len == 2:
     result = findDocComment(n[1])
+  else:
+    result = nil
 
 proc extractDocComment(g: ModuleGraph; s: PSym): string =
   var n = findDocComment(s.ast)
@@ -81,7 +85,16 @@ proc cmpSuggestions(a, b: Suggest): int =
   # independent of hashing order:
   result = cmp(a.name[], b.name[])
 
-proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int =
+proc scanForTrailingAsterisk(line: string, start: int): int =
+  result = 0
+  while start+result < line.len and line[start+result] in {' ', '\t'}:
+    inc result
+  if start+result < line.len and line[start+result] == '*':
+    inc result
+  else:
+    result = 0
+
+proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo; skipTrailingAsterisk: bool = false): int =
   let
     line = sourceLine(conf, info)
     column = toColumn(info)
@@ -101,12 +114,18 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
     result = skipUntil(line, '`', column)
     if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
       result = 0
+  elif column >= 0 and line[column] == '`' and isOpeningBacktick(column):
+    result = skipUntil(line, '`', column + 1) + 2
+    if cmpIgnoreStyle(line[column + 1..column + result - 2], ident) != 0:
+      result = 0
   elif ident[0] in linter.Letters and ident[^1] != '=':
     result = identLen(line, column)
-    if cmpIgnoreStyle(line[column..column + result - 1], ident) != 0:
+    if cmpIgnoreStyle(line[column..column + result - 1], ident[0..min(result-1,len(ident)-1)]) != 0:
       result = 0
+    if skipTrailingAsterisk and result > 0:
+      result += scanForTrailingAsterisk(line, column + result)
   else:
-    var sourceIdent: string
+    var sourceIdent: string = ""
     result = parseWhile(line, sourceIdent,
                         OpChars + {'[', '(', '{', ']', ')', '}'}, column)
     if ident[^1] == '=' and ident[0] in linter.Letters:
@@ -120,7 +139,9 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int
 proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo;
                   quality: range[0..100]; prefix: PrefixMatch;
                   inTypeContext: bool; scope: int;
-                  useSuppliedInfo = false): Suggest =
+                  useSuppliedInfo = false,
+                  endLine: uint16 = 0,
+                  endCol = 0, extractDocs = true): Suggest =
   new(result)
   result.section = section
   result.quality = quality
@@ -152,11 +173,15 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
       result.qualifiedPath.add(s.name.s)
 
     if s.typ != nil:
-      result.forth = typeToString(s.typ)
+      if section == ideInlayHints:
+        result.forth = typeToString(s.typ, preferInlayHint)
+      else:
+        result.forth = typeToString(s.typ, preferInferredEffects)
     else:
       result.forth = ""
     when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
-      result.doc = extractDocComment(g, s)
+      if extractDocs:
+        result.doc = extractDocComment(g, s)
   if s.kind == skModule and s.ast.len != 0 and section != ideHighlight:
     result.filePath = toFullPath(g.config, s.ast[0].info)
     result.line = 1
@@ -164,57 +189,121 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info
     result.tokenLen = 0
   else:
     let infox =
-      if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}:
+      if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline, ideDeclaration}:
         info
       else:
         s.info
     result.filePath = toFullPath(g.config, infox)
     result.line = toLinenumber(infox)
     result.column = toColumn(infox)
-    result.tokenLen = if section != ideHighlight:
+    result.tokenLen = if section notin {ideHighlight, ideInlayHints}:
                         s.name.s.len
                       else:
-                        getTokenLenFromSource(g.config, s.name.s, infox)
+                        getTokenLenFromSource(g.config, s.name.s, infox, section == ideInlayHints)
   result.version = g.config.suggestVersion
+  result.endLine = endLine
+  result.endCol = endCol
 
-proc `$`*(suggest: Suggest): string =
-  result = $suggest.section
+proc `$`*(suggest: SuggestInlayHint): string =
+  result = $suggest.kind
   result.add(sep)
-  if suggest.section == ideHighlight:
-    if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
-      result.add("skGlobalVar")
-    elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
-      result.add("skGlobalLet")
-    else:
-      result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
-    result.add(sep)
-    result.add($suggest.tokenLen)
+  result.add($suggest.line)
+  result.add(sep)
+  result.add($suggest.column)
+  result.add(sep)
+  result.add(suggest.label)
+  result.add(sep)
+  result.add($suggest.paddingLeft)
+  result.add(sep)
+  result.add($suggest.paddingRight)
+  result.add(sep)
+  result.add($suggest.allowInsert)
+  result.add(sep)
+  result.add(suggest.tooltip)
+
+proc `$`*(suggest: Suggest): string =
+  if suggest.section == ideInlayHints:
+    result = $suggest.inlayHintInfo
   else:
-    result.add($suggest.symkind.TSymKind)
-    result.add(sep)
-    if suggest.qualifiedPath.len != 0:
-      result.add(suggest.qualifiedPath.join("."))
-    result.add(sep)
-    result.add(suggest.forth)
-    result.add(sep)
-    result.add(suggest.filePath)
-    result.add(sep)
-    result.add($suggest.line)
-    result.add(sep)
-    result.add($suggest.column)
+    result = $suggest.section
     result.add(sep)
-    when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
-      result.add(suggest.doc.escape)
-    if suggest.version in {0, 3}:
+    if suggest.section == ideHighlight:
+      if suggest.symkind.TSymKind == skVar and suggest.isGlobal:
+        result.add("skGlobalVar")
+      elif suggest.symkind.TSymKind == skLet and suggest.isGlobal:
+        result.add("skGlobalLet")
+      else:
+        result.add($suggest.symkind.TSymKind)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
+      result.add(sep)
+      result.add($suggest.tokenLen)
+    else:
+      result.add($suggest.symkind.TSymKind)
+      result.add(sep)
+      if suggest.qualifiedPath.len != 0:
+        result.add(suggest.qualifiedPath.join("."))
       result.add(sep)
-      result.add($suggest.quality)
-      if suggest.section == ideSug:
+      result.add(suggest.forth)
+      result.add(sep)
+      result.add(suggest.filePath)
+      result.add(sep)
+      result.add($suggest.line)
+      result.add(sep)
+      result.add($suggest.column)
+      result.add(sep)
+      when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler):
+        result.add(suggest.doc.escape)
+      if suggest.version == 0 or suggest.version == 3:
         result.add(sep)
-        result.add($suggest.prefix)
+        result.add($suggest.quality)
+        if suggest.section == ideSug:
+          result.add(sep)
+          result.add($suggest.prefix)
+
+    if (suggest.version == 3 and suggest.section in {ideOutline, ideExpand}):
+      result.add(sep)
+      result.add($suggest.endLine)
+      result.add(sep)
+      result.add($suggest.endCol)
+
+proc suggestToSuggestInlayTypeHint*(sug: Suggest): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkType,
+    line: sug.line,
+    column: sug.column + sug.tokenLen,
+    label: ": " & sug.forth,
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: true,
+    tooltip: ""
+  )
+
+proc suggestToSuggestInlayExceptionHintLeft*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkException,
+    line: sug.line,
+    column: sug.column,
+    label: "try ",
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: false,
+    tooltip: "propagated exceptions: " & $propagatedExceptions
+  )
+
+proc suggestToSuggestInlayExceptionHintRight*(sug: Suggest, propagatedExceptions: seq[PType]): SuggestInlayHint =
+  SuggestInlayHint(
+    kind: sihkException,
+    line: sug.line,
+    column: sug.column + sug.tokenLen,
+    label: "!",
+    paddingLeft: false,
+    paddingRight: false,
+    allowInsert: false,
+    tooltip: "propagated exceptions: " & $propagatedExceptions
+  )
 
 proc suggestResult*(conf: ConfigRef; s: Suggest) =
   if not isNil(conf.suggestionResultHook):
@@ -244,13 +333,17 @@ proc filterSym(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
     of nkOpenSymChoice, nkClosedSymChoice, nkAccQuoted:
       if n.len > 0:
         result = prefixMatch(s, n[0])
-    else: discard
+      else:
+        result = default(PrefixMatch)
+    else: result = default(PrefixMatch)
   if s.kind != skModule:
     if prefix != nil:
       res = prefixMatch(s, prefix)
       result = res != PrefixMatch.None
     else:
       result = true
+  else:
+    result = false
 
 proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline.} =
   result = filterSym(s, prefix, res) and s.name.s[0] in lexer.SymChars and
@@ -264,17 +357,16 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} =
     for module in c.friendModules:
       if fmoduleId == module.id: return true
     if f.kind == skField:
-      var symObj = f.owner
-      if symObj.typ.kind in {tyRef, tyPtr}:
-        symObj = symObj.typ[0].sym
+      var symObj = f.owner.typ.toObjectFromRefPtrGeneric.sym
+      assert symObj != nil
       for scope in allScopes(c.currentScope):
         for sym in scope.allowPrivateAccess:
           if symObj.id == sym.id: return true
 
 proc getQuality(s: PSym): range[0..100] =
   result = 100
-  if s.typ != nil and s.typ.len > 1:
-    var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
+  if s.typ != nil and s.typ.paramsLen > 0:
+    var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
     if exp.kind == tyVarargs: exp = elemType(exp)
     if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: result = 50
 
@@ -283,15 +375,15 @@ proc getQuality(s: PSym): range[0..100] =
     result = result - 5
 
 proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) =
-  var pm: PrefixMatch
+  var pm: PrefixMatch = default(PrefixMatch)
   if filterSym(s, f, pm) and fieldVisible(c, s):
     outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info,
                               s.getQuality, pm, c.inTypeContext > 0, 0))
 
 template wholeSymTab(cond, section: untyped) {.dirty.} =
-  for (item, scopeN, isLocal) in allSyms(c):
+  for (item, scopeN, isLocal) in uniqueSyms(c):
     let it = item
-    var pm: PrefixMatch
+    var pm: PrefixMatch = default(PrefixMatch)
     if cond:
       outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it),
                                 pm, c.inTypeContext > 0, scopeN))
@@ -343,17 +435,19 @@ proc suggestVar(c: PContext, n: PNode, outputs: var Suggestions) =
   wholeSymTab(nameFits(c, it, n), ideCon)
 
 proc typeFits(c: PContext, s: PSym, firstArg: PType): bool {.inline.} =
-  if s.typ != nil and s.typ.len > 1 and s.typ[1] != nil:
+  if s.typ != nil and s.typ.paramsLen > 0 and s.typ.firstParamType != nil:
     # special rule: if system and some weird generic match via 'tyUntyped'
     # or 'tyGenericParam' we won't list it either to reduce the noise (nobody
     # wants 'system.`-|` as suggestion
     let m = s.getModule()
     if m != nil and sfSystemModule in m.flags:
       if s.kind == skType: return
-      var exp = s.typ[1].skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
+      var exp = s.typ.firstParamType.skipTypes({tyGenericInst, tyVar, tyLent, tyAlias, tySink})
       if exp.kind == tyVarargs: exp = elemType(exp)
       if exp.kind in {tyUntyped, tyTyped, tyGenericParam, tyAnything}: return
-    result = sigmatch.argtypeMatches(c, s.typ[1], firstArg)
+    result = sigmatch.argtypeMatches(c, s.typ.firstParamType, firstArg)
+  else:
+    result = false
 
 proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Suggestions) =
   assert typ != nil
@@ -362,8 +456,8 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges
 
 proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) =
   # do not produce too many symbols:
-  for (it, scopeN, isLocal) in allSyms(c):
-    var pm: PrefixMatch
+  for (it, scopeN, isLocal) in uniqueSyms(c):
+    var pm: PrefixMatch = default(PrefixMatch)
     if filterSym(it, f, pm):
       outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info,
                                it.getQuality, pm, c.inTypeContext > 0, scopeN))
@@ -372,7 +466,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
   # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but
   # ``myObj``.
   var typ = n.typ
-  var pm: PrefixMatch
+  var pm: PrefixMatch = default(PrefixMatch)
   when defined(nimsuggest):
     if n.kind == nkSym and n.sym.kind == skError and c.config.suggestVersion == 0:
       # consider 'foo.|' where 'foo' is some not imported module.
@@ -421,15 +515,23 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions)
       var t = typ
       while t != nil:
         suggestSymList(c, t.n, field, n.info, outputs)
-        t = t[0]
+        t = t.baseClass
     elif typ.kind == tyObject:
       var t = typ
       while true:
         suggestObject(c, t.n, field, n.info, outputs)
-        if t[0] == nil: break
-        t = skipTypes(t[0], skipPtrs)
+        if t.baseClass == nil: break
+        t = skipTypes(t.baseClass, skipPtrs)
     elif typ.kind == tyTuple and typ.n != nil:
-      suggestSymList(c, typ.n, field, n.info, outputs)
+      # All tuple fields are in scope
+      # So go through each field and add it to the suggestions (If it passes the filter)
+      for node in typ.n:
+        if node.kind == nkSym:
+          let s = node.sym
+          var pm: PrefixMatch = default(PrefixMatch)
+          if filterSym(s, field, pm):
+            outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, n.info,
+                                     s.getQuality, pm, c.inTypeContext > 0, 0))
 
     suggestOperations(c, n, field, orig, outputs)
     if typ != orig:
@@ -441,17 +543,34 @@ type
 
 proc inCheckpoint*(current, trackPos: TLineInfo): TCheckPointResult =
   if current.fileIndex == trackPos.fileIndex:
+    result = cpNone
     if current.line == trackPos.line and
         abs(current.col-trackPos.col) < 4:
       return cpExact
     if current.line >= trackPos.line:
       return cpFuzzy
+  else:
+    result = cpNone
 
 proc isTracked*(current, trackPos: TLineInfo, tokenLen: int): bool =
   if current.fileIndex==trackPos.fileIndex and current.line==trackPos.line:
     let col = trackPos.col
     if col >= current.col and col <= current.col+tokenLen-1:
-      return true
+      result = true
+    else:
+      result = false
+  else:
+    result = false
+
+proc isTracked*(current, trackPos: TinyLineInfo, tokenLen: int): bool =
+  if current.line==trackPos.line:
+    let col = trackPos.col
+    if col >= current.col and col <= current.col+tokenLen-1:
+      result = true
+    else:
+      result = false
+  else:
+    result = false
 
 when defined(nimsuggest):
   # Since TLineInfo defined a == operator that doesn't include the column,
@@ -488,7 +607,7 @@ proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym
   if s.isNil: return
   if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags):
     suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym))
-    if sfForward notin s.flags and g.config.suggestVersion != 3:
+    if sfForward notin s.flags and g.config.suggestVersion < 3:
       suggestQuit()
     else:
       usageSym = s
@@ -503,7 +622,7 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
   ## misnamed: should be 'symDeclared'
   let conf = g.config
   when defined(nimsuggest):
-    g.suggestSymbols.mgetOrPut(info.fileIndex, @[]).add SymInfoPair(sym: s, info: info)
+    g.suggestSymbols.add SymInfoPair(sym: s, info: info, isDecl: isDecl), optIdeExceptionInlayHints in g.config.globalOptions
 
     if conf.suggestVersion == 0:
       if s.allUsages.len == 0:
@@ -535,16 +654,6 @@ proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; i
       if parentFileIndex == conf.m.trackPos.fileIndex:
         suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0))
 
-proc extractPragma(s: PSym): PNode =
-  if s.kind in routineKinds:
-    result = s.ast[pragmasPos]
-  elif s.kind in {skType, skVar, skLet}:
-    if s.ast != nil and s.ast.len > 0:
-      if s.ast[0].kind == nkPragmaExpr and s.ast[0].len > 1:
-        # s.ast = nkTypedef / nkPragmaExpr / [nkSym, nkPragma]
-        result = s.ast[0][1]
-  doAssert result == nil or result.kind == nkPragma
-
 proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) =
   var pragmaNode: PNode
   pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s)
@@ -587,7 +696,7 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) =
       else:
         inc i
 
-proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
+proc markUsed(c: PContext; info: TLineInfo; s: PSym; checkStyle = true) =
   let conf = c.config
   incl(s.flags, sfUsed)
   if s.kind == skEnumField and s.owner != nil:
@@ -604,7 +713,8 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) =
     if sfError in s.flags: userError(conf, info, s)
   when defined(nimsuggest):
     suggestSym(c.graph, info, s, c.graph.usageSym, false)
-  styleCheckUse(c, info, s)
+  if checkStyle:
+    styleCheckUse(c, info, s)
   markOwnerModuleAsUsed(c, s)
 
 proc safeSemExpr*(c: PContext, n: PNode): PNode =
@@ -673,6 +783,9 @@ proc suggestDecl*(c: PContext, n: PNode; s: PSym) =
   if attached: inc(c.inTypeContext)
   defer:
     if attached: dec(c.inTypeContext)
+  # If user is typing out an enum field, then don't provide suggestions
+  if s.kind == skEnumField and c.config.cmd == cmdIdeTools and exactEquals(c.config.m.trackPos, n.info):
+    suggestQuit()
   suggestExpr(c, n)
 
 proc suggestStmt*(c: PContext, n: PNode) =
@@ -684,14 +797,46 @@ proc suggestEnum*(c: PContext; n: PNode; t: PType) =
   produceOutput(outputs, c.config)
   if outputs.len > 0: suggestQuit()
 
+proc suggestPragmas*(c: PContext, n: PNode) =
+  ## Suggests anything that might be a pragma
+  ## - template that has {.pragma.}
+  ## - macros
+  ## - user pragmas
+  let info = n.info
+  var outputs: Suggestions = @[]
+  # First filter for template/macros
+  wholeSymTab(filterSym(it, n, pm) and
+    (sfCustomPragma in it.flags or it.kind == skMacro),
+    ideSug)
+
+  # Now show suggestions for user pragmas
+  for pragma in c.userPragmas:
+    var pm = default(PrefixMatch)
+    if filterSym(pragma, n, pm):
+      outputs &= symToSuggest(c.graph, pragma, isLocal=true, ideSug, info,
+                                pragma.getQuality, pm, c.inTypeContext > 0, 0,
+                                extractDocs=false)
+
+  produceOutput(outputs, c.config)
+  if outputs.len > 0:
+    suggestQuit()
+
+template trySuggestPragmas*(c: PContext, n: PNode) =
+  ## Runs [suggestPragmas] when compiling nimsuggest and
+  ## we are querying the node
+  when defined(nimsuggest):
+    let tmp = n
+    if c.config.ideCmd == ideSug and exactEquals(c.config.m.trackPos, tmp.info):
+      suggestPragmas(c, tmp)
+
 proc suggestSentinel*(c: PContext) =
   if c.config.ideCmd != ideSug or c.module.position != c.config.m.trackPos.fileIndex.int32: return
   if c.compilesContextId > 0: return
   inc(c.compilesContextId)
   var outputs: Suggestions = @[]
   # suggest everything:
-  for (it, scopeN, isLocal) in allSyms(c):
-    var pm: PrefixMatch
+  for (it, scopeN, isLocal) in uniqueSyms(c):
+    var pm: PrefixMatch = default(PrefixMatch)
     if filterSymNoOpr(it, nil, pm):
       outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug,
           newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), it.getQuality,
@@ -702,7 +847,7 @@ proc suggestSentinel*(c: PContext) =
 
 when defined(nimsuggest):
   proc onDef(graph: ModuleGraph, s: PSym, info: TLineInfo) =
-    if graph.config.suggestVersion == 3 and info.exactEquals(s.info):
+    if graph.config.suggestVersion >= 3 and info.exactEquals(s.info):
        suggestSym(graph, info, s, graph.usageSym)
 
   template getPContext(): untyped =
diff --git a/compiler/suggestsymdb.nim b/compiler/suggestsymdb.nim
new file mode 100644
index 000000000..e1e67afbe
--- /dev/null
+++ b/compiler/suggestsymdb.nim
@@ -0,0 +1,212 @@
+import std/[intsets, tables, algorithm, assertions]
+import ast, lineinfos, msgs
+
+type
+  PackedBoolArray* = object
+    s: IntSet
+    len: int
+
+  TinyLineInfo* = object
+    line*: uint16
+    col*: int16
+
+  SymInfoPair* = object
+    sym*: PSym
+    info*: TLineInfo
+    caughtExceptions*: seq[PType]
+    caughtExceptionsSet*: bool
+    isDecl*: bool
+
+  SuggestFileSymbolDatabase* = object
+    lineInfo*: seq[TinyLineInfo]
+    sym*: seq[PSym]
+    caughtExceptions*: seq[seq[PType]]
+    caughtExceptionsSet*: PackedBoolArray
+    isDecl*: PackedBoolArray
+    fileIndex*: FileIndex
+    trackCaughtExceptions*: bool
+    isSorted*: bool
+
+  SuggestSymbolDatabase* = Table[FileIndex, SuggestFileSymbolDatabase]
+
+
+func newPackedBoolArray*(): PackedBoolArray =
+  PackedBoolArray(
+    s: initIntSet(),
+    len: 0
+  )
+
+func low*(s: PackedBoolArray): int =
+  0
+
+func high*(s: PackedBoolArray): int =
+  s.len - 1
+
+func `[]`*(s: PackedBoolArray; idx: int): bool =
+  s.s.contains(idx)
+
+proc `[]=`*(s: var PackedBoolArray; idx: int; v: bool) =
+  if v:
+    s.s.incl(idx)
+  else:
+    s.s.excl(idx)
+
+proc add*(s: var PackedBoolArray; v: bool) =
+  inc(s.len)
+  if v:
+    s.s.incl(s.len - 1)
+
+proc reverse*(s: var PackedBoolArray) =
+  var
+    reversedSet = initIntSet()
+  for i in 0..s.high:
+    if s.s.contains(i):
+      reversedSet.incl(s.high - i)
+  s.s = reversedSet
+
+proc getSymInfoPair*(s: SuggestFileSymbolDatabase; idx: int): SymInfoPair =
+  SymInfoPair(
+    sym: s.sym[idx],
+    info: TLineInfo(
+      line: s.lineInfo[idx].line,
+      col: s.lineInfo[idx].col,
+      fileIndex: s.fileIndex
+    ),
+    caughtExceptions:
+      if s.trackCaughtExceptions:
+        s.caughtExceptions[idx]
+      else:
+        @[],
+    caughtExceptionsSet:
+      if s.trackCaughtExceptions:
+        s.caughtExceptionsSet[idx]
+      else:
+        false,
+    isDecl: s.isDecl[idx]
+  )
+
+proc reverse*(s: var SuggestFileSymbolDatabase) =
+  s.lineInfo.reverse()
+  s.sym.reverse()
+  s.caughtExceptions.reverse()
+  s.caughtExceptionsSet.reverse()
+  s.isDecl.reverse()
+
+proc newSuggestFileSymbolDatabase*(aFileIndex: FileIndex; aTrackCaughtExceptions: bool): SuggestFileSymbolDatabase =
+  SuggestFileSymbolDatabase(
+    lineInfo: @[],
+    sym: @[],
+    caughtExceptions: @[],
+    caughtExceptionsSet: newPackedBoolArray(),
+    isDecl: newPackedBoolArray(),
+    fileIndex: aFileIndex,
+    trackCaughtExceptions: aTrackCaughtExceptions,
+    isSorted: true
+  )
+
+proc exactEquals*(a, b: TinyLineInfo): bool =
+  result = a.line == b.line and a.col == b.col
+
+proc `==`*(a, b: SymInfoPair): bool =
+  result = a.sym == b.sym and a.info.exactEquals(b.info)
+
+func cmp*(a: TinyLineInfo; b: TinyLineInfo): int =
+  result = cmp(a.line, b.line)
+  if result == 0:
+    result = cmp(a.col, b.col)
+
+func compare*(s: var SuggestFileSymbolDatabase; i, j: int): int =
+  result = cmp(s.lineInfo[i], s.lineInfo[j])
+  if result == 0:
+    result = cmp(s.isDecl[i], s.isDecl[j])
+
+proc exchange(s: var SuggestFileSymbolDatabase; i, j: int) =
+  if i == j:
+    return
+  var tmp1 = s.lineInfo[i]
+  s.lineInfo[i] = s.lineInfo[j]
+  s.lineInfo[j] = tmp1
+  if s.trackCaughtExceptions:
+    var tmp2 = s.caughtExceptions[i]
+    s.caughtExceptions[i] = s.caughtExceptions[j]
+    s.caughtExceptions[j] = tmp2
+    var tmp3 = s.caughtExceptionsSet[i]
+    s.caughtExceptionsSet[i] = s.caughtExceptionsSet[j]
+    s.caughtExceptionsSet[j] = tmp3
+  var tmp4 = s.isDecl[i]
+  s.isDecl[i] = s.isDecl[j]
+  s.isDecl[j] = tmp4
+  var tmp5 = s.sym[i]
+  s.sym[i] = s.sym[j]
+  s.sym[j] = tmp5
+
+proc quickSort(s: var SuggestFileSymbolDatabase; ll, rr: int) =
+  var
+    i, j, pivotIdx: int
+    l = ll
+    r = rr
+  while true:
+    i = l
+    j = r
+    pivotIdx = l + ((r - l) shr 1)
+    while true:
+      while (i < pivotIdx) and (s.compare(pivotIdx, i) > 0):
+        inc i
+      while (j > pivotIdx) and (s.compare(pivotIdx, j) < 0):
+        dec j
+      if i < j:
+        s.exchange(i, j)
+        if pivotIdx == i:
+          pivotIdx = j
+          inc i
+        elif pivotIdx == j:
+          pivotIdx = i
+          dec j
+        else:
+          inc i
+          dec j
+      else:
+        break
+    if (pivotIdx - l) < (r - pivotIdx):
+      if (l + 1) < pivotIdx:
+        s.quickSort(l, pivotIdx - 1)
+      l = pivotIdx + 1
+    else:
+      if (pivotIdx + 1) < r:
+        s.quickSort(pivotIdx + 1, r)
+      if (l + 1) < pivotIdx:
+        r = pivotIdx - 1
+      else:
+        break
+    if l >= r:
+      break
+
+proc sort*(s: var SuggestFileSymbolDatabase) =
+  s.quickSort(s.lineInfo.low, s.lineInfo.high)
+  s.isSorted = true
+
+proc add*(s: var SuggestFileSymbolDatabase; v: SymInfoPair) =
+  doAssert(v.info.fileIndex == s.fileIndex)
+  s.lineInfo.add(TinyLineInfo(
+    line: v.info.line,
+    col: v.info.col
+  ))
+  s.sym.add(v.sym)
+  s.isDecl.add(v.isDecl)
+  if s.trackCaughtExceptions:
+    s.caughtExceptions.add(v.caughtExceptions)
+    s.caughtExceptionsSet.add(v.caughtExceptionsSet)
+  s.isSorted = false
+
+proc add*(s: var SuggestSymbolDatabase; v: SymInfoPair; trackCaughtExceptions: bool) =
+  s.mgetOrPut(v.info.fileIndex, newSuggestFileSymbolDatabase(v.info.fileIndex, trackCaughtExceptions)).add(v)
+
+proc findSymInfoIndex*(s: var SuggestFileSymbolDatabase; li: TLineInfo): int =
+  doAssert(li.fileIndex == s.fileIndex)
+  if not s.isSorted:
+    s.sort()
+  var q = TinyLineInfo(
+    line: li.line,
+    col: li.col
+  )
+  result = binarySearch(s.lineInfo, q, cmp)
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index c00fe8b67..6b325c77f 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -10,9 +10,10 @@
 ## Implements the dispatcher for the different parsers.
 
 import
-  strutils, llstream, ast, idents, lexer, options, msgs, parser,
+  llstream, ast, idents, lexer, options, msgs, parser,
   filters, filter_tmpl, renderer, lineinfos, pathutils
 
+import std/strutils
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
 
@@ -36,6 +37,8 @@ proc containsShebang(s: string, i: int): bool =
     var j = i + 2
     while j < s.len and s[j] in Whitespace: inc(j)
     result = s[j] == '/'
+  else:
+    result = false
 
 proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache;
                config: ConfigRef): PNode =
@@ -57,13 +60,14 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache
       else:
         inc(i, 2)
         while i < line.len and line[i] in Whitespace: inc(i)
-        var p: Parser
+        var p: Parser = default(Parser)
         openParser(p, filename, llStreamOpen(substr(line, i)), cache, config)
         result = parseAll(p)
         closeParser(p)
     llStreamClose(s)
 
 proc getFilter(ident: PIdent): FilterKind =
+  result = filtNone
   for i in FilterKind:
     if cmpIgnoreStyle(ident.s, $i) == 0:
       return i
@@ -74,6 +78,7 @@ proc getCallee(conf: ConfigRef; n: PNode): PIdent =
   elif n.kind == nkIdent:
     result = n.ident
   else:
+    result = nil
     localError(conf, n.info, "invalid filter: " & renderTree(n))
 
 proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile,
@@ -124,7 +129,7 @@ proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream;
 proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
                    config: ConfigRef): bool =
   let filename = toFullPathConsiderDirty(config, fileIdx)
-  var f: File
+  var f: File = default(File)
   if not open(f, filename.string):
     rawMessage(config, errGenerated, "cannot open file: " & filename.string)
     return false
@@ -132,7 +137,9 @@ proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache;
   result = true
 
 proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode =
-  var p: Parser
+  var p: Parser = default(Parser)
   if setupParser(p, fileIdx, cache, config):
     result = parseAll(p)
     closeParser(p)
+  else:
+    result = nil
diff --git a/compiler/tccgen.nim b/compiler/tccgen.nim
index 83c891ca8..9ee8516c4 100644
--- a/compiler/tccgen.nim
+++ b/compiler/tccgen.nim
@@ -14,7 +14,7 @@ const tinyPrefix = "dist/nim-tinyc-archive".unixToNativePath
 const nimRoot = currentSourcePath.parentDir.parentDir
 const tinycRoot = nimRoot / tinyPrefix
 when not dirExists(tinycRoot):
-  static: doAssert false, $(tinycRoot, "requires: ./koch installdeps tinyc")
+  static: raiseAssert $(tinycRoot, "requires: ./koch installdeps tinyc")
 {.compile: tinycRoot / "tinyc/libtcc.c".}
 
 var
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 47d7fb3a5..8dd24e090 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -18,6 +18,8 @@
 # * performs lambda lifting for closure support
 # * transforms 'defer' into a 'try finally' statement
 
+import std / tables
+
 import
   options, ast, astalgo, trees, msgs,
   idents, renderer, types, semfold, magicsys, cgmeth,
@@ -27,13 +29,18 @@ import
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): PNode
+type
+  TransformFlag* = enum
+    useCache, keepOpenArrayConversions, force
+  TransformFlags* = set[TransformFlag]
+
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode
 
 import closureiters, lambdalifting
 
 type
   PTransCon = ref object # part of TContext; stackable
-    mapping: TIdNodeTable     # mapping from symbols to nodes
+    mapping: Table[ItemId, PNode]     # mapping from symbols to nodes
     owner: PSym               # current owner
     forStmt: PNode            # current for stmt
     forLoopBody: PNode   # transformed for loop body
@@ -46,9 +53,11 @@ type
     module: PSym
     transCon: PTransCon      # top of a TransCon stack
     inlining: int            # > 0 if we are in inlining context (copy vars)
-    nestedProcs: int         # > 0 if we are in a nested proc
     contSyms, breakSyms: seq[PSym]  # to transform 'continue' and 'break'
     deferDetected, tooEarly: bool
+    isIntroducingNewLocalVars: bool  # true if we are in `introducingNewLocalVars` (don't transform yields)
+    inAddr: bool
+    flags: TransformFlags
     graph: ModuleGraph
     idgen: IdGenerator
 
@@ -65,15 +74,12 @@ proc newTransNode(kind: TNodeKind, n: PNode,
                   sons: int): PNode {.inline.} =
   var x = newNodeIT(kind, n.info, n.typ)
   newSeq(x.sons, sons)
-  x.typ = n.typ
 #  x.flags = n.flags
   result = x
 
 proc newTransCon(owner: PSym): PTransCon =
   assert owner != nil
-  new(result)
-  initIdNodeTable(result.mapping)
-  result.owner = owner
+  result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner)
 
 proc pushTransCon(c: PTransf, t: PTransCon) =
   t.next = c.transCon
@@ -88,11 +94,11 @@ proc getCurrOwner(c: PTransf): PSym =
   else: result = c.module
 
 proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode =
-  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), nextSymId(c.idgen), getCurrOwner(c), info)
+  let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), info)
   r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink})
   incl(r.flags, sfFromGeneric)
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
     result = freshVarForClosureIter(c.graph, r, c.idgen, owner)
   else:
     result = newSymNode(r)
@@ -104,16 +110,18 @@ proc transformSons(c: PTransf, n: PNode): PNode =
   for i in 0..<n.len:
     result[i] = transform(c, n[i])
 
-proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode): PNode =
+proc newAsgnStmt(c: PTransf, kind: TNodeKind, le: PNode, ri: PNode; isFirstWrite: bool): PNode =
   result = newTransNode(kind, ri.info, 2)
   result[0] = le
+  if isFirstWrite:
+    le.flags.incl nfFirstWrite
   result[1] = ri
 
 proc transformSymAux(c: PTransf, n: PNode): PNode =
   let s = n.sym
   if s.typ != nil and s.typ.callConv == ccClosure:
     if s.kind in routineKinds:
-      discard transformBody(c.graph, c.idgen, s, true)
+      discard transformBody(c.graph, c.idgen, s, {useCache}+c.flags)
     if s.kind == skIterator:
       if c.tooEarly: return n
       else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c))
@@ -154,7 +162,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode =
   else:
     b = n
   while tc != nil:
-    result = idNodeTableGet(tc.mapping, b.sym)
+    result = getOrDefault(tc.mapping, b.sym.itemId)
     if result != nil:
       # this slightly convoluted way ensures the line info stays correct:
       if result.kind == nkSym:
@@ -169,10 +177,10 @@ proc transformSym(c: PTransf, n: PNode): PNode =
 
 proc freshVar(c: PTransf; v: PSym): PNode =
   let owner = getCurrOwner(c)
-  if owner.isIterator and not c.tooEarly:
+  if owner.isIterator and not c.tooEarly and not isDefined(c.graph.config, "nimOptIters"):
     result = freshVarForClosureIter(c.graph, v, c.idgen, owner)
   else:
-    var newVar = copySym(v, nextSymId(c.idgen))
+    var newVar = copySym(v, c.idgen)
     incl(newVar.flags, sfFromGeneric)
     newVar.owner = owner
     result = newSymNode(newVar)
@@ -184,10 +192,12 @@ proc transformVarSection(c: PTransf, v: PNode): PNode =
     if it.kind == nkCommentStmt:
       result[i] = it
     elif it.kind == nkIdentDefs:
-      if it[0].kind == nkSym:
+      var vn = it[0]
+      if vn.kind == nkPragmaExpr: vn = vn[0]
+      if vn.kind == nkSym:
         internalAssert(c.graph.config, it.len == 3)
-        let x = freshVar(c, it[0].sym)
-        idNodeTablePut(c.transCon.mapping, it[0].sym, x)
+        let x = freshVar(c, vn.sym)
+        c.transCon.mapping[vn.sym.itemId] = x
         var defs = newTransNode(nkIdentDefs, it.info, 3)
         if importantComments(c.graph.config):
           # keep documentation information:
@@ -208,7 +218,7 @@ proc transformVarSection(c: PTransf, v: PNode): PNode =
       for j in 0..<it.len-2:
         if it[j].kind == nkSym:
           let x = freshVar(c, it[j].sym)
-          idNodeTablePut(c.transCon.mapping, it[j].sym, x)
+          c.transCon.mapping[it[j].sym.itemId] = x
           defs[j] = x
         else:
           defs[j] = transform(c, it[j])
@@ -235,21 +245,21 @@ proc transformConstSection(c: PTransf, v: PNode): PNode =
 
 proc hasContinue(n: PNode): bool =
   case n.kind
-  of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: discard
+  of nkEmpty..nkNilLit, nkForStmt, nkParForStmt, nkWhileStmt: result = false
   of nkContinueStmt: result = true
   else:
+    result = false
     for i in 0..<n.len:
       if hasContinue(n[i]): return true
 
 proc newLabel(c: PTransf, n: PNode): PSym =
-  result = newSym(skLabel, nil, nextSymId(c.idgen), getCurrOwner(c), n.info)
-  result.name = getIdent(c.graph.cache, genPrefix)
+  result = newSym(skLabel, getIdent(c.graph.cache, genPrefix), c.idgen, getCurrOwner(c), n.info)
 
 proc transformBlock(c: PTransf, n: PNode): PNode =
   var labl: PSym
   if c.inlining > 0:
     labl = newLabel(c, n[0])
-    idNodeTablePut(c.transCon.mapping, n[0].sym, newSymNode(labl))
+    c.transCon.mapping[n[0].sym.itemId] = newSymNode(labl)
   else:
     labl =
       if n[0].kind != nkEmpty:
@@ -318,6 +328,14 @@ proc introduceNewLocalVars(c: PTransf, n: PNode): PNode =
     if a.kind == nkSym:
       n[1] = transformSymAux(c, a)
     return n
+  of nkProcDef: # todo optimize nosideeffects?
+    result = newTransNode(n)
+    let x = newSymNode(copySym(n[namePos].sym, c.idgen))
+    c.transCon.mapping[n[namePos].sym.itemId] = x
+    result[namePos] = x # we have to copy proc definitions for iters
+    for i in 1..<n.len:
+      result[i] = introduceNewLocalVars(c, n[i])
+    result[namePos].sym.ast = result
   else:
     result = newTransNode(n)
     for i in 0..<n.len:
@@ -363,10 +381,11 @@ proc transformYield(c: PTransf, n: PNode): PNode =
     case lhs.kind
     of nkSym:
       internalAssert c.graph.config, lhs.sym.kind == skForVar
-      result = newAsgnStmt(c, nkFastAsgn, lhs, rhs)
+      result = newAsgnStmt(c, nkFastAsgn, lhs, rhs, false)
     of nkDotExpr:
-      result = newAsgnStmt(c, nkAsgn, lhs, rhs)
+      result = newAsgnStmt(c, nkAsgn, lhs, rhs, false)
     else:
+      result = nil
       internalAssert c.graph.config, false
   result = newTransNode(nkStmtList, n.info, 0)
   var e = n[0]
@@ -397,9 +416,15 @@ proc transformYield(c: PTransf, n: PNode): PNode =
       result.add transform(c, v)
 
       for i in 0..<c.transCon.forStmt.len - 2:
-        let lhs = c.transCon.forStmt[i]
-        let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
-        result.add(asgnTo(lhs, rhs))
+        if c.transCon.forStmt[i].kind == nkVarTuple:
+          for j in 0..<c.transCon.forStmt[i].len-1:
+            let lhs = c.transCon.forStmt[i][j]
+            let rhs = transform(c, newTupleAccess(c.graph, newTupleAccess(c.graph, tmp, i), j))
+            result.add(asgnTo(lhs, rhs))
+        else:
+          let lhs = c.transCon.forStmt[i]
+          let rhs = transform(c, newTupleAccess(c.graph, tmp, i))
+          result.add(asgnTo(lhs, rhs))
     else:
       for i in 0..<c.transCon.forStmt.len - 2:
         let lhs = c.transCon.forStmt[i]
@@ -438,28 +463,33 @@ proc transformYield(c: PTransf, n: PNode): PNode =
       let rhs = transform(c, e)
       result.add(asgnTo(lhs, rhs))
 
+
+  # bug #23536; note that the info of forLoopBody should't change
+  for idx in 0 ..< result.len:
+    var changeNode = result[idx]
+    changeNode.info = c.transCon.forStmt.info
+    for i, child in changeNode:
+      child.info = changeNode.info
+
   inc(c.transCon.yieldStmts)
   if c.transCon.yieldStmts <= 1:
     # common case
     result.add(c.transCon.forLoopBody)
   else:
     # we need to introduce new local variables:
+    c.isIntroducingNewLocalVars = true # don't transform yields when introducing new local vars
     result.add(introduceNewLocalVars(c, c.transCon.forLoopBody))
+    c.isIntroducingNewLocalVars = false
 
-  for idx in 0 ..< result.len:
-    var changeNode = result[idx]
-    changeNode.info = c.transCon.forStmt.info
-    for i, child in changeNode:
-      child.info = changeNode.info
-
-proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
+proc transformAddrDeref(c: PTransf, n: PNode, kinds: TNodeKinds): PNode =
   result = transformSons(c, n)
-  if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return
+  # inlining of 'var openarray' iterators; bug #19977
+  if n.typ.kind != tyOpenArray and (c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags): return
   var n = result
   case n[0].kind
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
     var m = n[0][0]
-    if m.kind == a or m.kind == b:
+    if m.kind in kinds:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       n[0][0] = m[0]
       result = n[0]
@@ -469,7 +499,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
         result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n[0][1]
-    if m.kind == a or m.kind == b:
+    if m.kind in kinds:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       n[0][1] = m[0]
       result = n[0]
@@ -478,7 +508,15 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode =
       elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
         result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen)
   else:
-    if n[0].kind == a or n[0].kind == b:
+    if n[0].kind in kinds and
+        not (n[0][0].kind == nkSym and n[0][0].sym.kind == skForVar and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyTuple
+        ) and not (n[0][0].kind == nkSym and n[0][0].sym.kind == skParam and
+          n.typ.kind == tyVar and
+          n.typ.skipTypes(abstractVar).kind == tyOpenArray and
+          n[0][0].typ.skipTypes(abstractVar).kind == tyString)
+        : # elimination is harmful to `for tuple unpack` because of newTupleAccess
+          # it is also harmful to openArrayLoc (var openArray) for strings
       # addr ( deref ( x )) --> x
       result = n[0][0]
       if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
@@ -490,7 +528,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode =
 
   # we cannot generate a proper thunk here for GC-safety reasons
   # (see internal documentation):
-  if c.graph.config.backend == backendJs: return prc
+  if jsNoLambdaLifting in c.graph.config.legacyFeatures and c.graph.config.backend == backendJs: return prc
   result = newNodeIT(nkClosure, prc.info, dest)
   var conv = newNodeIT(nkHiddenSubConv, prc.info, dest)
   conv.add(newNodeI(nkEmpty, prc.info))
@@ -536,11 +574,14 @@ proc transformConv(c: PTransf, n: PNode): PNode =
     else:
       result = transformSons(c, n)
   of tyOpenArray, tyVarargs:
-    result = transform(c, n[1])
-    #result = transformSons(c, n)
-    result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
-    #echo n.info, " came here and produced ", typeToString(result.typ),
-    #   " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
+    if keepOpenArrayConversions in c.flags:
+      result = transformSons(c, n)
+    else:
+      result = transform(c, n[1])
+      #result = transformSons(c, n)
+      result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen)
+      #echo n.info, " came here and produced ", typeToString(result.typ),
+      #   " from ", typeToString(n.typ), " and ", typeToString(n[1].typ)
   of tyCstring:
     if source.kind == tyString:
       result = newTransNode(nkStringToCString, n, 1)
@@ -593,7 +634,7 @@ proc transformConv(c: PTransf, n: PNode): PNode =
 type
   TPutArgInto = enum
     paDirectMapping, paFastAsgn, paFastAsgnTakeTypeFromArg
-    paVarAsgn, paComplexOpenarray
+    paVarAsgn, paComplexOpenarray, paViaIndirection
 
 proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   # This analyses how to treat the mapping "formal <-> arg" in an
@@ -611,8 +652,11 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
   case arg.kind
   of nkEmpty..nkNilLit:
     result = paDirectMapping
-  of nkDotExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr:
+  of nkDotExpr, nkDerefExpr, nkHiddenDeref:
     result = putArgInto(arg[0], formal)
+  of nkAddr, nkHiddenAddr:
+    result = putArgInto(arg[0], formal)
+    if result == paViaIndirection: result = paFastAsgn
   of nkCurly, nkBracket:
     for i in 0..<arg.len:
       if putArgInto(arg[i], formal) != paDirectMapping:
@@ -625,6 +669,9 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto =
       if putArgInto(a, formal) != paDirectMapping:
         return paFastAsgn
     result = paDirectMapping
+  of nkBracketExpr:
+    if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
+    else: result = paViaIndirection
   else:
     if skipTypes(formal, abstractInst).kind in {tyVar, tyLent}: result = paVarAsgn
     else: result = paFastAsgn
@@ -638,7 +685,7 @@ proc findWrongOwners(c: PTransf, n: PNode) =
   else:
     for i in 0..<n.safeLen: findWrongOwners(c, n[i])
 
-proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool =
+proc isSimpleIteratorVar(c: PTransf; iter: PSym; call: PNode; owner: PSym): bool =
   proc rec(n: PNode; owner: PSym; dangerousYields: var int) =
     case n.kind
     of nkEmpty..nkNilLit: discard
@@ -650,9 +697,22 @@ proc isSimpleIteratorVar(c: PTransf; iter: PSym): bool =
     else:
       for c in n: rec(c, owner, dangerousYields)
 
+  proc recSym(n: PNode; owner: PSym; sameOwner: var bool) =
+    case n.kind
+    of {nkEmpty..nkNilLit} - {nkSym}: discard
+    of nkSym:
+      if n.sym.owner != owner:
+        sameOwner = false
+    else:
+      for c in n: recSym(c, owner, sameOwner)
+
   var dangerousYields = 0
   rec(getBody(c.graph, iter), iter, dangerousYields)
   result = dangerousYields == 0
+  # the parameters should be owned by the owner
+  # bug #22237
+  for i in 1..<call.len:
+    recSym(call[i], owner, result)
 
 template destructor(t: PType): PSym = getAttachedOp(c.graph, t, attachedDestructor)
 
@@ -696,7 +756,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
       for j in 0..<n[i].len-1:
         addVar(v, copyTree(n[i][j])) # declare new vars
     else:
-      if n[i].kind == nkSym and isSimpleIteratorVar(c, iter):
+      if n[i].kind == nkSym and isSimpleIteratorVar(c, iter, call, n[i].sym.owner):
         incl n[i].sym.flags, sfCursor
       addVar(v, copyTree(n[i])) # declare new vars
   stmtList.add(v)
@@ -720,7 +780,7 @@ proc transformFor(c: PTransf, n: PNode): PNode =
     let pa = putArgInto(arg, formal.typ)
     case pa
     of paDirectMapping:
-      idNodeTablePut(newC.mapping, formal, arg)
+      newC.mapping[formal.itemId] = arg
     of paFastAsgn, paFastAsgnTakeTypeFromArg:
       var t = formal.typ
       if pa == paFastAsgnTakeTypeFromArg:
@@ -731,21 +791,32 @@ proc transformFor(c: PTransf, n: PNode): PNode =
         t = arg.typ
       # generate a temporary and produce an assignment statement:
       var temp = newTemp(c, t, formal.info)
+      #incl(temp.sym.flags, sfCursor)
       addVar(v, temp)
-      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
-      idNodeTablePut(newC.mapping, formal, temp)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
+      newC.mapping[formal.itemId] = temp
     of paVarAsgn:
-      assert(skipTypes(formal.typ, abstractInst).kind in {tyVar})
-      idNodeTablePut(newC.mapping, formal, arg)
+      assert(skipTypes(formal.typ, abstractInst).kind in {tyVar, tyLent})
+      newC.mapping[formal.itemId] = arg
       # XXX BUG still not correct if the arg has a side effect!
+    of paViaIndirection:
+      let t = formal.typ
+      let vt = makeVarType(t.owner, t, c.idgen)
+      vt.flags.incl tfVarIsPtr
+      var temp = newTemp(c, vt, formal.info)
+      addVar(v, temp)
+      var addrExp = newNodeIT(nkHiddenAddr, formal.info, makeVarType(t.owner, t, c.idgen, tyPtr))
+      addrExp.add(arg)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, addrExp, true))
+      newC.mapping[formal.itemId] = newDeref(temp)
     of paComplexOpenarray:
       # arrays will deep copy here (pretty bad).
       var temp = newTemp(c, arg.typ, formal.info)
       addVar(v, temp)
-      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg))
-      idNodeTablePut(newC.mapping, formal, temp)
+      stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg, true))
+      newC.mapping[formal.itemId] = temp
 
-  let body = transformBody(c.graph, c.idgen, iter, true)
+  let body = transformBody(c.graph, c.idgen, iter, {useCache}+c.flags)
   pushInfoContext(c.graph.config, n.info)
   inc(c.inlining)
   stmtList.add(transform(c, body))
@@ -803,7 +874,9 @@ proc getMergeOp(n: PNode): PSym =
      nkCallStrLit:
     if n[0].kind == nkSym and n[0].sym.magic == mConStrStr:
       result = n[0].sym
-  else: discard
+    else:
+      result = nil
+  else: result = nil
 
 proc flattenTreeAux(d, a: PNode, op: PSym) =
   ## Optimizes away the `&` calls in the children nodes and
@@ -843,10 +916,6 @@ proc transformCall(c: PTransf, n: PNode): PNode =
           inc(j)
       result.add(a)
     if result.len == 2: result = result[1]
-  elif magic == mAddr:
-    result = newTransNode(nkAddr, n, 1)
-    result[0] = n[1]
-    result = transformAddrDeref(c, result, nkDerefExpr, nkHiddenDeref)
   elif magic in {mNBindSym, mTypeOf, mRunnableExamples}:
     # for bindSym(myconst) we MUST NOT perform constant folding:
     result = n
@@ -932,6 +1001,15 @@ proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode)
     else:
       result = n
 
+proc transformDerefBlock(c: PTransf, n: PNode): PNode =
+  # We transform (block: x)[] to (block: x[])
+  let e0 = n[0]
+  result = shallowCopy(e0)
+  result.typ = n.typ
+  for i in 0 ..< e0.len - 1:
+    result[i] = e0[i]
+  result[e0.len-1] = newTreeIT(nkHiddenDeref, n.info, n.typ, e0[e0.len-1])
+
 proc transform(c: PTransf, n: PNode): PNode =
   when false:
     var oldDeferAnchor: PNode
@@ -998,10 +1076,22 @@ proc transform(c: PTransf, n: PNode): PNode =
   of nkBreakStmt: result = transformBreak(c, n)
   of nkCallKinds:
     result = transformCall(c, n)
-  of nkAddr, nkHiddenAddr:
-    result = transformAddrDeref(c, n, nkDerefExpr, nkHiddenDeref)
-  of nkDerefExpr, nkHiddenDeref:
-    result = transformAddrDeref(c, n, nkAddr, nkHiddenAddr)
+  of nkHiddenAddr:
+    result = transformAddrDeref(c, n, {nkHiddenDeref})
+  of nkAddr:
+    let oldInAddr = c.inAddr
+    c.inAddr = true
+    result = transformAddrDeref(c, n, {nkDerefExpr, nkHiddenDeref})
+    c.inAddr = oldInAddr
+  of nkDerefExpr:
+    result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
+  of nkHiddenDeref:
+    if n[0].kind in {nkBlockExpr, nkBlockStmt}:
+      # bug #20107 bug #21540. Watch out to not deref the pointer too late.
+      let e = transformDerefBlock(c, n)
+      result = transformBlock(c, e)
+    else:
+      result = transformAddrDeref(c, n, {nkAddr, nkHiddenAddr})
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     result = transformConv(c, n)
   of nkDiscardStmt:
@@ -1028,7 +1118,7 @@ proc transform(c: PTransf, n: PNode): PNode =
     else:
       result = transformSons(c, n)
   of nkYieldStmt:
-    if c.inlining > 0:
+    if c.inlining > 0 and not c.isIntroducingNewLocalVars:
       result = transformYield(c, n)
     else:
       result = transformSons(c, n)
@@ -1036,7 +1126,7 @@ proc transform(c: PTransf, n: PNode): PNode =
     result = transformAsgn(c, n)
   of nkIdentDefs, nkConstDef:
     result = newTransNode(n)
-    result[0] = transform(c, n[0])
+    result[0] = transform(c, skipPragmaExpr(n[0]))
     # Skip the second son since it only contains an unsemanticized copy of the
     # variable type used by docgen
     let last = n.len-1
@@ -1072,7 +1162,7 @@ proc transform(c: PTransf, n: PNode): PNode =
   let exprIsPointerCast = n.kind in {nkCast, nkConv, nkHiddenStdConv} and
                           n.typ != nil and
                           n.typ.kind == tyPointer
-  if not exprIsPointerCast:
+  if not exprIsPointerCast and not c.inAddr:
     var cnst = getConstExpr(c.module, result, c.idgen, c.graph)
     # we inline constants if they are not complex constants:
     if cnst != nil and not dontInlineConstant(n, cnst):
@@ -1088,13 +1178,8 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode =
   popTransCon(c)
   incl(result.flags, nfTransf)
 
-proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator): PTransf =
-  new(result)
-  result.contSyms = @[]
-  result.breakSyms = @[]
-  result.module = module
-  result.graph = g
-  result.idgen = idgen
+proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator; flags: TransformFlags): PTransf =
+  result = PTransf(module: module, graph: g, idgen: idgen, flags: flags)
 
 proc flattenStmts(n: PNode) =
   var goOn = true
@@ -1137,7 +1222,7 @@ template liftDefer(c, root) =
   if c.deferDetected:
     liftDeferAux(root)
 
-proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): PNode =
+proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode =
   assert prc.kind in routineKinds
 
   if prc.transformedBody != nil:
@@ -1146,8 +1231,8 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool):
     result = getBody(g, prc)
   else:
     prc.transformedBody = newNode(nkEmpty) # protects from recursion
-    var c = openTransf(g, prc.getModule, "", idgen)
-    result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen)
+    var c = openTransf(g, prc.getModule, "", idgen, flags)
+    result = liftLambdas(g, prc, getBody(g, prc), c.tooEarly, c.idgen, flags)
     result = processTransf(c, result, prc)
     liftDefer(c, result)
     result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen)
@@ -1157,7 +1242,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool):
 
     incl(result.flags, nfTransf)
 
-    if cache or prc.typ.callConv == ccInline:
+    if useCache in flags or prc.typ.callConv == ccInline:
       # genProc for inline procs will be called multiple times from different modules,
       # it is important to transform exactly once to get sym ids and locations right
       prc.transformedBody = result
@@ -1168,21 +1253,21 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool):
   #if prc.name.s == "main":
   #  echo "transformed into ", renderTree(result, {renderIds})
 
-proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode =
+proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "", idgen)
+    var c = openTransf(g, module, "", idgen, flags)
     result = processTransf(c, n, module)
     liftDefer(c, result)
     #result = liftLambdasForTopLevel(module, result)
     incl(result.flags, nfTransf)
 
-proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode =
+proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode =
   if nfTransf in n.flags:
     result = n
   else:
-    var c = openTransf(g, module, "", idgen)
+    var c = openTransf(g, module, "", idgen, flags)
     result = processTransf(c, n, module)
     liftDefer(c, result)
     # expressions are not to be injected with destructor calls as that
diff --git a/compiler/trees.nim b/compiler/trees.nim
index 05c060595..41b54eb09 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -13,6 +13,7 @@ import
   ast, wordrecg, idents
 
 proc cyclicTreeAux(n: PNode, visited: var seq[PNode]): bool =
+  result = false
   if n == nil: return
   for v in visited:
     if v == n: return true
@@ -53,8 +54,13 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool =
           if not exprStructuralEquivalent(a[i], b[i],
                                           strictSymEquality): return
         result = true
+      else:
+        result = false
+  else:
+    result = false
 
 proc sameTree*(a, b: PNode): bool =
+  result = false
   if a == b:
     result = true
   elif a != nil and b != nil and a.kind == b.kind:
@@ -91,6 +97,7 @@ proc isConstExpr*(n: PNode): bool =
   n.kind in atomKinds or nfAllConst in n.flags
 
 proc isCaseObj*(n: PNode): bool =
+  result = false
   if n.kind == nkRecCase: return true
   for i in 0..<n.safeLen:
     if n[i].isCaseObj: return true
@@ -109,7 +116,7 @@ proc isDeepConstExpr*(n: PNode; preventInheritance = false): bool =
       let t = n.typ.skipTypes({tyGenericInst, tyDistinct, tyAlias, tySink, tyOwned})
       if t.kind in {tyRef, tyPtr} or tfUnion in t.flags: return false
       if t.kind == tyObject:
-        if preventInheritance and t[0] != nil:
+        if preventInheritance and t.baseClass != nil:
           result = false
         elif isCaseObj(t.n):
           result = false
@@ -117,7 +124,7 @@ proc isDeepConstExpr*(n: PNode; preventInheritance = false): bool =
           result = true
       else:
         result = true
-  else: discard
+  else: result = false
 
 proc isRange*(n: PNode): bool {.inline.} =
   if n.kind in nkCallKinds:
@@ -127,16 +134,22 @@ proc isRange*(n: PNode): bool {.inline.} =
        (callee.kind in {nkClosedSymChoice, nkOpenSymChoice} and
         callee[1].sym.name.id == ord(wDotDot)):
       result = true
+    else:
+      result = false
+  else:
+    result = false
 
 proc whichPragma*(n: PNode): TSpecialWord =
   let key = if n.kind in nkPragmaCallKinds and n.len > 0: n[0] else: n
   case key.kind
   of nkIdent: result = whichKeyword(key.ident)
   of nkSym: result = whichKeyword(key.sym.name)
-  of nkCast: result = wCast
+  of nkCast: return wCast
   of nkClosedSymChoice, nkOpenSymChoice:
-    result = whichPragma(key[0])
-  else: result = wInvalid
+    return whichPragma(key[0])
+  else: return wInvalid
+  if result in nonPragmaWordsLow..nonPragmaWordsHigh:
+    result = wInvalid
 
 proc isNoSideEffectPragma*(n: PNode): bool =
   var k = whichPragma(n)
@@ -145,12 +158,14 @@ proc isNoSideEffectPragma*(n: PNode): bool =
   result = k == wNoSideEffect
 
 proc findPragma*(n: PNode, which: TSpecialWord): PNode =
+  result = nil
   if n.kind == nkPragma:
     for son in n:
       if whichPragma(son) == which:
         return son
 
 proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode =
+  result = nil
   for i in 0..<n.len:
     var it = n[i]
     if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
@@ -161,6 +176,7 @@ proc effectSpec*(n: PNode, effectType: TSpecialWord): PNode =
       return
 
 proc propSpec*(n: PNode, effectType: TSpecialWord): PNode =
+  result = nil
   for i in 0..<n.len:
     var it = n[i]
     if it.kind == nkExprColonExpr and whichPragma(it) == effectType:
@@ -182,10 +198,6 @@ proc extractRange*(k: TNodeKind, n: PNode, a, b: int): PNode =
   result = newNodeI(k, n.info, b-a+1)
   for i in 0..b-a: result[i] = n[i+a]
 
-proc isTrue*(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
 proc getRoot*(n: PNode): PSym =
   ## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
   ## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
@@ -194,6 +206,8 @@ proc getRoot*(n: PNode): PSym =
   of nkSym:
     if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}:
       result = n.sym
+    else:
+      result = nil
   of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
       nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkHiddenAddr, nkAddr:
     result = getRoot(n[0])
@@ -201,7 +215,8 @@ proc getRoot*(n: PNode): PSym =
     result = getRoot(n[1])
   of nkCallKinds:
     if getMagic(n) == mSlice: result = getRoot(n[1])
-  else: discard
+    else: result = nil
+  else: result = nil
 
 proc stupidStmtListExpr*(n: PNode): bool =
   for i in 0..<n.len-1:
@@ -211,6 +226,13 @@ proc stupidStmtListExpr*(n: PNode): bool =
 proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} =
   # symbols that expand to a complex constant (array, etc.) should not be
   # inlined, unless it's the empty array:
-  result = orig.kind != cnst.kind and
-           cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
+  result = cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and
            cnst.len > ord(cnst.kind == nkObjConstr)
+
+proc isRunnableExamples*(n: PNode): bool =
+  # Templates and generics don't perform symbol lookups.
+  result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
+    n.kind == nkIdent and n.ident.id == ord(wRunnableExamples)
+
+proc skipAddr*(n: PNode): PNode {.inline.} =
+  result = if n.kind in {nkAddr, nkHiddenAddr}: n[0] else: n
diff --git a/compiler/treetab.nim b/compiler/treetab.nim
index d8dd8d33e..6685c4a89 100644
--- a/compiler/treetab.nim
+++ b/compiler/treetab.nim
@@ -9,8 +9,9 @@
 
 # Implements a table from trees to trees. Does structural equivalence checking.
 
-import
-  hashes, ast, astalgo, types
+import ast, astalgo, types
+
+import std/hashes
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -57,7 +58,11 @@ proc treesEquivalent(a, b: PNode): bool =
         for i in 0..<a.len:
           if not treesEquivalent(a[i], b[i]): return
         result = true
+      else:
+        result = false
     if result: result = sameTypeOrNil(a.typ, b.typ)
+  else:
+    result = false
 
 proc nodeTableRawGet(t: TNodeTable, k: Hash, key: PNode): int =
   var h: Hash = k and high(t.data)
diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim
index 57dd039ad..39193a42d 100644
--- a/compiler/typeallowed.nim
+++ b/compiler/typeallowed.nim
@@ -8,10 +8,10 @@
 #
 
 ## This module contains 'typeAllowed' and friends which check
-## for invalid types like 'openArray[var int]'.
+## for invalid types like `openArray[var int]`.
 
-import
-  intsets, ast, renderer, options, semdata, types
+import ast, renderer, options, semdata, types
+import std/intsets
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -25,6 +25,9 @@ type
     taNoUntyped
     taIsTemplateOrMacro
     taProcContextIsNotMacro
+    taIsCastable
+    taIsDefaultField
+    taVoid # only allow direct void fields of objects/tuples
 
   TTypeAllowedFlags* = set[TTypeAllowedFlag]
 
@@ -46,6 +49,8 @@ proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind,
           let it = n[i]
           result = typeAllowedNode(marker, it, kind, c, flags)
           if result != nil: break
+  else:
+    result = nil
 
 proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
                     c: PContext; flags: TTypeAllowedFlags = {}): PType =
@@ -56,6 +61,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   if typ == nil: return nil
   if containsOrIncl(marker, typ.id): return nil
   var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink})
+
+  let flags = if t.kind == tyVoid: flags else: flags-{taVoid}
   case t.kind
   of tyVar, tyLent:
     if kind in {skProc, skFunc, skConst} and (views notin c.features):
@@ -63,10 +70,13 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
     elif taIsOpenArray in flags:
       result = t
     elif t.kind == tyLent and ((kind != skResult and views notin c.features) or
-                              kind == skParam): # lent can't be used as parameters.
+      (kind == skParam and {taIsCastable, taField} * flags == {})): # lent cannot be used as parameters.
+                                                       # except in the cast environment and as the field of an object
+      result = t
+    elif isOutParam(t) and kind != skParam:
       result = t
     else:
-      var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc, tySink})
+      var t2 = skipTypes(t.elementType, abstractInst-{tyTypeDesc, tySink})
       case t2.kind
       of tyVar, tyLent:
         if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap
@@ -89,11 +99,11 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
         # only closure iterators may be assigned to anything.
         result = t
       let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags
-      for i in 1..<t.len:
+      for _, a in t.paramTypes:
         if result != nil: break
-        result = typeAllowedAux(marker, t[i], skParam, c, f-{taIsOpenArray})
-      if result.isNil and t[0] != nil:
-        result = typeAllowedAux(marker, t[0], skResult, c, flags)
+        result = typeAllowedAux(marker, a, skParam, c, f-{taIsOpenArray})
+      if result.isNil and t.returnType != nil:
+        result = typeAllowedAux(marker, t.returnType, skResult, c, flags)
   of tyTypeDesc:
     if kind in {skVar, skLet, skConst} and taProcContextIsNotMacro in flags:
       result = t
@@ -108,12 +118,12 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   of tyStatic:
     if kind notin {skParam}: result = t
   of tyVoid:
-    if taField notin flags: result = t
+    if taVoid notin flags: result = t
   of tyTypeClasses:
     if tfGenericTypeParam in t.flags or taConcept in flags: #or taField notin flags:
       discard
     elif t.isResolvedUserTypeClass:
-      result = typeAllowedAux(marker, t.lastSon, kind, c, flags)
+      result = typeAllowedAux(marker, t.last, kind, c, flags)
     elif kind notin {skParam, skResult}:
       result = t
   of tyGenericBody, tyGenericParam, tyGenericInvocation,
@@ -126,74 +136,77 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind,
   of tyOrdinal:
     if kind != skParam: result = t
   of tyGenericInst, tyDistinct, tyAlias, tyInferred:
-    result = typeAllowedAux(marker, lastSon(t), kind, c, flags)
+    result = typeAllowedAux(marker, skipModifier(t), kind, c, flags)
   of tyRange:
-    if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin
+    if skipTypes(t.elementType, abstractInst-{tyTypeDesc}).kind notin
       {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t
   of tyOpenArray:
     # you cannot nest openArrays/sinks/etc.
     if (kind != skParam or taIsOpenArray in flags) and views notin c.features:
       result = t
     else:
-      result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
   of tyVarargs:
     # you cannot nest openArrays/sinks/etc.
     if kind != skParam or taIsOpenArray in flags:
       result = t
     else:
-      result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray})
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taIsOpenArray})
   of tySink:
     # you cannot nest openArrays/sinks/etc.
-    if kind != skParam or taIsOpenArray in flags or t[0].kind in {tySink, tyLent, tyVar}:
+    if kind != skParam or taIsOpenArray in flags or t.elementType.kind in {tySink, tyLent, tyVar}:
       result = t
     else:
-      result = typeAllowedAux(marker, t[0], kind, c, flags)
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags)
   of tyUncheckedArray:
     if kind != skParam and taHeap notin flags:
       result = t
     else:
-      result = typeAllowedAux(marker, lastSon(t), kind, c, flags-{taHeap})
+      result = typeAllowedAux(marker, elementType(t), kind, c, flags-{taHeap})
   of tySequence:
-    if t[0].kind != tyEmpty:
-      result = typeAllowedAux(marker, t[0], kind, c, flags+{taHeap})
+    if t.elementType.kind != tyEmpty:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
     elif kind in {skVar, skLet}:
-      result = t[0]
+      result = t.elementType
   of tyArray:
-    if t[1].kind == tyTypeDesc:
-      result = t[1]
-    elif t[1].kind != tyEmpty:
-      result = typeAllowedAux(marker, t[1], kind, c, flags)
+    if t.elementType.kind == tyTypeDesc:
+      result = t.elementType
+    elif t.elementType.kind != tyEmpty:
+      result = typeAllowedAux(marker, t.elementType, kind, c, flags)
     elif kind in {skVar, skLet}:
-      result = t[1]
+      result = t.elementType
   of tyRef:
-    if kind == skConst: result = t
-    else: result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+    if kind == skConst and taIsDefaultField notin flags: result = t
+    else: result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
   of tyPtr:
-    result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+    result = typeAllowedAux(marker, t.elementType, kind, c, flags+{taHeap})
   of tySet:
-    for i in 0..<t.len:
-      result = typeAllowedAux(marker, t[i], kind, c, flags)
-      if result != nil: break
-  of tyObject, tyTuple:
+    result = typeAllowedAux(marker, t.elementType, kind, c, flags)
+  of tyObject:
     if kind in {skProc, skFunc, skConst} and
-        t.kind == tyObject and t[0] != nil:
+        t.baseClass != nil and taIsDefaultField notin flags:
       result = t
     else:
-      let flags = flags+{taField}
-      for i in 0..<t.len:
-        result = typeAllowedAux(marker, t[i], kind, c, flags)
-        if result != nil: break
+      let flags = flags+{taField, taVoid}
+      result = typeAllowedAux(marker, t.baseClass, kind, c, flags)
       if result.isNil and t.n != nil:
         result = typeAllowedNode(marker, t.n, kind, c, flags)
+  of tyTuple:
+    let flags = flags+{taField, taVoid}
+    for a in t.kids:
+      result = typeAllowedAux(marker, a, kind, c, flags)
+      if result != nil: break
+    if result.isNil and t.n != nil:
+      result = typeAllowedNode(marker, t.n, kind, c, flags)
   of tyEmpty:
     if kind in {skVar, skLet}: result = t
-  of tyProxy:
+  of tyError:
     # for now same as error node; we say it's a valid type as it should
     # prevent cascading errors:
     result = nil
   of tyOwned:
-    if t.len == 1 and t[0].skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
-      result = typeAllowedAux(marker, t.lastSon, kind, c, flags+{taHeap})
+    if t.hasElementType and t.skipModifier.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyProc}:
+      result = typeAllowedAux(marker, t.skipModifier, kind, c, flags+{taHeap})
     else:
       result = t
   of tyConcept:
@@ -240,23 +253,23 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind =
     result = immutableView
   of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned,
      tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic:
-    result = classifyViewTypeAux(marker, lastSon(t))
+    result = classifyViewTypeAux(marker, skipModifier(t))
   of tyFromExpr:
-    if t.len > 0:
-      result = classifyViewTypeAux(marker, lastSon(t))
+    if t.hasElementType:
+      result = classifyViewTypeAux(marker, skipModifier(t))
     else:
       result = noView
   of tyTuple:
     result = noView
-    for i in 0..<t.len:
-      result.combine classifyViewTypeAux(marker, t[i])
+    for a in t.kids:
+      result.combine classifyViewTypeAux(marker, a)
       if result == mutableView: break
   of tyObject:
     result = noView
     if t.n != nil:
       result = classifyViewTypeNode(marker, t.n)
-    if t[0] != nil:
-      result.combine classifyViewTypeAux(marker, t[0])
+    if t.baseClass != nil:
+      result.combine classifyViewTypeAux(marker, t.baseClass)
   else:
     # it doesn't matter what these types contain, 'ptr openArray' is not a
     # view type!
@@ -274,7 +287,7 @@ proc directViewType*(t: PType): ViewTypeKind =
   of tyLent, tyOpenArray:
     result = immutableView
   of abstractInst-{tyTypeDesc}:
-    result = directViewType(t.lastSon)
+    result = directViewType(t.skipModifier)
   else:
     result = noView
 
diff --git a/compiler/types.nim b/compiler/types.nim
index 0d98becda..a441b0ea2 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -10,8 +10,10 @@
 # this module contains routines for accessing and iterating over types
 
 import
-  intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options,
-  lineinfos, int128, modulegraphs, astmsgs
+  ast, astalgo, trees, msgs, platform, renderer, options,
+  lineinfos, int128, modulegraphs, astmsgs, wordrecg
+
+import std/[intsets, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, formatfloat]
@@ -28,6 +30,8 @@ type
     preferMixed,
       # most useful, shows: symbol + resolved symbols if it differs, e.g.:
       # tuple[a: MyInt{int}, b: float]
+    preferInlayHint,
+    preferInferredEffects,
 
   TTypeRelation* = enum      # order is important!
     isNone, isConvertible,
@@ -48,7 +52,6 @@ type
   ProcConvMismatch* = enum
     pcmNoSideEffect
     pcmNotGcSafe
-    pcmLockDifference
     pcmNotIterator
     pcmDifferentCallConv
 
@@ -63,19 +66,13 @@ proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) =
 
 template `$`*(typ: PType): string = typeToString(typ)
 
-proc base*(t: PType): PType =
-  result = t[0]
-
 # ------------------- type iterator: ----------------------------------------
 type
   TTypeIter* = proc (t: PType, closure: RootRef): bool {.nimcall.} # true if iteration should stop
-  TTypeMutator* = proc (t: PType, closure: RootRef): PType {.nimcall.} # copy t and mutate it
   TTypePredicate* = proc (t: PType): bool {.nimcall.}
 
 proc iterOverType*(t: PType, iter: TTypeIter, closure: RootRef): bool
   # Returns result of `iter`.
-proc mutateType*(t: PType, iter: TTypeMutator, closure: RootRef): PType
-  # Returns result of `iter`.
 
 type
   TParamsEquality* = enum     # they are equal, but their
@@ -107,18 +104,18 @@ const
   typedescInst* = abstractInst + {tyTypeDesc, tyOwned, tyUserTypeClass}
 
 proc invalidGenericInst*(f: PType): bool =
-  result = f.kind == tyGenericInst and lastSon(f) == nil
+  result = f.kind == tyGenericInst and skipModifier(f) == nil
 
 proc isPureObject*(typ: PType): bool =
   var t = typ
-  while t.kind == tyObject and t[0] != nil:
-    t = t[0].skipTypes(skipPtrs)
+  while t.kind == tyObject and t.baseClass != nil:
+    t = t.baseClass.skipTypes(skipPtrs)
   result = t.sym != nil and sfPure in t.sym.flags
 
 proc isUnsigned*(t: PType): bool =
   t.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}
 
-proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
+proc getOrdValueAux*(n: PNode, err: var bool): Int128 =
   var k = n.kind
   if n.typ != nil and n.typ.skipTypes(abstractInst).kind in {tyChar, tyUInt..tyUInt64}:
     k = nkUIntLit
@@ -134,13 +131,22 @@ proc getOrdValue*(n: PNode; onError = high(Int128)): Int128 =
     toInt128(n.intVal)
   of nkNilLit:
     int128.Zero
-  of nkHiddenStdConv: getOrdValue(n[1], onError)
+  of nkHiddenStdConv:
+    getOrdValueAux(n[1], err)
   else:
-    # XXX: The idea behind the introduction of int128 was to finally
-    # have all calculations numerically far away from any
-    # overflows. This command just introduces such overflows and
-    # should therefore really be revisited.
-    onError
+    err = true
+    int128.Zero
+
+proc getOrdValue*(n: PNode): Int128 =
+  var err: bool = false
+  result = getOrdValueAux(n, err)
+  #assert err == false
+
+proc getOrdValue*(n: PNode, onError: Int128): Int128 =
+  var err = false
+  result = getOrdValueAux(n, err)
+  if err:
+    result = onError
 
 proc getFloatValue*(n: PNode): BiggestFloat =
   case n.kind
@@ -182,10 +188,10 @@ proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferNa
 proc elemType*(t: PType): PType =
   assert(t != nil)
   case t.kind
-  of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(lastSon(t))
-  of tyArray: result = t[1]
+  of tyGenericInst, tyDistinct, tyAlias, tySink: result = elemType(skipModifier(t))
+  of tyArray: result = t.elementType
   of tyError: result = t
-  else: result = t.lastSon
+  else: result = t.elementType
   assert(result != nil)
 
 proc enumHasHoles*(t: PType): bool =
@@ -198,7 +204,7 @@ proc isOrdinalType*(t: PType, allowEnumWithHoles: bool = false): bool =
     baseKinds = {tyChar, tyInt..tyInt64, tyUInt..tyUInt64, tyBool, tyEnum}
     parentKinds = {tyRange, tyOrdinal, tyGenericInst, tyAlias, tySink, tyDistinct}
   result = (t.kind in baseKinds and (not t.enumHasHoles or allowEnumWithHoles)) or
-    (t.kind in parentKinds and isOrdinalType(t.lastSon, allowEnumWithHoles))
+    (t.kind in parentKinds and isOrdinalType(t.skipModifier, allowEnumWithHoles))
 
 proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
                      closure: RootRef): bool
@@ -210,9 +216,13 @@ proc iterOverNode(marker: var IntSet, n: PNode, iter: TTypeIter,
       # a leaf
       result = iterOverTypeAux(marker, n.typ, iter, closure)
     else:
+      result = iterOverTypeAux(marker, n.typ, iter, closure)
+      if result: return
       for i in 0..<n.len:
         result = iterOverNode(marker, n[i], iter, closure)
         if result: return
+  else:
+    result = false
 
 proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
                      closure: RootRef): bool =
@@ -222,11 +232,15 @@ proc iterOverTypeAux(marker: var IntSet, t: PType, iter: TTypeIter,
   if result: return
   if not containsOrIncl(marker, t.id):
     case t.kind
-    of tyGenericInst, tyGenericBody, tyAlias, tySink, tyInferred:
-      result = iterOverTypeAux(marker, lastSon(t), iter, closure)
+    of tyGenericBody:
+      # treat as atomic, containsUnresolvedType wants always false,
+      # containsGenericType always gives true
+      discard
+    of tyGenericInst, tyAlias, tySink, tyInferred:
+      result = iterOverTypeAux(marker, skipModifier(t), iter, closure)
     else:
-      for i in 0..<t.len:
-        result = iterOverTypeAux(marker, t[i], iter, closure)
+      for a in t.kids:
+        result = iterOverTypeAux(marker, a, iter, closure)
         if result: return
       if t.n != nil and t.kind != tyProc: result = iterOverNode(marker, t.n, iter, closure)
 
@@ -269,14 +283,14 @@ proc searchTypeForAux(t: PType, predicate: TTypePredicate,
   if result: return
   case t.kind
   of tyObject:
-    if t[0] != nil:
-      result = searchTypeForAux(t[0].skipTypes(skipPtrs), predicate, marker)
+    if t.baseClass != nil:
+      result = searchTypeForAux(t.baseClass.skipTypes(skipPtrs), predicate, marker)
     if not result: result = searchTypeNodeForAux(t.n, predicate, marker)
   of tyGenericInst, tyDistinct, tyAlias, tySink:
-    result = searchTypeForAux(lastSon(t), predicate, marker)
+    result = searchTypeForAux(skipModifier(t), predicate, marker)
   of tyArray, tySet, tyTuple:
-    for i in 0..<t.len:
-      result = searchTypeForAux(t[i], predicate, marker)
+    for a in t.kids:
+      result = searchTypeForAux(a, predicate, marker)
       if result: return
   else:
     discard
@@ -292,7 +306,7 @@ proc containsObject*(t: PType): bool =
   result = searchTypeFor(t, isObjectPredicate)
 
 proc isObjectWithTypeFieldPredicate(t: PType): bool =
-  result = t.kind == tyObject and t[0] == nil and
+  result = t.kind == tyObject and t.baseClass == nil and
       not (t.sym != nil and {sfPure, sfInfixCall} * t.sym.flags != {}) and
       tfFinal notin t.flags
 
@@ -304,7 +318,6 @@ type
 
 proc analyseObjectWithTypeFieldAux(t: PType,
                                    marker: var IntSet): TTypeFieldResult =
-  var res: TTypeFieldResult
   result = frNone
   if t == nil: return
   case t.kind
@@ -312,20 +325,19 @@ proc analyseObjectWithTypeFieldAux(t: PType,
     if t.n != nil:
       if searchTypeNodeForAux(t.n, isObjectWithTypeFieldPredicate, marker):
         return frEmbedded
-    for i in 0..<t.len:
-      var x = t[i]
-      if x != nil: x = x.skipTypes(skipPtrs)
-      res = analyseObjectWithTypeFieldAux(x, marker)
-      if res == frEmbedded:
-        return frEmbedded
-      if res == frHeader: result = frHeader
+    var x = t.baseClass
+    if x != nil: x = x.skipTypes(skipPtrs)
+    let res = analyseObjectWithTypeFieldAux(x, marker)
+    if res == frEmbedded:
+      return frEmbedded
+    if res == frHeader: result = frHeader
     if result == frNone:
       if isObjectWithTypeFieldPredicate(t): result = frHeader
   of tyGenericInst, tyDistinct, tyAlias, tySink:
-    result = analyseObjectWithTypeFieldAux(lastSon(t), marker)
+    result = analyseObjectWithTypeFieldAux(skipModifier(t), marker)
   of tyArray, tyTuple:
-    for i in 0..<t.len:
-      res = analyseObjectWithTypeFieldAux(t[i], marker)
+    for a in t.kids:
+      let res = analyseObjectWithTypeFieldAux(a, marker)
       if res != frNone:
         return frEmbedded
   else:
@@ -371,41 +383,68 @@ proc containsHiddenPointer*(typ: PType): bool =
   # that need to be copied deeply)
   result = searchTypeFor(typ, isHiddenPointer)
 
-proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool
-proc canFormAcycleNode(marker: var IntSet, n: PNode, startId: int): bool =
+proc canFormAcycleAux(g: ModuleGraph; marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool
+proc canFormAcycleNode(g: ModuleGraph; marker: var IntSet, n: PNode, orig: PType, withRef: bool, hasTrace: bool): bool =
   result = false
   if n != nil:
-    result = canFormAcycleAux(marker, n.typ, startId)
-    if not result:
-      case n.kind
-      of nkNone..nkNilLit:
-        discard
-      else:
-        for i in 0..<n.len:
-          result = canFormAcycleNode(marker, n[i], startId)
-          if result: return
+    var hasCursor = n.kind == nkSym and sfCursor in n.sym.flags
+    # cursor fields don't own the refs, which cannot form reference cycles
+    if hasTrace or not hasCursor:
+      result = canFormAcycleAux(g, marker, n.typ, orig, withRef, hasTrace)
+      if not result:
+        case n.kind
+        of nkNone..nkNilLit:
+          discard
+        else:
+          for i in 0..<n.len:
+            result = canFormAcycleNode(g, marker, n[i], orig, withRef, hasTrace)
+            if result: return
+
 
-proc canFormAcycleAux(marker: var IntSet, typ: PType, startId: int): bool =
+proc sameBackendType*(x, y: PType): bool
+proc canFormAcycleAux(g: ModuleGraph, marker: var IntSet, typ: PType, orig: PType, withRef: bool, hasTrace: bool): bool =
   result = false
   if typ == nil: return
   if tfAcyclic in typ.flags: return
   var t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
   if tfAcyclic in t.flags: return
   case t.kind
-  of tyTuple, tyObject, tyRef, tySequence, tyArray, tyOpenArray, tyVarargs:
-    if t.id == startId:
+  of tyRef, tyPtr, tyUncheckedArray:
+    if t.kind == tyRef or hasTrace:
+      if withRef and sameBackendType(t, orig):
+        result = true
+      elif not containsOrIncl(marker, t.id):
+        result = canFormAcycleAux(g, marker, t.elementType, orig, withRef or t.kind != tyUncheckedArray, hasTrace)
+  of tyObject:
+    if withRef and sameBackendType(t, orig):
       result = true
     elif not containsOrIncl(marker, t.id):
-      for i in 0..<t.len:
-        result = canFormAcycleAux(marker, t[i], startId)
+      var hasTrace = hasTrace
+      let op = getAttachedOp(g, t.skipTypes({tyRef}), attachedTrace)
+      if op != nil and sfOverridden in op.flags:
+        hasTrace = true
+      if t.baseClass != nil:
+        result = canFormAcycleAux(g, marker, t.baseClass, orig, withRef, hasTrace)
         if result: return
-      if t.n != nil: result = canFormAcycleNode(marker, t.n, startId)
+      if t.n != nil: result = canFormAcycleNode(g, marker, t.n, orig, withRef, hasTrace)
     # Inheritance can introduce cyclic types, however this is not relevant
     # as the type that is passed to 'new' is statically known!
     # er but we use it also for the write barrier ...
-    if t.kind == tyObject and tfFinal notin t.flags:
+    if tfFinal notin t.flags:
       # damn inheritance may introduce cycles:
       result = true
+  of tyTuple:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      for a in t.kids:
+        result = canFormAcycleAux(g, marker, a, orig, withRef, hasTrace)
+        if result: return
+  of tySequence, tyArray, tyOpenArray, tyVarargs:
+    if withRef and sameBackendType(t, orig):
+      result = true
+    elif not containsOrIncl(marker, t.id):
+      result = canFormAcycleAux(g, marker, t.elementType, orig, withRef, hasTrace)
   of tyProc: result = typ.callConv == ccClosure
   else: discard
 
@@ -413,47 +452,20 @@ proc isFinal*(t: PType): bool =
   let t = t.skipTypes(abstractInst)
   result = t.kind != tyObject or tfFinal in t.flags or isPureObject(t)
 
-proc canFormAcycle*(typ: PType): bool =
+proc canFormAcycle*(g: ModuleGraph, typ: PType): bool =
   var marker = initIntSet()
   let t = skipTypes(typ, abstractInst+{tyOwned}-{tyTypeDesc})
-  result = canFormAcycleAux(marker, t, t.id)
-
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType
-proc mutateNode(marker: var IntSet, n: PNode, iter: TTypeMutator,
-                closure: RootRef): PNode =
-  result = nil
-  if n != nil:
-    result = copyNode(n)
-    result.typ = mutateTypeAux(marker, n.typ, iter, closure)
-    case n.kind
-    of nkNone..nkNilLit:
-      # a leaf
-      discard
-    else:
-      for i in 0..<n.len:
-        result.add mutateNode(marker, n[i], iter, closure)
-
-proc mutateTypeAux(marker: var IntSet, t: PType, iter: TTypeMutator,
-                   closure: RootRef): PType =
-  result = nil
-  if t == nil: return
-  result = iter(t, closure)
-  if not containsOrIncl(marker, t.id):
-    for i in 0..<t.len:
-      result[i] = mutateTypeAux(marker, result[i], iter, closure)
-    if t.n != nil: result.n = mutateNode(marker, t.n, iter, closure)
-  assert(result != nil)
-
-proc mutateType(t: PType, iter: TTypeMutator, closure: RootRef): PType =
-  var marker = initIntSet()
-  result = mutateTypeAux(marker, t, iter, closure)
+  result = canFormAcycleAux(g, marker, t, t, false, false)
 
 proc valueToString(a: PNode): string =
   case a.kind
-  of nkCharLit..nkUInt64Lit: result = $a.intVal
+  of nkCharLit, nkUIntLit..nkUInt64Lit:
+    result = $cast[uint64](a.intVal)
+  of nkIntLit..nkInt64Lit:
+    result = $a.intVal
   of nkFloatLit..nkFloat128Lit: result = $a.floatVal
   of nkStrLit..nkTripleStrLit: result = a.strVal
+  of nkStaticExpr: result = "static(" & a[0].renderTree & ")"
   else: result = "<invalid value>"
 
 proc rangeToStr(n: PNode): string =
@@ -479,7 +491,7 @@ const
     "void", "iterable"]
 
 const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo,
-  preferGenericArg, preferResolved, preferMixed}
+  preferGenericArg, preferResolved, preferMixed, preferInlayHint, preferInferredEffects}
 
 template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
   tc.add concrete
@@ -487,7 +499,7 @@ template bindConcreteTypeToUserTypeClass*(tc, concrete: PType) =
 
 # TODO: It would be a good idea to kill the special state of a resolved
 # concept by switching to tyAlias within the instantiated procs.
-# Currently, tyAlias is always skipped with lastSon, which means that
+# Currently, tyAlias is always skipped with skipModifier, which means that
 # we can store information about the matched concept in another position.
 # Then builtInFieldAccess can be modified to properly read the derived
 # consts and types stored within the concept.
@@ -513,32 +525,31 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     if prefer in preferToResolveSymbols and t.sym != nil and
          sfAnon notin t.sym.flags and t.kind != tySequence:
       if t.kind == tyInt and isIntLit(t):
-        result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
-      elif t.kind == tyAlias and t[0].kind != tyAlias:
-        result = typeToString(t[0])
+        if prefer == preferInlayHint:
+          result = t.sym.name.s
+        else:
+          result = t.sym.name.s & " literal(" & $t.n.intVal & ")"
+      elif t.kind == tyAlias and t.elementType.kind != tyAlias:
+        result = typeToString(t.elementType)
       elif prefer in {preferResolved, preferMixed}:
         case t.kind
         of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}:
           result = typeToStr[t.kind]
         of tyGenericBody:
-          result = typeToString(t.lastSon)
+          result = typeToString(t.last)
         of tyCompositeTypeClass:
           # avoids showing `A[any]` in `proc fun(a: A)` with `A = object[T]`
-          result = typeToString(t.lastSon.lastSon)
+          result = typeToString(t.last.last)
         else:
           result = t.sym.name.s
         if prefer == preferMixed and result != t.sym.name.s:
           result = t.sym.name.s & "{" & result & "}"
-      elif prefer in {preferName, preferTypeName} or t.sym.owner.isNil:
+      elif prefer in {preferName, preferTypeName, preferInlayHint, preferInferredEffects} or t.sym.owner.isNil:
         # note: should probably be: {preferName, preferTypeName, preferGenericArg}
         result = t.sym.name.s
-        if t.kind == tyGenericParam and t.len > 0:
+        if t.kind == tyGenericParam and t.genericParamHasConstraints:
           result.add ": "
-          var first = true
-          for son in t.sons:
-            if not first: result.add " or "
-            result.add son.typeToString
-            first = false
+          result.add t.elementType.typeToString
       else:
         result = t.sym.owner.name.s & '.' & t.sym.name.s
       result.addTypeFlags(t)
@@ -548,39 +559,49 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
       if not isIntLit(t) or prefer == preferExported:
         result = typeToStr[t.kind]
       else:
-        if prefer == preferGenericArg:
+        case prefer:
+        of preferGenericArg:
           result = $t.n.intVal
+        of preferInlayHint:
+          result = "int"
         else:
           result = "int literal(" & $t.n.intVal & ")"
-    of tyGenericInst, tyGenericInvocation:
-      result = typeToString(t[0]) & '['
-      for i in 1..<t.len-ord(t.kind != tyGenericInvocation):
-        if i > 1: result.add(", ")
-        result.add(typeToString(t[i], preferGenericArg))
+    of tyGenericInst:
+      result = typeToString(t.genericHead) & '['
+      for needsComma, a in t.genericInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
+      result.add(']')
+    of tyGenericInvocation:
+      result = typeToString(t.genericHead) & '['
+      for needsComma, a in t.genericInvocationParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a, preferGenericArg))
       result.add(']')
     of tyGenericBody:
-      result = typeToString(t.lastSon) & '['
-      for i in 0..<t.len-1:
+      result = typeToString(t.typeBodyImpl) & '['
+      for i, a in t.genericBodyParams:
         if i > 0: result.add(", ")
-        result.add(typeToString(t[i], preferTypeName))
+        result.add(typeToString(a, preferTypeName))
       result.add(']')
     of tyTypeDesc:
-      if t[0].kind == tyNone: result = "typedesc"
-      else: result = "typedesc[" & typeToString(t[0]) & "]"
+      if t.elementType.kind == tyNone: result = "typedesc"
+      else: result = "typedesc[" & typeToString(t.elementType) & "]"
     of tyStatic:
       if prefer == preferGenericArg and t.n != nil:
         result = t.n.renderTree
       else:
-        result = "static[" & (if t.len > 0: typeToString(t[0]) else: "") & "]"
+        result = "static[" & (if t.hasElementType: typeToString(t.skipModifier) else: "") & "]"
         if t.n != nil: result.add "(" & renderTree(t.n) & ")"
     of tyUserTypeClass:
       if t.sym != nil and t.sym.owner != nil:
-        if t.isResolvedUserTypeClass: return typeToString(t.lastSon)
+        if t.isResolvedUserTypeClass: return typeToString(t.last)
         return t.sym.owner.name.s
       else:
         result = "<invalid tyUserTypeClass>"
     of tyBuiltInTypeClass:
-      result = case t.base.kind
+      result =
+        case t.base.kind
         of tyVar: "var"
         of tyRef: "ref"
         of tyPtr: "ptr"
@@ -601,22 +622,20 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
     of tyUserTypeClassInst:
       let body = t.base
       result = body.sym.name.s & "["
-      for i in 1..<t.len - 1:
-        if i > 1: result.add(", ")
-        result.add(typeToString(t[i]))
+      for needsComma, a in t.userTypeClassInstParams:
+        if needsComma: result.add(", ")
+        result.add(typeToString(a))
       result.add "]"
     of tyAnd:
-      for i, son in t.sons:
+      for i, son in t.ikids:
+        if i > 0: result.add(" and ")
         result.add(typeToString(son))
-        if i < t.sons.high:
-          result.add(" and ")
     of tyOr:
-      for i, son in t.sons:
+      for i, son in t.ikids:
+        if i > 0: result.add(" or ")
         result.add(typeToString(son))
-        if i < t.sons.high:
-          result.add(" or ")
     of tyNot:
-      result = "not " & typeToString(t[0])
+      result = "not " & typeToString(t.elementType)
     of tyUntyped:
       #internalAssert t.len == 0
       result = "untyped"
@@ -627,80 +646,75 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
         result = "typeof(" & renderTree(t.n) & ")"
     of tyArray:
       result = "array"
-      if t.len > 0:
-        if t[0].kind == tyRange:
-          result &= "[" & rangeToStr(t[0].n) & ", " &
-              typeToString(t[1]) & ']'
+      if t.hasElementType:
+        if t.indexType.kind == tyRange:
+          result &= "[" & rangeToStr(t.indexType.n) & ", " &
+              typeToString(t.elementType) & ']'
         else:
-          result &= "[" & typeToString(t[0]) & ", " &
-              typeToString(t[1]) & ']'
+          result &= "[" & typeToString(t.indexType) & ", " &
+              typeToString(t.elementType) & ']'
     of tyUncheckedArray:
       result = "UncheckedArray"
-      if t.len > 0:
-        result &= "[" & typeToString(t[0]) & ']'
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
     of tySequence:
       if t.sym != nil and prefer != preferResolved:
         result = t.sym.name.s
       else:
         result = "seq"
-        if t.len > 0:
-          result &= "[" & typeToString(t[0]) & ']'
+        if t.hasElementType:
+          result &= "[" & typeToString(t.elementType) & ']'
     of tyOrdinal:
       result = "ordinal"
-      if t.len > 0:
-        result &= "[" & typeToString(t[0]) & ']'
+      if t.hasElementType:
+        result &= "[" & typeToString(t.skipModifier) & ']'
     of tySet:
       result = "set"
-      if t.len > 0:
-        result &= "[" & typeToString(t[0]) & ']'
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
     of tyOpenArray:
       result = "openArray"
-      if t.len > 0:
-        result &= "[" & typeToString(t[0]) & ']'
+      if t.hasElementType:
+        result &= "[" & typeToString(t.elementType) & ']'
     of tyDistinct:
-      result = "distinct " & typeToString(t[0],
+      result = "distinct " & typeToString(t.elementType,
         if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName)
     of tyIterable:
       # xxx factor this pattern
       result = "iterable"
-      if t.len > 0:
-        result &= "[" & typeToString(t[0]) & ']'
+      if t.hasElementType:
+        result &= "[" & typeToString(t.skipModifier) & ']'
     of tyTuple:
       # we iterate over t.sons here, because t.n may be nil
       if t.n != nil:
         result = "tuple["
-        assert(t.n.len == t.len)
         for i in 0..<t.n.len:
           assert(t.n[i].kind == nkSym)
-          result.add(t.n[i].sym.name.s & ": " & typeToString(t[i]))
+          result.add(t.n[i].sym.name.s & ": " & typeToString(t.n[i].sym.typ))
           if i < t.n.len - 1: result.add(", ")
         result.add(']')
-      elif t.len == 0:
+      elif t.isEmptyTupleType:
         result = "tuple[]"
+      elif t.isSingletonTupleType:
+        result = "("
+        for son in t.kids:
+          result.add(typeToString(son))
+        result.add(",)")
       else:
         result = "("
-        for i in 0..<t.len:
-          result.add(typeToString(t[i]))
-          if i < t.len - 1: result.add(", ")
-          elif t.len == 1: result.add(",")
+        for i, son in t.ikids:
+          if i > 0: result.add ", "
+          result.add(typeToString(son))
         result.add(')')
     of tyPtr, tyRef, tyVar, tyLent:
-      result = typeToStr[t.kind]
-      if t.len >= 2:
-        setLen(result, result.len-1)
-        result.add '['
-        for i in 0..<t.len:
-          result.add(typeToString(t[i]))
-          if i < t.len - 1: result.add(", ")
-        result.add ']'
-      else:
-        result.add typeToString(t[0])
+      result = if isOutParam(t): "out " else: typeToStr[t.kind]
+      result.add typeToString(t.elementType)
     of tyRange:
       result = "range "
       if t.n != nil and t.n.kind == nkRange:
         result.add rangeToStr(t.n)
       if prefer != preferExported:
-        result.add("(" & typeToString(t[0]) & ")")
+        result.add("(" & typeToString(t.elementType) & ")")
     of tyProc:
       result = if tfIterator in t.flags: "iterator "
                elif t.owner != nil:
@@ -713,31 +727,62 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
                 "proc "
       if tfUnresolved in t.flags: result.add "[*missing parameters*]"
       result.add "("
-      for i in 1..<t.len:
-        if t.n != nil and i < t.n.len and t.n[i].kind == nkSym:
-          result.add(t.n[i].sym.name.s)
+      for i, a in t.paramTypes:
+        if i > FirstParamAt: result.add(", ")
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym:
+          result.add(t.n[j].sym.name.s)
           result.add(": ")
-        result.add(typeToString(t[i]))
-        if i < t.len - 1: result.add(", ")
+        result.add(typeToString(a))
       result.add(')')
-      if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0]))
+      if t.returnType != nil: result.add(": " & typeToString(t.returnType))
       var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv
+      var hasImplicitRaises = false
+      if not isNil(t.owner) and not isNil(t.owner.ast) and (t.owner.ast.len - 1) >= pragmasPos:
+        let pragmasNode = t.owner.ast[pragmasPos]
+        let raisesSpec = effectSpec(pragmasNode, wRaises)
+        if not isNil(raisesSpec):
+          addSep(prag)
+          prag.add("raises: ")
+          prag.add($raisesSpec)
+          hasImplicitRaises = true
       if tfNoSideEffect in t.flags:
         addSep(prag)
         prag.add("noSideEffect")
       if tfThread in t.flags:
         addSep(prag)
         prag.add("gcsafe")
-      if t.lockLevel.ord != UnspecifiedLockLevel.ord:
+      var effectsOfStr = ""
+      for i, a in t.paramTypes:
+        let j = paramTypeToNodeIndex(i)
+        if t.n != nil and j < t.n.len and t.n[j].kind == nkSym and t.n[j].sym.kind == skParam and sfEffectsDelayed in t.n[j].sym.flags:
+          addSep(effectsOfStr)
+          effectsOfStr.add(t.n[j].sym.name.s)
+      if effectsOfStr != "":
         addSep(prag)
-        prag.add("locks: " & $t.lockLevel)
+        prag.add("effectsOf: ")
+        prag.add(effectsOfStr)
+      if not hasImplicitRaises and prefer == preferInferredEffects and not isNil(t.owner) and not isNil(t.owner.typ) and not isNil(t.owner.typ.n) and (t.owner.typ.n.len > 0):
+        let effects = t.owner.typ.n[0]
+        if effects.kind == nkEffectList and effects.len == effectListLen:
+          var inferredRaisesStr = ""
+          let effs = effects[exceptionEffects]
+          if not isNil(effs):
+            for eff in items(effs):
+              if not isNil(eff):
+                addSep(inferredRaisesStr)
+                inferredRaisesStr.add($eff.typ)
+          addSep(prag)
+          prag.add("raises: <inferred> [")
+          prag.add(inferredRaisesStr)
+          prag.add("]")
       if prag.len != 0: result.add("{." & prag & ".}")
     of tyVarargs:
-      result = typeToStr[t.kind] % typeToString(t[0])
+      result = typeToStr[t.kind] % typeToString(t.elementType)
     of tySink:
-      result = "sink " & typeToString(t[0])
+      result = "sink " & typeToString(t.skipModifier)
     of tyOwned:
-      result = "owned " & typeToString(t[0])
+      result = "owned " & typeToString(t.elementType)
     else:
       result = typeToStr[t.kind]
     result.addTypeFlags(t)
@@ -745,17 +790,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string =
 
 proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   case t.kind
-  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyProxy:
+  of tyBool, tyChar, tySequence, tyOpenArray, tyString, tyVarargs, tyError:
     result = Zero
-  of tySet, tyVar: result = firstOrd(conf, t[0])
-  of tyArray: result = firstOrd(conf, t[0])
+  of tySet, tyVar: result = firstOrd(conf, t.elementType)
+  of tyArray: result = firstOrd(conf, t.indexType)
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n[0])
   of tyInt:
-    if conf != nil and conf.target.intSize == 4:
-      result = toInt128(-2147483648)
+    if conf != nil:
+      case conf.target.intSize
+      of 8: result = toInt128(0x8000000000000000'i64)
+      of 4: result = toInt128(-2147483648)
+      of 2: result = toInt128(-32768)
+      of 1: result = toInt128(-128)
+      else: result = Zero
     else:
       result = toInt128(0x8000000000000000'i64)
   of tyInt8: result =  toInt128(-128)
@@ -765,23 +815,29 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
   of tyUInt..tyUInt64: result = Zero
   of tyEnum:
     # if basetype <> nil then return firstOrd of basetype
-    if t.len > 0 and t[0] != nil:
-      result = firstOrd(conf, t[0])
+    if t.baseClass != nil:
+      result = firstOrd(conf, t.baseClass)
     else:
       if t.n.len > 0:
         assert(t.n[0].kind == nkSym)
         result = toInt128(t.n[0].sym.position)
+      else:
+        result = Zero
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
-     tyStatic, tyInferred, tyUserTypeClasses, tyLent:
-    result = firstOrd(conf, lastSon(t))
+     tyStatic, tyInferred, tyLent:
+    result = firstOrd(conf, skipModifier(t))
+  of tyUserTypeClasses:
+    result = firstOrd(conf, last(t))
   of tyOrdinal:
-    if t.len > 0: result = firstOrd(conf, lastSon(t))
-    else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
+    if t.hasElementType: result = firstOrd(conf, skipModifier(t))
+    else:
+      result = Zero
+      fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
   of tyUncheckedArray, tyCstring:
     result = Zero
   else:
-    internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
     result = Zero
+    fatal(conf, unknownLineInfo, "invalid kind for firstOrd(" & $t.kind & ')')
 
 proc firstFloat*(t: PType): BiggestFloat =
   case t.kind
@@ -790,26 +846,57 @@ proc firstFloat*(t: PType): BiggestFloat =
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     getFloatValue(t.n[0])
-  of tyVar: firstFloat(t[0])
+  of tyVar: firstFloat(t.elementType)
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
-     tyStatic, tyInferred, tyUserTypeClasses:
-    firstFloat(lastSon(t))
+     tyStatic, tyInferred:
+    firstFloat(skipModifier(t))
+  of tyUserTypeClasses:
+    firstFloat(last(t))
   else:
     internalError(newPartialConfigRef(), "invalid kind for firstFloat(" & $t.kind & ')')
     NaN
 
+proc targetSizeSignedToKind*(conf: ConfigRef): TTypeKind =
+  case conf.target.intSize
+  of 8: result = tyInt64
+  of 4: result = tyInt32
+  of 2: result = tyInt16
+  else: result = tyNone
+
+proc targetSizeUnsignedToKind*(conf: ConfigRef): TTypeKind =
+  case conf.target.intSize
+  of 8: result = tyUInt64
+  of 4: result = tyUInt32
+  of 2: result = tyUInt16
+  else: result = tyNone
+
+proc normalizeKind*(conf: ConfigRef, k: TTypeKind): TTypeKind =
+  case k
+  of tyInt:
+    result = conf.targetSizeSignedToKind()
+  of tyUInt:
+    result = conf.targetSizeUnsignedToKind()
+  else:
+    result = k
+
 proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
   case t.kind
   of tyBool: result = toInt128(1'u)
   of tyChar: result = toInt128(255'u)
-  of tySet, tyVar: result = lastOrd(conf, t[0])
-  of tyArray: result = lastOrd(conf, t[0])
+  of tySet, tyVar: result = lastOrd(conf, t.elementType)
+  of tyArray: result = lastOrd(conf, t.indexType)
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     result = getOrdValue(t.n[1])
   of tyInt:
-    if conf != nil and conf.target.intSize == 4: result = toInt128(0x7FFFFFFF)
+    if conf != nil:
+      case conf.target.intSize
+      of 8: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
+      of 4: result = toInt128(0x7FFFFFFF)
+      of 2: result = toInt128(0x00007FFF)
+      of 1: result = toInt128(0x0000007F)
+      else: result = Zero
     else: result = toInt128(0x7FFFFFFFFFFFFFFF'u64)
   of tyInt8: result = toInt128(0x0000007F)
   of tyInt16: result = toInt128(0x00007FFF)
@@ -829,30 +916,38 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 =
     if t.n.len > 0:
       assert(t.n[^1].kind == nkSym)
       result = toInt128(t.n[^1].sym.position)
+    else:
+      result = Zero
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
-     tyStatic, tyInferred, tyUserTypeClasses, tyLent:
-    result = lastOrd(conf, lastSon(t))
-  of tyProxy: result = Zero
+     tyStatic, tyInferred, tyLent:
+    result = lastOrd(conf, skipModifier(t))
+  of tyUserTypeClasses:
+    result = lastOrd(conf, last(t))
+  of tyError: result = Zero
   of tyOrdinal:
-    if t.len > 0: result = lastOrd(conf, lastSon(t))
-    else: internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
+    if t.hasElementType: result = lastOrd(conf, skipModifier(t))
+    else:
+      result = Zero
+      fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
   of tyUncheckedArray:
     result = Zero
   else:
-    internalError(conf, "invalid kind for lastOrd(" & $t.kind & ')')
     result = Zero
+    fatal(conf, unknownLineInfo, "invalid kind for lastOrd(" & $t.kind & ')')
 
 proc lastFloat*(t: PType): BiggestFloat =
   case t.kind
   of tyFloat..tyFloat128: Inf
-  of tyVar: lastFloat(t[0])
+  of tyVar: lastFloat(t.elementType)
   of tyRange:
     assert(t.n != nil)        # range directly given:
     assert(t.n.kind == nkRange)
     getFloatValue(t.n[1])
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
-     tyStatic, tyInferred, tyUserTypeClasses:
-    lastFloat(lastSon(t))
+     tyStatic, tyInferred:
+    lastFloat(skipModifier(t))
+  of tyUserTypeClasses:
+    lastFloat(last(t))
   else:
     internalError(newPartialConfigRef(), "invalid kind for lastFloat(" & $t.kind & ')')
     NaN
@@ -866,17 +961,19 @@ proc floatRangeCheck*(x: BiggestFloat, t: PType): bool =
   of tyRange:
     x in firstFloat(t)..lastFloat(t)
   of tyVar:
-    floatRangeCheck(x, t[0])
+    floatRangeCheck(x, t.elementType)
   of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink,
-     tyStatic, tyInferred, tyUserTypeClasses:
-    floatRangeCheck(x, lastSon(t))
+     tyStatic, tyInferred:
+    floatRangeCheck(x, skipModifier(t))
+  of tyUserTypeClasses:
+    floatRangeCheck(x, last(t))
   else:
     internalError(newPartialConfigRef(), "invalid kind for floatRangeCheck:" & $t.kind)
     false
 
 proc lengthOrd*(conf: ConfigRef; t: PType): Int128 =
   if t.skipTypes(tyUserTypeClasses).kind == tyDistinct:
-    result = lengthOrd(conf, t[0])
+    result = lengthOrd(conf, t.skipModifier)
   else:
     let last = lastOrd(conf, t)
     let first = firstOrd(conf, t)
@@ -900,6 +997,9 @@ type
     ExactGcSafety
     AllowCommonBase
     PickyCAliases  # be picky about the distinction between 'cint' and 'int32'
+    IgnoreFlags    # used for borrowed functions and methods; ignores the tfVarIsPtr flag
+    PickyBackendAliases # be picky about different aliases
+    IgnoreRangeShallow
 
   TTypeCmpFlags* = set[TTypeCmpFlag]
 
@@ -912,7 +1012,7 @@ type
 
 proc initSameTypeClosure: TSameTypeClosure =
   # we do the initialization lazily for performance (avoids memory allocations)
-  discard
+  result = TSameTypeClosure()
 
 proc containsOrIncl(c: var TSameTypeClosure, a, b: PType): bool =
   result = c.s.len > 0 and c.s.contains((a.id, b.id))
@@ -951,6 +1051,8 @@ proc equalParam(a, b: PSym): TParamsEquality =
       result = paramsEqual
     elif b.ast != nil:
       result = paramsIncompatible
+    else:
+      result = paramsNotEqual
   else:
     result = paramsNotEqual
 
@@ -995,11 +1097,11 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
   # two tuples are equivalent iff the names, types and positions are the same;
   # however, both types may not have any field names (t.n may be nil) which
   # complicates the matter a bit.
-  if a.len == b.len:
+  if sameTupleLengths(a, b):
     result = true
-    for i in 0..<a.len:
-      var x = a[i]
-      var y = b[i]
+    for i, aa, bb in tupleTypePairs(a, b):
+      var x = aa
+      var y = bb
       if IgnoreTupleFields in c.flags:
         x = skipTypes(x, {tyRange, tyGenericInst, tyAlias})
         y = skipTypes(y, {tyRange, tyGenericInst, tyAlias})
@@ -1018,6 +1120,8 @@ proc sameTuple(a, b: PType, c: var TSameTypeClosure): bool =
           return false
     elif a.n != b.n and (a.n == nil or b.n == nil) and IgnoreTupleFields notin c.flags:
       result = false
+  else:
+    result = false
 
 template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
   if tfFromGeneric notin a.flags + b.flags:
@@ -1037,6 +1141,8 @@ template ifFastObjectTypeCheckFailed(a, b: PType, body: untyped) =
     if tfFromGeneric in a.flags * b.flags and a.sym.id == b.sym.id:
       # ok, we need the expensive structural check
       body
+    else:
+      result = false
 
 proc sameObjectTypes*(a, b: PType): bool =
   # specialized for efficiency (sigmatch uses it)
@@ -1074,32 +1180,44 @@ proc sameObjectTree(a, b: PNode, c: var TSameTypeClosure): bool =
           for i in 0..<a.len:
             if not sameObjectTree(a[i], b[i], c): return
           result = true
+        else:
+          result = false
+    else:
+      result = false
+  else:
+    result = false
 
 proc sameObjectStructures(a, b: PType, c: var TSameTypeClosure): bool =
-  # check base types:
-  if a.len != b.len: return
-  for i in 0..<a.len:
-    if not sameTypeOrNilAux(a[i], b[i], c): return
-  if not sameObjectTree(a.n, b.n, c): return
+  if not sameTypeOrNilAux(a.baseClass, b.baseClass, c): return false
+  if not sameObjectTree(a.n, b.n, c): return false
   result = true
 
 proc sameChildrenAux(a, b: PType, c: var TSameTypeClosure): bool =
-  if a.len != b.len: return false
+  if not sameTupleLengths(a, b): return false
+  # XXX This is not tuple specific.
   result = true
-  for i in 0..<a.len:
-    result = sameTypeOrNilAux(a[i], b[i], c)
+  for _, x, y in tupleTypePairs(a, b):
+    result = sameTypeOrNilAux(x, y, c)
     if not result: return
 
 proc isGenericAlias*(t: PType): bool =
-  return t.kind == tyGenericInst and t.lastSon.kind == tyGenericInst
+  return t.kind == tyGenericInst and t.skipModifier.kind == tyGenericInst
+
+proc genericAliasDepth*(t: PType): int =
+  result = 0
+  var it = t
+  while it.isGenericAlias:
+    it = it.skipModifier
+    inc result
 
 proc skipGenericAlias*(t: PType): PType =
-  return if t.isGenericAlias: t.lastSon else: t
+  return if t.isGenericAlias: t.skipModifier else: t
 
 proc sameFlags*(a, b: PType): bool {.inline.} =
   result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
 
 proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
+  result = false
   template cycleCheck() =
     # believe it or not, the direct check for ``containsOrIncl(c, a, b)``
     # increases bootstrapping time from 2.4s to 3.3s on my laptop! So we cheat
@@ -1110,26 +1228,40 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
       inc c.recCheck
     else:
       if containsOrIncl(c, a, b): return true
+  template maybeSkipRange(x: set[TTypeKind]): set[TTypeKind] =
+    if IgnoreRangeShallow in c.flags:
+      x + {tyRange}
+    else:
+      x
+  
+  template withoutShallowFlags(body) =
+    let oldFlags = c.flags
+    c.flags.excl IgnoreRangeShallow
+    body
+    c.flags = oldFlags
 
   if x == y: return true
-  var a = skipTypes(x, {tyGenericInst, tyAlias})
+  let aliasSkipSet = maybeSkipRange({tyAlias})
+  var a = skipTypes(x, aliasSkipSet)
   while a.kind == tyUserTypeClass and tfResolved in a.flags:
-    a = skipTypes(a[^1], {tyGenericInst, tyAlias})
-  var b = skipTypes(y, {tyGenericInst, tyAlias})
+    a = skipTypes(a.last, aliasSkipSet)
+  var b = skipTypes(y, aliasSkipSet)
   while b.kind == tyUserTypeClass and tfResolved in b.flags:
-    b = skipTypes(b[^1], {tyGenericInst, tyAlias})
+    b = skipTypes(b.last, aliasSkipSet)
   assert(a != nil)
   assert(b != nil)
-  if a.kind != b.kind:
-    case c.cmp
-    of dcEq: return false
-    of dcEqIgnoreDistinct:
-      a = a.skipTypes({tyDistinct, tyGenericInst})
-      b = b.skipTypes({tyDistinct, tyGenericInst})
-      if a.kind != b.kind: return false
-    of dcEqOrDistinctOf:
-      a = a.skipTypes({tyDistinct, tyGenericInst})
-      if a.kind != b.kind: return false
+  case c.cmp
+  of dcEq:
+    if a.kind != b.kind: return false
+  of dcEqIgnoreDistinct:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    b = b.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
+  of dcEqOrDistinctOf:
+    let distinctSkipSet = maybeSkipRange({tyDistinct, tyGenericInst})
+    a = a.skipTypes(distinctSkipSet)
+    if a.kind != b.kind: return false
 
   #[
     The following code should not run in the case either side is an generic alias,
@@ -1137,54 +1269,62 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     objects ie `type A[T] = SomeObject`
   ]#
   # this is required by tunique_type but makes no sense really:
-  if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags:
+  if c.cmp == dcEq and x.kind == tyGenericInst and
+      IgnoreTupleFields notin c.flags and tyDistinct != y.kind:
     let
       lhs = x.skipGenericAlias
       rhs = y.skipGenericAlias
-    if rhs.kind != tyGenericInst or lhs.base != rhs.base:
+    if rhs.kind != tyGenericInst or lhs.base != rhs.base or rhs.kidsLen != lhs.kidsLen:
       return false
-    for i in 1..<lhs.len - 1:
-      let ff = rhs[i]
-      let aa = lhs[i]
-      if not sameTypeAux(ff, aa, c): return false
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(rhs, lhs, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
     return true
 
   case a.kind
   of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring,
      tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid:
     result = sameFlags(a, b)
-    if result and PickyCAliases in c.flags:
+    if result and {PickyCAliases, ExactTypeDescValues} <= c.flags:
       # additional requirement for the caching of generics for importc'ed types:
       # the symbols must be identical too:
       let symFlagsA = if a.sym != nil: a.sym.flags else: {}
       let symFlagsB = if b.sym != nil: b.sym.flags else: {}
       if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
         result = symFlagsA == symFlagsB
+    elif result and PickyBackendAliases in c.flags:
+      let symFlagsA = if a.sym != nil: a.sym.flags else: {}
+      let symFlagsB = if b.sym != nil: b.sym.flags else: {}
+      if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}:
+        result = a.id == b.id
+
   of tyStatic, tyFromExpr:
     result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b)
-    if result and a.len == b.len and a.len == 1:
+    if result and sameTupleLengths(a, b) and a.hasElementType:
       cycleCheck()
-      result = sameTypeAux(a[0], b[0], c)
+      result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyObject:
-    ifFastObjectTypeCheckFailed(a, b):
-      cycleCheck()
-      result = sameObjectStructures(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      ifFastObjectTypeCheckFailed(a, b):
+        cycleCheck()
+        result = sameObjectStructures(a, b, c) and sameFlags(a, b)
   of tyDistinct:
     cycleCheck()
     if c.cmp == dcEq:
       if sameFlags(a, b):
         ifFastObjectTypeCheckFailed(a, b):
-          result = sameTypeAux(a[0], b[0], c)
+          result = sameTypeAux(a.elementType, b.elementType, c)
     else:
-      result = sameTypeAux(a[0], b[0], c) and sameFlags(a, b)
+      result = sameTypeAux(a.elementType, b.elementType, c) and sameFlags(a, b)
   of tyEnum, tyForward:
     # XXX generic enums do not make much sense, but require structural checking
     result = a.id == b.id and sameFlags(a, b)
   of tyError:
     result = b.kind == tyError
   of tyTuple:
-    cycleCheck()
-    result = sameTuple(a, b, c) and sameFlags(a, b)
+    withoutShallowFlags:
+      cycleCheck()
+      result = sameTuple(a, b, c) and sameFlags(a, b)
   of tyTypeDesc:
     if c.cmp == dcEqIgnoreDistinct: result = false
     elif ExactTypeDescValues in c.flags:
@@ -1197,21 +1337,22 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     if result and {ExactGenericParams, ExactTypeDescValues} * c.flags != {}:
       result = a.sym.position == b.sym.position
   of tyBuiltInTypeClass:
-    assert a.len == 1
-    assert a[0].len == 0
-    assert b.len == 1
-    assert b[0].len == 0
-    result = a[0].kind == b[0].kind
+    result = a.elementType.kind == b.elementType.kind and sameFlags(a.elementType, b.elementType)
+    if result and a.elementType.kind == tyProc and IgnoreCC notin c.flags:
+      let ecc = a.elementType.flags * {tfExplicitCallConv}
+      result = ecc == b.elementType.flags * {tfExplicitCallConv} and
+               (ecc == {} or a.elementType.callConv == b.elementType.callConv)
   of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef,
      tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs,
      tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst,
      tyAnd, tyOr, tyNot, tyAnything, tyOwned:
     cycleCheck()
     if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n
-    result = sameChildrenAux(a, b, c)
-    if result:
+    withoutShallowFlags:
+      result = sameChildrenAux(a, b, c)
+    if result and IgnoreFlags notin c.flags:
       if IgnoreTupleFields in c.flags:
-        result = a.flags * {tfVarIsPtr} == b.flags * {tfVarIsPtr}
+        result = a.flags * {tfVarIsPtr, tfIsOutParam} == b.flags * {tfVarIsPtr, tfIsOutParam}
       else:
         result = sameFlags(a, b)
     if result and ExactGcSafety in c.flags:
@@ -1221,12 +1362,22 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
                ((ExactConstraints notin c.flags) or sameConstraints(a.n, b.n))
   of tyRange:
     cycleCheck()
-    result = sameTypeOrNilAux(a[0], b[0], c) and
-        sameValue(a.n[0], b.n[0]) and
+    result = sameTypeOrNilAux(a.elementType, b.elementType, c)
+    if result and IgnoreRangeShallow notin c.flags:
+      result = sameValue(a.n[0], b.n[0]) and
         sameValue(a.n[1], b.n[1])
-  of tyGenericInst, tyAlias, tyInferred, tyIterable:
+  of tyAlias, tyInferred, tyIterable:
+    cycleCheck()
+    result = sameTypeAux(a.skipModifier, b.skipModifier, c)
+  of tyGenericInst:
+    # BUG #23445
+    # The type system must distinguish between `T[int] = object #[empty]#`
+    # and `T[float] = object #[empty]#`!
     cycleCheck()
-    result = sameTypeAux(a.lastSon, b.lastSon, c)
+    withoutShallowFlags:
+      for ff, aa in underspecifiedPairs(a, b, 1, -1):
+        if not sameTypeAux(ff, aa, c): return false
+    result = sameTypeAux(a.skipModifier, b.skipModifier, c)
   of tyNone: result = false
   of tyConcept:
     result = exprStructuralEquivalent(a.n, b.n)
@@ -1237,6 +1388,19 @@ proc sameBackendType*(x, y: PType): bool =
   c.cmp = dcEqIgnoreDistinct
   result = sameTypeAux(x, y, c)
 
+proc sameBackendTypeIgnoreRange*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl IgnoreTupleFields
+  c.flags.incl IgnoreRangeShallow
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
+proc sameBackendTypePickyAliases*(x, y: PType): bool =
+  var c = initSameTypeClosure()
+  c.flags.incl {IgnoreTupleFields, PickyCAliases, PickyBackendAliases}
+  c.cmp = dcEqIgnoreDistinct
+  result = sameTypeAux(x, y, c)
+
 proc compareTypes*(x, y: PType,
                    cmp: TDistinctCompare = dcEq,
                    flags: TTypeCmpFlags = {}): bool =
@@ -1261,18 +1425,19 @@ proc inheritanceDiff*(a, b: PType): int =
   while x != nil:
     x = skipTypes(x, skipPtrs)
     if sameObjectTypes(x, b): return
-    x = x[0]
+    x = x.baseClass
     dec(result)
   var y = b
   result = 0
   while y != nil:
     y = skipTypes(y, skipPtrs)
     if sameObjectTypes(y, a): return
-    y = y[0]
+    y = y.baseClass
     inc(result)
   result = high(int)
 
 proc commonSuperclass*(a, b: PType): PType =
+  result = nil
   # quick check: are they the same?
   if sameObjectTypes(a, b): return a
 
@@ -1285,7 +1450,7 @@ proc commonSuperclass*(a, b: PType): PType =
   while x != nil:
     x = skipTypes(x, skipPtrs)
     ancestors.incl(x.id)
-    x = x[0]
+    x = x.baseClass
   var y = b
   while y != nil:
     var t = y # bug #7818, save type before skip
@@ -1294,17 +1459,10 @@ proc commonSuperclass*(a, b: PType): PType =
       # bug #7818, defer the previous skipTypes
       if t.kind != tyGenericInst: t = y
       return t
-    y = y[0]
-
-proc matchType*(a: PType, pattern: openArray[tuple[k:TTypeKind, i:int]],
-                last: TTypeKind): bool =
-  var a = a
-  for k, i in pattern.items:
-    if a.kind != k: return false
-    if i >= a.len or a[i] == nil: return false
-    a = a[i]
-  result = a.kind == last
+    y = y.baseClass
 
+proc lacksMTypeField*(typ: PType): bool {.inline.} =
+  (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
 
 include sizealignoffsetimpl
 
@@ -1315,7 +1473,7 @@ proc computeSize*(conf: ConfigRef; typ: PType): BiggestInt =
 proc getReturnType*(s: PSym): PType =
   # Obtains the return type of a iterator/proc/macro/template
   assert s.kind in skProcKinds
-  result = s.typ[0]
+  result = s.typ.returnType
 
 proc getAlign*(conf: ConfigRef; typ: PType): BiggestInt =
   computeSizeAlign(conf, typ)
@@ -1341,17 +1499,34 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool =
 proc containsGenericType*(t: PType): bool =
   result = iterOverType(t, containsGenericTypeIter, nil)
 
+proc containsUnresolvedTypeIter(t: PType, closure: RootRef): bool =
+  if tfUnresolved in t.flags: return true
+  case t.kind
+  of tyStatic:
+    return t.n == nil
+  of tyTypeDesc:
+    if t.base.kind == tyNone: return true
+    if containsUnresolvedTypeIter(t.base, closure): return true
+    return false
+  of tyGenericInvocation, tyGenericParam, tyFromExpr, tyAnything:
+    return true
+  else:
+    return false
+
+proc containsUnresolvedType*(t: PType): bool =
+  result = iterOverType(t, containsUnresolvedTypeIter, nil)
+
 proc baseOfDistinct*(t: PType; g: ModuleGraph; idgen: IdGenerator): PType =
   if t.kind == tyDistinct:
-    result = t[0]
+    result = t.elementType
   else:
-    result = copyType(t, nextTypeId idgen, t.owner)
+    result = copyType(t, idgen, t.owner)
     copyTypeProps(g, idgen.module, result, t)
     var parent: PType = nil
     var it = result
     while it.kind in {tyPtr, tyRef, tyOwned}:
       parent = it
-      it = it.lastSon
+      it = it.elementType
     if it.kind == tyDistinct and parent != nil:
       parent[0] = it[0]
 
@@ -1372,7 +1547,21 @@ proc compatibleEffectsAux(se, re: PNode): bool =
       return false
   result = true
 
+proc isDefectException*(t: PType): bool
+proc compatibleExceptions(se, re: PNode): bool =
+  if re.isNil: return false
+  for r in items(re):
+    block search:
+      if isDefectException(r.typ):
+        break search
+      for s in items(se):
+        if safeInheritanceDiff(r.typ, s.typ) <= 0:
+          break search
+      return false
+  result = true
+
 proc hasIncompatibleEffect(se, re: PNode): bool =
+  result = false
   if re.isNil: return false
   for r in items(re):
     for s in items(se):
@@ -1386,7 +1575,6 @@ type
     efRaisesUnknown
     efTagsDiffer
     efTagsUnknown
-    efLockLevelsDiffer
     efEffectsDelayed
     efTagsIllegal
 
@@ -1411,7 +1599,7 @@ proc compatibleEffects*(formal, actual: PType): EffectsCompat =
     if not isNil(se) and se.kind != nkArgList:
       # spec requires some exception or tag, but we don't know anything:
       if real.len == 0: return efRaisesUnknown
-      let res = compatibleEffectsAux(se, real[exceptionEffects])
+      let res = compatibleExceptions(se, real[exceptionEffects])
       if not res: return efRaisesDiffer
 
     let st = spec[tagEffects]
@@ -1430,25 +1618,21 @@ proc compatibleEffects*(formal, actual: PType): EffectsCompat =
       elif hasIncompatibleEffect(sn, real[tagEffects]):
         return efTagsIllegal
 
-  if formal.lockLevel.ord < 0 or
-      actual.lockLevel.ord <= formal.lockLevel.ord:
+  for i in 1 ..< min(formal.n.len, actual.n.len):
+    if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}:
+      result = efEffectsDelayed
+      break
 
-    for i in 1 ..< min(formal.n.len, actual.n.len):
-      if formal.n[i].sym.flags * {sfEffectsDelayed} != actual.n[i].sym.flags * {sfEffectsDelayed}:
-        result = efEffectsDelayed
-        break
+  result = efCompat
 
-    result = efCompat
-  else:
-    result = efLockLevelsDiffer
 
 proc isCompileTimeOnly*(t: PType): bool {.inline.} =
-  result = t.kind in {tyTypeDesc, tyStatic}
+  result = t.kind in {tyTypeDesc, tyStatic, tyGenericParam}
 
 proc containsCompileTimeOnly*(t: PType): bool =
   if isCompileTimeOnly(t): return true
-  for i in 0..<t.len:
-    if t[i] != nil and isCompileTimeOnly(t[i]):
+  for a in t.kids:
+    if a != nil and isCompileTimeOnly(a):
       return true
   return false
 
@@ -1457,7 +1641,7 @@ proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
   result = t
   var seen = initIntSet()
   while result.kind in kinds and not containsOrIncl(seen, result.id):
-    result = lastSon(result)
+    result = skipModifier(result)
 
 type
   OrdinalType* = enum
@@ -1506,10 +1690,9 @@ proc skipConvTakeType*(n: PNode): PNode =
 proc isEmptyContainer*(t: PType): bool =
   case t.kind
   of tyUntyped, tyNil: result = true
-  of tyArray: result = t[1].kind == tyEmpty
-  of tySet, tySequence, tyOpenArray, tyVarargs:
-    result = t[0].kind == tyEmpty
-  of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon)
+  of tyArray, tySet, tySequence, tyOpenArray, tyVarargs:
+    result = t.elementType.kind == tyEmpty
+  of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.skipModifier)
   else: result = false
 
 proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
@@ -1520,7 +1703,7 @@ proc takeType*(formal, arg: PType; g: ModuleGraph; idgen: IdGenerator): PType =
     result = formal
   elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and
       arg.isEmptyContainer:
-    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextTypeId(idgen), arg.owner)
+    let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), idgen, arg.owner)
     copyTypeProps(g, idgen.module, a, arg)
     a[ord(arg.kind == tyArray)] = formal[0]
     result = a
@@ -1577,13 +1760,6 @@ proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcCon
       result[1] = isNone
       result[0].incl pcmDifferentCallConv
 
-  if f.lockLevel.ord != UnspecifiedLockLevel.ord and
-     a.lockLevel.ord != UnspecifiedLockLevel.ord:
-       # proctypeRel has more logic to catch this difference,
-       # so dont need to do `rel = isNone`
-       # but it's a pragma mismatch reason which is why it's here
-       result[0].incl pcmLockDifference
-
 proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, conf: ConfigRef) =
   assert formal.kind == tyProc and actual.kind == tyProc
   let (convMismatch, _) = getProcConvMismatch(conf, formal, actual)
@@ -1598,9 +1774,6 @@ proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, c
       expectedPragmas.add "noSideEffect, "
     of pcmNotGcSafe:
       expectedPragmas.add "gcsafe, "
-    of pcmLockDifference:
-      gotPragmas.add("locks: " & $actual.lockLevel & ", ")
-      expectedPragmas.add("locks: " & $formal.lockLevel & ", ")
     of pcmNotIterator: discard
 
   if expectedPragmas.len > 0:
@@ -1621,13 +1794,18 @@ proc processPragmaAndCallConvMismatch(msg: var string, formal, actual: PType, co
       msg.add "\n.tag effects differ"
     of efTagsUnknown:
       msg.add "\n.tag effect is 'any tag allowed'"
-    of efLockLevelsDiffer:
-      msg.add "\nlock levels differ"
     of efEffectsDelayed:
       msg.add "\n.effectsOf annotations differ"
     of efTagsIllegal:
       msg.add "\n.notTag catched an illegal effect"
 
+proc typeNameAndDesc*(t: PType): string =
+  result = typeToString(t)
+  let desc = typeToString(t, preferDesc)
+  if result != desc:
+    result.add(" = ")
+    result.add(desc)
+
 proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) =
   if formal.kind != tyError and actual.kind != tyError:
     let actualStr = typeToString(actual)
@@ -1667,14 +1845,17 @@ proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool =
     return true
   case t.kind
   of tyTuple:
+    result = false
     var cycleDetectorCopy: IntSet
-    for i in 0..<t.len:
-      assign(cycleDetectorCopy, cycleDetector)
-      if isTupleRecursive(t[i], cycleDetectorCopy):
+    for a in t.kids:
+      cycleDetectorCopy = cycleDetector
+      if isTupleRecursive(a, cycleDetectorCopy):
         return true
-  of tyAlias, tyRef, tyPtr, tyGenericInst, tyVar, tyLent, tySink,
+  of tyRef, tyPtr, tyVar, tyLent, tySink,
       tyArray, tyUncheckedArray, tySequence, tyDistinct:
-    return isTupleRecursive(t.lastSon, cycleDetector)
+    return isTupleRecursive(t.elementType, cycleDetector)
+  of tyAlias, tyGenericInst:
+    return isTupleRecursive(t.skipModifier, cycleDetector)
   else:
     return false
 
@@ -1689,8 +1870,8 @@ proc isException*(t: PType): bool =
   var t = t.skipTypes(abstractInst)
   while t.kind == tyObject:
     if t.sym != nil and t.sym.magic == mException: return true
-    if t[0] == nil: break
-    t = skipTypes(t[0], abstractPtrs)
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
   return false
 
 proc isDefectException*(t: PType): bool =
@@ -1700,8 +1881,20 @@ proc isDefectException*(t: PType): bool =
         sfSystemModule in t.sym.owner.flags and
         t.sym.name.s == "Defect":
       return true
-    if t[0] == nil: break
-    t = skipTypes(t[0], abstractPtrs)
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
+  return false
+
+proc isDefectOrCatchableError*(t: PType): bool =
+  var t = t.skipTypes(abstractPtrs)
+  while t.kind == tyObject:
+    if t.sym != nil and t.sym.owner != nil and
+        sfSystemModule in t.sym.owner.flags and
+        (t.sym.name.s == "Defect" or
+        t.sym.name.s == "CatchableError"):
+      return true
+    if t.baseClass == nil: break
+    t = skipTypes(t.baseClass, abstractPtrs)
   return false
 
 proc isSinkTypeForParam*(t: PType): bool =
@@ -1716,29 +1909,29 @@ proc isSinkTypeForParam*(t: PType): bool =
         result = true
 
 proc lookupFieldAgain*(ty: PType; field: PSym): PSym =
+  result = nil
   var ty = ty
   while ty != nil:
     ty = ty.skipTypes(skipPtrs)
     assert(ty.kind in {tyTuple, tyObject})
     result = lookupInRecord(ty.n, field.name)
     if result != nil: break
-    ty = ty[0]
+    ty = ty.baseClass
   if result == nil: result = field
 
 proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool =
   let t = t.skipTypes(abstractInst)
   if t.kind == tyPtr:
-    let pointsTo = t[0].skipTypes(abstractInst)
+    let pointsTo = t.elementType.skipTypes(abstractInst)
     case pointsTo.kind
     of tyUncheckedArray:
-      result = pointsTo[0].kind == tyChar
+      result = pointsTo.elementType.kind == tyChar
     of tyArray:
-      result = pointsTo[1].kind == tyChar and firstOrd(nil, pointsTo[0]) == 0 and
-        skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64}
+      result = pointsTo.elementType.kind == tyChar and firstOrd(nil, pointsTo.indexType) == 0 and
+        skipTypes(pointsTo.indexType, {tyRange}).kind in {tyInt..tyInt64}
     of tyChar:
       result = allowPointerToChar
     else:
-      discard
-
-proc lacksMTypeField*(typ: PType): bool {.inline.} =
-  (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags
+      result = false
+  else:
+    result = false
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index f59f63659..72bcddb05 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -7,7 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-import renderer, strutils, ast, types
+import renderer, ast, types
+import std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -64,9 +65,8 @@ proc renderType(n: PNode, toNormalize: bool): string =
       result = "ptr"
   of nkProcTy:
     assert n.len != 1
-    if n.len > 1:
+    if n.len > 1 and n[0].kind == nkFormalParams:
       let params = n[0]
-      assert params.kind == nkFormalParams
       assert params.len > 0
       result = "proc("
       for i in 1..<params.len: result.add(renderType(params[i], toNormalize) & ',')
@@ -100,6 +100,7 @@ proc renderType(n: PNode, toNormalize: bool): string =
 
 proc renderParamNames*(n: PNode, toNormalize=false): seq[string] =
   ## Returns parameter names of routine `n`.
+  result = @[]
   doAssert n.kind == nkFormalParams
   case n.kind
   of nkFormalParams:
diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim
index 60eed5c78..1711fea46 100644
--- a/compiler/varpartitions.nim
+++ b/compiler/varpartitions.nim
@@ -98,12 +98,15 @@ type
 
   Partitions* = object
     abstractTime: AbstractTime
+    defers: seq[PNode]
+    processDefer: bool
     s: seq[VarIndex]
     graphs: seq[MutationInfo]
     goals: set[Goal]
     unanalysableMutation: bool
     inAsgnSource, inConstructor, inNoSideEffectSection: int
     inConditional, inLoop: int
+    inConvHasDestructor: int
     owner: PSym
     g: ModuleGraph
 
@@ -286,7 +289,9 @@ proc borrowFromConstExpr(n: PNode): bool =
       result = true
       for i in 1..<n.len:
         if not borrowFromConstExpr(n[i]): return false
-  else: discard
+    else:
+      result = false
+  else: result = false
 
 proc pathExpr(node: PNode; owner: PSym): PNode =
   #[ From the spec:
@@ -396,15 +401,14 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) =
         if typ != nil:
           typ = skipTypes(typ, abstractInst)
           if typ.kind != tyProc: typ = nil
-          else: assert(typ.len == typ.n.len)
 
         for i in 1 ..< n.len:
           let it = n[i]
-          if typ != nil and i < typ.len:
+          if typ != nil and i < typ.n.len:
             assert(typ.n[i].kind == nkSym)
             let paramType = typ.n[i].typ
-            if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and
-                canAlias(paramType, typ.sons[0]):
+            if not paramType.isCompileTimeOnly and not typ.returnType.isEmptyType and
+                canAlias(paramType, typ.returnType):
               allRoots(it, result, RootEscapes)
           else:
             allRoots(it, result, RootEscapes)
@@ -424,16 +428,28 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
     # primitive literals including the empty are harmless:
     discard
 
-  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv:
+  of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast:
     destMightOwn(c, dest, n[1])
 
+  of nkConv:
+    if hasDestructor(n.typ):
+      inc c.inConvHasDestructor
+      destMightOwn(c, dest, n[1])
+      dec c.inConvHasDestructor
+    else:
+      destMightOwn(c, dest, n[1])
+
   of nkIfStmt, nkIfExpr:
     for i in 0..<n.len:
+      inc c.inConditional
       destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
 
   of nkCaseStmt:
     for i in 1..<n.len:
+      inc c.inConditional
       destMightOwn(c, dest, n[i].lastSon)
+      dec c.inConditional
 
   of nkStmtList, nkStmtListExpr:
     if n.len > 0:
@@ -478,16 +494,25 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) =
 
   of nkCallKinds:
     if n.typ != nil:
-      if hasDestructor(n.typ):
+      if hasDestructor(n.typ) or c.inConvHasDestructor > 0:
         # calls do construct, what we construct must be destroyed,
         # so dest cannot be a cursor:
         dest.flags.incl ownsData
       elif n.typ.kind in {tyLent, tyVar} and n.len > 1:
         # we know the result is derived from the first argument:
-        var roots: seq[(PSym, int)]
+        var roots: seq[(PSym, int)] = @[]
         allRoots(n[1], roots, RootEscapes)
-        for r in roots:
-          connect(c, dest.sym, r[0], n[1].info)
+        if roots.len == 0 and c.inConditional > 0:
+          # when in a conditional expression,
+          # to ensure that the first argument isn't outlived
+          # by the lvalue, we need find the root, otherwise
+          # it is probably a local temporary
+          # (e.g. a return value from a call),
+          # we should prevent cursorfication
+          dest.flags.incl preventCursor
+        else:
+          for r in roots:
+            connect(c, dest.sym, r[0], n[1].info)
 
       else:
         let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone
@@ -616,7 +641,8 @@ proc deps(c: var Partitions; dest, src: PNode) =
   if borrowChecking in c.goals:
     borrowingAsgn(c, dest, src)
 
-  var targets, sources: seq[(PSym, int)]
+  var targets: seq[(PSym, int)] = @[]
+  var sources: seq[(PSym, int)] = @[]
   allRoots(dest, targets, 0)
   allRoots(src, sources, 0)
 
@@ -666,7 +692,7 @@ proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) =
   if constParameters in c.goals and tfNoSideEffect in callee.flags:
     discard "we know there are no hidden mutations through an immutable parameter"
   elif c.inNoSideEffectSection == 0 and containsPointer(n.typ):
-    var roots: seq[(PSym, int)]
+    var roots: seq[(PSym, int)] = @[]
     allRoots(n, roots, RootEscapes)
     for r in roots: potentialMutation(c, r[0], r[1], n.info)
 
@@ -686,7 +712,7 @@ proc traverse(c: var Partitions; n: PNode) =
         for i in 0..<child.len-2:
           #registerVariable(c, child[i])
           deps(c, child[i], last)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     traverse(c, n[0])
     inc c.inAsgnSource
     traverse(c, n[1])
@@ -702,15 +728,19 @@ proc traverse(c: var Partitions; n: PNode) =
     for child in n: traverse(c, child)
 
     let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
+    let L = if parameters != nil: parameters.signatureLen else: 0
     let m = getMagic(n)
 
+    if m == mEnsureMove and n[1].kind == nkSym:
+      # we know that it must be moved so it cannot be a cursor
+      noCursor(c, n[1].sym)
+
     for i in 1..<n.len:
       let it = n[i]
       if i < L:
         let paramType = parameters[i].skipTypes({tyGenericInst, tyAlias})
         if not paramType.isCompileTimeOnly and paramType.kind in {tyVar, tySink, tyOwned}:
-          var roots: seq[(PSym, int)]
+          var roots: seq[(PSym, int)] = @[]
           allRoots(it, roots, RootEscapes)
           if paramType.kind == tyVar:
             if c.inNoSideEffectSection == 0:
@@ -779,8 +809,11 @@ proc traverse(c: var Partitions; n: PNode) =
 
     if n.kind == nkWhileStmt:
       traverse(c, n[0])
-      # variables in while condition has longer alive time than local variables 
+      # variables in while condition has longer alive time than local variables
       # in the while loop body
+  of nkDefer:
+    if c.processDefer:
+      for child in n: traverse(c, child)
   else:
     for child in n: traverse(c, child)
 
@@ -811,7 +844,11 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
           registerVariable(c, child[i])
           #deps(c, child[i], last)
 
-  of nkAsgn, nkFastAsgn:
+        if c.inLoop > 0 and child[0].kind == nkSym: # bug #22787
+          let vid = variableId(c, child[0].sym)
+          if child[^1].kind != nkEmpty:
+            markAsReassigned(c, vid)
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     computeLiveRanges(c, n[0])
     computeLiveRanges(c, n[1])
     if n[0].kind == nkSym:
@@ -819,6 +856,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
       if vid >= 0:
         if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id):
           c.s[vid].reassignedTo = n[1].sym.id
+          if c.inConditional > 0 and c.inLoop > 0:
+            # bug #22200: live ranges with loops and conditionals are too
+            # complex for our current analysis, so we prevent the cursorfication.
+            c.s[vid].flags.incl isConditionallyReassigned
         else:
           markAsReassigned(c, vid)
 
@@ -838,7 +879,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
     for child in n: computeLiveRanges(c, child)
 
     let parameters = n[0].typ
-    let L = if parameters != nil: parameters.len else: 0
+    let L = if parameters != nil: parameters.signatureLen else: 0
 
     for i in 1..<n.len:
       let it = n[i]
@@ -871,12 +912,17 @@ proc computeLiveRanges(c: var Partitions; n: PNode) =
 
     if n.kind == nkWhileStmt:
       computeLiveRanges(c, n[0])
-      # variables in while condition has longer alive time than local variables 
+      # variables in while condition has longer alive time than local variables
       # in the while loop body
   of nkElifBranch, nkElifExpr, nkElse, nkOfBranch:
     inc c.inConditional
     for child in n: computeLiveRanges(c, child)
     dec c.inConditional
+  of nkDefer:
+    if c.processDefer:
+      for child in n: computeLiveRanges(c, child)
+    else:
+      c.defers.add n
   else:
     for child in n: computeLiveRanges(c, child)
 
@@ -890,9 +936,17 @@ proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal]
       registerResult(result, s.ast[resultPos])
 
   computeLiveRanges(result, n)
+  result.processDefer = true
+  for i in countdown(len(result.defers)-1, 0):
+    computeLiveRanges(result, result.defers[i])
+  result.processDefer = false
   # restart the timer for the second pass:
   result.abstractTime = AbstractTime 0
   traverse(result, n)
+  result.processDefer = true
+  for i in countdown(len(result.defers)-1, 0):
+    traverse(result, result.defers[i])
+  result.processDefer = false
 
 proc dangerousMutation(g: MutationInfo; v: VarIndex): bool =
   #echo "range ", v.aliveStart, " .. ", v.aliveEnd, " ", v.sym
@@ -953,7 +1007,9 @@ proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) =
     if v.flags * {ownsData, preventCursor, isConditionallyReassigned} == {} and
         v.sym.kind notin {skParam, skResult} and
         v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and
-        v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned:
+        v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned and
+        (getAttachedOp(g, v.sym.typ, attachedAsgn) == nil or
+        sfError notin getAttachedOp(g, v.sym.typ, attachedAsgn).flags):
       let rid = root(par, i)
       if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]):
         discard "cannot cursor into a graph that is mutated"
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 1717fe119..161b025a6 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -10,17 +10,16 @@
 ## This file implements the new evaluation engine for Nim code.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-
+import semmacrosanity
 import
   std/[strutils, tables, parseutils],
-  msgs, vmdef, vmgen, nimsets, types, passes,
+  msgs, vmdef, vmgen, nimsets, types,
   parser, vmdeps, idents, trees, renderer, options, transf,
   gorgeimpl, lineinfos, btrees, macrocacheimpl,
   modulegraphs, sighashes, int128, vmprofiler
 
 when defined(nimPreviewSlimSystem):
   import std/formatfloat
-
 import ast except getstr
 from semfold import leValueConv, ordinalValToString
 from evaltempl import evalTemplate
@@ -75,8 +74,11 @@ proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int,
   let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo
   liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin)
 
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
 template stackTrace(c: PCtx, tos: PStackFrame, pc: int,
-                    msg: string, lineInfo: TLineInfo = TLineInfo.default) =
+                    msg: string, lineInfo: TLineInfo = TLineInfo.default) {.callsite.} =
   stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true))
   return
 
@@ -118,7 +120,7 @@ template decodeBx(k: untyped) {.dirty.} =
   ensureKind(k)
 
 template move(a, b: untyped) {.dirty.} =
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     a = move b
   else:
     system.shallowCopy(a, b)
@@ -379,6 +381,7 @@ proc cleanUpOnReturn(c: PCtx; f: PStackFrame): int =
       return pc + 1
 
 proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): bool =
+  result = false
   if desttyp.kind == tyString:
     dest.ensureKind(rkNode)
     dest.node = newNode(nkStrLit)
@@ -441,12 +444,16 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
       of tyFloat..tyFloat64:
         dest.intVal = int(src.floatVal)
       else:
-        let srcSize = getSize(c.config, styp)
         let destSize = getSize(c.config, desttyp)
-        let srcDist = (sizeof(src.intVal) - srcSize) * 8
         let destDist = (sizeof(dest.intVal) - destSize) * 8
         var value = cast[BiggestUInt](src.intVal)
-        value = (value shl srcDist) shr srcDist
+        when false:
+          # this would make uint64(-5'i8) evaluate to 251
+          # but at runtime, uint64(-5'i8) is 18446744073709551611
+          # so don't do it
+          let srcSize = getSize(c.config, styp)
+          let srcDist = (sizeof(src.intVal) - srcSize) * 8
+          value = (value shl srcDist) shr srcDist
         value = (value shl destDist) shr destDist
         dest.intVal = cast[BiggestInt](value)
     of tyBool:
@@ -457,9 +464,12 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType):
           else: int(src.intVal != 0)
     of tyFloat..tyFloat64:
       dest.ensureKind(rkFloat)
-      case skipTypes(srctyp, abstractRange).kind
+      let srcKind = skipTypes(srctyp, abstractRange).kind
+      case srcKind
       of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar:
         dest.floatVal = toBiggestFloat(src.intVal)
+      elif src.kind == rkInt:
+        dest.floatVal = toBiggestFloat(src.intVal)
       else:
         dest.floatVal = src.floatVal
     of tyObject:
@@ -497,7 +507,7 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
   setLen(node.sons, newLen)
   if oldLen < newLen:
     for i in oldLen..<newLen:
-      node[i] = getNullValue(typ[0], info, c.config)
+      node[i] = getNullValue(c, typ.elementType, info, c.config)
 
 const
   errNilAccess = "attempt to access a nil address"
@@ -517,7 +527,7 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool =
   if nfIsPtr in node.flags or (typ != nil and typ.kind == tyPtr):
     assert node.kind == nkIntLit, $(node.kind)
     assert typ != nil
-    let typ2 = if typ.kind == tyPtr: typ[0] else: typ
+    let typ2 = if typ.kind == tyPtr: typ.elementType else: typ
     if not derefPtrToReg(node.intVal, typ2, reg, isAssign = isAssign2):
       # tyObject not supported in this context
       stackTrace(c, tos, pc, "deref unsupported ptr type: " & $(typeToString(typ), typ.kind))
@@ -525,20 +535,27 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool =
   else:
     false
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
-
 template takeAddress(reg, source) =
   reg.nodeAddr = addr source
   GC_ref source
 
+proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg =
+  let typ = newType(tyPtr, c.idgen, c.module.owner)
+  typ.add getSysType(c.graph, c.debug[pc], tyChar)
+  var node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
+  node.intVal = cast[int](src.strVal[index].addr)
+  node.flags.incl nfIsPtr
+  TFullReg(kind: rkNode, node: node)
+
+
 proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
+  result = TFullReg(kind: rkNone)
   var pc = start
   var tos = tos
   # Used to keep track of where the execution is resumed.
   var savedPC = -1
-  var savedFrame: PStackFrame
-  when defined(gcArc) or defined(gcOrc):
+  var savedFrame: PStackFrame = nil
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     template updateRegsAlias = discard
     template regs: untyped = tos.slots
   else:
@@ -592,7 +609,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcYldVal: assert false
     of opcAsgnInt:
       decodeB(rkInt)
-      regs[ra].intVal = regs[rb].intVal
+      if regs[rb].kind == rkInt:
+        regs[ra].intVal = regs[rb].intVal
+      else:
+        stackTrace(c, tos, pc, "opcAsgnInt: got " & $regs[rb].kind)
     of opcAsgnFloat:
       decodeB(rkFloat)
       regs[ra].floatVal = regs[rb].floatVal
@@ -622,6 +642,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           regs[ra].intVal = cast[int](regs[rb].node.intVal)
         of rkNodeAddr:
           regs[ra].intVal = cast[int](regs[rb].nodeAddr)
+        of rkRegisterAddr:
+          regs[ra].intVal = cast[int](regs[rb].regAddr)
+        of rkInt:
+          regs[ra].intVal = regs[rb].intVal
         else:
           stackTrace(c, tos, pc, "opcCastPtrToInt: got " & $regs[rb].kind)
       of 2: # tyRef
@@ -655,16 +679,61 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       else:
         assert regs[rb].kind == rkNode
         let nb = regs[rb].node
-        case nb.kind
-        of nkCharLit..nkUInt64Lit:
-          ensureKind(rkInt)
-          regs[ra].intVal = nb.intVal
-        of nkFloatLit..nkFloat64Lit:
-          ensureKind(rkFloat)
-          regs[ra].floatVal = nb.floatVal
+        if nb == nil:
+          stackTrace(c, tos, pc, errNilAccess)
         else:
-          ensureKind(rkNode)
-          regs[ra].node = nb
+          case nb.kind
+          of nkCharLit..nkUInt64Lit:
+            ensureKind(rkInt)
+            regs[ra].intVal = nb.intVal
+          of nkFloatLit..nkFloat64Lit:
+            ensureKind(rkFloat)
+            regs[ra].floatVal = nb.floatVal
+          else:
+            ensureKind(rkNode)
+            regs[ra].node = nb
+    of opcSlice:
+      # A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
+      # nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
+      decodeBC(rkNode)
+      let
+        collection = regs[ra].node
+        leftInd = regs[rb].intVal
+        rightInd = regs[rc].intVal
+
+      proc rangeCheck(left, right: BiggestInt, safeLen: BiggestInt) =
+        if left < 0:
+          stackTrace(c, tos, pc, formatErrorIndexBound(left, safeLen))
+
+        if right > safeLen:
+          stackTrace(c, tos, pc, formatErrorIndexBound(right, safeLen))
+
+      case collection.kind
+      of nkTupleConstr: # slice of a slice
+        let safeLen = collection[2].intVal - collection[1].intVal
+        rangeCheck(leftInd, rightInd, safeLen)
+        let
+          leftInd = leftInd + collection[1].intVal # Slice is from the start of the old
+          rightInd = rightInd + collection[1].intVal
+
+        regs[ra].node = newTree(
+          nkTupleConstr,
+          collection[0],
+          newIntNode(nkIntLit, BiggestInt leftInd),
+          newIntNode(nkIntLit, BiggestInt rightInd)
+        )
+
+      else:
+        let safeLen = safeArrLen(collection) - 1
+        rangeCheck(leftInd, rightInd, safeLen)
+        regs[ra].node = newTree(
+          nkTupleConstr,
+          collection,
+          newIntNode(nkIntLit, BiggestInt leftInd),
+          newIntNode(nkIntLit, BiggestInt rightInd)
+        )
+
+
     of opcLdArr:
       # a = b[c]
       decodeBC(rkNode)
@@ -672,7 +741,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
       let idx = regs[rc].intVal.int
       let src = regs[rb].node
-      if src.kind in {nkStrLit..nkTripleStrLit}:
+      case src.kind
+      of nkTupleConstr: # refer to `of opcSlice`
+        let
+          left = src[1].intVal
+          right = src[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left):
+          case src[0].kind
+          of nkStrKinds:
+            regs[ra].node =  newIntNode(nkCharLit, ord src[0].strVal[int realIndex])
+          of nkBracket:
+            regs[ra].node = src[0][int realIndex]
+          else:
+            stackTrace(c, tos, pc, "opcLdArr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
+
+      of nkStrLit..nkTripleStrLit:
         if idx <% src.strVal.len:
           regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
           regs[ra].node.intVal = src.strVal[idx].ord
@@ -689,14 +775,33 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
       let idx = regs[rc].intVal.int
       let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
-      if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
-        takeAddress regs[ra], src.sons[idx]
+      case src.kind
+      of nkTupleConstr:
+        let
+          left = src[1].intVal
+          right = src[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left): # Refer to `opcSlice`
+          case src[0].kind
+          of nkStrKinds:
+            regs[ra] = takeCharAddress(c, src[0], realIndex, pc)
+          of nkBracket:
+            takeAddress regs[ra], src.sons[0].sons[realIndex]
+          else:
+            stackTrace(c, tos, pc, "opcLdArrAddr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
       else:
-        stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
+        if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
+          takeAddress regs[ra], src.sons[idx]
+        elif src.kind in nkStrKinds and idx <% src.strVal.len:
+          regs[ra] = takeCharAddress(c, src, idx, pc)
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
     of opcLdStrIdx:
       decodeBC(rkInt)
       let idx = regs[rc].intVal.int
-      let s = regs[rb].node.strVal
+      let s {.cursor.} = regs[rb].node.strVal
       if idx <% s.len:
         regs[ra].intVal = s[idx].ord
       else:
@@ -709,21 +814,37 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let idx = regs[rc].intVal.int
       let s = regs[rb].node.strVal.addr # or `byaddr`
       if idx <% s[].len:
-         # `makePtrType` not accessible from vm.nim
-        let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner)
-        typ.add getSysType(c.graph, c.debug[pc], tyChar)
-        let node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
-        node.intVal = cast[int](s[][idx].addr)
-        node.flags.incl nfIsPtr
-        regs[ra].node = node
+        regs[ra] = takeCharAddress(c, regs[rb].node, idx, pc)
       else:
         stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1))
     of opcWrArr:
       # a[b] = c
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
+      assert regs[ra].kind == rkNode
       let arr = regs[ra].node
-      if arr.kind in {nkStrLit..nkTripleStrLit}:
+      case arr.kind
+      of nkTupleConstr: # refer to `opcSlice`
+        let
+          src = arr[0]
+          left = arr[1].intVal
+          right = arr[2].intVal
+          realIndex = left + idx
+        if idx in 0..(right - left):
+          case src.kind
+          of nkStrKinds:
+            src.strVal[int(realIndex)] = char(regs[rc].intVal)
+          of nkBracket:
+            if regs[rc].kind == rkInt:
+              src[int(realIndex)] = newIntNode(nkIntLit, regs[rc].intVal)
+            else:
+              assert regs[rc].kind == rkNode
+              src[int(realIndex)] = regs[rc].node
+          else:
+            stackTrace(c, tos, pc, "opcWrArr internal error")
+        else:
+          stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
+      of {nkStrLit..nkTripleStrLit}:
         if idx <% arr.strVal.len:
           arr.strVal[idx] = chr(regs[rc].intVal)
         else:
@@ -735,19 +856,30 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLdObj:
       # a = b.c
       decodeBC(rkNode)
-      let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
-      case src.kind
-      of nkEmpty..nkNilLit:
-        # for nkPtrLit, this could be supported in the future, use something like:
-        # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
-        # where we compute the offset in bytes for field rc
-        stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
-      of nkObjConstr:
-        let n = src[rc + 1].skipColon
-        regs[ra].node = n
+      if rb >= regs.len or regs[rb].kind == rkNone or 
+        (regs[rb].kind == rkNode and regs[rb].node == nil) or
+        (regs[rb].kind == rkNodeAddr and regs[rb].nodeAddr[] == nil): 
+        stackTrace(c, tos, pc, errNilAccess)
       else:
-        let n = src[rc]
-        regs[ra].node = n
+        let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
+        case src.kind
+        of nkEmpty..nkNilLit:
+          # for nkPtrLit, this could be supported in the future, use something like:
+          # derefPtrToReg(src.intVal + offsetof(src.typ, rc), typ_field, regs[ra], isAssign = false)
+          # where we compute the offset in bytes for field rc
+          stackTrace(c, tos, pc, errNilAccess & " " & $("kind", src.kind, "typ", typeToString(src.typ), "rc", rc))
+        of nkObjConstr:
+          let n = src[rc + 1].skipColon
+          regs[ra].node = n
+        of nkTupleConstr:
+          let n = if src.typ != nil and tfTriggersCompileTime in src.typ.flags:
+              src[rc]
+            else:
+              src[rc].skipColon
+          regs[ra].node = n
+        else:
+          let n = src[rc]
+          regs[ra].node = n
     of opcLdObjAddr:
       # a = addr(b.c)
       decodeBC(rkNodeAddr)
@@ -773,8 +905,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         stackTrace(c, tos, pc, errNilAccess)
       elif dest[shiftedRb].kind == nkExprColonExpr:
         writeField(dest[shiftedRb][1], regs[rc])
+        dest[shiftedRb][1].flags.incl nfSkipFieldChecking
       else:
         writeField(dest[shiftedRb], regs[rc])
+        dest[shiftedRb].flags.incl nfSkipFieldChecking
     of opcWrStrIdx:
       decodeBC(rkNode)
       let idx = regs[rb].intVal.int
@@ -881,14 +1015,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLenSeq:
       decodeBImm(rkInt)
       #assert regs[rb].kind == nkBracket
-      let high = (imm and 1) # discard flags
+      let
+        high = (imm and 1) # discard flags
+        node = regs[rb].node
       if (imm and nimNodeFlag) != 0:
         # used by mNLen (NimNode.len)
         regs[ra].intVal = regs[rb].node.safeLen - high
       else:
-        # safeArrLen also return string node len
-        # used when string is passed as openArray in VM
-        regs[ra].intVal = regs[rb].node.safeArrLen - high
+        case node.kind
+        of nkTupleConstr: # refer to `of opcSlice`
+          regs[ra].intVal = node[2].intVal - node[1].intVal + 1 - high
+        else:
+          # safeArrLen also return string node len
+          # used when string is passed as openArray in VM
+          regs[ra].intVal = node.safeArrLen - high
+
     of opcLenStr:
       decodeBImm(rkInt)
       assert regs[rb].kind == rkNode
@@ -896,7 +1037,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcLenCstring:
       decodeBImm(rkInt)
       assert regs[rb].kind == rkNode
-      regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm
+      if regs[rb].node.kind == nkNilLit:
+        regs[ra].intVal = -imm
+      else:
+        regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm
     of opcIncl:
       decodeB(rkNode)
       let b = regs[rb].regToNode
@@ -1094,6 +1238,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcEqStr:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].node.strVal == regs[rc].node.strVal)
+    of opcEqCString:
+      decodeBC(rkInt)
+      let bNil = regs[rb].node.kind == nkNilLit
+      let cNil = regs[rc].node.kind == nkNilLit
+      regs[ra].intVal = ord((bNil and cNil) or
+        (not bNil and not cNil and regs[rb].node.strVal == regs[rc].node.strVal))
     of opcLeStr:
       decodeBC(rkInt)
       regs[ra].intVal = ord(regs[rb].node.strVal <= regs[rc].node.strVal)
@@ -1165,7 +1315,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
             let ast = a.sym.ast.shallowCopy
             for i in 0..<a.sym.ast.len:
               ast[i] = a.sym.ast[i]
-            ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, cache=true)
+            ast[bodyPos] = transformBody(c.graph, c.idgen, a.sym, {useCache, force})
             ast.copyTree()
     of opcSymOwner:
       decodeB(rkNode)
@@ -1183,7 +1333,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if a.kind == nkSym and a.sym.kind in skProcKinds and
          b.kind == nkSym and b.sym.kind in skProcKinds:
         regs[ra].intVal =
-          if sfFromGeneric in a.sym.flags and a.sym.owner == b.sym: 1
+          if sfFromGeneric in a.sym.flags and a.sym.instantiatedFrom == b.sym: 1
           else: 0
       else:
         stackTrace(c, tos, pc, "node is not a proc symbol")
@@ -1202,15 +1352,32 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode))
     of opcParseFloat:
       decodeBC(rkInt)
-      inc pc
-      assert c.code[pc].opcode == opcParseFloat
-      let rd = c.code[pc].regA
       var rcAddr = addr(regs[rc])
       if rcAddr.kind == rkRegisterAddr: rcAddr = rcAddr.regAddr
       elif regs[rc].kind != rkFloat:
         regs[rc] = TFullReg(kind: rkFloat)
-      regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal,
-                                          rcAddr.floatVal, regs[rd].intVal.int)
+
+      let coll = regs[rb].node
+
+      case coll.kind
+      of nkTupleConstr:
+        let
+          data = coll[0]
+          left = coll[1].intVal
+          right = coll[2].intVal
+        case data.kind
+        of nkStrKinds:
+          regs[ra].intVal = parseBiggestFloat(data.strVal.toOpenArray(int left, int right), rcAddr.floatVal)
+        of nkBracket:
+          var s = newStringOfCap(right - left + 1)
+          for i in left..right:
+            s.add char data[int i].intVal
+          regs[ra].intVal = parseBiggestFloat(s, rcAddr.floatVal)
+        else:
+          internalError(c.config, c.debug[pc], "opcParseFloat: Incorrectly created openarray")
+      else:
+        regs[ra].intVal = parseBiggestFloat(regs[rb].node.strVal, rcAddr.floatVal)
+
     of opcRangeChck:
       let rb = instr.regB
       let rc = instr.regC
@@ -1224,11 +1391,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       let rb = instr.regB
       let rc = instr.regC
       let bb = regs[rb].node
+      if bb.kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
       let isClosure = bb.kind == nkTupleConstr
+      if isClosure and bb[0].kind == nkNilLit:
+        stackTrace(c, tos, pc, "attempt to call nil closure")
       let prc = if not isClosure: bb.sym else: bb[0].sym
       if prc.offset < -1:
         # it's a callback:
-        c.callbacks[-prc.offset-2].value(
+        c.callbacks[-prc.offset-2](
           VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]),
                  currentException: c.currentExceptionA,
                  currentLineInfo: c.debug[pc])
@@ -1245,8 +1416,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           let prcValue = c.globals[prc.position-1]
           if prcValue.kind == nkEmpty:
             globalError(c.config, c.debug[pc], "cannot run " & prc.name.s)
-          var slots2: TNodeSeq
-          slots2.setLen(tos.slots.len)
+          var slots2: TNodeSeq = newSeq[PNode](tos.slots.len)
           for i in 0..<tos.slots.len:
             slots2[i] = regToNode(tos.slots[i])
           let newValue = callForeignFunction(c.config, prcValue, prc.typ, slots2,
@@ -1264,8 +1434,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         #echo "new pc ", newPc, " calling: ", prc.name.s
         var newFrame = PStackFrame(prc: prc, comesFrom: pc, next: tos)
         newSeq(newFrame.slots, prc.offset+ord(isClosure))
-        if not isEmptyType(prc.typ[0]):
-          putIntoReg(newFrame.slots[0], getNullValue(prc.typ[0], prc.info, c.config))
+        if not isEmptyType(prc.typ.returnType):
+          putIntoReg(newFrame.slots[0], getNullValue(c, prc.typ.returnType, prc.info, c.config))
         for i in 1..rc-1:
           newFrame.slots[i] = regs[rb+i]
         if isClosure:
@@ -1285,6 +1455,9 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         for i in 1..rc-1:
           let node = regs[rb+i].regToNode
           node.info = c.debug[pc]
+          if prc.typ[i].kind notin {tyTyped, tyUntyped}:
+            node.annotateType(prc.typ[i], c.config)
+
           macroCall.add(node)
         var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter, c.idgen)
         if a.kind == nkStmtList and a.len == 1: a = a[0]
@@ -1332,7 +1505,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcExcept:
       # This opcode is never executed, it only holds information for the
       # exception handling routines.
-      doAssert(false)
+      raiseAssert "unreachable"
     of opcFinally:
       # Pop the last safepoint introduced by a opcTry. This opcode is only
       # executed _iff_ no exception was raised in the body of the `try`
@@ -1357,7 +1530,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
           regs[ra].node
       c.currentExceptionA = raised
       # Set the `name` field of the exception
-      c.currentExceptionA[2].skipColon.strVal = c.currentExceptionA.typ.sym.name.s
+      var exceptionNameNode = newStrNode(nkStrLit, c.currentExceptionA.typ.sym.name.s)
+      if c.currentExceptionA[2].kind == nkExprColonExpr:
+        exceptionNameNode.typ = c.currentExceptionA[2][1].typ
+        c.currentExceptionA[2][1] = exceptionNameNode
+      else:
+        exceptionNameNode.typ = c.currentExceptionA[2].typ
+        c.currentExceptionA[2] = exceptionNameNode
       c.exceptionInstr = pc
 
       var frame = tos
@@ -1366,7 +1545,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         frame = frame.next
         jumpTo = findExceptionHandler(c, frame, raised)
 
-      case jumpTo.why:
+      case jumpTo.why
       of ExceptionGotoHandler:
         # Jump to the handler, do nothing when the `finally` block ends.
         savedPC = -1
@@ -1389,7 +1568,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], c.config)
+      regs[ra].node = getNullValue(c, typ, c.debug[pc], c.config)
       regs[ra].node.flags.incl nfIsRef
     of opcNewSeq:
       let typ = c.types[instr.regBx - wordExcess]
@@ -1401,7 +1580,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[i] = getNullValue(typ[0], c.debug[pc], c.config)
+        regs[ra].node[i] = getNullValue(c, typ.elementType, c.debug[pc], c.config)
     of opcNewStr:
       decodeB(rkNode)
       regs[ra].node = newNodeI(nkStrLit, c.debug[pc])
@@ -1413,7 +1592,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], c.config)
+      regs[ra].node = getNullValue(c, 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 whether
@@ -1463,7 +1642,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         node2.flags.incl nfIsPtr
         regs[ra].node = node2
       elif not derefPtrToReg(node.intVal, typ, regs[ra], isAssign = false):
-        stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ[0].kind))
+        stackTrace(c, tos, pc, "opcLdDeref unsupported type: " & $(typeToString(typ), typ.elementType.kind))
     of opcLdGlobalAddrDerefFFI:
       let rb = instr.regBx - wordExcess - 1
       let node = c.globals[rb]
@@ -1480,7 +1659,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcRepr:
       decodeB(rkNode)
       createStr regs[ra]
-      regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments})
+      regs[ra].node.strVal = renderTree(regs[rb].regToNode, {renderNoComments, renderDocComments, renderNonExportedFields})
     of opcQuit:
       if c.mode in {emRepl, emStaticExpr, emStaticStmt}:
         message(c.config, c.debug[pc], hintQuitCalled)
@@ -1551,7 +1730,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         rb = instr.regB
         rc = instr.regC
         idx = int(regs[rb+rc-1].intVal)
-        callback = c.callbacks[idx].value
+        callback = c.callbacks[idx]
         args = VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]),
                 currentException: c.currentExceptionA,
                 currentLineInfo: c.debug[pc])
@@ -1729,7 +1908,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       if regs[rb].node.kind != nkSym:
         stackTrace(c, tos, pc, "node is not a symbol")
       else:
-        regs[ra].node.strVal = $sigHash(regs[rb].node.sym)
+        regs[ra].node.strVal = $sigHash(regs[rb].node.sym, c.config)
     of opcSlurp:
       decodeB(rkNode)
       createStr regs[ra]
@@ -1764,12 +1943,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         message(c.config, info, hintUser, a.strVal)
     of opcParseExprToAst:
       decodeBC(rkNode)
-      var error: string
+      var error: string = ""
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
                             regs[rc].node.strVal, 0,
-                            proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} =
+                            proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.len == 0 and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
+
+      regs[ra].node = newNode(nkEmpty)
       if error.len > 0:
         c.errorFlag = error
       elif ast.len != 1:
@@ -1779,14 +1960,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         regs[ra].node = ast[0]
     of opcParseStmtToAst:
       decodeBC(rkNode)
-      var error: string
+      var error: string = ""
       let ast = parseString(regs[rb].node.strVal, c.cache, c.config,
                             regs[rc].node.strVal, 0,
-                            proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} =
+                            proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) =
                               if error.len == 0 and msg <= errMax:
                                 error = formatMsg(conf, info, msg, arg))
       if error.len > 0:
         c.errorFlag = error
+        regs[ra].node = newNode(nkEmpty)
       else:
         regs[ra].node = ast
     of opcQueryErrorFlag:
@@ -1806,14 +1988,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       of 1: # getLine
         regs[ra].node = newIntNode(nkIntLit, n.info.line.int)
       of 2: # getColumn
-        regs[ra].node = newIntNode(nkIntLit, n.info.col)
+        regs[ra].node = newIntNode(nkIntLit, n.info.col.int)
       else:
         internalAssert c.config, false
       regs[ra].node.info = n.info
       regs[ra].node.typ = n.typ
-    of opcNSetLineInfo:
+    of opcNCopyLineInfo:
       decodeB(rkNode)
       regs[ra].node.info = regs[rb].node.info
+    of opcNSetLineInfoLine:
+      decodeB(rkNode)
+      regs[ra].node.info.line = regs[rb].intVal.uint16
+    of opcNSetLineInfoColumn:
+      decodeB(rkNode)
+      regs[ra].node.info.col = regs[rb].intVal.int16
+    of opcNSetLineInfoFile:
+      decodeB(rkNode)
+      regs[ra].node.info.fileIndex =
+        fileInfoIdx(c.config, RelativeFile regs[rb].node.strVal)
     of opcEqIdent:
       decodeBC(rkInt)
       # aliases for shorter and easier to understand code below
@@ -1938,12 +2130,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
         dest.ident = regs[rb].node.ident
       else:
         stackTrace(c, tos, pc, errFieldXNotFound & "ident")
-    of opcNSetType:
-      decodeB(rkNode)
-      let b = regs[rb].node
-      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)
       var dest = regs[ra].node
@@ -1993,18 +2179,18 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
                  else: regs[rc].node.strVal
       if k < 0 or k > ord(high(TSymKind)):
         internalError(c.config, c.debug[pc], "request to create symbol of invalid kind")
-      var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextSymId c.idgen, c.module.owner, c.debug[pc])
+      var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.idgen, c.module.owner, c.debug[pc])
       incl(sym.flags, sfGenSym)
       regs[ra].node = newSymNode(sym)
       regs[ra].node.flags.incl nfIsRef
     of opcNccValue:
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal = getOrDefault(c.graph.cacheCounters, destKey)
     of opcNccInc:
       let g = c.graph
       declBC()
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       let by = regs[rc].intVal
       let v = getOrDefault(g.cacheCounters, destKey)
       g.cacheCounters[destKey] = v+by
@@ -2012,7 +2198,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNcsAdd:
       let g = c.graph
       declBC()
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       let val = regs[rc].node
       if not contains(g.cacheSeqs, destKey):
         g.cacheSeqs[destKey] = newTree(nkStmtList, val)
@@ -2022,7 +2208,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNcsIncl:
       let g = c.graph
       declBC()
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       let val = regs[rc].node
       if not contains(g.cacheSeqs, destKey):
         g.cacheSeqs[destKey] = newTree(nkStmtList, val)
@@ -2036,22 +2222,22 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNcsLen:
       let g = c.graph
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if contains(g.cacheSeqs, destKey): g.cacheSeqs[destKey].len else: 0
     of opcNcsAt:
       let g = c.graph
       decodeBC(rkNode)
       let idx = regs[rc].intVal
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len:
         regs[ra].node = g.cacheSeqs[destKey][idx.int]
       else:
         stackTrace(c, tos, pc, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1))
     of opcNctPut:
       let g = c.graph
-      let destKey = regs[ra].node.strVal
-      let key = regs[instr.regB].node.strVal
+      let destKey {.cursor.} = regs[ra].node.strVal
+      let key {.cursor.} = regs[instr.regB].node.strVal
       let val = regs[instr.regC].node
       if not contains(g.cacheTables, destKey):
         g.cacheTables[destKey] = initBTree[string, PNode]()
@@ -2063,14 +2249,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctLen:
       let g = c.graph
       decodeB(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if contains(g.cacheTables, destKey): g.cacheTables[destKey].len else: 0
     of opcNctGet:
       let g = c.graph
       decodeBC(rkNode)
-      let destKey = regs[rb].node.strVal
-      let key = regs[rc].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
+      let key {.cursor.} = regs[rc].node.strVal
       if contains(g.cacheTables, destKey):
         if contains(g.cacheTables[destKey], key):
           regs[ra].node = getOrDefault(g.cacheTables[destKey], key)
@@ -2081,7 +2267,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctHasNext:
       let g = c.graph
       decodeBC(rkInt)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       regs[ra].intVal =
         if g.cacheTables.contains(destKey):
           ord(btrees.hasNext(g.cacheTables[destKey], regs[rc].intVal.int))
@@ -2090,7 +2276,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     of opcNctNext:
       let g = c.graph
       decodeBC(rkNode)
-      let destKey = regs[rb].node.strVal
+      let destKey {.cursor.} = regs[rb].node.strVal
       let index = regs[rc].intVal
       if contains(g.cacheTables, destKey):
         let (k, v, nextIndex) = btrees.next(g.cacheTables[destKey], index.int)
@@ -2105,7 +2291,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkNode)
       var typ = regs[rb].node.typ
       internalAssert c.config, typ != nil
-      while typ.kind == tyTypeDesc and typ.len > 0: typ = typ[0]
+      while typ.kind == tyTypeDesc and typ.hasElementType: typ = typ.skipModifier
       createStr regs[ra]
       regs[ra].node.strVal = typ.typeToString(preferExported)
 
@@ -2121,10 +2307,11 @@ proc execute(c: PCtx, start: int): PNode =
 proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
   c.loopIterations = c.config.maxLoopIterationsVM
   if sym.kind in routineKinds:
-    if sym.typ.len-1 != args.len:
+    if sym.typ.paramsLen != args.len:
+      result = nil
       localError(c.config, sym.info,
         "NimScript: expected $# arguments, but got $#" % [
-        $(sym.typ.len-1), $args.len])
+        $(sym.typ.paramsLen), $args.len])
     else:
       let start = genProc(c, sym)
 
@@ -2133,14 +2320,15 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode =
       newSeq(tos.slots, maxSlots)
 
       # setup parameters:
-      if not isEmptyType(sym.typ[0]) or sym.kind == skMacro:
-        putIntoReg(tos.slots[0], getNullValue(sym.typ[0], sym.info, c.config))
+      if not isEmptyType(sym.typ.returnType) or sym.kind == skMacro:
+        putIntoReg(tos.slots[0], getNullValue(c, sym.typ.returnType, 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])
+      for i in 0..<sym.typ.paramsLen:
+        putIntoReg(tos.slots[i+1], args[i])
 
       result = rawExecute(c, start, tos).regToNode
   else:
+    result = nil
     localError(c.config, sym.info,
       "NimScript: attempt to call non-routine: " & sym.name.s)
 
@@ -2179,7 +2367,7 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) =
   else:
     refresh(PCtx graph.vm, module, idgen)
 
-proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} =
+proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext =
   #var c = newEvalContext(module, emRepl)
   #c.features = {allowCast, allowInfiniteLoops}
   #pushStackFrame(c, newStackFrame())
@@ -2188,7 +2376,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext
   setupGlobalCtx(module, graph, idgen)
   result = PCtx graph.vm
 
-proc myProcess(c: PPassContext, n: PNode): PNode =
+proc interpreterCode*(c: PPassContext, n: PNode): PNode =
   let c = PCtx(c)
   # don't eval errornous code:
   if c.oldErrorCount == c.config.errorCounter:
@@ -2198,14 +2386,12 @@ proc myProcess(c: PPassContext, n: PNode): PNode =
     result = n
   c.oldErrorCount = c.config.errorCounter
 
-proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode =
-  result = myProcess(c, n)
-
-const evalPass* = makePass(myOpen, myProcess, myClose)
-
 proc evalConstExprAux(module: PSym; idgen: IdGenerator;
                       g: ModuleGraph; prc: PSym, n: PNode,
                       mode: TEvalMode): PNode =
+  when defined(nimsuggest):
+    if g.config.expandDone():
+      return n
   #if g.config.errorCounter > 0: return n
   let n = transformExpr(g, idgen, module, n)
   setupGlobalCtx(module, g, idgen)
@@ -2263,6 +2449,7 @@ proc prepareVMValue(arg: PNode): PNode =
 proc setupMacroParam(x: PNode, typ: PType): TFullReg =
   case typ.kind
   of tyStatic:
+    result = TFullReg(kind: rkNone)
     putIntoReg(result, prepareVMValue(x))
   else:
     var n = x
@@ -2275,7 +2462,7 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
   let gp = macroSym.ast[genericParamsPos]
   for i in 0..<gp.len:
     let genericParam = gp[i].sym
-    let posInCall = macroSym.typ.len + i
+    let posInCall = macroSym.typ.signatureLen + i
     if posInCall < call.len:
       yield (genericParam, call[posInCall])
 
@@ -2284,7 +2471,7 @@ const evalMacroLimit = 1000
 
 #proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode =
 #  result = newNodeI(nkEmpty, n.info)
-#  result.typ = newType(tyError, nextTypeId idgen, owner)
+#  result.typ = newType(tyError, idgen, owner)
 #  result.typ.flags.incl tfCheckedForDestructor
 
 proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int;
@@ -2298,9 +2485,10 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
 
   # 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:
+  let sl = sym.typ.signatureLen
+  if sl > n.safeLen and sl > 1:
     globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [
-        n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)])
+        n.renderTree, $(n.safeLen-1), $(sym.typ.paramsLen)])
 
   setupGlobalCtx(module, g, idgen)
   var c = PCtx g.vm
@@ -2325,12 +2513,12 @@ proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstC
   tos.slots[0] = TFullReg(kind: rkNode, node: newNodeI(nkEmpty, n.info))
 
   # setup parameters:
-  for i in 1..<sym.typ.len:
-    tos.slots[i] = setupMacroParam(n[i], sym.typ[i])
+  for i, param in paramTypes(sym.typ):
+    tos.slots[i-FirstParamAt+1] = setupMacroParam(n[i-FirstParamAt+1], param)
 
   let gp = sym.ast[genericParamsPos]
   for i in 0..<gp.len:
-    let idx = sym.typ.len + i
+    let idx = sym.typ.signatureLen + i
     if idx < n.len:
       tos.slots[idx] = setupMacroParam(n[idx], gp[i].sym.typ)
     else:
diff --git a/compiler/vmconv.nim b/compiler/vmconv.nim
index b82fb2ff3..45d925df0 100644
--- a/compiler/vmconv.nim
+++ b/compiler/vmconv.nim
@@ -1,4 +1,7 @@
-import ast
+import ast except elementType
+import idents, lineinfos, astalgo
+import vmdef
+import std/times
 
 template elementType*(T: typedesc): typedesc =
   typeof(block:
@@ -14,7 +17,7 @@ proc fromLit*(a: PNode, T: typedesc): auto =
     for ai in a:
       result.incl Ti(ai.intVal)
   else:
-    static: doAssert false, "not yet supported: " & $T # add as needed
+    static: raiseAssert "not yet supported: " & $T # add as needed
 
 proc toLit*[T](a: T): PNode =
   ## generic type => PNode
@@ -41,5 +44,14 @@ proc toLit*[T](a: T): PNode =
       reti.add ai.toLit
       result.add reti
   else:
-    static: doAssert false, "not yet supported: " & $T # add as needed
+    static: raiseAssert "not yet supported: " & $T # add as needed
 
+proc toTimeLit*(a: Time, c: PCtx, obj: PNode, info: TLineInfo): PNode =
+  # probably refactor it into `toLit` in the future
+  result = newTree(nkObjConstr)
+  result.add(newNode(nkEmpty)) # can be changed to a symbol according to PType
+  for k, ai in fieldPairs(a):
+    let reti = newNode(nkExprColonExpr)
+    reti.add newSymNode(lookupInRecord(obj, getIdent(c.cache, k)), info)
+    reti.add ai.toLit
+    result.add reti
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index c653501ca..bdb0aeed1 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 tables
+import std/[tables, strutils]
 
 import ast, idents, options, modulegraphs, lineinfos
 
@@ -81,6 +81,7 @@ type
     opcWrStrIdx,
     opcLdStrIdx, # a = b[c]
     opcLdStrIdxAddr,  # a = addr(b[c])
+    opcSlice, # toOpenArray(collection, left, right)
 
     opcAddInt,
     opcAddImmInt,
@@ -98,7 +99,7 @@ type
     opcLeFloat, opcLtFloat, opcLeu, opcLtu,
     opcEqRef, opcEqNimNode, opcSameNodeType,
     opcXor, opcNot, opcUnaryMinusInt, opcUnaryMinusFloat, opcBitnotInt,
-    opcEqStr, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
+    opcEqStr, opcEqCString, opcLeStr, opcLtStr, opcEqSet, opcLeSet, opcLtSet,
     opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr,
     opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq,
     opcIsNil, opcOf, opcIs,
@@ -126,7 +127,7 @@ type
     opcNGetSize,
 
     opcNSetIntVal,
-    opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetType, opcNSetStrVal,
+    opcNSetFloatVal, opcNSetSymbol, opcNSetIdent, opcNSetStrVal,
     opcNNewNimNode, opcNCopyNimNode, opcNCopyNimTree, opcNDel, opcGenSym,
 
     opcNccValue, opcNccInc, opcNcsAdd, opcNcsIncl, opcNcsLen, opcNcsAt,
@@ -140,7 +141,8 @@ type
     opcNError,
     opcNWarning,
     opcNHint,
-    opcNGetLineInfo, opcNSetLineInfo,
+    opcNGetLineInfo, opcNCopyLineInfo, opcNSetLineInfoLine,
+    opcNSetLineInfoColumn, opcNSetLineInfoFile
     opcEqIdent,
     opcStrToIdent,
     opcGetImpl,
@@ -257,7 +259,8 @@ type
     traceActive*: bool
     loopIterations*: int
     comesFromHeuristic*: TLineInfo # Heuristic for better macro stack traces
-    callbacks*: seq[tuple[key: string, value: VmCallback]]
+    callbacks*: seq[VmCallback]
+    callbackIndex*: Table[string, int]
     errorFlag*: string
     cache*: IdentCache
     config*: ConfigRef
@@ -290,7 +293,7 @@ proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator
   PCtx(code: @[], debug: @[],
     globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[],
     prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM,
-    comesFromHeuristic: unknownLineInfo, callbacks: @[], errorFlag: "",
+    comesFromHeuristic: unknownLineInfo, callbacks: @[], callbackIndex: initTable[string, int](), errorFlag: "",
     cache: cache, config: g.config, graph: g, idgen: idgen)
 
 proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
@@ -299,9 +302,18 @@ proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) =
   c.loopIterations = c.config.maxLoopIterationsVM
   c.idgen = idgen
 
+proc reverseName(s: string): string =
+  result = newStringOfCap(s.len)
+  let y = s.split('.')
+  for i in 1..y.len:
+    result.add y[^i]
+    if i != y.len:
+      result.add '.'
+
 proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} =
   result = c.callbacks.len
-  c.callbacks.add((name, callback))
+  c.callbacks.add(callback)
+  c.callbackIndex[reverseName(name)] = result
 
 const
   firstABxInstr* = opcTJmp
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 118a3031e..294aaaa79 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,9 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, options, idents, lineinfos
+import ast, types, msgs, options, idents, lineinfos
 from pathutils import AbsoluteFile
 
+import std/os
+
 when defined(nimPreviewSlimSystem):
   import std/syncio
 
@@ -29,7 +31,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str
 
 proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo;
                  idgen: IdGenerator): PNode =
-  let sym = newSym(skType, getIdent(cache, name), nextSymId(idgen), t.owner, info)
+  let sym = newSym(skType, getIdent(cache, name), idgen, t.owner, info)
   sym.magic = m
   sym.typ = t
   result = newSymNode(sym)
@@ -47,13 +49,13 @@ proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; inf
                        inst=false): PNode =
   result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
   result.add atomicTypeX(cache, name, m, t, info, idgen)
-  for i in 0..<t.len:
-    if t[i] == nil:
-      let void = atomicTypeX(cache, "void", mVoid, t, info, idgen)
-      void.typ = newType(tyVoid, nextTypeId(idgen), t.owner)
-      result.add void
+  for a in t.kids:
+    if a == nil:
+      let voidt = atomicTypeX(cache, "void", mVoid, t, info, idgen)
+      voidt.typ = newType(tyVoid, idgen, t.owner)
+      result.add voidt
     else:
-      result.add mapTypeToAstX(cache, t[i], info, idgen, inst)
+      result.add mapTypeToAstX(cache, a, info, idgen, inst)
 
 proc objectNode(cache: IdentCache; n: PNode; idgen: IdGenerator): PNode =
   if n.kind == nkSym:
@@ -72,9 +74,9 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   var allowRecursion = allowRecursionX
   template atomicType(name, m): untyped = atomicTypeX(cache, name, m, t, info, idgen)
   template atomicType(s): untyped = atomicTypeX(s, info)
-  template mapTypeToAst(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
-  template mapTypeToAstR(t,info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
-  template mapTypeToAst(t,i,info): untyped =
+  template mapTypeToAst(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst)
+  template mapTypeToAstR(t, info): untyped = mapTypeToAstX(cache, t, info, idgen, inst, true)
+  template mapTypeToAst(t, i, info): untyped =
     if i<t.len and t[i]!=nil: mapTypeToAstX(cache, t[i], info, idgen, inst)
     else: newNodeI(nkEmpty, info)
   template mapTypeToBracket(name, m, t, info): untyped =
@@ -105,19 +107,19 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyUncheckedArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("UncheckedArray", mUncheckedArray)
-    result.add mapTypeToAst(t[0], info)
+    result.add mapTypeToAst(t.elementType, info)
   of tyArray:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
     result.add atomicType("array", mArray)
-    if inst and t[0].kind == tyRange:
+    if inst and t.indexType.kind == tyRange:
       var rng = newNodeX(nkInfix)
       rng.add newIdentNode(getIdent(cache, ".."), info)
-      rng.add t[0].n[0].copyTree
-      rng.add t[0].n[1].copyTree
+      rng.add t.indexType.n[0].copyTree
+      rng.add t.indexType.n[1].copyTree
       result.add rng
     else:
-      result.add mapTypeToAst(t[0], info)
-    result.add mapTypeToAst(t[1], info)
+      result.add mapTypeToAst(t.indexType, info)
+    result.add mapTypeToAst(t.elementType, info)
   of tyTypeDesc:
     if t.base != nil:
       result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
@@ -127,33 +129,37 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       result = atomicType("typeDesc", mTypeDesc)
   of tyGenericInvocation:
     result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t)
-    for i in 0..<t.len:
-      result.add mapTypeToAst(t[i], info)
+    for a in t.kids:
+      result.add mapTypeToAst(a, info)
   of tyGenericInst:
     if inst:
       if allowRecursion:
-        result = mapTypeToAstR(t.lastSon, info)
+        result = mapTypeToAstR(t.skipModifier, info)
+        # keep original type info for getType calls on the output node:
+        result.typ = t
       else:
         result = newNodeX(nkBracketExpr)
-        #result.add mapTypeToAst(t.lastSon, info)
-        result.add mapTypeToAst(t[0], info)
-        for i in 1..<t.len-1:
-          result.add mapTypeToAst(t[i], info)
+        #result.add mapTypeToAst(t.last, info)
+        result.add mapTypeToAst(t.genericHead, info)
+        for _, a in t.genericInstParams:
+          result.add mapTypeToAst(a, info)
     else:
-      result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
+      result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
+      # keep original type info for getType calls on the output node:
+      result.typ = t
   of tyGenericBody:
     if inst:
-      result = mapTypeToAstR(t.lastSon, info)
+      result = mapTypeToAstR(t.typeBodyImpl, info)
     else:
-      result = mapTypeToAst(t.lastSon, info)
+      result = mapTypeToAst(t.typeBodyImpl, info)
   of tyAlias:
-    result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
+    result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
   of tyOrdinal:
-    result = mapTypeToAst(t.lastSon, info)
+    result = mapTypeToAst(t.skipModifier, info)
   of tyDistinct:
     if inst:
       result = newNodeX(nkDistinctTy)
-      result.add mapTypeToAst(t[0], info)
+      result.add mapTypeToAst(t.skipModifier, info)
     else:
       if allowRecursion or t.sym == nil:
         result = mapTypeToBracket("distinct", mDistinct, t, info)
@@ -168,11 +174,11 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       if objectDef.kind == nkRefTy:
         objectDef = objectDef[0]
       result.add objectDef[0].copyTree  # copy object pragmas
-      if t[0] == nil:
+      if t.baseClass == nil:
         result.add newNodeI(nkEmpty, info)
       else:  # handle parent object
         var nn = newNodeX(nkOfInherit)
-        nn.add mapTypeToAst(t[0], info)
+        nn.add mapTypeToAst(t.baseClass, info)
         result.add nn
       if t.n.len > 0:
         result.add objectNode(cache, t.n, idgen)
@@ -182,10 +188,10 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       if allowRecursion or t.sym == nil:
         result = newNodeIT(nkObjectTy, if t.n.isNil: info else: t.n.info, t)
         result.add newNodeI(nkEmpty, info)
-        if t[0] == nil:
+        if t.baseClass == nil:
           result.add newNodeI(nkEmpty, info)
         else:
-          result.add mapTypeToAst(t[0], info)
+          result.add mapTypeToAst(t.baseClass, info)
         result.add copyTree(t.n)
       else:
         result = atomicType(t.sym)
@@ -199,7 +205,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
       # only named tuples have a node, unnamed tuples don't
       if t.n.isNil:
         result = newNodeX(nkTupleConstr)
-        for subType in t.sons:
+        for subType in t.kids:
           result.add mapTypeToAst(subType, info)
       else:
         result = newNodeX(nkTupleTy)
@@ -211,19 +217,19 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyPtr:
     if inst:
       result = newNodeX(nkPtrTy)
-      result.add mapTypeToAst(t[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("ptr", mPtr, t, info)
   of tyRef:
     if inst:
       result = newNodeX(nkRefTy)
-      result.add mapTypeToAst(t[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("ref", mRef, t, info)
   of tyVar:
     if inst:
       result = newNodeX(nkVarTy)
-      result.add mapTypeToAst(t[0], info)
+      result.add mapTypeToAst(t.elementType, info)
     else:
       result = mapTypeToBracket("var", mVar, t, info)
   of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info)
@@ -231,17 +237,24 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tySequence: result = mapTypeToBracket("seq", mSeq, t, info)
   of tyProc:
     if inst:
-      result = newNodeX(nkProcTy)
+      result = newNodeX(if tfIterator in t.flags: nkIteratorTy else: nkProcTy)
       var fp = newNodeX(nkFormalParams)
-      if t[0] == nil:
+      if t.returnType == nil:
         fp.add newNodeI(nkEmpty, info)
       else:
-        fp.add mapTypeToAst(t[0], t.n[0].info)
-      for i in 1..<t.len:
+        fp.add mapTypeToAst(t.returnType, t.n[0].info)
+      for i in FirstParamAt..<t.kidsLen:
         fp.add newIdentDefs(t.n[i], t[i])
       result.add fp
-      result.add if t.n[0].len > 0: t.n[0][pragmasEffects].copyTree
-                 else: newNodeI(nkEmpty, info)
+      var prag =
+        if t.n[0].len > 0:
+          t.n[0][pragmasEffects].copyTree
+        else:
+          newNodeI(nkEmpty, info)
+      if t.callConv != ccClosure or tfExplicitCallConv in t.flags:
+        if prag.kind == nkEmpty: prag = newNodeI(nkPragma, info)
+        prag.add newIdentNode(getIdent(cache, $t.callConv), info)
+      result.add prag
     else:
       result = mapTypeToBracket("proc", mNone, t, info)
   of tyOpenArray: result = mapTypeToBracket("openArray", mOpenArray, t, info)
@@ -276,12 +289,12 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyUInt32: result = atomicType("uint32", mUInt32)
   of tyUInt64: result = atomicType("uint64", mUInt64)
   of tyVarargs: result = mapTypeToBracket("varargs", mVarargs, t, info)
-  of tyProxy: result = atomicType("error", mNone)
+  of tyError: result = atomicType("error", mNone)
   of tyBuiltInTypeClass:
     result = mapTypeToBracket("builtinTypeClass", mNone, t, info)
   of tyUserTypeClass, tyUserTypeClassInst:
     if t.isResolvedUserTypeClass:
-      result = mapTypeToAst(t.lastSon, info)
+      result = mapTypeToAst(t.last, info)
     else:
       result = mapTypeToBracket("concept", mNone, t, info)
       result.add t.n.copyTree
@@ -292,7 +305,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo;
   of tyNot: result = mapTypeToBracket("not", mNot, t, info)
   of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info)
   of tyAnything: result = atomicType("anything", mNone)
-  of tyInferred: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion)
+  of tyInferred: result = mapTypeToAstX(cache, t.skipModifier, info, idgen, inst, allowRecursion)
   of tyStatic, tyFromExpr:
     if inst:
       if t.n != nil: result = t.n.copyTree
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index f15cb2752..0c7a49984 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -26,14 +26,14 @@
 # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need
 # this copy depends on the involved types.
 
-import tables
+import std/[tables, intsets, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 import
-  strutils, ast, types, msgs, renderer, vmdef,
-  intsets, magicsys, options, lowerings, lineinfos, transf, astmsgs
+  ast, types, msgs, renderer, vmdef, trees,
+  magicsys, options, lowerings, lineinfos, transf, astmsgs
 
 from modulegraphs import getBody
 
@@ -53,6 +53,7 @@ type
     gfNode # Affects how variables are loaded - always loads as rkNode
     gfNodeAddr # Affects how variables are loaded - always loads as rkNodeAddr
     gfIsParam # do not deepcopy parameters, they are immutable
+    gfIsSinkParam # deepcopy sink parameters
   TGenFlags = set[TGenFlag]
 
 proc debugInfo(c: PCtx; info: TLineInfo): string =
@@ -115,7 +116,8 @@ proc echoCode*(c: PCtx; start=0; last = -1) {.deprecated.} =
   codeListing(c, buf, start, last)
   echo buf
 
-proc gABC(ctx: PCtx; n: PNode; opc: TOpcode; a, b, c: TRegister = 0) =
+proc gABC(ctx: PCtx; n: PNode; opc: TOpcode;
+          a: TRegister = 0, b: TRegister = 0, c: TRegister = 0) =
   ## Takes the registers `b` and `c`, applies the operation `opc` to them, and
   ## stores the result into register `a`
   ## The node is needed for debug information
@@ -244,7 +246,7 @@ proc getTemp(cc: PCtx; tt: PType): TRegister =
 
 proc freeTemp(c: PCtx; r: TRegister) =
   let c = c.prc
-  if c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
+  if r < c.regInfo.len and c.regInfo[r].kind in {slotSomeTemp..slotTempComplex}:
     # this seems to cause https://github.com/nim-lang/Nim/issues/10647
     c.regInfo[r].inUse = false
 
@@ -306,6 +308,8 @@ proc genx(c: PCtx; n: PNode; flags: TGenFlags = {}): TRegister =
   #internalAssert c.config, tmp >= 0 # 'nim check' does not like this internalAssert.
   if tmp >= 0:
     result = TRegister(tmp)
+  else:
+    result = 0
 
 proc clearDest(c: PCtx; n: PNode; dest: var TDest) {.inline.} =
   # stmt is different from 'void' in meta programming contexts.
@@ -318,10 +322,6 @@ proc isNotOpr(n: PNode): bool =
   n.kind in nkCallKinds and n[0].kind == nkSym and
     n[0].sym.magic == mNot
 
-proc isTrue(n: PNode): bool =
-  n.kind == nkSym and n.sym.kind == skEnumField and n.sym.position != 0 or
-    n.kind == nkIntLit and n.intVal != 0
-
 proc genWhile(c: PCtx; n: PNode) =
   # lab1:
   #   cond, tmp
@@ -358,12 +358,13 @@ proc genBlock(c: PCtx; n: PNode; dest: var TDest) =
     #if c.prc.regInfo[i].kind in {slotFixedVar, slotFixedLet}:
     if i != dest:
       when not defined(release):
-        if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
-                                  slotTempInt,
-                                  slotTempFloat,
-                                  slotTempStr,
-                                  slotTempComplex}:
-          doAssert false, "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
+        if c.config.cmd != cmdCheck:
+          if c.prc.regInfo[i].inUse and c.prc.regInfo[i].kind in {slotTempUnknown,
+                                    slotTempInt,
+                                    slotTempFloat,
+                                    slotTempStr,
+                                    slotTempComplex}:
+            raiseAssert "leaking temporary " & $i & " " & $c.prc.regInfo[i].kind
       c.prc.regInfo[i] = (inUse: false, kind: slotEmpty)
 
   c.clearDest(n, dest)
@@ -405,13 +406,19 @@ proc genIf(c: PCtx, n: PNode; dest: var TDest) =
           c.gen(it[0], tmp)
           elsePos = c.xjmp(it[0], opcFJmp, tmp) # if false
       c.clearDest(n, dest)
-      c.gen(it[1], dest) # then part
+      if isEmptyType(it[1].typ): # maybe noreturn call, don't touch `dest`
+        c.gen(it[1])
+      else:
+        c.gen(it[1], dest) # then part
       if i < n.len-1:
         endings.add(c.xjmp(it[1], opcJmp, 0))
       c.patch(elsePos)
     else:
       c.clearDest(n, dest)
-      c.gen(it[0], dest)
+      if isEmptyType(it[0].typ): # maybe noreturn call, don't touch `dest`
+        c.gen(it[0])
+      else:
+        c.gen(it[0], dest)
   for endPos in endings: c.patch(endPos)
   c.clearDest(n, dest)
 
@@ -442,6 +449,7 @@ proc rawGenLiteral(c: PCtx; n: PNode): int =
   result = c.constants.len
   #assert(n.kind != nkCall)
   n.flags.incl nfAllConst
+  n.flags.excl nfIsRef
   c.constants.add n
   internalAssert c.config, result < regBxMax
 
@@ -504,17 +512,25 @@ proc genCase(c: PCtx; n: PNode; dest: var TDest) =
       let it = n[i]
       if it.len == 1:
         # else stmt:
-        if it[0].kind != nkNilLit or it[0].typ != nil:
+        let body = it[0]
+        if body.kind != nkNilLit or body.typ != nil:
           # an nkNilLit with nil for typ implies there is no else branch, this
           # avoids unused related errors as we've already consumed the dest
-          c.gen(it[0], dest)
+          if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+            c.gen(body)
+          else:
+            c.gen(body, dest)
       else:
         let b = rawGenLiteral(c, it)
         c.gABx(it, opcBranch, tmp, b)
-        let elsePos = c.xjmp(it.lastSon, opcFJmp, tmp)
-        c.gen(it.lastSon, dest)
+        let body = it.lastSon
+        let elsePos = c.xjmp(body, opcFJmp, tmp)
+        if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+          c.gen(body)
+        else:
+          c.gen(body, dest)
         if i < n.len-1:
-          endings.add(c.xjmp(it.lastSon, opcJmp, 0))
+          endings.add(c.xjmp(body, opcJmp, 0))
         c.patch(elsePos)
       c.clearDest(n, dest)
   for endPos in endings: c.patch(endPos)
@@ -530,7 +546,10 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
   if dest < 0 and not isEmptyType(n.typ): dest = getTemp(c, n.typ)
   var endings: seq[TPosition] = @[]
   let ehPos = c.xjmp(n, opcTry, 0)
-  c.gen(n[0], dest)
+  if isEmptyType(n[0].typ): # maybe noreturn call, don't touch `dest`
+    c.gen(n[0])
+  else:
+    c.gen(n[0], dest)
   c.clearDest(n, dest)
   # Add a jump past the exception handling code
   let jumpToFinally = c.xjmp(n, opcJmp, 0)
@@ -548,7 +567,11 @@ proc genTry(c: PCtx; n: PNode; dest: var TDest) =
       if it.len == 1:
         # general except section:
         c.gABx(it, opcExcept, 0, 0)
-      c.gen(it.lastSon, dest)
+      let body = it.lastSon
+      if isEmptyType(body.typ): # maybe noreturn call, don't touch `dest`
+        c.gen(body)
+      else:
+        c.gen(body, dest)
       c.clearDest(n, dest)
       if i < n.len:
         endings.add(c.xjmp(it, opcJmp, 0))
@@ -598,10 +621,17 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) =
   let fntyp = skipTypes(n[0].typ, abstractInst)
   for i in 0..<n.len:
     var r: TRegister = x+i
-    c.gen(n[i], r, {gfIsParam})
-    if i >= fntyp.len:
+    if i >= fntyp.signatureLen:
+      c.gen(n[i], r, {gfIsParam})
       internalAssert c.config, tfVarargs in fntyp.flags
       c.gABx(n, opcSetType, r, c.genType(n[i].typ))
+    else:
+      if fntyp[i] != nil and fntyp[i].kind == tySink and
+          fntyp[i].skipTypes({tySink}).kind in {tyObject, tyString, tySequence}:
+        c.gen(n[i], r, {gfIsSinkParam})
+      else:
+        c.gen(n[i], r, {gfIsParam})
+
   if dest < 0:
     c.gABC(n, opcIndCall, 0, x, n.len)
   else:
@@ -640,9 +670,19 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
 proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le[0], {gfNode})
-    let idx = c.genIndex(le[1], le[0].typ)
-    c.gABC(le, opcWrArr, dest, idx, value)
+    let
+      dest = c.genx(le[0], {gfNode})
+      idx = c.genIndex(le[1], le[0].typ)
+      collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
+
+    case collTyp.kind
+    of tyString, tyCstring:
+      c.gABC(le, opcWrStrIdx, dest, idx, value)
+    of tyTuple:
+      c.gABC(le, opcWrObj, dest, int le[1].intVal, value)
+    else:
+      c.gABC(le, opcWrArr, dest, idx, value)
+
     c.freeTemp(dest)
     c.freeTemp(idx)
   of nkCheckedFieldExpr:
@@ -665,6 +705,9 @@ proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
       let dest = c.genx(le, {gfNodeAddr})
       c.gABC(le, opcWrDeref, dest, 0, value)
       c.freeTemp(dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgnPatch(c, le[1], value)
   else:
     discard
 
@@ -818,10 +861,14 @@ proc genVarargsABC(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
 proc isInt8Lit(n: PNode): bool =
   if n.kind in {nkCharLit..nkUInt64Lit}:
     result = n.intVal >= low(int8) and n.intVal <= high(int8)
+  else:
+    result = false
 
 proc isInt16Lit(n: PNode): bool =
   if n.kind in {nkCharLit..nkUInt64Lit}:
     result = n.intVal >= low(int16) and n.intVal <= high(int16)
+  else:
+    result = false
 
 proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
   if n[2].isInt8Lit:
@@ -833,19 +880,21 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) =
     genBinaryABC(c, n, dest, opc)
   c.genNarrow(n, dest)
 
-proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
+proc genConv(c: PCtx; n, arg: PNode; dest: var TDest, flags: TGenFlags = {}; opc=opcConv) =
   let t2 = n.typ.skipTypes({tyDistinct})
   let targ2 = arg.typ.skipTypes({tyDistinct})
 
   proc implicitConv(): bool =
-    if sameType(t2, targ2): return true
+    if sameBackendType(t2, targ2): return true
     # xxx consider whether to use t2 and targ2 here
     if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc:
       # don't do anything for lambda lifting conversions:
-      return true
+      result = true
+    else:
+      result = false
 
   if implicitConv():
-    gen(c, arg, dest)
+    gen(c, arg, dest, flags)
     return
 
   let tmp = c.genx(arg)
@@ -863,31 +912,42 @@ proc genCard(c: PCtx; n: PNode; dest: var TDest) =
   c.freeTemp(tmp)
 
 proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
-  const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar}
-  var signedIntegers = {tyInt..tyInt64}
-  var unsignedIntegers = {tyUInt..tyUInt64, tyChar}
+  template isSigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) < 0 or
+    typ.kind in {tyInt..tyInt64}
+  template isUnsigned(typ: PType): bool {.dirty.} =
+    typ.kind == tyEnum and firstOrd(c.config, typ) >= 0 or
+    typ.kind in {tyUInt..tyUInt64, tyChar, tyBool}
+
+  const allowedIntegers = {tyInt..tyInt64, tyUInt..tyUInt64, tyChar, tyEnum, tyBool}
+
   let src = n[1].typ.skipTypes(abstractRange)#.kind
   let dst = n[0].typ.skipTypes(abstractRange)#.kind
   let srcSize = getSize(c.config, src)
   let dstSize = getSize(c.config, dst)
+  const unsupportedCastDifferentSize =
+    "VM does not support 'cast' from $1 with size $2 to $3 with size $4 due to different sizes"
   if src.kind in allowedIntegers and dst.kind in allowedIntegers:
     let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n[0].typ)
     c.gABC(n, opcAsgnInt, dest, tmp)
     if dstSize != sizeof(BiggestInt): # don't do anything on biggest int types
-      if dst.kind in signedIntegers: # we need to do sign extensions
+      if isSigned(dst): # we need to do sign extensions
         if dstSize <= srcSize:
           # Sign extension can be omitted when the size increases.
           c.gABC(n, opcSignExtend, dest, TRegister(dstSize*8))
-      elif dst.kind in unsignedIntegers:
-        if src.kind in signedIntegers or dstSize < srcSize:
+      elif isUnsigned(dst):
+        if isSigned(src) or dstSize < srcSize:
           # Cast from signed to unsigned always needs narrowing. Cast
           # from unsigned to unsigned only needs narrowing when target
           # is smaller than source.
           c.gABC(n, opcNarrowU, dest, TRegister(dstSize*8))
     c.freeTemp(tmp)
-  elif srcSize == dstSize and src.kind in allowedIntegers and
-                           dst.kind in {tyFloat, tyFloat32, tyFloat64}:
+  elif src.kind in allowedIntegers and
+      dst.kind in {tyFloat, tyFloat32, tyFloat64}:
+    if srcSize != dstSize:
+      globalError(c.config, n.info, unsupportedCastDifferentSize %
+        [$src.kind, $srcSize, $dst.kind, $dstSize])
     let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n[0].typ)
     if dst.kind == tyFloat32:
@@ -896,13 +956,16 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) =
       c.gABC(n, opcCastIntToFloat64, dest, tmp)
     c.freeTemp(tmp)
 
-  elif srcSize == dstSize and src.kind in {tyFloat, tyFloat32, tyFloat64} and
+  elif src.kind in {tyFloat, tyFloat32, tyFloat64} and
                            dst.kind in allowedIntegers:
+    if srcSize != dstSize:
+      globalError(c.config, n.info, unsupportedCastDifferentSize %
+        [$src.kind, $srcSize, $dst.kind, $dstSize])
     let tmp = c.genx(n[1])
     if dest < 0: dest = c.getTemp(n[0].typ)
     if src.kind == tyFloat32:
       c.gABC(n, opcCastFloatToInt32, dest, tmp)
-      if dst.kind in unsignedIntegers:
+      if isUnsigned(dst):
         # integers are sign extended by default.
         # since there is no opcCastFloatToUInt32, narrowing should do the trick.
         c.gABC(n, opcNarrowU, dest, TRegister(32))
@@ -999,7 +1062,7 @@ proc whichAsgnOpc(n: PNode; requiresCopy = true): TOpcode =
   else:
     (if requiresCopy: opcAsgnComplex else: opcFastAsgnComplex)
 
-proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
+proc genMagic(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}, m: TMagic) =
   case m
   of mAnd: c.genAndOr(n, opcFJmp, dest)
   of mOr:  c.genAndOr(n, opcTJmp, dest)
@@ -1050,10 +1113,22 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mLengthOpenArray, mLengthArray, mLengthSeq:
     genUnaryABI(c, n, dest, opcLenSeq)
   of mLengthStr:
-    case n[1].typ.kind
+    case n[1].typ.skipTypes(abstractVarRange).kind
     of tyString: genUnaryABI(c, n, dest, opcLenStr)
     of tyCstring: genUnaryABI(c, n, dest, opcLenCstring)
-    else: doAssert false, $n[1].typ.kind
+    else: raiseAssert $n[1].typ.kind
+  of mSlice:
+    var
+      d = c.genx(n[1])
+      left = c.genIndex(n[2], n[1].typ)
+      right = c.genIndex(n[3], n[1].typ)
+    if dest < 0: dest = c.getTemp(n.typ)
+    c.gABC(n, opcNodeToReg, dest, d)
+    c.gABC(n, opcSlice, dest, left, right)
+    c.freeTemp(left)
+    c.freeTemp(right)
+    c.freeTemp(d)
+
   of mIncl, mExcl:
     unused(c, n, dest)
     var d = c.genx(n[1])
@@ -1125,9 +1200,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     let size = getSize(c.config, t)
     if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8):
       c.gABC(n, opcNarrowU, dest, TRegister(size*8))
-  of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr:
-    genConv(c, n, n[1], dest)
-  of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr)
+  of mCharToStr, mBoolToStr, mCStrToStr, mStrToStr, mEnumToStr:
+    genConv(c, n, n[1], dest, flags)
+  of mEqStr: genBinaryABC(c, n, dest, opcEqStr)
+  of mEqCString: genBinaryABC(c, n, dest, opcEqCString)
   of mLeStr: genBinaryABC(c, n, dest, opcLeStr)
   of mLtStr: genBinaryABC(c, n, dest, opcLtStr)
   of mEqSet: genBinarySet(c, n, dest, opcEqSet)
@@ -1167,21 +1243,11 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
       d2 = c.genx(d2AsNode)
     var
       tmp1 = c.genx(n[1])
-      tmp3 = c.genx(n[3])
     c.gABC(n, opcParseFloat, dest, tmp1, d2)
-    c.gABC(n, opcParseFloat, tmp3)
     c.freeTemp(tmp1)
-    c.freeTemp(tmp3)
     c.genAsgnPatch(d2AsNode, d2)
     c.freeTemp(d2)
-  of mReset:
-    unused(c, n, dest)
-    var d = c.genx(n[1])
-    # XXX use ldNullOpcode() here?
-    c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
-    c.gABx(n, opcNodeToReg, d, d)
-    c.genAsgnPatch(n[1], d)
-  of mDefault:
+  of mDefault, mZeroDefault:
     if dest < 0: dest = c.getTemp(n.typ)
     c.gABx(n, ldNullOpcode(n.typ), dest, c.genType(n.typ))
   of mOf, mIs:
@@ -1294,9 +1360,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mNSetIdent:
     unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetIdent)
-  of mNSetType:
-    unused(c, n, dest)
-    genBinaryStmt(c, n, opcNSetType)
   of mNSetStrVal:
     unused(c, n, dest)
     genBinaryStmt(c, n, opcNSetStrVal)
@@ -1316,7 +1379,19 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     of "copyLineInfo":
       internalAssert c.config, n.len == 3
       unused(c, n, dest)
-      genBinaryStmt(c, n, opcNSetLineInfo)
+      genBinaryStmt(c, n, opcNCopyLineInfo)
+    of "setLine":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoLine)
+    of "setColumn":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoColumn)
+    of "setFile":
+      internalAssert c.config, n.len == 3
+      unused(c, n, dest)
+      genBinaryStmt(c, n, opcNSetLineInfoFile)
     else: internalAssert c.config, false
   of mNHint:
     unused(c, n, dest)
@@ -1360,6 +1435,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
   of mRunnableExamples:
     discard "just ignore any call to runnableExamples"
   of mDestroy, mTrace: discard "ignore calls to the default destructor"
+  of mEnsureMove:
+    gen(c, n[1], dest)
   of mMove:
     let arg = n[1]
     let a = c.genx(arg)
@@ -1371,6 +1448,12 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
     # c.gABx(n, opcNodeToReg, a, a)
     # c.genAsgnPatch(arg, a)
     c.freeTemp(a)
+  of mDup:
+    let arg = n[1]
+    let a = c.genx(arg)
+    if dest < 0: dest = c.getTemp(arg.typ)
+    gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a)
+    c.freeTemp(a)
   of mNodeId:
     c.genUnaryABC(n, dest, opcNodeId)
   else:
@@ -1380,7 +1463,8 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
 proc unneededIndirection(n: PNode): bool =
   n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef
 
-proc canElimAddr(n: PNode): PNode =
+proc canElimAddr(n: PNode; idgen: IdGenerator): PNode =
+  result = nil
   case n[0].kind
   of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
     var m = n[0][0]
@@ -1388,19 +1472,28 @@ proc canElimAddr(n: PNode): PNode =
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       result = copyNode(n[0])
       result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     var m = n[0][1]
     if m.kind in {nkDerefExpr, nkHiddenDeref}:
       # addr ( nkConv ( deref ( x ) ) ) --> nkConv(x)
       result = copyNode(n[0])
+      result.add n[0][0]
       result.add m[0]
+      if n.typ.skipTypes(abstractVar).kind != tyOpenArray:
+        result.typ = n.typ
+      elif n.typ.skipTypes(abstractInst).kind in {tyVar}:
+        result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, idgen)
   else:
     if n[0].kind in {nkDerefExpr, nkHiddenDeref}:
       # addr ( deref ( x )) --> x
       result = n[0][0]
 
 proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) =
-  if (let m = canElimAddr(n); m != nil):
+  if (let m = canElimAddr(n, c.idgen); m != nil):
     gen(c, m, dest, flags)
     return
 
@@ -1448,11 +1541,16 @@ proc setSlot(c: PCtx; v: PSym) =
   if v.position == 0:
     v.position = getFreeRegister(c, if v.kind == skLet: slotFixedLet else: slotFixedVar, start = 1)
 
-proc cannotEval(c: PCtx; n: PNode) {.noinline.} =
+template cannotEval(c: PCtx; n: PNode) =
+  if c.config.cmd == cmdCheck:
+    localError(c.config, n.info, "cannot evaluate at compile time: " & 
+    n.renderTree)
+    return
   globalError(c.config, n.info, "cannot evaluate at compile time: " &
     n.renderTree)
 
 proc isOwnedBy(a, b: PSym): bool =
+  result = false
   var a = a.owner
   while a != nil and a.kind != skModule:
     if a == b: return true
@@ -1465,7 +1563,9 @@ proc getOwner(c: PCtx): PSym =
 proc importcCondVar*(s: PSym): bool {.inline.} =
   # see also importcCond
   if sfImportc in s.flags:
-    return s.kind in {skVar, skLet, skConst}
+    result = s.kind in {skVar, skLet, skConst}
+  else:
+    result = false
 
 proc checkCanEval(c: PCtx; n: PNode) =
   # we need to ensure that we don't evaluate 'x' here:
@@ -1478,6 +1578,7 @@ proc checkCanEval(c: PCtx; n: PNode) =
     # little hack ahead for bug #12612: assume gensym'ed variables
     # are in the right scope:
     if sfGenSym in s.flags and c.prc.sym == nil: discard
+    elif s.kind == skParam and s.typ.kind == tyTypeDesc: discard
     else: cannotEval(c, n)
   elif s.kind in {skProc, skFunc, skConverter, skMethod,
                   skIterator} and sfForward in s.flags:
@@ -1507,12 +1608,16 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
 proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
   case le.kind
   of nkBracketExpr:
-    let dest = c.genx(le[0], {gfNode})
-    let idx = c.genIndex(le[1], le[0].typ)
-    let tmp = c.genx(ri)
-    if le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
-        tyString, tyCstring}:
+    let
+      dest = c.genx(le[0], {gfNode})
+      idx = c.genIndex(le[1], le[0].typ)
+      tmp = c.genx(ri)
+      collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
+    case collTyp.kind
+    of tyString, tyCstring:
       c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
+    of tyTuple:
+      c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp)
     else:
       c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
     c.freeTemp(tmp)
@@ -1563,6 +1668,9 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    if sameBackendType(le.typ, le[1].typ):
+      genAsgn(c, le[1], ri, requiresCopy)
   else:
     let dest = c.genx(le, {gfNodeAddr})
     genAsgn(c, dest, ri, requiresCopy)
@@ -1573,11 +1681,21 @@ proc genTypeLit(c: PCtx; t: PType; dest: var TDest) =
   n.typ = t
   genLit(c, n, dest)
 
+proc isEmptyBody(n: PNode): bool =
+  case n.kind
+  of nkStmtList:
+    for i in 0..<n.len:
+      if not isEmptyBody(n[i]): return false
+    result = true
+  else:
+    result = n.kind in {nkCommentStmt, nkEmpty}
+
 proc importcCond*(c: PCtx; s: PSym): bool {.inline.} =
   ## return true to importc `s`, false to execute its body instead (refs #8405)
+  result = false
   if sfImportc in s.flags:
     if s.kind in routineKinds:
-      return getBody(c.graph, s).kind == nkEmpty
+      return isEmptyBody(getBody(c.graph, s))
 
 proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
   when hasFFI:
@@ -1591,10 +1709,10 @@ proc importcSym(c: PCtx; info: TLineInfo; s: PSym) =
     localError(c.config, info,
                "cannot 'importc' variable at compile time; " & s.name.s)
 
-proc getNullValue*(typ: PType, info: TLineInfo; conf: ConfigRef): PNode
+proc getNullValue*(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode
 
 proc genGlobalInit(c: PCtx; n: PNode; s: PSym) =
-  c.globals.add(getNullValue(s.typ, n.info, c.config))
+  c.globals.add(getNullValue(c, 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:
@@ -1630,6 +1748,8 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
         c.gABx(n, opcLdGlobalAddr, dest, s.position)
     elif isImportcVar:
       c.gABx(n, opcLdGlobalDerefFFI, dest, s.position)
+    elif gfIsSinkParam in flags:
+      genAsgn(c, dest, n, requiresCopy = true)
     elif fitsRegister(s.typ) and gfNode notin flags:
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
@@ -1643,7 +1763,7 @@ 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.config, c.prc.regInfo[dest].kind < slotSomeTemp)
+        internalAssert(c.config, c.prc.regInfo.len > dest and c.prc.regInfo[dest].kind < slotSomeTemp)
       else:
         # we need to generate an assignment:
         let requiresCopy = c.prc.regInfo[dest].kind >= slotSomeTemp and
@@ -1676,9 +1796,7 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
   c.freeTemp(a)
   c.freeTemp(b)
 
-proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
-  let a = c.genx(n[0], flags)
-  let b = genField(c, n[1])
+proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) =
   if dest < 0: dest = c.getTemp(n.typ)
   if {gfNodeAddr} * flags != {}:
     c.gABC(n, opcLdObjAddr, dest, a, b)
@@ -1691,6 +1809,11 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
     c.gABC(n, opcLdObj, dest, a, b)
   c.freeTemp(a)
 
+proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
+  genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags)
+
+
+
 proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   internalAssert c.config, n.kind == nkCheckedFieldExpr
   # nkDotExpr to access the requested field
@@ -1715,7 +1838,6 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
   let setLit = c.genx(checkExpr[1])
   var rs = c.getTemp(getSysType(c.graph, n.info, tyBool))
   c.gABC(n, opcContainsSet, rs, setLit, discVal)
-  c.freeTemp(discVal)
   c.freeTemp(setLit)
   # If the check fails let the user know
   let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs)
@@ -1728,6 +1850,7 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
   strLit.typ = strType
   c.genLit(strLit, msgReg)
   c.gABC(n, opcInvalidField, msgReg, discVal)
+  c.freeTemp(discVal)
   c.freeTemp(msgReg)
   c.patch(lab1)
 
@@ -1758,36 +1881,41 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
 
 proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
   let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
-  if arrayType in {tyString, tyCstring}:
+  case arrayType
+  of tyString, tyCstring:
     let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx
     genArrAccessOpcode(c, n, dest, opc, flags)
-  elif arrayType == tyTypeDesc:
+  of tyTuple:
+    c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags)
+  of tyTypeDesc:
     c.genTypeLit(n.typ, dest)
   else:
     let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
     genArrAccessOpcode(c, n, dest, opc, flags)
 
-proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
-  if t != nil and t.len > 0 and t[0] != nil:
-    let b = skipTypes(t[0], skipPtrs)
-    getNullValueAux(b, b.n, result, conf, currPosition)
+proc getNullValueAux(c: PCtx; t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) =
+  if t != nil and t.baseClass != nil:
+    let b = skipTypes(t.baseClass, skipPtrs)
+    getNullValueAux(c, b, b.n, result, conf, currPosition)
   case obj.kind
   of nkRecList:
-    for i in 0..<obj.len: getNullValueAux(nil, obj[i], result, conf, currPosition)
+    for i in 0..<obj.len: getNullValueAux(c, nil, obj[i], result, conf, currPosition)
   of nkRecCase:
-    getNullValueAux(nil, obj[0], result, conf, currPosition)
+    getNullValueAux(c, nil, obj[0], result, conf, currPosition)
     for i in 1..<obj.len:
-      getNullValueAux(nil, lastSon(obj[i]), result, conf, currPosition)
+      getNullValueAux(c, nil, lastSon(obj[i]), result, conf, currPosition)
   of nkSym:
     let field = newNodeI(nkExprColonExpr, result.info)
     field.add(obj)
-    field.add(getNullValue(obj.sym.typ, result.info, conf))
+    let value = getNullValue(c, obj.sym.typ, result.info, conf)
+    value.flags.incl nfSkipFieldChecking
+    field.add(value)
     result.add field
     doAssert obj.sym.position == currPosition
     inc currPosition
   else: globalError(conf, result.info, "cannot create null element for: " & $obj)
 
-proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
+proc getNullValue(c: PCtx; typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
   var t = skipTypes(typ, abstractRange+{tyStatic, tyOwned}-{tyTypeDesc})
   case t.kind
   of tyBool, tyEnum, tyChar, tyInt..tyInt64:
@@ -1796,10 +1924,10 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
     result = newNodeIT(nkUIntLit, info, t)
   of tyFloat..tyFloat128:
     result = newNodeIT(nkFloatLit, info, t)
-  of tyCstring, tyString:
+  of tyString:
     result = newNodeIT(nkStrLit, info, t)
     result.strVal = ""
-  of tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
+  of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped,
      tyTyped, tyTypeDesc, tyRef, tyNil:
     result = newNodeIT(nkNilLit, info, t)
   of tyProc:
@@ -1807,22 +1935,22 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode =
       result = newNodeIT(nkNilLit, info, t)
     else:
       result = newNodeIT(nkTupleConstr, info, t)
-      result.add(newNodeIT(nkNilLit, info, t))
-      result.add(newNodeIT(nkNilLit, info, t))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
+      result.add(newNodeIT(nkNilLit, info, getSysType(c.graph, info, tyPointer)))
   of tyObject:
     result = newNodeIT(nkObjConstr, info, t)
     result.add(newNodeIT(nkEmpty, info, t))
     # initialize inherited fields, and all in the correct order:
     var currPosition = 0
-    getNullValueAux(t, t.n, result, conf, currPosition)
+    getNullValueAux(c, t, t.n, result, conf, currPosition)
   of tyArray:
     result = newNodeIT(nkBracket, info, t)
     for i in 0..<toInt(lengthOrd(conf, t)):
-      result.add getNullValue(elemType(t), info, conf)
+      result.add getNullValue(c, elemType(t), info, conf)
   of tyTuple:
     result = newNodeIT(nkTupleConstr, info, t)
-    for i in 0..<t.len:
-      result.add getNullValue(t[i], info, conf)
+    for a in t.kids:
+      result.add getNullValue(c, a, info, conf)
   of tySet:
     result = newNodeIT(nkCurly, info, t)
   of tySequence, tyOpenArray:
@@ -1845,21 +1973,38 @@ proc genVarSection(c: PCtx; n: PNode) =
       let s = a[0].sym
       checkCanEval(c, a[0])
       if s.isGlobal:
+        let runtimeAccessToCompileTime = c.mode == emRepl and
+              sfCompileTime in s.flags and s.position > 0
         if s.position == 0:
           if importcCond(c, s): c.importcSym(a.info, s)
           else:
-            let sa = getNullValue(s.typ, a.info, c.config)
+            let sa = getNullValue(c, s.typ, a.info, c.config)
             #if s.ast.isNil: getNullValue(s.typ, a.info)
             #else: s.ast
             assert sa.kind != nkCall
             c.globals.add(sa)
             s.position = c.globals.len
-        if a[2].kind != nkEmpty:
+        if runtimeAccessToCompileTime:
+          discard
+        elif a[2].kind != nkEmpty:
           let tmp = c.genx(a[0], {gfNodeAddr})
           let val = c.genx(a[2])
           c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
+        elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and
+                sfPure notin s.flags: # fixes #10938
+          # there is a pre-existing issue with closure types in VM
+          # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you;
+          # you might remove the second condition.
+          # the problem is that closure types are tuples in VM, but the types of its children
+          # shouldn't have the same type as closure types.
+          let tmp = c.genx(a[0], {gfNodeAddr})
+          let sa = getNullValue(c, s.typ, a.info, c.config)
+          let val = c.genx(sa)
+          c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val)
+          c.freeTemp(val)
+          c.freeTemp(tmp)
       else:
         setSlot(c, s)
         if a[2].kind == nkEmpty:
@@ -1925,10 +2070,12 @@ proc genSetConstr(c: PCtx, n: PNode, dest: var TDest) =
       c.freeTemp(a)
 
 proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) =
+  if tfUnion in n.typ.flags: # bug #22708 # bug #13481
+    globalError(c.config, n.info, "object with '{.union.}' pragmas is not supported by VM")
   if dest < 0: dest = c.getTemp(n.typ)
   let t = n.typ.skipTypes(abstractRange+{tyOwned}-{tyTypeDesc})
   if t.kind == tyRef:
-    c.gABx(n, opcNew, dest, c.genType(t[0]))
+    c.gABx(n, opcNew, dest, c.genType(t.elementType))
   else:
     c.gABx(n, opcLdNull, dest, c.genType(n.typ))
   for i in 1..<n.len:
@@ -1962,25 +2109,30 @@ proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) =
 
 proc genProc*(c: PCtx; s: PSym): int
 
-proc matches(s: PSym; x: string): bool =
-  let y = x.split('.')
+proc toKey(s: PSym): string =
+  result = ""
   var s = s
-  for i in 1..y.len:
-    if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"):
-      return false
-    s = if sfFromGeneric in s.flags: s.owner.owner else: s.owner
-    while s != nil and s.kind == skPackage and s.owner != nil: s = s.owner
-  result = true
+  while s != nil:
+    result.add s.name.s
+    if s.owner != nil:
+      if sfFromGeneric in s.flags:
+        s = s.instantiatedFrom.owner
+      else:
+        s = s.owner
+      result.add "."
+    else:
+      break
 
 proc procIsCallback(c: PCtx; s: PSym): bool =
   if s.offset < -1: return true
-  var i = -2
-  for key, value in items(c.callbacks):
-    if s.matches(key):
-      doAssert s.offset == -1
-      s.offset = i
-      return true
-    dec i
+  let key = toKey(s)
+  if c.callbackIndex.contains(key):
+    let index = c.callbackIndex[key]
+    doAssert s.offset == -1
+    s.offset = -2'i32 - index.int32
+    result = true
+  else:
+    result = false
 
 proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   when defined(nimCompilerStacktraceHints):
@@ -1990,8 +2142,13 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     let s = n.sym
     checkCanEval(c, n)
     case s.kind
-    of skVar, skForVar, skTemp, skLet, skParam, skResult:
+    of skVar, skForVar, skTemp, skLet, skResult:
       genRdVar(c, n, dest, flags)
+    of skParam:
+      if s.typ.kind == tyTypeDesc:
+        genTypeLit(c, s.typ.skipTypes({tyTypeDesc}), dest)
+      else:
+        genRdVar(c, n, dest, flags)
     of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator:
       # 'skTemplate' is only allowed for 'getAst' support:
       if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure:
@@ -2000,8 +2157,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
       elif importcCond(c, s): c.importcSym(n.info, s)
       genLit(c, n, dest)
     of skConst:
-      let constVal = if s.ast != nil: s.ast else: s.typ.n
-      gen(c, constVal, dest)
+      let constVal = if s.astdef != nil: s.astdef else: s.typ.n
+      if dontInlineConstant(n, constVal):
+        genLit(c, constVal, dest)
+      else:
+        gen(c, constVal, dest)
     of skEnumField:
       # we never reach this case - as of the time of this comment,
       # skEnumField is folded to an int in semfold.nim, but this code
@@ -2025,7 +2185,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     if n[0].kind == nkSym:
       let s = n[0].sym
       if s.magic != mNone:
-        genMagic(c, n, dest, s.magic)
+        genMagic(c, n, dest, flags, s.magic)
       elif s.kind == skMethod:
         localError(c.config, n.info, "cannot call method " & s.name.s &
           " at compile time")
@@ -2043,9 +2203,9 @@ 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, c.config), dest)
+    if not n.typ.isEmptyType: genLit(c, getNullValue(c, n.typ, n.info, c.config), dest)
     else: unused(c, n, dest)
-  of nkAsgn, nkFastAsgn:
+  of nkAsgn, nkFastAsgn, nkSinkAsgn:
     unused(c, n, dest)
     genAsgn(c, n[0], n[1], n.kind == nkAsgn)
   of nkDotExpr: genObjAccess(c, n, dest, flags)
@@ -2082,36 +2242,37 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     unused(c, n, dest)
     gen(c, n[0])
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
-    genConv(c, n, n[1], dest)
+    genConv(c, n, n[1], dest, flags)
   of nkObjDownConv:
-    genConv(c, n, n[0], dest)
+    genConv(c, n, n[0], dest, flags)
   of nkObjUpConv:
-    genConv(c, n, n[0], dest)
+    genConv(c, n, n[0], dest, flags)
   of nkVarSection, nkLetSection:
     unused(c, n, dest)
     genVarSection(c, n)
-  of declarativeDefs, nkMacroDef:
-    unused(c, n, dest)
   of nkLambdaKinds:
     #let s = n[namePos].sym
     #discard genProc(c, s)
     genLit(c, newSymNode(n[namePos].sym), dest)
   of nkChckRangeF, nkChckRange64, nkChckRange:
-    let
-      tmp0 = c.genx(n[0])
-      tmp1 = c.genx(n[1])
-      tmp2 = c.genx(n[2])
-    c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
-    c.freeTemp(tmp1)
-    c.freeTemp(tmp2)
-    if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0)
-      c.freeTemp(tmp0)
+    if skipTypes(n.typ, abstractVar).kind in {tyUInt..tyUInt64}:
+      genConv(c, n, n[0], dest, flags)
     else:
-      dest = tmp0
+      let
+        tmp0 = c.genx(n[0])
+        tmp1 = c.genx(n[1])
+        tmp2 = c.genx(n[2])
+      c.gABC(n, opcRangeChck, tmp0, tmp1, tmp2)
+      c.freeTemp(tmp1)
+      c.freeTemp(tmp2)
+      if dest >= 0:
+        gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+        c.freeTemp(tmp0)
+      else:
+        dest = tmp0
   of nkEmpty, nkCommentStmt, nkTypeSection, nkConstSection, nkPragma,
      nkTemplateDef, nkIncludeStmt, nkImportStmt, nkFromStmt, nkExportStmt,
-     nkMixinStmt, nkBindStmt:
+     nkMixinStmt, nkBindStmt, declarativeDefs, nkMacroDef:
     unused(c, n, dest)
   of nkStringToCString, nkCStringToString:
     gen(c, n[0], dest)
@@ -2121,7 +2282,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
   of nkPar, nkClosure, nkTupleConstr: genTupleConstr(c, n, dest)
   of nkCast:
     if allowCast in c.features:
-      genConv(c, n, n[1], dest, opcCast)
+      genConv(c, n, n[1], dest, flags, opcCast)
     else:
       genCastIntFloat(c, n, dest)
   of nkTypeOfExpr:
@@ -2242,7 +2403,7 @@ proc genProc(c: PCtx; s: PSym): int =
     #if s.name.s == "outterMacro" or s.name.s == "innerProc":
     #  echo "GENERATING CODE FOR ", s.name.s
     let last = c.code.len-1
-    var eofInstr: TInstr
+    var eofInstr: TInstr = default(TInstr)
     if last >= 0 and c.code[last].opcode == opcEof:
       eofInstr = c.code[last]
       c.code.setLen(last)
@@ -2252,7 +2413,7 @@ proc genProc(c: PCtx; s: PSym): int =
     c.procToCodePos[s.id] = result
     # thanks to the jmp we can add top level statements easily and also nest
     # procs easily:
-    let body = transformBody(c.graph, c.idgen, s, cache = not isCompileTimeProc(s))
+    let body = transformBody(c.graph, c.idgen, s, if isCompileTimeProc(s): {} else: {useCache})
     let procStart = c.xjmp(body, opcJmp, 0)
     var p = PProc(blocks: @[], sym: s)
     let oldPrc = c.prc
@@ -2274,7 +2435,7 @@ proc genProc(c: PCtx; s: PSym): int =
     c.patch(procStart)
     c.gABC(body, opcEof, eofInstr.regA)
     c.optimizeJumps(result)
-    s.offset = c.prc.regInfo.len
+    s.offset = c.prc.regInfo.len.int32
     #if s.name.s == "main" or s.name.s == "[]":
     #  echo renderTree(body)
     #  c.echoCode(result)
diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim
index 84ecf586f..2d7ad63e7 100644
--- a/compiler/vmhooks.nim
+++ b/compiler/vmhooks.nim
@@ -39,6 +39,13 @@ proc setResult*(a: VmArgs; v: seq[string]) =
   for x in v: n.add newStrNode(nkStrLit, x)
   a.slots[a.ra].node = n
 
+proc setResult*(a: VmArgs; v: (BiggestInt, BiggestInt)) =
+  a.slots[a.ra].ensureKind(rkNode)
+  var tuplen = newNode(nkTupleConstr)
+  tuplen.add newIntNode(nkIntLit, v[0])
+  tuplen.add newIntNode(nkIntLit, v[1]) 
+  a.slots[a.ra].node = tuplen
+
 template getReg(a, i): untyped =
   doAssert i < a.rc-1
   a.slots[i+a.rb+1].unsafeAddr
@@ -62,7 +69,7 @@ proc getVar*(a: VmArgs; i: Natural): PNode =
   case p.kind
   of rkRegisterAddr: result = p.regAddr.node
   of rkNodeAddr: result = p.nodeAddr[]
-  else: doAssert false, $p.kind
+  else: raiseAssert $p.kind
 
 proc getNodeAddr*(a: VmArgs; i: Natural): PNode =
   let nodeAddr = getX(rkNodeAddr, nodeAddr)
diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim
index e44bc0bea..0e67ededa 100644
--- a/compiler/vmmarshal.nim
+++ b/compiler/vmmarshal.nim
@@ -9,9 +9,11 @@
 
 ## Implements marshaling for the VM.
 
-import streams, json, intsets, tables, ast, astalgo, idents, types, msgs,
+import ast, astalgo, idents, types, msgs,
   options, lineinfos
 
+import std/[streams, json, intsets, tables]
+
 when defined(nimPreviewSlimSystem):
   import std/[assertions, formatfloat]
 
@@ -21,6 +23,7 @@ proc ptrToInt(x: PNode): int {.inline.} =
 proc getField(n: PNode; position: int): PSym =
   case n.kind
   of nkRecList:
+    result = nil
     for i in 0..<n.len:
       result = getField(n[i], position)
       if result != nil: return
@@ -35,7 +38,8 @@ proc getField(n: PNode; position: int): PSym =
       else: discard
   of nkSym:
     if n.sym.position == position: result = n.sym
-  else: discard
+    else: result = nil
+  else: result = nil
 
 proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; conf: ConfigRef)
 
@@ -78,11 +82,11 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet;
       s.add("]")
   of tyTuple:
     s.add("{")
-    for i in 0..<t.len:
+    for i, ti in t.ikids:
       if i > 0: s.add(", ")
       s.add("\"Field" & $i)
       s.add("\": ")
-      storeAny(s, t[i], a[i].skipColon, stored, conf)
+      storeAny(s, ti, a[i].skipColon, stored, conf)
     s.add("}")
   of tyObject:
     s.add("{")
@@ -94,17 +98,17 @@ 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, conf)
+        storeAny(s, t.elementType, x, stored, conf)
         inc x.intVal
         while x.intVal <= a[i][1].intVal:
           s.add(", ")
-          storeAny(s, t.lastSon, x, stored, conf)
+          storeAny(s, t.elementType, x, stored, conf)
           inc x.intVal
       else:
-        storeAny(s, t.lastSon, a[i], stored, conf)
+        storeAny(s, t.elementType, a[i], stored, conf)
     s.add("]")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    storeAny(s, t.lastSon, a, stored, conf)
+    storeAny(s, t.skipModifier, a, stored, conf)
   of tyEnum:
     # we need a slow linear search because of enums with holes:
     for e in items(t.n):
@@ -123,7 +127,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, conf)
+      storeAny(s, t.elementType, a, stored, conf)
       s.add("]")
   of tyString, tyCstring:
     if a.kind == nkNilLit: s.add("null")
@@ -143,7 +147,9 @@ proc loadAny(p: var JsonParser, t: PType,
              conf: ConfigRef;
              idgen: IdGenerator): PNode =
   case t.kind
-  of tyNone: assert false
+  of tyNone:
+    result = nil
+    assert false
   of tyBool:
     case p.kind
     of jsonFalse: result = newIntNode(nkIntLit, 0)
@@ -153,6 +159,7 @@ proc loadAny(p: var JsonParser, t: PType,
   of tyChar:
     if p.kind == jsonString:
       var x = p.str
+      result = nil
       if x.len == 1:
         result = newIntNode(nkIntLit, ord(x[0]))
         next(p)
@@ -161,8 +168,11 @@ proc loadAny(p: var JsonParser, t: PType,
       result = newIntNode(nkIntLit, getInt(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "string of length 1 expected for a char")
   of tyEnum:
+    result = nil
     if p.kind == jsonString:
       for e in items(t.n):
         if e.sym.name.s == p.str:
@@ -191,17 +201,19 @@ proc loadAny(p: var JsonParser, t: PType,
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "")
     else:
+      result = nil
       raiseParseErr(p, "'[' expected for a seq")
   of tyTuple:
     if p.kind != jsonObjectStart: raiseParseErr(p, "'{' expected for an object")
     next(p)
     result = newNode(nkTupleConstr)
     var i = 0
+    let tupleLen = t.kidsLen
     while p.kind != jsonObjectEnd and p.kind != jsonEof:
       if p.kind != jsonString:
         raiseParseErr(p, "string expected for a field name")
       next(p)
-      if i >= t.len:
+      if i >= tupleLen:
         raiseParseErr(p, "too many fields to tuple type " & typeToString(t))
       result.add loadAny(p, t[i], tab, cache, conf, idgen)
       inc i
@@ -224,7 +236,7 @@ proc loadAny(p: var JsonParser, t: PType,
       if pos >= result.len:
         setLen(result.sons, pos + 1)
       let fieldNode = newNode(nkExprColonExpr)
-      fieldNode.add newSymNode(newSym(skField, ident, nextSymId(idgen), nil, unknownLineInfo))
+      fieldNode.add newSymNode(newSym(skField, ident, idgen, nil, unknownLineInfo))
       fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen)
       result[pos] = fieldNode
     if p.kind == jsonObjectEnd: next(p)
@@ -234,7 +246,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, cache, conf, idgen)
+      result.add loadAny(p, t.elementType, tab, cache, conf, idgen)
     if p.kind == jsonArrayEnd: next(p)
     else: raiseParseErr(p, "']' end of array expected")
   of tyPtr, tyRef:
@@ -248,16 +260,19 @@ proc loadAny(p: var JsonParser, t: PType,
         raiseParseErr(p, "cannot load object with address " & $p.getInt)
       next(p)
     of jsonArrayStart:
+      result = nil
       next(p)
       if p.kind == jsonInt:
         let idx = p.getInt
         next(p)
-        result = loadAny(p, t.lastSon, tab, cache, conf, idgen)
+        result = loadAny(p, t.elementType, tab, cache, conf, idgen)
         tab[idx] = result
       else: raiseParseErr(p, "index for ref type expected")
       if p.kind == jsonArrayEnd: next(p)
       else: raiseParseErr(p, "']' end of ref-address pair expected")
-    else: raiseParseErr(p, "int for pointer type expected")
+    else:
+      result = nil
+      raiseParseErr(p, "int for pointer type expected")
   of tyString, tyCstring:
     case p.kind
     of jsonNull:
@@ -266,27 +281,34 @@ proc loadAny(p: var JsonParser, t: PType,
     of jsonString:
       result = newStrNode(nkStrLit, p.str)
       next(p)
-    else: raiseParseErr(p, "string expected")
+    else:
+      result = nil
+      raiseParseErr(p, "string expected")
   of tyInt..tyInt64, tyUInt..tyUInt64:
     if p.kind == jsonInt:
       result = newIntNode(nkIntLit, getInt(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "int expected")
   of tyFloat..tyFloat128:
     if p.kind == jsonFloat:
       result = newFloatNode(nkFloatLit, getFloat(p))
       next(p)
       return
+    else:
+      result = nil
     raiseParseErr(p, "float expected")
   of tyRange, tyGenericInst, tyAlias, tySink:
-    result = loadAny(p, t.lastSon, tab, cache, conf, idgen)
+    result = loadAny(p, t.skipModifier, tab, cache, conf, idgen)
   else:
+    result = nil
     internalError conf, "cannot marshal at compile-time " & t.typeToString
 
 proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode =
   var tab = initTable[BiggestInt, PNode]()
-  var p: JsonParser
+  var p: JsonParser = default(JsonParser)
   open(p, newStringStream(s), "unknown file")
   next(p)
   result = loadAny(p, t, tab, cache, conf, idgen)
diff --git a/compiler/vmops.nim b/compiler/vmops.nim
index fef76940e..45194e633 100644
--- a/compiler/vmops.nim
+++ b/compiler/vmops.nim
@@ -12,7 +12,7 @@
 from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin,
   arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc,
   floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma,
-  lgamma
+  lgamma, divmod
 from std/sequtils import toSeq
 when declared(math.copySign):
   # pending bug #18762, avoid renaming math
@@ -22,8 +22,12 @@ when declared(math.signbit):
   # ditto
   from std/math as math3 import signbit
 
-from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs,
-  dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError
+
+from std/envvars import getEnv, existsEnv, delEnv, putEnv, envPairs
+from std/os import getAppFilename
+from std/private/oscommon import dirExists, fileExists
+from std/private/osdirs import walkDir, createDir
+from std/private/ospaths2 import getCurrentDir
 
 from std/times import cpuTime
 from std/hashes import hash
@@ -33,7 +37,8 @@ from std/osproc import nil
 when defined(nimPreviewSlimSystem):
   import std/syncio
 else:
-  from std/formatfloat import addFloatRoundtrip, addFloatSprintf 
+  from std/formatfloat import addFloatRoundtrip, addFloatSprintf
+
 
 # There are some useful procs in vmconv.
 import vmconv, vmmarshal
@@ -44,6 +49,15 @@ template mathop(op) {.dirty.} =
 template osop(op) {.dirty.} =
   registerCallback(c, "stdlib.os." & astToStr(op), `op Wrapper`)
 
+template oscommonop(op) {.dirty.} =
+  registerCallback(c, "stdlib.oscommon." & astToStr(op), `op Wrapper`)
+
+template osdirsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.osdirs." & astToStr(op), `op Wrapper`)
+
+template envvarsop(op) {.dirty.} =
+  registerCallback(c, "stdlib.envvars." & astToStr(op), `op Wrapper`)
+
 template timesop(op) {.dirty.} =
   registerCallback(c, "stdlib.times." & astToStr(op), `op Wrapper`)
 
@@ -67,6 +81,11 @@ template wrap2fMath(op) {.dirty.} =
     setResult(a, op(getFloat(a, 0), getFloat(a, 1)))
   mathop op
 
+template wrap2iMath(op) {.dirty.} =
+  proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+    setResult(a, op(getInt(a, 0), getInt(a, 1)))
+  mathop op
+
 template wrap0(op, modop) {.dirty.} =
   proc `op Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, op())
@@ -97,7 +116,17 @@ template wrap2svoid(op, modop) {.dirty.} =
     op(getString(a, 0), getString(a, 1))
   modop op
 
-template wrapDangerous(op, modop) {.dirty.} =
+template wrapDangerous1svoid(op, modop) {.dirty.} =
+  if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      discard
+    modop op
+  else:
+    proc `op Wrapper`(a: VmArgs) {.nimcall.} =
+      op(getString(a, 0))
+    modop op
+
+template wrapDangerous2svoid(op, modop) {.dirty.} =
   if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck):
     proc `op Wrapper`(a: VmArgs) {.nimcall.} =
       discard
@@ -119,39 +148,41 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode =
   for k, f in walkDir(path, relative):
     result.add toLit((k, f))
 
-when defined(nimHasInvariant):
-  from std / compilesettings import SingleValueSetting, MultipleValueSetting
-
-  proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
-    case SingleValueSetting(switch)
-    of arguments: result = conf.arguments
-    of outFile: result = conf.outFile.string
-    of outDir: result = conf.outDir.string
-    of nimcacheDir: result = conf.getNimcacheDir().string
-    of projectName: result = conf.projectName
-    of projectPath: result = conf.projectPath.string
-    of projectFull: result = conf.projectFull.string
-    of command: result = conf.command
-    of commandLine: result = conf.commandLine
-    of linkOptions: result = conf.linkOptions
-    of compileOptions: result = conf.compileOptions
-    of ccompilerPath: result = conf.cCompilerPath
-    of backend: result = $conf.backend
-    of libPath: result = conf.libpath.string
-    of gc: result = $conf.selectedGC
-    of mm: result = $conf.selectedGC
-
-  proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
-    template copySeq(field: untyped): untyped =
-      for i in field: result.add i.string
-
-    case MultipleValueSetting(switch)
-    of nimblePaths: copySeq(conf.nimblePaths)
-    of searchPaths: copySeq(conf.searchPaths)
-    of lazyPaths: copySeq(conf.lazyPaths)
-    of commandArgs: result = conf.commandArgs
-    of cincludes: copySeq(conf.cIncludes)
-    of clibs: copySeq(conf.cLibs)
+from std / compilesettings import SingleValueSetting, MultipleValueSetting
+
+proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string =
+  {.push warning[Deprecated]:off.}
+  case SingleValueSetting(switch)
+  of arguments: result = conf.arguments
+  of outFile: result = conf.outFile.string
+  of outDir: result = conf.outDir.string
+  of nimcacheDir: result = conf.getNimcacheDir().string
+  of projectName: result = conf.projectName
+  of projectPath: result = conf.projectPath.string
+  of projectFull: result = conf.projectFull.string
+  of command: result = conf.command
+  of commandLine: result = conf.commandLine
+  of linkOptions: result = conf.linkOptions
+  of compileOptions: result = conf.compileOptions
+  of ccompilerPath: result = conf.cCompilerPath
+  of backend: result = $conf.backend
+  of libPath: result = conf.libpath.string
+  of gc: result = $conf.selectedGC
+  of mm: result = $conf.selectedGC
+  {.pop.}
+
+proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] =
+  template copySeq(field: untyped): untyped =
+    result = @[]
+    for i in field: result.add i.string
+
+  case MultipleValueSetting(switch)
+  of nimblePaths: copySeq(conf.nimblePaths)
+  of searchPaths: copySeq(conf.searchPaths)
+  of lazyPaths: copySeq(conf.lazyPaths)
+  of commandArgs: result = conf.commandArgs
+  of cincludes: copySeq(conf.cIncludes)
+  of clibs: copySeq(conf.cLibs)
 
 proc stackTrace2(c: PCtx, msg: string, n: PNode) =
   stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info)
@@ -200,6 +231,7 @@ proc registerAdditionalOps*(c: PCtx) =
   wrap1fMath(erfc)
   wrap1fMath(gamma)
   wrap1fMath(lgamma)
+  wrap2iMath(divmod)
 
   when declared(copySign):
     wrap2fMath(copySign)
@@ -212,31 +244,35 @@ proc registerAdditionalOps*(c: PCtx) =
     case n
     of 1: setResult(a, round(getFloat(a, 0)))
     of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int))
-    else: doAssert false, $n
+    else: raiseAssert $n
 
   proc `mod Wrapper`(a: VmArgs) {.nimcall.} =
     setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1)))
   registerCallback(c, "stdlib.math.mod", `mod Wrapper`)
 
   when defined(nimcore):
-    wrap2s(getEnv, osop)
-    wrap1s(existsEnv, osop)
-    wrap2svoid(putEnv, osop)
-    wrap1svoid(delEnv, osop)
-    wrap1s(dirExists, osop)
-    wrap1s(fileExists, osop)
-    wrapDangerous(writeFile, ioop)
+    wrap2s(getEnv, envvarsop)
+    wrap1s(existsEnv, envvarsop)
+    wrap2svoid(putEnv, envvarsop)
+    wrap1svoid(delEnv, envvarsop)
+    wrap1s(dirExists, oscommonop)
+    wrap1s(fileExists, oscommonop)
+    wrapDangerous2svoid(writeFile, ioop)
+    wrapDangerous1svoid(createDir, osdirsop)
     wrap1s(readFile, ioop)
     wrap2si(readLines, ioop)
     systemop getCurrentExceptionMsg
     systemop getCurrentException
-    registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
+    registerCallback c, "stdlib.osdirs.staticWalkDir", proc (a: VmArgs) {.nimcall.} =
       setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1)))
-    when defined(nimHasInvariant):
-      registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
-        setResult(a, querySettingImpl(c.config, getInt(a, 0)))
-      registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
-        setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
+    registerCallback c, "stdlib.staticos.staticDirExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, dirExists(getString(a, 0)))
+    registerCallback c, "stdlib.staticos.staticFileExists", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, fileExists(getString(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) =
+      setResult(a, querySettingImpl(c.config, getInt(a, 0)))
+    registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) =
+      setResult(a, querySettingSeqImpl(c.config, getInt(a, 0)))
 
     if defined(nimsuggest) or c.config.cmd == cmdCheck:
       discard "don't run staticExec for 'nim suggest'"
@@ -259,6 +295,12 @@ proc registerAdditionalOps*(c: PCtx) =
       stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n)
     setResult(a, sfExported in n.sym.flags)
 
+  registerCallback c, "stdlib.macrocache.hasKey", proc (a: VmArgs) =
+    let
+      table = getString(a, 0)
+      key = getString(a, 1)
+    setResult(a, table in c.graph.cacheTables and key in c.graph.cacheTables[table])
+
   registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) =
     c.config.isVmTrace = getBool(a, 0)
 
@@ -300,13 +342,14 @@ proc registerAdditionalOps*(c: PCtx) =
     ## reproducible builds and users need to understand that this runs at CT.
     ## Note that `staticExec` can already do equal amount of damage so it's more
     ## of a semantic issue than a security issue.
-    registerCallback c, "stdlib.os.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
-      setResult(a, os.getCurrentDir())
+    registerCallback c, "stdlib.ospaths2.getCurrentDir", proc (a: VmArgs) {.nimcall.} =
+      setResult(a, getCurrentDir())
     registerCallback c, "stdlib.osproc.execCmdEx", proc (a: VmArgs) {.nimcall.} =
       let options = getNode(a, 1).fromLit(set[osproc.ProcessOption])
       a.setResult osproc.execCmdEx(getString(a, 0), options).toLit
-    registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} =
-      setResult(a, times.getTime().toLit)
+    registerCallback c, "stdlib.times.getTimeImpl", proc (a: VmArgs) =
+      let obj = a.getNode(0).typ.n
+      setResult(a, times.getTime().toTimeLit(c, obj, a.currentLineInfo))
 
   proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) =
     let fn = getNode(a, 0)
@@ -350,7 +393,11 @@ proc registerAdditionalOps*(c: PCtx) =
     let x = a.getFloat(1)
     addFloatSprintf(p.strVal, x)
 
-  wrapIterator("stdlib.os.envPairsImplSeq"): envPairs()
+  registerCallback c, "stdlib.strutils.formatBiggestFloat", proc(a: VmArgs) =
+    setResult(a, formatBiggestFloat(a.getFloat(0), FloatFormatMode(a.getInt(1)),
+                                    a.getInt(2), chr(a.getInt(3))))
+
+  wrapIterator("stdlib.envvars.envPairsImplSeq"): envPairs()
 
   registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) =
     let typ = a.getNode(0).typ
@@ -365,6 +412,6 @@ proc registerAdditionalOps*(c: PCtx) =
   registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) =
     let typ = a.getNode(0).typ
     let p = a.getReg(1)
-    var res: string
+    var res: string = ""
     storeAny(res, typ, regToNode(p[]), c.config)
     setResult(a, res)
diff --git a/compiler/vmprofiler.nim b/compiler/vmprofiler.nim
index f586c8ffe..3f0db84bd 100644
--- a/compiler/vmprofiler.nim
+++ b/compiler/vmprofiler.nim
@@ -1,7 +1,7 @@
 
-import
-  options, vmdef, times, lineinfos, strutils, tables,
-  msgs
+import options, vmdef, lineinfos, msgs
+
+import std/[times, strutils, tables]
 
 proc enter*(prof: var Profiler, c: PCtx, tos: PStackFrame) {.inline.} =
   if optProfileVM in c.config.globalOptions:
@@ -28,11 +28,11 @@ proc leave*(prof: var Profiler, c: PCtx) {.inline.} =
 
 proc dump*(conf: ConfigRef, pd: ProfileData): string =
   var data = pd.data
-  echo "\nprof:     µs    #instr  location"
+  result = "\nprof:     µs    #instr  location"
   for i in 0..<32:
     var tMax: float
-    var infoMax: ProfileInfo
-    var flMax: TLineInfo
+    var infoMax: ProfileInfo = default(ProfileInfo)
+    var flMax: TLineInfo = default(TLineInfo)
     for fl, info in data:
       if info.time > infoMax.time:
         infoMax = info
diff --git a/compiler/vtables.nim b/compiler/vtables.nim
new file mode 100644
index 000000000..928c64dd5
--- /dev/null
+++ b/compiler/vtables.nim
@@ -0,0 +1,167 @@
+import ast, modulegraphs, magicsys, lineinfos, options, cgmeth, types
+import std/[algorithm, tables, intsets, assertions]
+
+
+
+proc genVTableDispatcher(g: ModuleGraph; methods: seq[PSym]; index: int): PSym =
+#[
+proc dispatch(x: Base, params: ...) =
+  cast[proc bar(x: Base, params: ...)](x.vTable[index])(x, params)
+]#
+  var base = methods[0].ast[dispatcherPos].sym
+  result = base
+  var paramLen = base.typ.signatureLen
+  var body = newNodeI(nkStmtList, base.info)
+
+  var disp = newNodeI(nkIfStmt, base.info)
+
+  var vTableAccess = newNodeIT(nkBracketExpr, base.info, base.typ)
+  let nimGetVTableSym = getCompilerProc(g, "nimGetVTable")
+  let ptrPNimType = nimGetVTableSym.typ.n[1].sym.typ
+
+  var nTyp = base.typ.n[1].sym.typ
+  var dispatchObject = newSymNode(base.typ.n[1].sym)
+  if nTyp.kind == tyObject:
+    dispatchObject = newTree(nkAddr, dispatchObject)
+  else:
+    if g.config.backend != backendCpp: # TODO: maybe handle ptr?
+      if nTyp.kind == tyVar and nTyp.skipTypes({tyVar}).kind != tyObject:
+        dispatchObject = newTree(nkDerefExpr, dispatchObject)
+
+  var getVTableCall = newTree(nkCall,
+    newSymNode(nimGetVTableSym),
+    dispatchObject,
+    newIntNode(nkIntLit, index)
+  )
+  getVTableCall.typ = base.typ
+  var vTableCall = newNodeIT(nkCall, base.info, base.typ.returnType)
+  var castNode = newTree(nkCast,
+        newNodeIT(nkType, base.info, base.typ),
+        getVTableCall)
+
+  castNode.typ = base.typ
+  vTableCall.add castNode
+  for col in 1..<paramLen:
+    let param = base.typ.n[col].sym
+    vTableCall.add newSymNode(param)
+
+  var ret: PNode
+  if base.typ.returnType != nil:
+    var a = newNodeI(nkFastAsgn, base.info)
+    a.add newSymNode(base.ast[resultPos].sym)
+    a.add vTableCall
+    ret = newNodeI(nkReturnStmt, base.info)
+    ret.add a
+  else:
+    ret = vTableCall
+
+  if base.typ.n[1].sym.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
+    let ifBranch = newNodeI(nkElifBranch, base.info)
+    let boolType = getSysType(g, unknownLineInfo, tyBool)
+    var isNil = getSysMagic(g, unknownLineInfo, "isNil", mIsNil)
+    let checkSelf = newNodeIT(nkCall, base.info, boolType)
+    checkSelf.add newSymNode(isNil)
+    checkSelf.add newSymNode(base.typ.n[1].sym)
+    ifBranch.add checkSelf
+    ifBranch.add newTree(nkCall,
+        newSymNode(getCompilerProc(g, "chckNilDisp")), newSymNode(base.typ.n[1].sym))
+    let elseBranch = newTree(nkElifBranch, ret)
+    disp.add ifBranch
+    disp.add elseBranch
+  else:
+    disp = ret
+
+  body.add disp
+  body.flags.incl nfTransf # should not be further transformed
+  result.ast[bodyPos] = body
+
+proc containGenerics(base: PType, s: seq[tuple[depth: int, value: PType]]): bool =
+  result = tfHasMeta in base.flags
+  for i in s:
+    if tfHasMeta in i.value.flags:
+      result = true
+      break
+
+proc collectVTableDispatchers*(g: ModuleGraph) =
+  var itemTable = initTable[ItemId, seq[LazySym]]()
+  var rootTypeSeq = newSeq[PType]()
+  var rootItemIdCount = initCountTable[ItemId]()
+  for bucket in 0..<g.methods.len:
+    var relevantCols = initIntSet()
+    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
+    sortBucket(g.methods[bucket].methods, relevantCols)
+    let base = g.methods[bucket].methods[^1]
+    let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
+    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
+      let methodIndexLen = g.bucketTable[baseType.itemId]
+      if baseType.itemId notin itemTable: # once is enough
+        rootTypeSeq.add baseType
+        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
+
+        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
+          if x.depth >= y.depth: 1
+          else: -1
+          )
+
+        for item in g.objectTree[baseType.itemId]:
+          if item.value.itemId notin itemTable:
+            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
+
+      var mIndex = 0 # here is the correpsonding index
+      if baseType.itemId notin rootItemIdCount:
+        rootItemIdCount[baseType.itemId] = 1
+      else:
+        mIndex = rootItemIdCount[baseType.itemId]
+        rootItemIdCount.inc(baseType.itemId)
+      for idx in 0..<g.methods[bucket].methods.len:
+        let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
+        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
+      g.addDispatchers genVTableDispatcher(g, g.methods[bucket].methods, mIndex)
+    else: # if the base object doesn't have this method
+      g.addDispatchers genIfDispatcher(g, g.methods[bucket].methods, relevantCols, g.idgen)
+
+proc sortVTableDispatchers*(g: ModuleGraph) =
+  var itemTable = initTable[ItemId, seq[LazySym]]()
+  var rootTypeSeq = newSeq[ItemId]()
+  var rootItemIdCount = initCountTable[ItemId]()
+  for bucket in 0..<g.methods.len:
+    var relevantCols = initIntSet()
+    if relevantCol(g.methods[bucket].methods, 1): incl(relevantCols, 1)
+    sortBucket(g.methods[bucket].methods, relevantCols)
+    let base = g.methods[bucket].methods[^1]
+    let baseType = base.typ.firstParamType.skipTypes(skipPtrs-{tyTypeDesc})
+    if baseType.itemId in g.objectTree and not containGenerics(baseType, g.objectTree[baseType.itemId]):
+      let methodIndexLen = g.bucketTable[baseType.itemId]
+      if baseType.itemId notin itemTable: # once is enough
+        rootTypeSeq.add baseType.itemId
+        itemTable[baseType.itemId] = newSeq[LazySym](methodIndexLen)
+
+        sort(g.objectTree[baseType.itemId], cmp = proc (x, y: tuple[depth: int, value: PType]): int =
+          if x.depth >= y.depth: 1
+          else: -1
+          )
+
+        for item in g.objectTree[baseType.itemId]:
+          if item.value.itemId notin itemTable:
+            itemTable[item.value.itemId] = newSeq[LazySym](methodIndexLen)
+
+      var mIndex = 0 # here is the correpsonding index
+      if baseType.itemId notin rootItemIdCount:
+        rootItemIdCount[baseType.itemId] = 1
+      else:
+        mIndex = rootItemIdCount[baseType.itemId]
+        rootItemIdCount.inc(baseType.itemId)
+      for idx in 0..<g.methods[bucket].methods.len:
+        let obj = g.methods[bucket].methods[idx].typ.firstParamType.skipTypes(skipPtrs)
+        itemTable[obj.itemId][mIndex] = LazySym(sym: g.methods[bucket].methods[idx])
+
+  for baseType in rootTypeSeq:
+    g.setMethodsPerType(baseType, itemTable[baseType])
+    for item in g.objectTree[baseType]:
+      let typ = item.value.skipTypes(skipPtrs)
+      let idx = typ.itemId
+      for mIndex in 0..<itemTable[idx].len:
+        if itemTable[idx][mIndex].sym == nil:
+          let parentIndex = typ.baseClass.skipTypes(skipPtrs).itemId
+          itemTable[idx][mIndex] = itemTable[parentIndex][mIndex]
+      g.setMethodsPerType(idx, itemTable[idx])
diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim
index d33982b0f..39e0b2e25 100644
--- a/compiler/wordrecg.nim
+++ b/compiler/wordrecg.nim
@@ -32,11 +32,12 @@ type
 
     wColon = ":", wColonColon = "::", wEquals = "=", wDot = ".", wDotDot = "..",
     wStar = "*", wMinus = "-",
+    wUnderscore = "_",
     wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler",
     wMemTracker = "memtracker", wObjChecks = "objchecks",
     wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine",
     wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf",
-    wUncheckedAssign = "uncheckedAssign",
+    wUncheckedAssign = "uncheckedAssign", wRunnableExamples = "runnableExamples",
 
     wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor",
     wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp",
@@ -48,7 +49,7 @@ type
     wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align",
     wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header",
     wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn",
-    wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib",
+    wNosinks = "nosinks", wLib = "lib", wDynlib = "dynlib",
     wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar",
     wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning",
     wHint = "hint",
@@ -83,35 +84,45 @@ type
     wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype",
     wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty",
     wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit",
-    wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic",
+    wAsmNoStackFrame = "asmNoStackFrame", wAsmSyntax = "asmSyntax", wImplicitStatic = "implicitStatic",
     wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked",
     wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain",
-    wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises",
-    wRedefine = "redefine",
+    wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wSystemRaisesDefect = "systemRaisesDefect",
+    wRedefine = "redefine", wCallsite = "callsite",
+    wQuirky = "quirky",
 
+    # codegen keywords, but first the ones that are also pragmas:
+    wExtern = "extern", wGoto = "goto", wRegister = "register",
+    wUnion = "union", wPacked = "packed", wVirtual = "virtual",
+    wVolatile = "volatile", wMember = "member",
+    wByCopy = "bycopy", wByRef = "byref",
+
+    # codegen keywords but not pragmas:
     wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char",
     wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default",
     wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast",
-    wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float",
-    wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable",
+    wExplicit = "explicit", wFalse = "false", wFloat = "float",
+    wFriend = "friend", wInt = "int", wLong = "long", wMutable = "mutable",
     wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private",
-    wProtected = "protected", wPublic = "public", wRegister = "register",
+    wProtected = "protected", wPublic = "public",
     wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short",
     wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct",
     wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef",
     wTypeid = "typeid", wTypeof = "typeof",  wTypename = "typename",
-    wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual",
-    wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t",
+    wUnsigned = "unsigned", wVoid = "void", 
 
     wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype",
     wNullptr = "nullptr", wNoexcept = "noexcept",
     wThreadLocal = "thread_local", wStaticAssert = "static_assert",
-    wChar16 = "char16_t", wChar32 = "char32_t",
+    wChar16 = "char16_t", wChar32 = "char32_t", wWchar = "wchar_t",
 
     wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr",
 
-    wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway",
+    wInOut = "inout", wOneWay = "oneway",
+    # end of codegen keywords
+
     wBitsize = "bitsize", wImportHidden = "all",
+    wSendable = "sendable"
 
   TSpecialWords* = set[TSpecialWord]
 
@@ -122,28 +133,18 @@ const
   nimKeywordsLow* = ord(wAsm)
   nimKeywordsHigh* = ord(wYield)
 
-  ccgKeywordsLow* = ord(wAuto)
+  ccgKeywordsLow* = ord(wExtern)
   ccgKeywordsHigh* = ord(wOneWay)
 
   cppNimSharedKeywords* = {
     wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport,
     wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing}
+  
+  nonPragmaWordsLow* = wAuto
+  nonPragmaWordsHigh* = wOneWay
 
 
-const enumUtilsExist = compiles:
-  import std/enumutils
-
-when enumUtilsExist:
-  from std/enumutils import genEnumCaseStmt
-  from strutils import normalize
-  proc findStr*[T: enum](a, b: static[T], s: string, default: T): T =
-    genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize)
-
-else:
-  from strutils import cmpIgnoreStyle
-  proc findStr*[T: enum](a, b: static[T], s: string, default: T): T {.deprecated.} =
-    # used for compiler bootstrapping only
-    for i in a..b:
-      if cmpIgnoreStyle($i, s) == 0:
-        return i
-    result = default
+from std/enumutils import genEnumCaseStmt
+from std/strutils import normalize
+proc findStr*[T: enum](a, b: static[T], s: string, default: T): T =
+  genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize)
diff --git a/config/build_config.txt b/config/build_config.txt
index 15f44bb57..66390e695 100644
--- a/config/build_config.txt
+++ b/config/build_config.txt
@@ -1,5 +1,5 @@
 nim_comment="key-value pairs for windows/posix bootstrapping build scripts"
-nim_csourcesDir=csources_v1
-nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git
+nim_csourcesDir=csources_v2
+nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git
 nim_csourcesBranch=master
-nim_csourcesHash=561b417c65791cd8356b5f73620914ceff845d10
+nim_csourcesHash=86742fb02c6606ab01a532a0085784effb2e753e
diff --git a/config/config.nims b/config/config.nims
index aa1eda894..b8979e8e3 100644
--- a/config/config.nims
+++ b/config/config.nims
@@ -11,7 +11,7 @@ cppDefine "NAN"
 when defined(nimStrictMode):
   # xxx add more flags here, and use `-d:nimStrictMode` in more contexts in CI.
 
-  # pending bug #14246, enable this:
+  # enable this:
   # when defined(nimHasWarningAsError):
   #   switch("warningAsError", "UnusedImport")
 
@@ -20,9 +20,4 @@ when defined(nimStrictMode):
     switch("hintAsError", "ConvFromXtoItselfNotNeeded")
     # future work: XDeclaredButNotUsed
 
-when defined(windows) and not defined(booting):
-  # Avoid some rare stack corruption while using exceptions with a SEH-enabled
-  # toolchain: https://github.com/nim-lang/Nim/pull/19197
-  switch("define", "nimRawSetjmp")
-
-switch("define", "nimVersion:" & NimVersion)
+switch("define", "nimVersion:" & NimVersion) # deadcode
diff --git a/config/nim.cfg b/config/nim.cfg
index 55b7a41c1..7c9958139 100644
--- a/config/nim.cfg
+++ b/config/nim.cfg
@@ -14,7 +14,10 @@ cc = gcc
 # additional options always passed to the compiler:
 --parallel_build: "0" # 0 to auto-detect number of processors
 
-hint[LineTooLong]=off
+@if not nimHasNolineTooLong:
+  hint[LineTooLong]=off
+@end
+
 #hint[XDeclaredButNotUsed]=off
 
 threads:on
@@ -34,7 +37,7 @@ arm64.linux.gcc.linkerexe = "aarch64-linux-gnu-gcc"
 riscv32.linux.gcc.exe = "riscv64-linux-gnu-gcc"
 riscv32.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc"
 riscv64.linux.gcc.exe = "riscv64-linux-gnu-gcc"
-riscv64.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc"
+riscv64.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc"
 
 # For OpenWRT, you will also need to adjust PATH to point to your toolchain.
 mips.linux.gcc.exe = "mips-openwrt-linux-gcc"
@@ -95,6 +98,11 @@ nimblepath="$home/.nimble/pkgs/"
   gcc.options.always %= "${gcc.options.always} -fsanitize=null -fsanitize-undefined-trap-on-error"
 @end
 
+# Turn off threads support when compiling for bare-metal targets (--os:any)
+@if any:
+  threads:off
+@end
+
 @if unix and mingw:
   # Cross compile for Windows from Linux/OSX using MinGW
   i386.windows.gcc.exe = "i686-w64-mingw32-gcc"
@@ -169,15 +177,19 @@ nimblepath="$home/.nimble/pkgs/"
 # Configuration for the GNU C/C++ compiler:
 @if windows:
   #gcc.path = r"$nim\dist\mingw\bin"
-  @if gcc or tcc:
+  @if gcc:
+    gcc.options.linker %= "-Wl,-Bstatic -lpthread"
+  @end
+  @if tcc:
     tlsEmulation:on
   @end
 @end
 
 gcc.maxerrorsimpl = "-fmax-errors=3"
 
-@if bsd:
-  # at least NetBSD has problems with thread local storage:
+@if freebsd or netbsd:
+  tlsEmulation:off
+@elif bsd:
   tlsEmulation:on
 @end
 
@@ -245,14 +257,17 @@ clang.objc.options.linker = "-lobjc -lgnustep-base"
   gcc.options.linker %= "-L $WIND_BASE/target/lib/usr/lib/ppc/PPC32/common -mrtp -fno-strict-aliasing -D_C99 -D_HAS_C9X -std=c99 -fasm -Wall -Wno-write-strings"
 @end
 
-gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
+# -fno-math-errno is default in OSX, iOS, BSD, Musl, Libm, LLVM, Clang, ICC.
+# See https://itnext.io/why-standard-c-math-functions-are-slow-d10d02554e33
+# and https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Optimize-Options.html#Optimize-Options
+gcc.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno"
 gcc.options.size = "-Os -fno-ident"
 @if windows:
   gcc.options.debug = "-g3 -Og -gdwarf-3"
 @else:
   gcc.options.debug = "-g3 -Og"
 @end
-gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident"
+gcc.cpp.options.speed = "-O3 -fno-strict-aliasing -fno-ident -fno-math-errno"
 gcc.cpp.options.size = "-Os -fno-ident"
 gcc.cpp.options.debug = "-g3 -Og"
 #passl = "-pg"
@@ -316,24 +331,30 @@ tcc.options.always = "-w"
    vcc.cpp.options.always%= "${vcc.cpp.options.always} /GL /Gw /Gy"
    vcc.options.linker %= "${vcc.options.linker} /link /LTCG:incremental"
    vcc.cpp.options.linker %= "${vcc.cpp.options.linker} /link /LTCG:incremental"
+   clang_cl.options.always%= "${clang_cl.options.always} -flto=thin"
+   clang_cl.cpp.options.always%= "${clang.cpp.options.always} -flto=thin"
+   clang.options.always%= "${clang.options.always} -flto=thin"
+   clang.cpp.options.always%= "${clang.cpp.options.always} -flto=thin"
+   clang.options.linker %= "${clang.options.linker} -flto=thin"
+   clang.cpp.options.linker %= "${clang.cpp.options.linker} -flto=thin"
   @else:
    vcc.options.always%= "${vcc.options.always} /GL"
    vcc.cpp.options.always%= "${vcc.cpp.options.always} /GL"
    vcc.options.linker %= "${vcc.options.linker} /link /LTCG"
    vcc.cpp.options.linker %= "${vcc.cpp.options.linker} /link /LTCG"
+   clang_cl.options.always%= "${clang_cl.options.always} -flto"
+   clang_cl.cpp.options.always%= "${clang.cpp.options.always} -flto"
+   clang.options.always%= "${clang.options.always} -flto"
+   clang.cpp.options.always%= "${clang.cpp.options.always} -flto"
+   clang.options.linker %= "${clang.options.linker} -flto"
+   clang.cpp.options.linker %= "${clang.cpp.options.linker} -flto"
   @end
-  clang_cl.options.always%= "${clang_cl.options.always} -flto"
-  clang_cl.cpp.options.always%= "${clang.cpp.options.always} -flto"
-  clang.options.always%= "${clang.options.always} -flto"
-  clang.cpp.options.always%= "${clang.cpp.options.always} -flto"
   icl.options.always %= "${icl.options.always} /Qipo"
   icl.cpp.options.always %= "${icl.cpp.options.always} /Qipo"
-  gcc.options.always %= "${gcc.options.always} -flto"
-  gcc.cpp.options.always %= "${gcc.cpp.options.always} -flto"
-  clang.options.linker %= "${clang.options.linker} -fuse-ld=lld -flto"
-  clang.cpp.options.linker %= "${clang.cpp.options.linker} -fuse-ld=lld -flto"
-  gcc.options.linker %= "${gcc.options.linker} -flto"
-  gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -flto"
+  gcc.options.always %= "${gcc.options.always} -flto=auto"
+  gcc.cpp.options.always %= "${gcc.cpp.options.always} -flto=auto"
+  gcc.options.linker %= "${gcc.options.linker} -flto=auto -Wno-stringop-overflow"  # https://github.com/nim-lang/Nim/issues/21595
+  gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -flto=auto"
 @end
 @if strip:
   gcc.options.linker %= "${gcc.options.linker} -s"
diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg
index d10c2f318..99751f79d 100644
--- a/config/nimdoc.cfg
+++ b/config/nimdoc.cfg
@@ -147,7 +147,7 @@ doc.body_toc_group = """
       <ul class="simple-boot">
         <li><a href="manual.html">Manual</a></li>
         <li><a href="lib.html">Standard library</a></li>
-        <li> <a href="$theindexhref">Index</a></li>
+        <li> <a id="indexLink" href="$theindexhref">Index</a></li>
         <li><a href="compiler/$theindexhref">Compiler docs</a></li>
         <li><a href="https://nim-lang.github.io/fusion/theindex.html">Fusion docs</a></li>
         <li><a href="https://nim-lang.github.io/Nim/">devel</a>, <a href="https://nim-lang.org/documentation.html">stable</a></li>
@@ -155,7 +155,7 @@ doc.body_toc_group = """
     </div>
     <div id="searchInputDiv">
       Search: <input type="search" id="searchInput"
-        onkeyup="search()" />
+        oninput="search()" />
     </div>
     $body_toc_groupsection
     $tableofcontents
@@ -185,11 +185,11 @@ doc.body_toc_group = """
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="$theindexhref">Index</a></li>
+        <li><a id="indexLink" href="$theindexhref">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
@@ -226,25 +226,25 @@ doc.listing_end = "</pre>"
 doc.file = """<?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>$title</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
-<link rel="stylesheet" type="text/css" href="$nimdoccss">
+<link rel="stylesheet" type="text/css" href="${nimdoccss}?v=$nimVersion">
 
 <!-- JS -->
-<script type="text/javascript" src="$dochackjs"></script>
+<script type="text/javascript" src="${dochackjs}?v=$nimVersion"></script>
 </head>
 <body>
   <div class="document" id="documentId">
diff --git a/copying.txt b/copying.txt
index 60b6a0217..819330be3 100644
--- a/copying.txt
+++ b/copying.txt
@@ -1,7 +1,7 @@
 =====================================================
 Nim -- a Compiler for Nim. https://nim-lang.org/
 
-Copyright (C) 2006-2022 Andreas Rumpf. All rights reserved.
+Copyright (C) 2006-2024 Andreas Rumpf. All rights reserved.
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/doc/advopt.txt b/doc/advopt.txt
index 3f439fdab..e4d11081a 100644
--- a/doc/advopt.txt
+++ b/doc/advopt.txt
@@ -4,8 +4,11 @@ Advanced commands:
   //compileToOC, objc       compile project to Objective C code
   //js                      compile project to Javascript
   //e                       run a Nimscript file
+  //md2html                 convert a Markdown file to HTML
+                            use `--docCmd:skip` to skip compiling snippets
   //rst2html                convert a reStructuredText file to HTML
                             use `--docCmd:skip` to skip compiling snippets
+  //md2tex                  convert a Markdown file to LaTeX
   //rst2tex                 convert a reStructuredText file to LaTeX
   //doc2tex                 extract the documentation to a LaTeX file
   //jsondoc                 extract the documentation to a json file
@@ -45,7 +48,7 @@ Advanced options:
   --unitsep:on|off          use the ASCII unit separator (31) between error
                             messages, useful for IDE-like tooling
   --declaredLocs:on|off     show declaration locations in messages
-  --spellSuggest|:num       show at most `num >= 0` spelling suggestions on typos.
+  --spellSuggest:num        show at most `num >= 0` spelling suggestions on typos.
                             if `num` is not specified (or `auto`), return
                             an implementation defined set of suggestions.
   --hints:on|off|list.      `on|off` enables or disables hints.
@@ -130,7 +133,9 @@ Advanced options:
                             select which memory management to use; default is 'orc'
   --exceptions:setjmp|cpp|goto|quirky
                             select the exception handling implementation
-  --index:on|off            turn index file generation on|off
+  --index:on|off|only       docgen: turn index file generation on|off (`only` means
+                            not generate output files like HTML)
+  --noImportdoc:on|off      turn loading documentation ``.idx`` files on|off
   --putenv:key=value        set an environment variable
   --NimblePath:PATH         add a path for Nimble support
   --noNimblePath            deactivate the Nimble path
@@ -164,9 +169,11 @@ Advanced options:
                             enable experimental language feature
   --legacy:$2
                             enable obsolete/legacy language feature
-  --useVersion:1.0|1.2|1.6  emulate Nim version X of the Nim compiler, for testing
   --benchmarkVM:on|off      turn benchmarking of VM code with cpuTime() on|off
   --profileVM:on|off        turn compile time VM profiler on|off
-  --sinkInference:on|off    turn sink parameter inference on|off (default: on)
   --panics:on|off           turn panics into process terminations (default: off)
   --deepcopy:on|off         enable 'system.deepCopy' for ``--mm:arc|orc``
+  --jsbigint64:on|off       toggle the use of BigInt for 64-bit integers for
+                            the JavaScript backend (default: on)
+  --nimBasePattern:nimbase.h
+                            allows to specify a custom pattern for `nimbase.h`
diff --git a/doc/astspec.txt b/doc/astspec.txt
index bfaec7155..7a7053a2d 100644
--- a/doc/astspec.txt
+++ b/doc/astspec.txt
@@ -893,7 +893,7 @@ on what keywords are present. Let's start with the simplest form.
 Concrete syntax:
 
   ```nim
-  import math
+  import std/math
   ```
 
 AST:
@@ -907,7 +907,7 @@ With ``except``, we get ``nnkImportExceptStmt``.
 Concrete syntax:
 
   ```nim
-  import math except pow
+  import std/math except pow
   ```
 
 AST:
@@ -916,13 +916,13 @@ AST:
   nnkImportExceptStmt(nnkIdent("math"),nnkIdent("pow"))
   ```
 
-Note that ``import math as m`` does not use a different node; rather,
+Note that ``import std/math as m`` does not use a different node; rather,
 we use ``nnkImportStmt`` with ``as`` as an infix operator.
 
 Concrete syntax:
 
   ```nim
-  import strutils as su
+  import std/strutils as su
   ```
 
 AST:
@@ -945,7 +945,7 @@ If we use ``from ... import``, the result is different, too.
 Concrete syntax:
 
   ```nim
-  from math import pow
+  from std/math import pow
   ```
 
 AST:
@@ -954,7 +954,7 @@ AST:
   nnkFromStmt(nnkIdent("math"), nnkIdent("pow"))
   ```
 
-Using ``from math as m import pow`` works identically to the ``as`` modifier
+Using ``from std/math as m import pow`` works identically to the ``as`` modifier
 with the ``import`` statement, but wrapped in ``nnkFromStmt``.
 
 Export statement
@@ -1348,7 +1348,7 @@ Generic parameters are treated in the type, not the ``proc`` itself.
 Concrete syntax:
 
   ```nim
-  type MyProc[T] = proc(x: T)
+  type MyProc[T] = proc(x: T) {.nimcall.}
   ```
 
 AST:
@@ -1363,7 +1363,8 @@ AST:
     nnkProcTy( # behaves like a procedure declaration from here on
       nnkFormalParams(
         # ...
-      )
+      ),
+      nnkPragma(nnkIdent("nimcall"))
     )
   )
   ```
@@ -1371,6 +1372,37 @@ AST:
 The same syntax applies to ``iterator`` (with ``nnkIteratorTy``), but
 *does not* apply to ``converter`` or ``template``.
 
+Type class versions of these nodes generally share the same node kind but
+without any child nodes. The ``tuple`` type class is represented by
+``nnkTupleClassTy``, while a ``proc`` or ``iterator`` type class with pragmas
+has an ``nnkEmpty`` node in place of the ``nnkFormalParams`` node of a
+concrete ``proc`` or ``iterator`` type node.
+
+  ```nim
+  type TypeClass = proc {.nimcall.} | ref | tuple
+  ```
+
+AST:
+
+  ```nim
+  nnkTypeDef(
+    nnkIdent("TypeClass"),
+    nnkEmpty(),
+    nnkInfix(
+      nnkIdent("|"),
+      nnkProcTy(
+        nnkEmpty(),
+        nnkPragma(nnkIdent("nimcall"))
+      ),
+      nnkInfix(
+        nnkIdent("|"),
+        nnkRefTy(),
+        nnkTupleClassTy()
+      )
+    )
+  )
+  ```
+
 Mixin statement
 ---------------
 
diff --git a/doc/backends.md b/doc/backends.md
index 5258e9b4d..9f0c54835 100644
--- a/doc/backends.md
+++ b/doc/backends.md
@@ -250,6 +250,8 @@ which will likely make your program crash at runtime.
 The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch.
 Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`.
 
+When compiling to static or dynamic libraries, they don't call destructors of global variables as normal Nim programs would do. A C API `NimDestroyGlobals` is provided to call these global destructors.
+
 
 ### Nim invocation example from C
 
@@ -300,7 +302,7 @@ Instead of depending on the generation of the individual ``.c`` files you can
 also ask the Nim compiler to generate a statically linked library:
 
   ```cmd
-  nim c --app:staticLib --noMain fib.nim
+  nim c --app:staticLib fib.nim
   gcc -o m -Inimcache -Ipath/to/nim/lib maths.c libfib.nim.a
   ```
 
@@ -371,11 +373,7 @@ The manual mentions that [Nim strings are implicitly convertible to
 cstrings](manual.html#types-cstring-type) which makes interaction usually
 painless. Most C functions accepting a Nim string converted to a
 `cstring` will likely not need to keep this string around and by the time
-they return the string won't be needed anymore. However, for the rare cases
-where a Nim string has to be preserved and made available to the C backend
-as a `cstring`, you will need to manually prevent the string data
-from being freed with [GC_ref](system.html#GC_ref,string) and [GC_unref](
-system.html#GC_unref,string).
+they return the string won't be needed anymore.
 
 A similar thing happens with C code invoking Nim code which returns a
 `cstring`. Consider the following proc:
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index a9aa4b8fa..e8133d227 100644
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -1,4 +1,3 @@
-::
 
     nim command [options] [projectfile] [arguments]
 
diff --git a/doc/contributing.md b/doc/contributing.md
index 83ca85663..420c1438e 100644
--- a/doc/contributing.md
+++ b/doc/contributing.md
@@ -11,7 +11,7 @@ Contributing
 Contributing happens via "Pull requests" (PR) on GitHub. Every PR needs to be
 reviewed before it can be merged and the Continuous Integration should be green.
 The title of a PR should contain a brief description. If it fixes an issue,
-in addition to the number of the issue, the title should also contain a description 
+in addition to the number of the issue, the title should also contain a description
 of the issue.
 
 The PR has to be approved by two core developers or by Araq.
@@ -137,9 +137,9 @@ You can run the tests with
 which will run a good subset of tests. Some tests may fail. If you
 only want to see the output of failing tests, go for
 
-```cmd
+  ```cmd
   ./koch tests --failing all
-```
+  ```
 
 You can also run only a single category of tests. A category is a subdirectory
 in the ``tests/`` directory. There are a couple of special categories; for a
@@ -198,7 +198,7 @@ tell you if any new tests passed/failed.
 Deprecation
 ===========
 
-Backwards compatibility is important. When renaming types, procedures, etc. the old name 
+Backwards compatibility is important. When renaming types, procedures, etc. the old name
 must be marked as deprecated using the `deprecated` pragma:
 
   ```nim
@@ -314,14 +314,14 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files:
   programming languages, including identifiers, in ``*.nim`` files.
 
   For languages other than Nim add a role after final backtick,
-  e.g. for C++ inline highlighting::
+  e.g. for C++ inline highlighting:
 
-    `#include <stdio.h>`:cpp:
+      `#include <stdio.h>`:cpp:
 
   For a currently unsupported language add the `:code:` role,
-  like for SQL in this example::
+  like for SQL in this example:
 
-    `SELECT * FROM <table_name>;`:code:
+      `SELECT * FROM <table_name>;`:code:
 
   Highlight shell commands by ``:cmd:`` role; for command line options use
   ``:option:`` role, e.g.: \`--docInternal\`:option:.
@@ -335,17 +335,17 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files:
     ``\`` and a final \` would get escaped)
 
 .. Note:: ``*.rst`` files have ``:literal:`` as their default role.
-          So for them the rule above is only applicable if the ``:nim:`` role
-          is set up manually as the default \[*]::
+  So for them the rule above is only applicable if the ``:nim:`` role
+  is set up manually as the default [^1]:
 
-            .. role:: nim(code)
-               :language: nim
-            .. default-role:: nim
+      .. role:: nim(code)
+         :language: nim
+      .. default-role:: nim
 
-          The first 2 lines are for other RST implementations,
-          including Github one.
+  The first 2 lines are for other RST implementations,
+  including Github one.
 
-          \[*] this is fulfilled when ``doc/rstcommon.rst`` is included.
+  [^1]: this is fulfilled when ``doc/rstcommon.rst`` is included.
 
 Best practices
 ==============
@@ -489,9 +489,9 @@ General commit rules
      git diff --check --cached || exit $?
      ```
 5. Describe your commit and use your common sense.
-   Example commit message::
+   Example commit message:
 
-     Fixes #123; refs #124
+       Fixes #123; refs #124
 
    indicates that issue ``#123`` is completely fixed (GitHub may automatically
    close it when the PR is committed), whereas issue ``#124`` is referenced
@@ -513,7 +513,7 @@ General commit rules
    https://github.com/nim-lang/Nim/pull/9356
 
 8. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or
-   automated changes (e.g. nimfix) with other code changes: these should be in
+   automated changes with other code changes: these should be in
    separate commits (and the merge on GitHub should not squash these into 1).
 
 
@@ -581,11 +581,9 @@ Code reviews
 3. In addition, you can view GitHub-like diffs locally to identify what was changed
    within a code block using `diff-highlight`:cmd: or `diff-so-fancy`:cmd:, e.g.:
 
-   ::
-
-      # put this in ~/.gitconfig:
-      [core]
-        pager = "diff-so-fancy | less -R" # or: use: `diff-highlight`
+        # put this in ~/.gitconfig:
+        [core]
+          pager = "diff-so-fancy | less -R" # or: use: `diff-highlight`
 
 
 
diff --git a/doc/destructors.md b/doc/destructors.md
index b0dd42597..e192fd362 100644
--- a/doc/destructors.md
+++ b/doc/destructors.md
@@ -13,12 +13,12 @@ Nim Destructors and Move Semantics
 About this document
 ===================
 
-This document describes the upcoming Nim runtime which does
+This document describes the ARC/ORC Nim runtime which does
 not use classical GC algorithms anymore but is based on destructors and
-move semantics. The new runtime's advantages are that Nim programs become
+move semantics. The advantages are that Nim programs become
 oblivious to the involved heap sizes and programs are easier to write to make
 effective use of multi-core machines. As a nice bonus, files and sockets and
-the like will not require manual `close` calls anymore.
+the like can be written not to require manual `close` calls anymore.
 
 This document aims to be a precise specification about how
 move semantics and destructors work in Nim.
@@ -30,17 +30,20 @@ Motivating example
 With the language mechanisms described here, a custom seq could be
 written as:
 
-  ```nim
+  ```nim test
   type
     myseq*[T] = object
       len, cap: int
       data: ptr UncheckedArray[T]
 
-  proc `=destroy`*[T](x: var myseq[T]) =
+  proc `=destroy`*[T](x: myseq[T]) =
     if x.data != nil:
       for i in 0..<x.len: `=destroy`(x.data[i])
       dealloc(x.data)
 
+  proc `=wasMoved`*[T](x: var myseq[T]) =
+    x.data = nil
+
   proc `=trace`[T](x: var myseq[T]; env: pointer) =
     # `=trace` allows the cycle collector `--mm:orc`
     # to understand how to trace the object graph.
@@ -51,7 +54,7 @@ written as:
     # do nothing for self-assignments:
     if a.data == b.data: return
     `=destroy`(a)
-    wasMoved(a)
+    `=wasMoved`(a)
     a.len = b.len
     a.cap = b.cap
     if b.data != nil:
@@ -59,11 +62,19 @@ written as:
       for i in 0..<a.len:
         a.data[i] = b.data[i]
 
+  proc `=dup`*[T](a: myseq[T]): myseq[T] {.nodestroy.} =
+    # an optimized version of `=wasMoved(tmp); `=copy(tmp, src)`
+    # usually present if a custom `=copy` hook is overridden
+    result = myseq[T](len: a.len, cap: a.cap, data: nil)
+    if a.data != nil:
+      result.data = cast[typeof(result.data)](alloc(result.cap * sizeof(T)))
+      for i in 0..<result.len:
+        result.data[i] = `=dup`(a.data[i])
+
   proc `=sink`*[T](a: var myseq[T]; b: myseq[T]) =
     # move assignment, optional.
     # Compiler is using `=destroy` and `copyMem` when not provided
     `=destroy`(a)
-    wasMoved(a)
     a.len = b.len
     a.cap = b.cap
     a.data = b.data
@@ -84,9 +95,10 @@ written as:
     x.data[i] = y
 
   proc createSeq*[T](elems: varargs[T]): myseq[T] =
-    result.cap = elems.len
-    result.len = elems.len
-    result.data = cast[typeof(result.data)](alloc(result.cap * sizeof(T)))
+    result = myseq[T](
+      len: elems.len,
+      cap: elems.len,
+      data: cast[typeof(result.data)](alloc(result.cap * sizeof(T))))
     for i in 0..<result.len: result.data[i] = elems[i]
 
   proc len*[T](x: myseq[T]): int {.inline.} = x.len
@@ -101,7 +113,7 @@ well as other standard collections is performed via so-called
 "Lifetime-tracking hooks", which are particular [type bound operators](
 manual.html#procedures-type-bound-operators).
 
-There are 4 different hooks for each (generic or concrete) object type `T` (`T` can also be a
+There are 6 different hooks for each (generic or concrete) object type `T` (`T` can also be a
 `distinct` type) that are called implicitly by the compiler.
 
 (Note: The word "hook" here does not imply any kind of dynamic binding
@@ -117,21 +129,48 @@ other associated resources. Variables are destroyed via this hook when
 they go out of scope or when the routine they were declared in is about
 to return.
 
-The prototype of this hook for a type `T` needs to be:
+A `=destroy` hook is allowed to have a parameter of a `var T` or `T` type. Taking a `var T` type is deprecated. The prototype of this hook for a type `T` needs to be:
 
   ```nim
-  proc `=destroy`(x: var T)
+  proc `=destroy`(x: T)
   ```
 
 The general pattern in `=destroy` looks like:
 
   ```nim
-  proc `=destroy`(x: var T) =
+  proc `=destroy`(x: T) =
     # first check if 'x' was moved to somewhere else:
     if x.field != nil:
       freeResource(x.field)
   ```
 
+A `=destroy` is implicitly annotated with `.raises: []`; a destructor
+should not raise exceptions. For backwards compatibility the compiler
+produces a warning for a `=destroy` that does raise.
+
+A `=destroy` can explicitly list the exceptions it can raise, if any,
+but this of little utility as a raising destructor is implementation defined
+behavior. Later versions of the language specification might cover this case precisely.
+
+
+`=wasMoved` hook
+----------------
+
+A `=wasMoved` hook sets the object to a state that signifies to the destructor there is nothing to destroy.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=wasMoved`(x: var T)
+  ```
+
+Usually some pointer field inside the object is set to `nil`:
+
+  ```nim
+  proc `=wasMoved`(x: var T) =
+    x.field = nil
+  ```
+
 
 `=sink` hook
 ------------
@@ -238,10 +277,10 @@ The general pattern in using `=destroy` with `=trace` looks like:
     Test[T](size: size, arr: cast[ptr UncheckedArray[T]](alloc0(sizeof(T) * size)))
 
 
-  proc `=destroy`[T](dest: var Test[T]) =
+  proc `=destroy`[T](dest: Test[T]) =
     if dest.arr != nil:
       for i in 0 ..< dest.size: dest.arr[i].`=destroy`
-      dest.arr.dealloc
+      dealloc dest.arr
 
   proc `=trace`[T](dest: var Test[T]; env: pointer) =
     if dest.arr != nil:
@@ -255,6 +294,31 @@ The general pattern in using `=destroy` with `=trace` looks like:
 than the other hooks.
 
 
+`=dup` hook
+-----------
+
+A `=dup` hook duplicates an object. `=dup(x)` can be regarded as an optimization replacing a `wasMoved(dest); =copy(dest, x)` operation.
+
+The prototype of this hook for a type `T` needs to be:
+
+  ```nim
+  proc `=dup`(x: T): T
+  ```
+
+The general pattern in implementing `=dup` looks like:
+
+  ```nim
+  type
+    Ref[T] = object
+      data: ptr T
+      rc: ptr int
+
+  proc `=dup`[T](x: Ref[T]): Ref[T] =
+    result = x
+    if x.rc != nil:
+      inc x.rc[]
+  ```
+
 Move semantics
 ==============
 
@@ -264,6 +328,24 @@ document uses the notation `lastReadOf(x)` to describe that `x` is not
 used afterward. This property is computed by a static control flow analysis
 but can also be enforced by using `system.move` explicitly.
 
+One can query if the analysis is able to perform a move with `system.ensureMove`.
+`move` enforces a move operation and calls `=wasMoved` whereas `ensureMove` is
+an annotation that implies no runtime operation. An `ensureMove` annotation leads to a static error
+if the compiler cannot prove that a move would be safe.
+
+For example:
+
+  ```nim
+  proc main(normalParam: string; sinkParam: sink string) =
+    var x = "abc"
+    # valid:
+    let valid = ensureMove x
+    # invalid:
+    let invalid = ensureMove normalParam
+    # valid:
+    let alsoValid = ensureMove sinkParam
+  ```
+
 
 Swap
 ====
@@ -358,58 +440,57 @@ Rewrite rules
 The current implementation follows strategy (2). This means that resources are
 destroyed at the scope exit.
 
-::
 
-  var x: T; stmts
-  ---------------             (destroy-var)
-  var x: T; try stmts
-  finally: `=destroy`(x)
+    var x: T; stmts
+    ---------------             (destroy-var)
+    var x: T; try stmts
+    finally: `=destroy`(x)
 
 
-  g(f(...))
-  ------------------------    (nested-function-call)
-  g(let tmp;
-  bitwiseCopy tmp, f(...);
-  tmp)
-  finally: `=destroy`(tmp)
+    g(f(...))
+    ------------------------    (nested-function-call)
+    g(let tmp;
+    bitwiseCopy tmp, f(...);
+    tmp)
+    finally: `=destroy`(tmp)
 
 
-  x = f(...)
-  ------------------------    (function-sink)
-  `=sink`(x, f(...))
+    x = f(...)
+    ------------------------    (function-sink)
+    `=sink`(x, f(...))
 
 
-  x = lastReadOf z
-  ------------------          (move-optimization)
-  `=sink`(x, z)
-  wasMoved(z)
+    x = lastReadOf z
+    ------------------          (move-optimization)
+    `=sink`(x, z)
+    `=wasMoved`(z)
 
 
-  v = v
-  ------------------   (self-assignment-removal)
-  discard "nop"
+    v = v
+    ------------------   (self-assignment-removal)
+    discard "nop"
 
 
-  x = y
-  ------------------          (copy)
-  `=copy`(x, y)
+    x = y
+    ------------------          (copy)
+    `=copy`(x, y)
 
 
-  f_sink(g())
-  -----------------------     (call-to-sink)
-  f_sink(g())
+    f_sink(g())
+    -----------------------     (call-to-sink)
+    f_sink(g())
 
 
-  f_sink(notLastReadOf y)
-  --------------------------     (copy-to-sink)
-  (let tmp; `=copy`(tmp, y);
-  f_sink(tmp))
+    f_sink(notLastReadOf y)
+    --------------------------     (copy-to-sink)
+    (let tmp = `=dup`(y);
+    f_sink(tmp))
 
 
-  f_sink(lastReadOf y)
-  -----------------------     (move-to-sink)
-  f_sink(y)
-  wasMoved(y)
+    f_sink(lastReadOf y)
+    -----------------------     (move-to-sink)
+    f_sink(y)
+    `=wasMoved`(y)
 
 
 Object and array construction
@@ -422,7 +503,7 @@ function has `sink` parameters.
 Destructor removal
 ==================
 
-`wasMoved(x);` followed by a `=destroy(x)` operation cancel each other
+`=wasMoved(x)` followed by a `=destroy(x)` operation cancel each other
 out. An implementation is encouraged to exploit this in order to improve
 efficiency and code sizes. The current implementation does perform this
 optimization.
@@ -431,11 +512,11 @@ optimization.
 Self assignments
 ================
 
-`=sink` in combination with `wasMoved` can handle self-assignments but
+`=sink` in combination with `=wasMoved` can handle self-assignments but
 it's subtle.
 
 The simple case of `x = x` cannot be turned
-into `=sink(x, x); wasMoved(x)` because that would lose `x`'s value.
+into `=sink(x, x); =wasMoved(x)` because that would lose `x`'s value.
 The solution is that simple self-assignments that consist of
 
 - Symbols: `x = x`
@@ -470,10 +551,10 @@ Is transformed into:
     try:
       if cond:
         `=sink`(result, a)
-        wasMoved(a)
+        `=wasMoved`(a)
       else:
         `=sink`(result, b)
-        wasMoved(b)
+        `=wasMoved`(b)
     finally:
       `=destroy`(b)
       `=destroy`(a)
@@ -487,10 +568,10 @@ Is transformed into:
       `=sink`(y, "xyz")
       `=sink`(x, select(true,
         let blitTmp = x
-        wasMoved(x)
+        `=wasMoved`(x)
         blitTmp,
         let blitTmp = y
-        wasMoved(y)
+        `=wasMoved`(y)
         blitTmp))
       echo [x]
     finally:
@@ -518,7 +599,7 @@ that the pointer does not outlive its origin. No destructor call is injected
 for expressions of type `lent T` or of type `var T`.
 
 
-  ```nim
+  ```nim test
   type
     Tree = object
       kids: seq[Tree]
@@ -526,13 +607,13 @@ for expressions of type `lent T` or of type `var T`.
   proc construct(kids: sink seq[Tree]): Tree =
     result = Tree(kids: kids)
     # converted into:
-    `=sink`(result.kids, kids); wasMoved(kids)
+    `=sink`(result.kids, kids); `=wasMoved`(kids)
     `=destroy`(kids)
 
   proc `[]`*(x: Tree; i: int): lent Tree =
     result = x.kids[i]
     # borrows from 'x', this is transformed into:
-    result = addr x.kids[i]
+    # result = addr x.kids[i]
     # This means 'lent' is like 'var T' a hidden pointer.
     # Unlike 'var' this hidden pointer cannot be used to mutate the object.
 
@@ -638,11 +719,11 @@ The ability to override a hook leads to a phase ordering problem:
     # error: destructor for 'f' called here before
     # it was seen in this module.
 
-  proc `=destroy`[T](f: var Foo[T]) =
+  proc `=destroy`[T](f: Foo[T]) =
     discard
   ```
 
-The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before
+The solution is to define ``proc `=destroy`[T](f: Foo[T])`` before
 it is used. The compiler generates implicit
 hooks for all types in *strategic places* so that an explicitly provided
 hook that comes too "late" can be detected reliably. These *strategic places*
@@ -663,7 +744,7 @@ The experimental `nodestroy`:idx: pragma inhibits hook injections. This can be
 used to specialize the object traversal in order to avoid deep recursions:
 
 
-  ```nim
+  ```nim test
   type Node = ref object
     x, y: int32
     left, right: Node
@@ -671,15 +752,15 @@ used to specialize the object traversal in order to avoid deep recursions:
   type Tree = object
     root: Node
 
-  proc `=destroy`(t: var Tree) {.nodestroy.} =
+  proc `=destroy`(t: Tree) {.nodestroy.} =
     # use an explicit stack so that we do not get stack overflows:
     var s: seq[Node] = @[t.root]
     while s.len > 0:
       let x = s.pop
       if x.left != nil: s.add(x.left)
       if x.right != nil: s.add(x.right)
-      # free the memory explicit:
-      dispose(x)
+      # free the memory explicitly:
+      `=dispose`(x)
     # notice how even the destructor for 's' is not called implicitly
     # anymore thanks to .nodestroy, so we have to call it on our own:
     `=destroy`(s)
diff --git a/doc/docgen.md b/doc/docgen.md
index 27530737a..3cc75fc18 100644
--- a/doc/docgen.md
+++ b/doc/docgen.md
@@ -9,17 +9,33 @@
 .. include:: rstcommon.rst
 .. contents::
 
+.. importdoc:: markdown_rst.md, compiler/docgen.nim
 
 Introduction
 ============
 
 This document describes the `documentation generation tools`:idx: built into
 the [Nim compiler](nimc.html), which can generate HTML, Latex and JSON output
-from input ``.nim`` files and projects, as well as HTML and LaTeX from input RST
-(reStructuredText) files. The output documentation will include the module
+from input ``.nim`` files and projects.
+The output documentation will include the module
 dependencies (`import`), any top-level documentation comments (`##`), and
 exported symbols (`*`), including procedures, types, and variables.
 
+===================   ==============
+command               output format
+===================   ==============
+`nim doc`:cmd:        ``.html`` HTML
+`nim doc2tex`:cmd:    ``.tex`` LaTeX
+`nim jsondoc`:cmd:    ``.json`` JSON
+===================   ==============
+
+Nim can generate HTML and LaTeX from input Markdown and
+RST (reStructuredText) files as well, which is intended for writing
+standalone documents like user's guides and technical specifications.
+See [Nim-flavored Markdown and reStructuredText] document for the description
+of this feature and particularly section [Command line usage] for the full
+list of supported commands.
+
 Quick start
 -----------
 
@@ -43,6 +59,7 @@ Generate HTML documentation for a whole project:
   # or `$nimcache/htmldocs` with `--usenimcache` which avoids clobbering your sources;
   # and likewise without `--project`.
   # Adding `-r` will open in a browser directly.
+  # Use `--showNonExports` to show non-exported fields of an exported type.
   ```
 
 Documentation Comments
@@ -64,10 +81,11 @@ Example:
     age: int
   ```
 
-Outputs::
-  Person* = object
-    name: string
-    age: int
+Outputs:
+
+    Person* = object
+      name: string
+      age: int
 
   This type contains a description of a person
 
@@ -102,6 +120,64 @@ won't influence RST formatting.
      ## Paragraph.
      ```
 
+Structuring output directories
+------------------------------
+
+Basic directory for output is set by `--outdir:OUTDIR`:option: switch,
+by default `OUTDIR` is ``htmldocs`` sub-directory in the directory of
+the processed file.
+
+There are 2 basic options as to how generated HTML output files are stored:
+
+1) complex hierarchy when docgen-compiling with `--project`:option:,
+   which follows directory structure of the project itself.
+   So `nim doc`:cmd: replicates project's directory structure
+   inside `--outdir:OUTDIR`:option: directory.
+   `--project`:option: is well suited for projects that have 1 main module.
+   File name clashes are impossible in this case.
+
+2) flattened structure, where user-provided script goes through all
+   needed input files and calls commands like `nim doc`:cmd:
+   with `--outdir:OUTDIR`:option: switch, thus putting all HTML (and
+   ``.idx``) files into 1 directory.
+
+   .. Important:: Make sure that you don't have files with same base name
+     like ``x.nim`` and ``x.md`` in the same package, otherwise you'll
+     have name conflict for ``x.html``.
+
+   .. Tip:: To structure your output directories and avoid file name
+     clashes you can split your project into
+     different *packages* -- parts of your repository that are
+     docgen-compiled with different `--outdir:OUTDIR`:option: options.
+
+     An example of such strategy is Nim repository itself which has:
+
+     * its stdlib ``.nim`` files from different directories and ``.md``
+       documentation from ``doc/`` directory are all docgen-compiled
+       into `--outdir:web/upload/<version>/`:option: directory
+     * its ``.nim`` files from ``compiler/`` directory are docgen-compiled
+       into `--outdir:web/upload/<version>/compiler/`:option: directory.
+       Interestingly, it's compiled with complex hierarchy using
+       `--project`:option: switch.
+
+     Contents of ``web/upload/<version>`` are then deployed into Nim's
+     Web server.
+
+     This output directory structure allows to work correctly with files like
+     ``compiler/docgen.nim`` (implementation) and ``doc/docgen.md`` (user
+     documentation) in 1 repository.
+
+
+Index files
+-----------
+
+Index (``.idx``) files are used for 2 different purposes:
+
+1. easy cross-referencing between different ``.nim`` and/or ``.md`` / ``.rst``
+   files described in [Nim external referencing]
+2. creating a whole-project index for searching of symbols and keywords,
+   see [Buildindex command].
+
 
 Document Types
 ==============
@@ -133,10 +209,11 @@ The `doc`:option: command:
   nim doc docgen_sample.nim
   ```
 
-Partial Output::
-  ...
-  proc helloWorld(times: int) {.raises: [], tags: [].}
-  ...
+Partial Output:
+
+    ...
+    proc helloWorld(times: int) {.raises: [], tags: [].}
+    ...
 
 The full output can be seen here: [docgen_sample.html](docgen_sample.html).
 It runs after semantic checking and includes pragmas attached implicitly by the
@@ -179,22 +256,23 @@ The `jsondoc`:option: command:
   nim jsondoc docgen_sample.nim
   ```
 
-Output::
-  {
-    "orig": "docgen_sample.nim",
-    "nimble": "",
-    "moduleDescription": "This module is a sample",
-    "entries": [
-      {
-        "name": "helloWorld",
-        "type": "skProc",
-        "line": 5,
-        "col": 0,
-        "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
-        "code": "proc helloWorld(times: int) {.raises: [], tags: [].}"
-      }
-    ]
-  }
+Output:
+
+    {
+      "orig": "docgen_sample.nim",
+      "nimble": "",
+      "moduleDescription": "This module is a sample",
+      "entries": [
+        {
+          "name": "helloWorld",
+          "type": "skProc",
+          "line": 5,
+          "col": 0,
+          "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
+          "code": "proc helloWorld(times: int) {.raises: [], tags: [].}"
+        }
+      ]
+    }
 
 Similarly to the old `doc`:option: command, the old `jsondoc`:option: command has been
 renamed to `jsondoc0`:option:.
@@ -205,39 +283,80 @@ The `jsondoc0`:option: command:
   nim jsondoc0 docgen_sample.nim
   ```
 
-Output::
-  [
-    {
-      "comment": "This module is a sample."
-    },
-    {
-      "name": "helloWorld",
-      "type": "skProc",
-      "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
-      "code": "proc helloWorld*(times: int)"
-    }
-  ]
+Output:
+
+    [
+      {
+        "comment": "This module is a sample."
+      },
+      {
+        "name": "helloWorld",
+        "type": "skProc",
+        "description": "Takes an integer and outputs as many &quot;hello world!&quot;s",
+        "code": "proc helloWorld*(times: int)"
+      }
+    ]
 
 Note that the `jsondoc`:option: command outputs its JSON without pretty-printing it,
 while `jsondoc0`:option: outputs pretty-printed JSON.
 
 
-Referencing Nim symbols: simple documentation links
-===================================================
-
-You can reference Nim identifiers from Nim documentation comments, currently
-only inside their ``.nim`` file (or inside a ``.rst`` file included from
-a ``.nim``). The point is that such links will be resolved automatically
-by `nim doc`:cmd: (or `nim jsondoc`:cmd: or `nim doc2tex`:cmd:).
+Simple documentation links
+==========================
+
+It's possible to use normal Markdown/RST syntax to *manually*
+reference Nim symbols using HTML anchors, however Nim has an *automatic*
+facility that makes referencing inside ``.nim`` and ``.md/.rst`` files and
+between them easy and seamless.
+The point is that such links will be resolved automatically
+by `nim doc`:cmd: (or `md2html`:option:, or `jsondoc`:option:,
+or `doc2tex`:option:, ...). And, unlike manual links, such automatic
+links **check** that their target exists -- a warning is emitted for
+any broken link, so you avoid broken links in your project.
+
+Nim treats both ``.md/.rst`` files and ``.nim`` modules (their doc comment
+part) as *documents* uniformly.
+Hence all directions of referencing are equally possible having the same syntax:
+
+1. ``.md/rst`` -> itself (internal). See [Markup local referencing].
+2. ``.md/rst`` -> external ``.md/rst``. See [Markup external referencing].
+   To summarize, referencing in `.md`/`.rst` files was already described in
+   [Nim-flavored Markdown and reStructuredText]
+   (particularly it described usage of index files for referencing),
+   while in this document we focus on Nim-specific details.
+3. ``.md/rst`` -> external ``.nim``. See [Nim external referencing].
+4. ``.nim`` -> itself (internal). See [Nim local referencing].
+5. ``.nim`` -> external ``.md/rst``. See [Markup external referencing].
+6. ``.nim`` -> external ``.nim``. See [Nim external referencing].
+
+To put it shortly, local referencing always works out of the box,
+external referencing requires to use ``.. importdoc:: <file>``
+directive to import `file` and to ensure that the corresponding
+``.idx`` file was generated.
+
+Syntax for referencing is basically the same as for normal markup.
+Recall from [Referencing] that our parser supports two equivalent syntaxes
+for referencing, Markdown and RST one.
+So to reference ``proc f`` one should use something like that,
+depending on markup type:
+
+    Markdown                    RST
+
+    Ref. [proc f] or [f]        Ref. `proc f`_ or just f_ for a one-word case
+
+Nim local referencing
+---------------------
+
+You can reference Nim identifiers from Nim documentation comments
+inside their ``.nim`` file (or inside a ``.rst`` file included from
+a ``.nim``).
 This pertains to any exported symbol like `proc`, `const`, `iterator`, etc.
-Syntax for referencing is basically a normal RST one: addition of
-underscore `_` to a *link text*.
-Link text is either one word or a group of words enclosed by backticks `\``
-(for a one word case backticks are usually omitted).
+Link text is either one word or a group of words enclosed by delimiters
+(brackets ``[...]`` for Markdown or backticks `\`...\`_` for RST).
 Link text will be displayed *as is* while *link target* will be set to
-the anchor \[*] of Nim symbol that corresponds to link text.
+the anchor [^1] of Nim symbol that corresponds to link text.
 
-\[*] anchors' format is described in [HTML anchor generation] section below.
+[^1] anchors' format is described in [HTML anchor generation] section below.
 
 If you have a constant:
 
@@ -247,10 +366,13 @@ If you have a constant:
 
 then it should be referenced in one of the 2 forms:
 
-A. non-qualified (no symbol kind specification)::
-     pi_
-B. qualified (with symbol kind specification)::
-     `const pi`_
+A. non-qualified (no symbol kind specification):
+
+       pi_
+
+B. qualified (with symbol kind specification):
+
+       `const pi`_
 
 For routine kinds there are more options. Consider this definition:
 
@@ -262,52 +384,52 @@ Generally following syntax is allowed for referencing `foo`:
 
 *  short (without parameters):
 
-   A. non-qualified::
+   A. non-qualified:
 
-        foo_
+          foo_
 
-   B. qualified::
+   B. qualified:
 
-        `proc foo`_
+          `proc foo`_
 
 *  longer variants (with parameters):
 
    A. non-qualified:
 
-      1) specifying parameters names::
+      1) specifying parameters names:
 
-          `foo(a, b)`_
+             `foo(a, b)`_
 
-      2) specifying parameters types::
+      2) specifying parameters types:
 
-          `foo(int, float)`_
+             `foo(int, float)`_
 
-      3) specifying both names and types::
+      3) specifying both names and types:
 
-          `foo(a: int, b: float)`_
+             `foo(a: int, b: float)`_
 
-      4) output parameter can also be specified if you wish::
+      4) output parameter can also be specified if you wish:
 
-          `foo(a: int, b: float): string`_
+             `foo(a: int, b: float): string`_
 
    B. qualified: all 4 options above are valid.
-      Particularly you can use the full format::
+      Particularly you can use the full format:
 
-        `proc foo(a: int, b: float): string`_
+          `proc foo(a: int, b: float): string`_
 
 .. Tip:: Avoid cluttering your text with extraneous information by using
-   one of shorter forms::
+   one of shorter forms:
 
-     binarySearch_
-     `binarySearch(a, key, cmp)`_
+       binarySearch_
+       `binarySearch(a, key, cmp)`_
 
    Brevity is better for reading! If you use a short form and have an
    ambiguity problem (see below) then just add some additional info.
 
-Symbol kind like `proc` can also be specified in the postfix form::
+Symbol kind like `proc` can also be specified in the postfix form:
 
-  `foo proc`_
-  `walkDir(d: string) iterator`_
+    `foo proc`_
+    `walkDir(d: string) iterator`_
 
 .. Warning:: An ambiguity in resolving documentation links may arise because of:
 
@@ -320,9 +442,9 @@ Symbol kind like `proc` can also be specified in the postfix form::
         `proc` and `template`. In this case they are split between their
         corresponding sections in output file. Qualified references are
         useful in this case -- just disambiguate by referring to these
-        sections explicitly::
+        sections explicitly:
 
-          See `foo proc`_ and `foo template`_.
+            See `foo proc`_ and `foo template`_.
 
       * because in Nim `proc` and `iterator` belong to different namespaces,
         so there can be a collision even if parameters are the same.
@@ -334,9 +456,9 @@ Symbol kind like `proc` can also be specified in the postfix form::
    (while procs have higher priority than other Nim symbol kinds).
 
 Generic parameters can also be used. All in all, this long form will be
-recognized fine::
+recognized fine:
 
-  `proc binarySearch*[T; K](a: openArray[T], key: K, cmp: proc(T, K)): int`_
+    `proc binarySearch*[T; K](a: openArray[T], key: K, cmp: proc(T, K)): int`_
 
 **Limitations**:
 
@@ -351,16 +473,16 @@ recognized fine::
       ```
 
    you cannot use names underlined by `~~` so it must be referenced with
-   ``cmp: proc(T, K)``. Hence these forms are valid::
+   ``cmp: proc(T, K)``. Hence these forms are valid:
 
-     `binarySearch(a: openArray[T], key: K, cmp: proc(T, K))`_
-     `binarySearch(openArray[T], K, proc(T, K))`_
-     `binarySearch(a, key, cmp)`_
+       `binarySearch(a: openArray[T], key: K, cmp: proc(T, K))`_
+       `binarySearch(openArray[T], K, proc(T, K))`_
+       `binarySearch(a, key, cmp)`_
 2. Default values in routine parameters are not recognized, one needs to
    specify the type and/or name instead. E.g. for referencing `proc f(x = 7)`
-   use one of the mentioned forms::
+   use one of the mentioned forms:
 
-     `f(int)`_ or `f(x)`_ or `f(x: int)`_.
+       `f(int)`_ or `f(x)`_ or `f(x: int)`_.
 3. Generic parameters must be given the same way as in the
    definition of referenced symbol.
 
@@ -376,27 +498,164 @@ recognized fine::
       func `[]`*[T](x: openArray[T]): T
       ```
 
-   A short form works without additional backticks::
+   A short form works without additional backticks:
 
-     `$`_
-     `[]`_
+       `$`_
+       `[]`_
 
    However for fully-qualified reference copy-pasting backticks (`) into other
    backticks will not work in our RST parser (because we use Markdown-like
    inline markup rules). You need either to delete backticks or keep
-   them and escape with backslash \\::
+   them and escape with backslash \\:
 
-      no backticks: `func $`_
-      escaped:  `func \`$\``_
-      no backticks: `func [][T](x: openArray[T]): T`_
-      escaped:  `func \`[]\`[T](x: openArray[T]): T`_
+       no backticks: `func $`_
+       escaped:  `func \`$\``_
+       no backticks: `func [][T](x: openArray[T]): T`_
+       escaped:  `func \`[]\`[T](x: openArray[T]): T`_
 
 .. Note:: Types that defined as `enum`, or `object`, or `tuple` can also be
-   referenced with those names directly (instead of `type`)::
+   referenced with those names directly (instead of `type`):
+
+       type CopyFlag = enum
+         ...
+       ## Ref. `CopyFlag enum`_
+
+Nim external referencing
+------------------------
+
+Just like for [Markup external referencing], which saves markup anchors,
+the Nim symbols are also saved in ``.idx`` files, so one needs
+to generate them beforehand, and they should be loaded by
+an ``.. importdoc::`` directive. Arguments to ``.. importdoc::`` is a
+comma-separated list of Nim modules or Markdown/RST documents.
+
+`--index:only`:option: tells Nim to only generate ``.idx`` file and
+do **not** attempt to generate HTML/LaTeX output.
+For ``.nim`` modules there are 2 alternatives to work with ``.idx`` files:
+
+1. using [Project switch] implies generation of ``.idx`` files,
+   however, if ``importdoc`` is called on upper modules as its arguments,
+   their ``.idx`` are not yet created. Thus one should generate **all**
+   required ``.idx`` first:
+     ```cmd
+     nim doc --project --index:only <main>.nim
+     nim doc --project <main>.nim
+     ```
+2. or run `nim doc --index:only <module.nim>`:cmd: command for **all** (used)
+   Nim modules in your project. Then run `nim doc <module.nim>` on them for
+   output HTML generation.
+
+   .. Warning:: A mere `nim doc --index:on`:cmd: may fail on an attempt to do
+      ``importdoc`` from another module (for which ``.idx`` was not yet
+      generated), that's why `--index:only`:option: shall be used instead.
+
+   For ``.md``/``.rst`` markup documents point 2 is the only option.
+
+Then, you can freely use something like this in ``your_module.nim``:
+
+  ```nim
+  ## .. importdoc::  user_manual.md, another_module.nim
+
+  ...
+  ## Ref. [some section from User Manual].
+
+  ...
+  ## Ref. [proc f]
+  ## (assuming you have a proc `f` in ``another_module``).
+  ```
+
+and compile it by `nim doc`:cmd:. Note that link text will
+be automatically prefixed by the module name of symbol,
+so you will see something like "Ref. [another_module: proc f](#)"
+in the generated output.
+
+It's also possible to reference a whole module by prefixing or
+suffixing full canonical module name with "module":
+
+    Ref. [module subdir/name] or [subdir/name module].
+
+Markup documents as a whole can be referenced just by their title
+(or by their file name if the title was not set) without any prefix.
+
+.. Tip:: During development process the stage of ``.idx`` files generation
+  can be done only *once*, after that you use already generated ``.idx``
+  files while working with a document *being developed* (unless you do
+  incompatible changes to *referenced* documents).
+
+.. Hint:: After changing a *referenced* document file one may need
+  to regenerate its corresponding ``.idx`` file to get correct results.
+  Of course, when referencing *internally* inside any given ``.nim`` file,
+  it's not needed, one can even immediately use any freshly added anchor
+  (a document's own ``.idx`` file is not used for resolving its internal links).
+
+If an ``importdoc`` directive fails to find a ``.idx``, then an error
+is emitted.
+
+In case of such compilation failures please note that:
 
-     type CopyFlag = enum
+* **all** relative paths, given to ``importdoc``, relate to insides of
+  ``OUTDIR``, and **not** project's directory structure.
+
+* ``importdoc`` searches for ``.idx`` in `--outdir:OUTDIR`:option: directory
+  (``htmldocs`` by default) and **not** around original modules, so:
+
+  .. Tip:: look into ``OUTDIR`` to understand what's going on.
+
+* also keep in mind that ``.html`` and ``.idx`` files should always be
+  output to the same directory, so check this and, if it's not true, check
+  that both runs *with* and *without* `--index:only`:option: have all
+  other options the same.
+
+To summarize, for 2 basic options of [Structuring output directories]
+compilation options are different:
+
+1) complex hierarchy with `--project`:option: switch.
+
+   As the **original** project's directory structure is replicated in
+   `OUTDIR`, all passed paths are related to this structure also.
+
+   E.g. if a module ``path1/module.nim`` does
+   ``.. importdoc:: path2/another.nim`` then docgen tries to load file
+   ``OUTDIR/path1/path2/another.idx``.
+
+   .. Note:: markup documents are just placed into the specified directory
+     `OUTDIR`:option: by default (i.e. they are **not** affected by
+     `--project`:option:), so if you have ``PROJECT/doc/manual.md``
+     document and want to use complex hierarchy (with ``doc/``),
+     compile it with `--docroot`:option:\:
+       ```cmd
+       # 1st stage
+       nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \
+            --index:only PROJECT/doc/manual.md
        ...
-     ## Ref. `CopyFlag enum`_
+       # 2nd stage
+       nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \
+                         PROJECT/doc/manual.md
+       ```
+
+     Then the output file will be placed as ``OUTDIR/doc/manual.idx``.
+     So if you have ``PROJECT/path1/module.nim``, then ``manual.md`` can
+     be referenced as ``../doc/manual.md``.
+
+2) flattened structure.
+
+   E.g. if a module ``path1/module.nim`` does
+   ``.. importdoc:: path2/another.nim`` then docgen tries to load
+   ``OUTDIR/path2/another.idx``, so the path ``path1``
+   does not matter and providing ``path2`` can be useful only
+   in the case it contains another package that was placed there
+   using `--outdir:OUTDIR/path2`:option:.
+
+   The links' text will be prefixed as ``another: ...`` in both cases.
+
+   .. Warning:: Again, the same `--outdir:OUTDIR`:option: option should
+     be provided to both `doc --index:only`:option: /
+     `md2html --index:only`:option: and final generation by
+     `doc`:option:/`md2html`:option: inside 1 package.
+
+To temporarily disable ``importdoc``, e.g. if you don't need
+correct link resolution at the moment, use a `--noImportdoc`:option: switch
+(only warnings about unresolved links will be generated for external references).
 
 Related Options
 ===============
@@ -427,10 +686,21 @@ index file is line-oriented (newlines have to be escaped). Each line
 represents a tab-separated record of several columns, the first two mandatory,
 the rest optional. See the [Index (idx) file format] section for details.
 
+.. Note:: `--index`:option: switch only affects creation of ``.idx``
+  index files, while user-searchable Index HTML file is created by
+  `buildIndex`:option: command.
+
+Buildindex command
+------------------
+
 Once index files have been generated for one or more modules, the Nim
-compiler command `buildIndex directory` can be run to go over all the index
+compiler command `nim buildIndex directory`:cmd: can be run to go over all the index
 files in the specified directory to generate a [theindex.html](theindex.html)
-file.
+file:
+
+  ```cmd
+  nim buildIndex -o:path/to/htmldocs/theindex.html path/to/htmldocs
+  ```
 
 See source switch
 -----------------
@@ -561,10 +831,22 @@ references so they can be later concatenated into a big index file with
 the file format in detail.
 
 Index files are line-oriented and tab-separated (newline and tab characters
-have to be escaped). Each line represents a record with at least two fields
-but can have up to four (additional columns are ignored). The content of these
-columns is:
-
+have to be escaped). Each line represents a record with 6 fields.
+The content of these columns is:
+
+0. Discriminator tag denoting type of the index entry, allowed values are:
+   `markupTitle`
+   : a title for ``.md``/``.rst`` document
+   `nimTitle`
+   : a title of ``.nim`` module
+   `heading`
+   : heading of sections, can be both in Nim and markup files
+   `idx`
+   : terms marked with :idx: role
+   `nim`
+   : a Nim symbol
+   `nimgrp`
+   : a Nim group for overloadable symbols like `proc`s
 1. Mandatory term being indexed. Terms can include quoting according to
    Nim's rules (e.g. \`^\`).
 2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``).
@@ -574,29 +856,20 @@ columns is:
    not for an API symbol but for a TOC entry.
 4. Optional title or description of the hyperlink. Browsers usually display
    this as a tooltip after hovering a moment over the hyperlink.
+5. A line number of file where the entry was defined.
 
-The index generation tools try to differentiate between documentation
-generated from ``.nim`` files and documentation generated from ``.txt`` or
-``.rst`` files. The former are always closely related to source code and
-consist mainly of API entries. The latter are generic documents meant for
-human reading.
-
-To differentiate both types (documents and APIs), the index generator will add
-to the index of documents an entry with the title of the document. Since the
-title is the topmost element, it will be added with a second field containing
-just the filename without any HTML anchor.  By convention, this entry without
-anchor is the *title entry*, and since entries in the index file are added as
-they are scanned, the title entry will be the first line. The title for APIs
-is not present because it can be generated concatenating the name of the file
-to the word **Module**.
-
-Normal symbols are added to the index with surrounding whitespaces removed. An
-exception to this are the table of content (TOC) entries. TOC entries are added to
-the index file with their third column having as much prefix spaces as their
-level is in the TOC (at least 1 character). The prefix whitespace helps to
-filter TOC entries from API or text symbols. This is important because the
-amount of spaces is used to replicate the hierarchy for document TOCs in the
-final index, and TOC entries found in ``.nim`` files are discarded.
+The index generation tools differentiate between documentation
+generated from ``.nim`` files and documentation generated from ``.md`` or
+``.rst`` files by tag `nimTitle` or `markupTitle` in the 1st line of
+the ``.idx`` file.
+
+.. TODO Normal symbols are added to the index with surrounding whitespaces removed. An
+  exception to this are the table of content (TOC) entries. TOC entries are added to
+  the index file with their third column having as much prefix spaces as their
+  level is in the TOC (at least 1 character). The prefix whitespace helps to
+  filter TOC entries from API or text symbols. This is important because the
+  amount of spaces is used to replicate the hierarchy for document TOCs in the
+  final index, and TOC entries found in ``.nim`` files are discarded.
 
 
 Additional resources
@@ -608,6 +881,8 @@ Additional resources
   [Markdown and RST markup languages](markdown_rst.html), which also
   contains the list of implemented features of these markup languages.
 
+* the implementation is in [module compiler/docgen].
+
 The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and
 ``config/nimdoc.tex.cfg`` configuration files. You can add and modify these
 files to your project to change the look of the docgen output.
diff --git a/doc/docgen_sample.nim b/doc/docgen_sample.nim
index 7a167cb45..06b8d7f8e 100644
--- a/doc/docgen_sample.nim
+++ b/doc/docgen_sample.nim
@@ -1,6 +1,6 @@
 ## This module is a sample.
 
-import strutils
+import std/strutils
 
 proc helloWorld*(times: int) =
   ## Takes an integer and outputs
diff --git a/doc/drnim.md b/doc/drnim.md
index 48a633446..1dc2b550f 100644
--- a/doc/drnim.md
+++ b/doc/drnim.md
@@ -66,9 +66,9 @@ without additional annotations:
   ```
 
 This program contains a famous "index out of bounds" bug. DrNim
-detects it and produces the following error message::
+detects it and produces the following error message:
 
-  cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck]
+    cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck]
 
 In other words for `i == 0` and `a.len == 0` (for example!) there would be
 an index out of bounds error.
@@ -146,9 +146,9 @@ Example: insertionSort
 Unfortunately, the invariants required to prove that this code is correct take more
 code than the imperative instructions. However, this effort can be compensated
 by the fact that the result needs very little testing. Be aware though that
-DrNim only proves that after `insertionSort` this condition holds::
+DrNim only proves that after `insertionSort` this condition holds:
 
-  forall(i in 1..<a.len, a[i-1] <= a[i])
+    forall(i in 1..<a.len, a[i-1] <= a[i])
 
 
 This is required, but not sufficient to describe that a `sort` operation
@@ -170,23 +170,23 @@ Syntax of propositions
 ======================
 
 The basic syntax is `ensures|requires|invariant: <prop>`.
-A `prop` is either a comparison or a compound::
-
-  prop = nim_bool_expression
-       | prop 'and' prop
-       | prop 'or' prop
-       | prop '->' prop # implication
-       | prop '<->' prop
-       | 'not' prop
-       | '(' prop ')' # you can group props via ()
-       | forallProp
-       | existsProp
-
-  forallProp = 'forall' '(' quantifierList ',' prop ')'
-  existsProp = 'exists' '(' quantifierList ',' prop ')'
-
-  quantifierList = quantifier (',' quantifier)*
-  quantifier = <new identifier> 'in' nim_iteration_expression
+A `prop` is either a comparison or a compound:
+
+    prop = nim_bool_expression
+         | prop 'and' prop
+         | prop 'or' prop
+         | prop '->' prop # implication
+         | prop '<->' prop
+         | 'not' prop
+         | '(' prop ')' # you can group props via ()
+         | forallProp
+         | existsProp
+
+    forallProp = 'forall' '(' quantifierList ',' prop ')'
+    existsProp = 'exists' '(' quantifierList ',' prop ')'
+
+    quantifierList = quantifier (',' quantifier)*
+    quantifier = <new identifier> 'in' nim_iteration_expression
 
 
 `nim_iteration_expression` here is an ordinary expression of Nim code
diff --git a/doc/estp.md b/doc/estp.md
index 550194d2d..8a986bdf3 100644
--- a/doc/estp.md
+++ b/doc/estp.md
@@ -28,7 +28,7 @@ Otherwise your program is profiled.
 
 ```nim
 when compileOption("profiler"):
-  import nimprof
+  import std/nimprof
 ```
 
 After your program has finished the profiler will create a
@@ -70,138 +70,137 @@ the trace acts like an explanation; in traditional profilers you can only find
 expensive leaf functions easily but the *reason* why they are invoked
 often remains mysterious.
 
-::
-  total executions of each stack trace:
-  Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%]
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%]
-    updateCrc32
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%]
-    updateCrc32
-    updateCrc32
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%]
-    crcFromFile
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%]
-    updateCrc32
-    crcFromFile
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%]
-    pop
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    nim
-  Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%]
-    doOperation
-    forAllChildrenAux
-    pop
-    newCrcFromRopeAux
-    crcFromRope
-    writeRopeIfNotEqual
-    shouldRecompile
-    writeModule
-    myClose
-    closePasses
-    processModule
-    CompileModule
-    CompileProject
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
-    ...
-    nim
-  Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%]
-    Contains
-    isAccessible
-    interiorAllocatedPtr
-    gcMark
-    markStackAndRegisters
-    collectCTBody
-    collectCT
-    rawNewObj
-    newObj
-    newNode
-    copyTree
-    matchesAux
-    matches
-    resolveOverloads
-    semOverloadedCall
-    semOverloadedCallAnalyseEffects
-    ...
-    CommandCompileToC
-    MainCommand
-    HandleCmdLine
+    total executions of each stack trace:
+    Entry: 0/3391 Calls: 84/4160 = 2.0% [sum: 84; 84/4160 = 2.0%]
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 1/3391 Calls: 46/4160 = 1.1% [sum: 130; 130/4160 = 3.1%]
+      updateCrc32
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 2/3391 Calls: 41/4160 = 0.99% [sum: 171; 171/4160 = 4.1%]
+      updateCrc32
+      updateCrc32
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 3/3391 Calls: 41/4160 = 0.99% [sum: 212; 212/4160 = 5.1%]
+      crcFromFile
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 4/3391 Calls: 41/4160 = 0.99% [sum: 253; 253/4160 = 6.1%]
+      updateCrc32
+      crcFromFile
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 5/3391 Calls: 32/4160 = 0.77% [sum: 285; 285/4160 = 6.9%]
+      pop
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      nim
+    Entry: 6/3391 Calls: 17/4160 = 0.41% [sum: 302; 302/4160 = 7.3%]
+      doOperation
+      forAllChildrenAux
+      pop
+      newCrcFromRopeAux
+      crcFromRope
+      writeRopeIfNotEqual
+      shouldRecompile
+      writeModule
+      myClose
+      closePasses
+      processModule
+      CompileModule
+      CompileProject
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
+      ...
+      nim
+    Entry: 7/3391 Calls: 14/4160 = 0.34% [sum: 316; 316/4160 = 7.6%]
+      Contains
+      isAccessible
+      interiorAllocatedPtr
+      gcMark
+      markStackAndRegisters
+      collectCTBody
+      collectCT
+      rawNewObj
+      newObj
+      newNode
+      copyTree
+      matchesAux
+      matches
+      resolveOverloads
+      semOverloadedCall
+      semOverloadedCallAnalyseEffects
+      ...
+      CommandCompileToC
+      MainCommand
+      HandleCmdLine
diff --git a/doc/filelist.txt b/doc/filelist.txt
index 74995f154..5522414fe 100644
--- a/doc/filelist.txt
+++ b/doc/filelist.txt
@@ -31,16 +31,14 @@ semstmts        contains the semantic checking phase for statements
 semtypes        contains the semantic checking phase for types
 seminst         instantiation of generic procs and types
 semfold         contains code to deal with constant folding
-semthreads      deep program analysis for threads
-evals           contains an AST interpreter for compile time evaluation
+sempass2        Second semantic checking pass over the AST
+vm              contains an AST interpreter for compile time evaluation
 pragmas         semantic checking of pragmas
 
 idents          implements a general mapping from identifiers to an internal
                 representation (`PIdent`) that is used so that a simple
                 id-comparison suffices to establish whether two Nim
                 identifiers are equivalent
-ropes           implements long strings represented as trees for
-                lazy evaluation; used mainly by the code generators
 
 transf          transformations on the AST that need to be done before
                 code generation
diff --git a/doc/filters.md b/doc/filters.md
index bf45788bc..9482b0b47 100644
--- a/doc/filters.md
+++ b/doc/filters.md
@@ -10,15 +10,15 @@ A `Source Code Filter (SCF)`  transforms the input character stream to an in-mem
 output stream before parsing. A filter can be used to provide templating
 systems or preprocessors.
 
-To use a filter for a source file the `#?` notation is used::
+To use a filter for a source file the `#?` notation is used:
 
-  #? stdtmpl(subsChar = '$', metaChar = '#')
-  #proc generateXML(name, age: string): string =
-  #  result = ""
-  <xml>
-    <name>$name</name>
-    <age>$age</age>
-  </xml>
+    #? stdtmpl(subsChar = '$', metaChar = '#')
+    #proc generateXML(name, age: string): string =
+    #  result = ""
+    <xml>
+      <name>$name</name>
+      <age>$age</age>
+    </xml>
 
 As the example shows, passing arguments to a filter can be done
 just like an ordinary procedure call with named or positional arguments. The
@@ -50,15 +50,15 @@ In your `main.nim`:
 Pipe operator
 =============
 
-Filters can be combined with the `|` pipe operator::
+Filters can be combined with the `|` pipe operator:
 
-  #? strip(startswith="<") | stdtmpl
-  #proc generateXML(name, age: string): string =
-  #  result = ""
-  <xml>
-    <name>$name</name>
-    <age>$age</age>
-  </xml>
+    #? strip(startswith="<") | stdtmpl
+    #proc generateXML(name, age: string): string =
+    #  result = ""
+    <xml>
+      <name>$name</name>
+      <age>$age</age>
+    </xml>
 
 
 Available filters
@@ -123,31 +123,31 @@ Parameters and their defaults:
 * `toString: string = "$"`
   : the operation that is applied to each expression
 
-Example::
-
-  #? stdtmpl | standard
-  #proc generateHTMLPage(title, currentTab, content: string,
-  #                      tabs: openArray[string]): string =
-  #  result = ""
-  <head><title>$title</title></head>
-  <body>
-    <div id="menu">
-      <ul>
-    #for tab in items(tabs):
-      #if currentTab == tab:
-      <li><a id="selected"
-      #else:
-      <li><a
-      #end if
-      href="${tab}.html">$tab</a></li>
-    #end for
-      </ul>
-    </div>
-    <div id="content">
-      $content
-      A dollar: $$.
-    </div>
-  </body>
+Example:
+
+    #? stdtmpl | standard
+    #proc generateHTMLPage(title, currentTab, content: string,
+    #                      tabs: openArray[string]): string =
+    #  result = ""
+    <head><title>$title</title></head>
+    <body>
+      <div id="menu">
+        <ul>
+      #for tab in items(tabs):
+        #if currentTab == tab:
+        <li><a id="selected"
+        #else:
+        <li><a
+        #end if
+        href="${tab}.html">$tab</a></li>
+      #end for
+        </ul>
+      </div>
+      <div id="content">
+        $content
+        A dollar: $$.
+      </div>
+    </body>
 
 The filter transforms this into:
 
@@ -183,36 +183,36 @@ whitespace) is converted to a string literal that is added to `result`.
 The substitution character introduces a Nim expression *e* within the
 string literal. *e* is converted to a string with the *toString* operation
 which defaults to `$`. For strong type checking, set `toString` to the
-empty string. *e* must match this PEG pattern::
+empty string. *e* must match this PEG pattern:
 
-  e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
-  x <- '{' x+ '}' / [^}]*
+    e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}'
+    x <- '{' x+ '}' / [^}]*
 
 To produce a single substitution character it has to be doubled: `$$`
 produces `$`.
 
 The template engine is quite flexible. It is easy to produce a procedure that
-writes the template code directly to a file::
-
-  #? stdtmpl(emit="f.write") | standard
-  #proc writeHTMLPage(f: File, title, currentTab, content: string,
-  #                   tabs: openArray[string]) =
-  <head><title>$title</title></head>
-  <body>
-    <div id="menu">
-      <ul>
-    #for tab in items(tabs):
-      #if currentTab == tab:
-      <li><a id="selected"
-      #else:
-      <li><a
-      #end if
-      href="${tab}.html" title = "$title - $tab">$tab</a></li>
-    #end for
-      </ul>
-    </div>
-    <div id="content">
-      $content
-      A dollar: $$.
-    </div>
-  </body>
+writes the template code directly to a file:
+
+    #? stdtmpl(emit="f.write") | standard
+    #proc writeHTMLPage(f: File, title, currentTab, content: string,
+    #                   tabs: openArray[string]) =
+    <head><title>$title</title></head>
+    <body>
+      <div id="menu">
+        <ul>
+      #for tab in items(tabs):
+        #if currentTab == tab:
+        <li><a id="selected"
+        #else:
+        <li><a
+        #end if
+        href="${tab}.html" title = "$title - $tab">$tab</a></li>
+      #end for
+        </ul>
+      </div>
+      <div id="content">
+        $content
+        A dollar: $$.
+      </div>
+    </body>
diff --git a/doc/grammar.txt b/doc/grammar.txt
index 878a4e5e1..51b3e0053 100644
--- a/doc/grammar.txt
+++ b/doc/grammar.txt
@@ -1,5 +1,5 @@
 # This file is generated by compiler/parser.nim.
-module = stmt ^* (';' / IND{=})
+module = complexOrSimpleStmt ^* (';' / IND{=})
 comma = ',' COMMENT?
 semicolon = ';' COMMENT?
 colon = ':' COMMENT?
@@ -7,7 +7,7 @@ colcom = ':' COMMENT?
 operator =  OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9
          | 'or' | 'xor' | 'and'
          | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from'
-         | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..'
+         | 'div' | 'mod' | 'shl' | 'shr' | 'not' | '..'
 prefixOperator = operator
 optInd = COMMENT? IND?
 optPar = (IND{>} | IND{=})?
@@ -26,11 +26,16 @@ operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 |
             'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' |
             'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor'
 symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`'
-       | IDENT | KEYW
-exprColonEqExpr = expr (':'|'=' expr)?
+       | IDENT | 'addr' | 'type' | 'static'
+symbolOrKeyword = symbol | KEYW
+exprColonEqExpr = expr ((':'|'=') expr
+                       / doBlock extraPostExprBlock*)?
+exprEqExpr = expr ('=' expr
+                  / doBlock extraPostExprBlock*)?
 exprList = expr ^+ comma
+optionalExprList = expr ^* comma
 exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)?
-qualifiedIdent = symbol ('.' optInd symbol)?
+qualifiedIdent = symbol ('.' optInd symbolOrKeyword)?
 setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}'
 castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') /
 parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try'
@@ -40,7 +45,8 @@ par = '(' optInd
           ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';'
           | ';' (ifExpr / complexOrSimpleStmt) ^+ ';'
           | pragmaStmt
-          | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
+          | simpleExpr ( (doBlock extraPostExprBlock*)
+                       | ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? )
                        | (':' expr (',' exprColonEqExpr     ^+ ',' )? ) ) )
           optPar ')'
 literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT
@@ -56,29 +62,30 @@ identOrLiteral = generalizedLit | symbol | literal
 tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')'
 arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']'
 primarySuffix = '(' (exprColonEqExpr comma?)* ')'
-      | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
-      | DOTLIKEOP optInd symbol generalizedLit?
+      | '.' optInd symbolOrKeyword ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit?
+      | DOTLIKEOP optInd symbolOrKeyword generalizedLit?
       | '[' optInd exprColonEqExprList optPar ']'
       | '{' optInd exprColonEqExprList optPar '}'
-      | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax
 pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}')
 identVis = symbol OPR?  # postfix position
-identVisDot = symbol '.' optInd symbol OPR?
+identVisDot = symbol '.' optInd symbolOrKeyword OPR?
 identWithPragma = identVis pragma?
 identWithPragmaDot = identVisDot pragma?
 declColonEquals = identWithPragma (comma identWithPragma)* comma?
-                  (':' optInd typeDesc)? ('=' optInd expr)?
+                  (':' optInd typeDescExpr)? ('=' optInd expr)?
 identColonEquals = IDENT (comma IDENT)* comma?
-     (':' optInd typeDesc)? ('=' optInd expr)?)
-tupleDecl = 'tuple'
-    '[' optInd  (identColonEquals (comma/semicolon)?)*  optPar ']' |
-    COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?
+     (':' optInd typeDescExpr)? ('=' optInd expr)?)
+tupleTypeBracket = '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']'
+tupleType = 'tuple' tupleTypeBracket
+tupleDecl = 'tuple' (tupleTypeBracket /
+    COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)?)
 paramList = '(' declColonEquals ^* (comma/semicolon) ')'
 paramListArrow = paramList? ('->' optInd typeDesc)?
 paramListColon = paramList? (':' optInd typeDesc)?
 doBlock = 'do' paramListArrow pragma? colcom stmt
 routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)?
-forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt
+routineType = ('proc' | 'iterator') paramListColon pragma?
+forStmt = 'for' ((varTuple / identWithPragma) ^+ comma) 'in' expr colcom stmt
 forExpr = forStmt
 expr = (blockExpr
       | ifExpr
@@ -87,25 +94,33 @@ expr = (blockExpr
       | forExpr
       | tryExpr)
       / simpleExpr
-primary = operatorB primary primarySuffix* |
-          tupleDecl | routineExpr | enumDecl
-          objectDecl | conceptDecl | ('bind' primary)
-          ('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary
-        /  prefixOperator* identOrLiteral primarySuffix*
-typeDesc = simpleExpr ('not' expr)?
-typeDefAux = simpleExpr ('not' expr
-                        | postExprBlocks)?
-postExprBlocks = ':' stmt? ( IND{=} doBlock
-                           | IND{=} 'of' exprList ':' stmt
-                           | IND{=} 'elif' expr ':' stmt
-                           | IND{=} 'except' exprList ':' stmt
-                           | IND{=} 'finally' ':' stmt
-                           | IND{=} 'else' ':' stmt )*
-exprStmt = simpleExpr
-         (( '=' optInd expr colonBody? )
-         / ( expr ^+ comma
-             postExprBlocks
-           ))?
+simplePrimary = SIGILLIKEOP? identOrLiteral primarySuffix*
+commandStart = &('`'|IDENT|literal|'cast'|'addr'|'type'|'var'|'out'|
+                 'static'|'enum'|'tuple'|'object'|'proc')
+primary = simplePrimary (commandStart expr (doBlock extraPostExprBlock*)?)?
+        / operatorB primary
+        / routineExpr
+        / rawTypeDesc
+        / prefixOperator primary
+rawTypeDesc = (tupleType | routineType | 'enum' | 'object' |
+                ('var' | 'out' | 'ref' | 'ptr' | 'distinct') typeDesc?)
+                ('not' primary)?
+typeDescExpr = (routineType / simpleExpr) ('not' primary)?
+typeDesc = rawTypeDesc / typeDescExpr
+typeDefValue = ((tupleDecl | enumDecl | objectDecl | conceptDecl |
+                 ('ref' | 'ptr' | 'distinct') (tupleDecl | objectDecl))
+               / (simpleExpr (exprEqExpr ^+ comma postExprBlocks?)?))
+               ('not' primary)?
+extraPostExprBlock = ( IND{=} doBlock
+                     | IND{=} 'of' exprList ':' stmt
+                     | IND{=} 'elif' expr ':' stmt
+                     | IND{=} 'except' optionalExprList ':' stmt
+                     | IND{=} 'finally' ':' stmt
+                     | IND{=} 'else' ':' stmt )
+postExprBlocks = (doBlock / ':' (extraPostExprBlock / stmt)) extraPostExprBlock*
+exprStmt = simpleExpr postExprBlocks?
+         / simplePrimary (exprEqExpr ^+ comma) postExprBlocks?
+         / simpleExpr '=' optInd (expr postExprBlocks?)
 importStmt = 'import' optInd expr
               ((comma expr)*
               / 'except' optInd (expr ^+ comma))
@@ -125,9 +140,9 @@ condStmt = expr colcom stmt COMMENT?
            (IND{=} 'else' colcom stmt)?
 ifStmt = 'if' condStmt
 whenStmt = 'when' condStmt
-condExpr = expr colcom expr optInd
-        ('elif' expr colcom expr optInd)*
-         'else' colcom expr
+condExpr = expr colcom stmt optInd
+        ('elif' expr colcom stmt optInd)*
+         'else' colcom stmt
 ifExpr = 'if' condExpr
 whenExpr = 'when' condExpr
 whileStmt = 'while' expr colcom stmt
@@ -139,10 +154,10 @@ caseStmt = 'case' expr ':'? COMMENT?
             (IND{>} ofBranches DED
             | IND{=} ofBranches)
 tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally')
-           (IND{=}? 'except' exprList colcom stmt)*
+           (IND{=}? 'except' optionalExprList colcom stmt)*
            (IND{=}? 'finally' colcom stmt)?
 tryExpr = 'try' colcom stmt &(optInd 'except'|'finally')
-           (optInd 'except' exprList colcom stmt)*
+           (optInd 'except' optionalExprList colcom stmt)*
            (optInd 'finally' colcom stmt)?
 blockStmt = 'block' symbol? colcom stmt
 blockExpr = 'block' symbol? colcom stmt
@@ -166,19 +181,19 @@ objectBranch = 'of' exprList colcom objectPart
 objectBranches = objectBranch (IND{=} objectBranch)*
                       (IND{=} 'elif' expr colcom objectPart)*
                       (IND{=} 'else' colcom objectPart)?
-objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT?
+objectCase = 'case' declColonEquals ':'? COMMENT?
             (IND{>} objectBranches DED
             | IND{=} objectBranches)
 objectPart = IND{>} objectPart^+IND{=} DED
            / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals
 objectDecl = 'object' ('of' typeDesc)? COMMENT? objectPart
-conceptParam = ('var' | 'out')? symbol
+conceptParam = ('var' | 'out' | 'ptr' | 'ref' | 'static' | 'type')? symbol
 conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')?
               &IND{>} stmt
-typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux
-            indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux
+typeDef = identVisDot genericParamList? pragma '=' optInd typeDefValue
             indAndComment?
-varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr
+varTupleLhs = '(' optInd (identWithPragma / varTupleLhs) ^+ comma optPar ')' (':' optInd typeDescExpr)?
+varTuple = varTupleLhs '=' optInd expr
 colonBody = colcom stmt postExprBlocks?
 variable = (varTuple / identColonEquals) colonBody? indAndComment
 constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment
diff --git a/doc/idetools.md b/doc/idetools.md
index 8f4b3e995..0388a76c0 100644
--- a/doc/idetools.md
+++ b/doc/idetools.md
@@ -36,13 +36,17 @@ Specifying the location of the query
 
 All of the available idetools commands require you to specify a
 query location through the `--track` or `--trackDirty` switches.
-The general idetools invocations are::
+The general idetools invocations are:
 
-    nim idetools --track:FILE,LINE,COL <switches> proj.nim
+  ```cmd
+  nim idetools --track:FILE,LINE,COL <switches> proj.nim
+  ```
 
-Or::
+Or:
 
-    nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
+  ```cmd
+  nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
+  ```
 
 `proj.nim`
 :   This is the main *project* filename. Most of the time you will
@@ -178,14 +182,18 @@ results of the compilation, and subsequent queries should be fast
 in the millisecond range, thus being responsive enough for IDEs.
 
 If you want to start the server using stdin/stdout as communication
-you need to type::
+you need to type:
 
-    nim serve --server.type:stdin proj.nim
+  ```cmd
+  nim serve --server.type:stdin proj.nim
+  ```
 
-If you want to start the server using tcp and a port, you need to type::
+If you want to start the server using tcp and a port, you need to type:
 
-    nim serve --server.type:tcp --server.port:6000 \
+  ```cmd
+  nim serve --server.type:tcp --server.port:6000 \
       --server.address:hostname proj.nim
+  ```
 
 In both cases the server will start up and await further commands.
 The syntax of the commands you can now send to the server is
@@ -542,10 +550,12 @@ Running the test suite
 
 At the moment idetools support is still in development so the test
 suite is not integrated with the main test suite and you have to
-run it manually. First you have to compile the tester::
+run it manually. First you have to compile the tester:
 
-	$ cd my/nim/checkout/tests
-	$ nim c testament/caasdriver.nim
+  ```cmd
+  $ cd my/nim/checkout/tests
+  $ nim c testament/caasdriver.nim
+  ```
 
 Running the `caasdriver` without parameters will attempt to process
 all the test cases in all three operation modes. If a test succeeds
@@ -567,9 +577,11 @@ If you don't want to run all the test case files you can pass any
 substring as a parameter to `caasdriver`. Only files matching the
 passed substring will be run. The filtering doesn't use any globbing
 metacharacters, it's a plain match. For example, to run only
-`*-compile*.txt` tests in verbose mode::
+`*-compile*.txt` tests in verbose mode:
 
-	./caasdriver verbose -compile
+  ```cmd
+  ./caasdriver verbose -compile
+  ```
 
 
 Test case file format
diff --git a/doc/intern.md b/doc/intern.md
index fe2e59f51..6b16bc71f 100644
--- a/doc/intern.md
+++ b/doc/intern.md
@@ -107,6 +107,10 @@ You can also bisect using custom options to build the compiler, for example if
 you don't need a debug version of the compiler (which runs slower), you can replace
 `./koch temp`:cmd: by explicit compilation command, see [Bootstrapping the compiler].
 
+See also:
+
+- Crossplatform C/Cpp/Valgrind/JS Bisect in GitHub: https://github.com/juancarlospaco/nimrun-action#examples
+
 
 Building an instrumented compiler
 ---------------------------------
@@ -364,7 +368,7 @@ Files that may need changed for your platform include:
   Add os/cpu to `Project.Platforms` field.
 * `lib/system/platforms.nim`
   Add os/cpu.
-* `lib/pure/include/osseps.nim`
+* `std/private/osseps.nim`
   Add os specializations.
 * `lib/pure/distros.nim`
   Add os, package handler.
@@ -593,14 +597,14 @@ Integer literals
 In Nim, there is a redundant way to specify the type of an
 integer literal. First, it should be unsurprising that every
 node has a node kind. The node of an integer literal can be any of the
-following values::
+following values:
 
     nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit,
     nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit
 
 On top of that, there is also the `typ` field for the type. The
 kind of the `typ` field can be one of the following ones, and it
-should be matching the literal kind::
+should be matching the literal kind:
 
     tyInt, tyInt8, tyInt16, tyInt32, tyInt64, tyUInt, tyUInt8,
     tyUInt16, tyUInt32, tyUInt64
@@ -656,7 +660,6 @@ pointing back to the integer literal node in the ast containing the
 integer value. These are the properties that hold true for integer
 literal types.
 
-::
     n.kind == nkIntLit
     n.typ.kind == tyInt
     n.typ.n == n
diff --git a/doc/lib.md b/doc/lib.md
index 468ee84e3..1507bbaac 100644
--- a/doc/lib.md
+++ b/doc/lib.md
@@ -37,15 +37,6 @@ Automatic imports
   implicitly by the compiler. Do not import it directly. It relies on compiler
   magic to work.
 
-* [threads](threads.html)
-  Basic Nim thread support. **Note:** This is part of the system module. Do not
-  import it explicitly. Enabled with `--threads:on`:option:.
-
-* [channels_builtin](channels_builtin.html)
-  Nim message passing support for threads. **Note:** This is part of the
-  system module. Do not import it explicitly. Enabled with `--threads:on`:option:.
-
-
 Core
 ----
 
@@ -55,14 +46,18 @@ Core
 * [bitops](bitops.html)
   Provides a series of low-level methods for bit manipulation.
 
+* [compilesettings](compilesettings.html)
+  Querying the compiler about diverse configuration settings from code.
+
 * [cpuinfo](cpuinfo.html)
-  This module implements procs to determine the number of CPUs / cores.
+  Procs to determine the number of CPUs / cores.
 
-* [endians](endians.html)
-  This module contains helpers that deal with different byte orders.
+* [effecttraits](effecttraits.html)
+  Access to the inferred .raises effects
+  for Nim's macro system.
 
-* [lenientops](lenientops.html)
-  Provides binary operators for mixed integer/float expressions for convenience.
+* [endians](endians.html)
+  Helpers that deal with different byte orders.
 
 * [locks](locks.html)
   Locks and condition variables for Nim.
@@ -80,10 +75,10 @@ Core
   Provides (unsafe) access to Nim's run-time type information.
 
 * [typetraits](typetraits.html)
-  This module defines compile-time reflection procs for working with types.
+  Compile-time reflection procs for working with types.
 
 * [volatile](volatile.html)
-  This module contains code for generating volatile loads and stores,
+  Code for generating volatile loads and stores,
   which are useful in embedded and systems programming.
 
 
@@ -91,24 +86,24 @@ Algorithms
 ----------
 
 * [algorithm](algorithm.html)
-  This module implements some common generic algorithms like sort or binary search.
+  Some common generic algorithms like sort or binary search.
 
 * [enumutils](enumutils.html)
-  This module adds functionality for the built-in `enum` type.
+  Additional functionality for the built-in `enum` type.
 
 * [sequtils](sequtils.html)
-  This module implements operations for the built-in `seq` type
+  Operations for the built-in `seq` type
   which were inspired by functional programming languages.
 
 * [setutils](setutils.html)
-  This module adds functionality for the built-in `set` type.
+  Additional functionality for the built-in `set` type.
 
 
 Collections
 -----------
 
 * [critbits](critbits.html)
-  This module implements a *crit bit tree* which is an efficient
+  A *crit bit tree* which is an efficient
   container for a sorted set of strings, or a sorted mapping of strings.
 
 * [deques](deques.html)
@@ -131,9 +126,19 @@ Collections
 * [packedsets](packedsets.html)
   Efficient implementation of a set of ordinals as a sparse bit set.
 
+* [ropes](ropes.html)
+  A *rope* data type.
+  Ropes can represent very long strings efficiently;
+  in particular, concatenation is done in O(1) instead of O(n).
+
 * [sets](sets.html)
   Nim hash set support.
 
+* [strtabs](strtabs.html)
+  The `strtabs` module implements an efficient hash table that is a mapping
+  from strings to strings. Supports a case-sensitive, case-insensitive and
+  style-insensitive modes.
+
 * [tables](tables.html)
   Nim hash table support. Contains tables, ordered tables, and count tables.
 
@@ -145,60 +150,55 @@ String handling
   Utilities for `cstring` handling.
 
 * [editdistance](editdistance.html)
-  This module contains an algorithm to compute the edit distance between two
+  An algorithm to compute the edit distance between two
   Unicode strings.
 
 * [encodings](encodings.html)
   Converts between different character encodings. On UNIX, this uses
   the `iconv` library, on Windows the Windows API.
 
-* [parseutils](parseutils.html)
-  This module contains helpers for parsing tokens, numbers, identifiers, etc.
+* [formatfloat](formatfloat.html)
+  Formatting floats as strings.
 
-* [pegs](pegs.html)
-  This module contains procedures and operators for handling PEGs.
+* [objectdollar](objectdollar.html)
+  A generic `$` operator to convert objects to strings.
 
 * [punycode](punycode.html)
   Implements a representation of Unicode with the limited ASCII character subset.
 
-* [ropes](ropes.html)
-  This module contains support for a *rope* data type.
-  Ropes can represent very long strings efficiently;
-  in particular, concatenation is done in O(1) instead of O(n).
-
 * [strbasics](strbasics.html)
-  This module provides some high performance string operations.
+  Some high performance string operations.
 
 * [strformat](strformat.html)
   Macro based standard string interpolation/formatting. Inspired by
-  Python's f-strings.
+  Python's f-strings.\
+  **Note:** if you need templating, consider using Nim
+  [Source Code Filters (SCF)](filters.html).
 
 * [strmisc](strmisc.html)
-  This module contains uncommon string handling operations that do not
-  fit with the commonly used operations in strutils.
+  Uncommon string handling operations that do not
+  fit with the commonly used operations in [strutils](strutils.html).
 
 * [strscans](strscans.html)
-  This module contains a `scanf` macro for convenient parsing of mini languages.
-
-* [strtabs](strtabs.html)
-  The `strtabs` module implements an efficient hash table that is a mapping
-  from strings to strings. Supports a case-sensitive, case-insensitive and
-  style-insensitive modes.
+  A `scanf` macro for convenient parsing of mini languages.
 
 * [strutils](strutils.html)
-  This module contains common string handling operations like changing
+  Common string handling operations like changing
   case of a string, splitting a string into substrings, searching for
   substrings, replacing substrings.
 
 * [unicode](unicode.html)
-  This module provides support to handle the Unicode UTF-8 encoding.
+  Support for handling the Unicode UTF-8 encoding.
 
 * [unidecode](unidecode.html)
   It provides a single proc that does Unicode to ASCII transliterations.
   Based on Python's Unidecode module.
 
+* [widestrs](widestrs.html)
+  Nim support for C/C++'s wide strings.
+
 * [wordwrap](wordwrap.html)
-  This module contains an algorithm to wordwrap a Unicode string.
+  An algorithm for word-wrapping Unicode strings.
 
 
 Time handling
@@ -214,8 +214,17 @@ Time handling
 Generic Operating System Services
 ---------------------------------
 
+* [appdirs](appdirs.html)
+  Helpers for determining special directories used by apps.
+
+* [cmdline](cmdline.html)
+  System facilities for reading command line parameters.
+
+* [dirs](dirs.html)
+  Directory handling.
+
 * [distros](distros.html)
-  This module implements the basics for OS distribution ("distro") detection
+  Basics for OS distribution ("distro") detection
   and the OS's native package manager.
   Its primary purpose is to produce output for Nimble packages,
   but it also contains the widely used **Distribution** enum
@@ -223,14 +232,19 @@ Generic Operating System Services
   See [packaging](packaging.html) for hints on distributing Nim using OS packages.
 
 * [dynlib](dynlib.html)
-  This module implements the ability to access symbols from shared libraries.
+  Accessing symbols from shared libraries.
 
-* [marshal](marshal.html)
-  Contains procs for serialization and deserialization of arbitrary Nim
-  data structures.
+* [envvars](envvars.html)
+  Environment variable handling.
+
+* [exitprocs](exitprocs.html)
+  Adding hooks to program exit.
+
+* [files](files.html)
+  File handling.
 
 * [memfiles](memfiles.html)
-  This module provides support for memory-mapped files (Posix's `mmap`)
+  Support for memory-mapped files (Posix's `mmap`)
   on the different operating systems.
 
 * [os](os.html)
@@ -238,35 +252,52 @@ Generic Operating System Services
   reading command line arguments, working with directories, running shell
   commands, etc.
 
+* [oserrors](oserrors.html)
+  OS error reporting.
+
 * [osproc](osproc.html)
   Module for process communication beyond `os.execShellCmd`.
 
+* [paths](paths.html)
+  Path handling.
+
+* [reservedmem](reservedmem.html)
+  Utilities for reserving portions of the
+  address space of a program without consuming physical memory.
+
 * [streams](streams.html)
-  This module provides a stream interface and two implementations thereof:
+  A stream interface and two implementations thereof:
   the `FileStream` and the `StringStream` which implement the stream
   interface for Nim file objects (`File`) and strings. Other modules
   may provide other implementations for this standard stream interface.
 
+* [symlinks](symlinks.html)
+  Symlink handling.
+
+* [syncio](syncio.html)
+  Various synchronized I/O operations.
+
 * [terminal](terminal.html)
-  This module contains a few procedures to control the *terminal*
-  (also called *console*). The implementation simply uses ANSI escape
-  sequences and does not depend on any other module.
+  A module to control the terminal output (also called *console*).
   
 * [tempfiles](tempfiles.html)
-  This module provides some utils to generate temporary path names and
-  create temporary files and directories.
+  Some utilities for generating temporary path names and
+  creating temporary files and directories.
 
 
 Math libraries
 --------------
 
 * [complex](complex.html)
-  This module implements complex numbers and relevant mathematical operations.
+  Complex numbers and relevant mathematical operations.
 
 * [fenv](fenv.html)
   Floating-point environment. Handling of floating-point rounding and
   exceptions (overflow, zero-divide, etc.).
 
+* [lenientops](lenientops.html)
+  Binary operators for mixed integer/float expressions for convenience.
+
 * [math](math.html)
   Mathematical operations like cosine, square root.
 
@@ -274,14 +305,11 @@ Math libraries
   Fast and tiny random number generator.
 
 * [rationals](rationals.html)
-  This module implements rational numbers and relevant mathematical operations.
+  Rational numbers and relevant mathematical operations.
 
 * [stats](stats.html)
   Statistical analysis.
 
-* [sums](sums.html)
-  Accurate summation functions.
-
 * [sysrand](sysrand.html)
   Cryptographically secure pseudorandom number generator.
 
@@ -289,80 +317,92 @@ Math libraries
 Internet Protocols and Support
 ------------------------------
 
+* [async](async.html)
+  Exports `asyncmacro` and `asyncfutures` for native backends, and `asyncjs` on the JS backend. 
+
 * [asyncdispatch](asyncdispatch.html)
-  This module implements an asynchronous dispatcher for IO operations.
+  An asynchronous dispatcher for IO operations.
 
 * [asyncfile](asyncfile.html)
-  This module implements asynchronous file reading and writing using
-  `asyncdispatch`.
+  An asynchronous file reading and writing using `asyncdispatch`.
+
+* [asyncftpclient](asyncftpclient.html)
+  An asynchronous FTP client using the `asyncnet` module.
 
-* `asyncftpclient](asyncftpclient.html)
-  [his module implements an asynchronous FTP client using the `asyncnet`
-  module.
+* [asynchttpserver](asynchttpserver.html)
+  An asynchronous HTTP server using the `asyncnet` module.
 
-* `asynchttpserver](asynchttpserver.html)
-  [his module implements an asynchronous HTTP server using the `asyncnet`
-  module.
+* [asyncmacro](asyncmacro.html)
+  `async` and `multisync` macros for `asyncdispatch`.
 
 * [asyncnet](asyncnet.html)
-  This module implements asynchronous sockets based on the `asyncdispatch`
-  module.
+  Asynchronous sockets based on the `asyncdispatch` module.
 
 * [asyncstreams](asyncstreams.html)
-  This module provides `FutureStream` - a future that acts as a queue.
+  `FutureStream` - a future that acts as a queue.
 
 * [cgi](cgi.html)
-  This module implements helpers for CGI applications.
+  Helpers for CGI applications.
 
 * [cookies](cookies.html)
-  This module contains helper procs for parsing and generating cookies.
+  Helper procs for parsing and generating cookies.
 
 * [httpclient](httpclient.html)
-  This module implements a simple HTTP client which supports both synchronous
+  A simple HTTP client with support for both synchronous
   and asynchronous retrieval of web pages.
 
 * [mimetypes](mimetypes.html)
-  This module implements a mimetypes database.
+  A mimetypes database.
 
 * [nativesockets](nativesockets.html)
-  This module implements a low-level sockets API.
+  A low-level sockets API.
 
 * [net](net.html)
-  This module implements a high-level sockets API. It replaces the
-  `sockets` module.
+  A high-level sockets API.
 
 * [selectors](selectors.html)
-  This module implements a selector API with backends specific to each OS.
-  Currently, epoll on Linux and select on other operating systems.
+  A selector API with backends specific to each OS.
+  Supported OS primitives: `epoll`, `kqueue`, `poll`, and `select` on Windows.
 
 * [smtp](smtp.html)
-  This module implements a simple SMTP client.
+  A simple SMTP client with support for both synchronous and asynchronous operation.
+
+* [socketstreams](socketstreams.html)
+  An implementation of the streams interface for sockets.
 
 * [uri](uri.html)
-  This module provides functions for working with URIs.
+  Functions for working with URIs and URLs.
 
 
 Threading
 ---------
 
+* [isolation](isolation.html)
+  The `Isolated[T]` type for
+  safe construction of isolated subgraphs that can be
+  passed efficiently to different channels and threads.
+
+* [tasks](tasks.html)
+  Basic primitives for creating parallel programs.
+
 * [threadpool](threadpool.html)
   Implements Nim's [spawn](manual_experimental.html#parallel-amp-spawn).
 
+* [typedthreads](typedthreads.html)
+  Basic Nim thread support.
+
 
 Parsers
 -------
 
 * [htmlparser](htmlparser.html)
-  This module parses an HTML document and creates its XML tree representation.
+  HTML document parser that creates a XML tree representation.
 
 * [json](json.html)
   High-performance JSON parser.
 
-* [jsonutils](jsonutils.html)
-  This module implements a hookable (de)serialization for arbitrary types.
-
 * [lexbase](lexbase.html)
-  This is a low-level module that implements an extremely efficient buffering
+  A low-level module that implements an extremely efficient buffering
   scheme for lexers and parsers. This is used by the diverse parsing modules.
 
 * [parsecfg](parsecfg.html)
@@ -376,7 +416,7 @@ Parsers
   The `parsecsv` module implements a simple high-performance CSV parser.
 
 * [parsejson](parsejson.html)
-  This module implements a JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right.
+  A JSON parser. It is used and exported by the [json](json.html) module, but can also be used in its own right.
 
 * [parseopt](parseopt.html)
   The `parseopt` module implements a command line option parser.
@@ -384,12 +424,18 @@ Parsers
 * [parsesql](parsesql.html)
   The `parsesql` module implements a simple high-performance SQL parser.
 
+* [parseutils](parseutils.html)
+  Helpers for parsing tokens, numbers, identifiers, etc.
+
 * [parsexml](parsexml.html)
   The `parsexml` module implements a simple high performance XML/HTML parser.
   The only encoding that is supported is UTF-8. The parser has been designed
   to be somewhat error-correcting, so that even some "wild HTML" found on the
   web can be parsed with it.
 
+* [pegs](pegs.html)
+  Procedures and operators for handling PEGs.
+
 
 Docutils
 --------
@@ -400,14 +446,14 @@ Docutils
   The interface supports one language nested in another.
 
 * [packages/docutils/rst](rst.html)
-  This module implements a reStructuredText parser. A large subset
+  A reStructuredText parser. A large subset
   is implemented. Some features of the markdown wiki syntax are also supported.
 
 * [packages/docutils/rstast](rstast.html)
-  This module implements an AST for the reStructuredText parser.
+  An AST for the reStructuredText parser.
 
 * [packages/docutils/rstgen](rstgen.html)
-  This module implements a generator of HTML/Latex from reStructuredText.
+  A generator of HTML/Latex from reStructuredText.
 
 
 XML Processing
@@ -418,14 +464,17 @@ XML Processing
   contains a macro for XML/HTML code generation.
 
 * [xmlparser](xmlparser.html)
-  This module parses an XML document and creates its XML tree representation.
+  XML document parser that creates a XML tree representation.
 
 
 Generators
 ----------
 
+* [genasts](genasts.html)
+  AST generation using captured variables for macros.
+
 * [htmlgen](htmlgen.html)
-  This module implements a simple XML and HTML code
+  A simple XML and HTML code
   generator. Each commonly used HTML tag has a corresponding macro
   that generates a string with its HTML representation.
 
@@ -434,49 +483,68 @@ Hashing
 -------
 
 * [base64](base64.html)
-  This module implements a Base64 encoder and decoder.
+  A Base64 encoder and decoder.
 
 * [hashes](hashes.html)
-  This module implements efficient computations of hash values for diverse
-  Nim types.
+  Efficient computations of hash values for diverse Nim types.
 
 * [md5](md5.html)
-  This module implements the MD5 checksum algorithm.
+  The MD5 checksum algorithm.
 
 * [oids](oids.html)
   An OID is a global ID that consists of a timestamp,
   a unique counter, and a random value. This combination should suffice to
-  produce a globally distributed unique ID. This implementation was extracted
-  from the MongoDB interface and it thus binary compatible with a MongoDB OID.
+  produce a globally distributed unique ID.
 
 * [sha1](sha1.html)
-  This module implements the SHA-1 checksum algorithm.
+  The SHA-1 checksum algorithm.
+
+
+Serialization
+-------------
+
+* [jsonutils](jsonutils.html)
+  Hookable (de)serialization for arbitrary types
+  using JSON.
+
+* [marshal](marshal.html)
+  Contains procs for serialization and deserialization of arbitrary Nim
+  data structures.
 
 
 Miscellaneous
 -------------
 
+* [assertions](assertions.html)
+  Assertion handling.
+
 * [browsers](browsers.html)
-  This module implements procs for opening URLs with the user's default
+  Procs for opening URLs with the user's default
   browser.
 
 * [colors](colors.html)
-  This module implements color handling for Nim.
+  Color handling.
 
 * [coro](coro.html)
-  This module implements experimental coroutines in Nim.
+  Experimental coroutines in Nim.
+
+* [decls](decls.html)
+  Syntax sugar for some declarations.
 
 * [enumerate](enumerate.html)
-  This module implements `enumerate` syntactic sugar based on Nim's macro system.
+  `enumerate` syntactic sugar based on Nim's macro system.
+
+* [importutils](importutils.html)
+  Utilities related to import and symbol resolution.
 
 * [logging](logging.html)
-  This module implements a simple logger.
+  A simple logger.
 
 * [segfaults](segfaults.html)
   Turns access violations or segfaults into a `NilAccessDefect` exception.
 
 * [sugar](sugar.html)
-  This module implements nice syntactic sugar based on Nim's macro system.
+  Nice syntactic sugar based on Nim's macro system.
 
 * [unittest](unittest.html)
   Implements a Unit testing DSL.
@@ -485,11 +553,14 @@ Miscellaneous
   Decode variable-length integers that are compatible with SQLite.
 
 * [with](with.html)
-  This module implements the `with` macro for easy function chaining.
+  The `with` macro for easy function chaining.
 
+* [wrapnils](wrapnils.html)
+  Allows evaluating expressions safely against nil dereferences.
 
-Modules for the JS backend
---------------------------
+
+Modules for the JavaScript backend
+----------------------------------
 
 * [asyncjs](asyncjs.html)
   Types and macros for writing asynchronous procedures in JavaScript.
@@ -507,9 +578,15 @@ Modules for the JS backend
   The wrapper of core JavaScript functions. For most purposes, you should be using
   the `math`, `json`, and `times` stdlib modules instead of this module.
 
+* [jsfetch](jsfetch.html)
+  Wrapper for `fetch`.
+
 * [jsffi](jsffi.html)
   Types and macros for easier interaction with JavaScript.
 
+* [jsre](jsre.html)
+  Regular Expressions for the JavaScript target.
+
 
 Impure libraries
 ================
@@ -518,21 +595,29 @@ Regular expressions
 -------------------
 
 * [re](re.html)
-  This module contains procedures and operators for handling regular
+  Procedures and operators for handling regular
   expressions. The current implementation uses PCRE.
 
+* [nre](nre.html)
+
+  Many help functions for handling regular expressions.
+  The current implementation uses PCRE.
 
 Database support
 ----------------
 
-* [db_postgres](db_postgres.html)
-  A higher level PostgreSQL database wrapper. The same interface is implemented
-  for other databases too.
-
 * [db_mysql](db_mysql.html)
   A higher level MySQL database wrapper. The same interface is implemented
   for other databases too.
 
+* [db_odbc](db_odbc.html)
+  A higher level ODBC database wrapper. The same interface is implemented
+  for other databases too.
+
+* [db_postgres](db_postgres.html)
+  A higher level PostgreSQL database wrapper. The same interface is implemented
+  for other databases too.
+
 * [db_sqlite](db_sqlite.html)
   A higher level SQLite database wrapper. The same interface is implemented
   for other databases too.
@@ -542,7 +627,7 @@ Generic Operating System Services
 ---------------------------------
 
 * [rdstdin](rdstdin.html)
-  This module contains code for reading from stdin.
+  Code for reading user input from stdin.
 
 
 Wrappers
@@ -556,7 +641,7 @@ Windows-specific
 ----------------
 
 * [winlean](winlean.html)
-  Contains a wrapper for a small subset of the Win32 API.
+  Wrapper for a small subset of the Win32 API.
 * [registry](registry.html)
   Windows registry support.
 
@@ -565,7 +650,7 @@ UNIX specific
 -------------
 
 * [posix](posix.html)
-  Contains a wrapper for the POSIX standard.
+  Wrapper for the POSIX standard.
 * [posix_utils](posix_utils.html)
   Contains helpers for the POSIX standard or specialized for Linux and BSDs.
 
@@ -580,14 +665,14 @@ Regular expressions
 Database support
 ----------------
 
-* [postgres](postgres.html)
-  Contains a wrapper for the PostgreSQL API.
 * [mysql](mysql.html)
-  Contains a wrapper for the mySQL API.
-* [sqlite3](sqlite3.html)
-  Contains a wrapper for the SQLite 3 API.
+  Wrapper for the mySQL API.
 * [odbcsql](odbcsql.html)
   interface to the ODBC driver.
+* [postgres](postgres.html)
+  Wrapper for the PostgreSQL API.
+* [sqlite3](sqlite3.html)
+  Wrapper for the SQLite 3 API.
 
 
 Network Programming and Internet Protocols
diff --git a/doc/manual.md b/doc/manual.md
index cd19f93a6..5c36a0a7b 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -47,16 +47,16 @@ is not ambiguous.
 
 Non-terminals start with a lowercase letter, abstract terminal symbols are in
 UPPERCASE. Verbatim terminal symbols (including keywords) are quoted
-with `'`. An example::
+with `'`. An example:
 
-  ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
+    ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
 
 The binary `^*` operator is used as a shorthand for 0 or more occurrences
 separated by its second argument; likewise `^+` means 1 or more
 occurrences: `a ^+ b` is short for `a (b a)*`
-and `a ^* b` is short for `(a (b a)*)?`. Example::
+and `a ^* b` is short for `(a (b a)*)?`. Example:
 
-  arrayConstructor = '[' expr ^* ',' ']'
+    arrayConstructor = '[' expr ^* ',' ']'
 
 Other parts of Nim, like scoping rules or runtime semantics, are
 described informally.
@@ -190,16 +190,16 @@ is another pseudo terminal that describes the *action* of popping a value
 from the stack, `IND{>}` then implies to push onto the stack.
 
 With this notation we can now easily define the core of the grammar: A block of
-statements (simplified example)::
+statements (simplified example):
 
-  ifStmt = 'if' expr ':' stmt
-           (IND{=} 'elif' expr ':' stmt)*
-           (IND{=} 'else' ':' stmt)?
+    ifStmt = 'if' expr ':' stmt
+             (IND{=} 'elif' expr ':' stmt)*
+             (IND{=} 'else' ':' stmt)?
 
-  simpleStmt = ifStmt / ...
+    simpleStmt = ifStmt / ...
 
-  stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
-       / simpleStmt                 # or a simple statement
+    stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
+         / simpleStmt                 # or a simple statement
 
 
 
@@ -257,6 +257,18 @@ Multiline documentation comments also exist and support nesting too:
     ]##
   ```
 
+You can also use the [discard statement](#statements-and-expressions-discard-statement) together with
+[triple quoted string literals](#lexical-analysis-triple-quoted-string-literals) to create multiline comments:
+
+  ```nim
+  discard """ You can have any Nim code text commented
+  out inside this with no indentation restrictions.
+        yes("May I ask a pointless question?") """
+  ```
+
+This was how multiline comments were done before version 0.13.0,
+and it is used to provide specifications to [testament](testament.html#writing-unit-tests) test framework.
+
 
 Identifiers & Keywords
 ----------------------
@@ -409,9 +421,9 @@ ending of the string literal is defined by the pattern `"""[^"]`, so this:
   """"long string within quotes""""
   ```
 
-Produces::
+Produces:
 
-  "long string within quotes"
+    "long string within quotes"
 
 
 Raw string literals
@@ -434,9 +446,9 @@ To produce a single `"` within a raw string literal, it has to be doubled:
   r"a""b"
   ```
 
-Produces::
+Produces:
 
-  a"b
+    a"b
 
 `r""""` is not possible with this notation, because the three leading
 quotes introduce a triple quoted string literal. `r"""` is the same
@@ -513,46 +525,46 @@ See also [custom numeric literals].
 Numeric literals
 ----------------
 
-Numeric literals have the form::
+Numeric literals have the form:
 
-  hexdigit = digit | 'A'..'F' | 'a'..'f'
-  octdigit = '0'..'7'
-  bindigit = '0'..'1'
-  unary_minus = '-' # See the section about unary minus
-  HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
-  DEC_LIT = unary_minus? digit ( ['_'] digit )*
-  OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )*
-  BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
+    hexdigit = digit | 'A'..'F' | 'a'..'f'
+    octdigit = '0'..'7'
+    bindigit = '0'..'1'
+    unary_minus = '-' # See the section about unary minus
+    HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
+    DEC_LIT = unary_minus? digit ( ['_'] digit )*
+    OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )*
+    BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*
 
-  INT_LIT = HEX_LIT
-          | DEC_LIT
-          | OCT_LIT
-          | BIN_LIT
+    INT_LIT = HEX_LIT
+            | DEC_LIT
+            | OCT_LIT
+            | BIN_LIT
 
-  INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
-  INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
-  INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
-  INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
+    INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
+    INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
+    INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
+    INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'
 
-  UINT_LIT = INT_LIT ['\''] ('u' | 'U')
-  UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
-  UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
-  UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
-  UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
+    UINT_LIT = INT_LIT ['\''] ('u' | 'U')
+    UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
+    UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
+    UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
+    UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'
 
-  exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
-  FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent)
-  FLOAT32_SUFFIX = ('f' | 'F') ['32']
-  FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
-              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
-  FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
-  FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
-              | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
+    exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
+    FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent)
+    FLOAT32_SUFFIX = ('f' | 'F') ['32']
+    FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
+                | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
+    FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
+    FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
+                | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX
 
-  CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX
+    CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX
 
-  # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not
-  # a pre-defined type suffix.
+    # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not
+    # a pre-defined type suffix.
 
 
 As can be seen in the productions, numeric literals can contain underscores
@@ -643,7 +655,7 @@ string containing the literal. The callable identifier needs to be declared
 with a special ``'`` prefix:
 
   ```nim
-  import strutils
+  import std/strutils
   type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble"
   proc `'u4`(n: string): u4 =
     # The leading ' is required.
@@ -658,7 +670,7 @@ corresponds to this transformation. The transformation naturally handles
 the case that additional parameters are passed to the callee:
 
   ```nim
-  import strutils
+  import std/strutils
   type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble"
   proc `'u4`(n: string; moreData: int): u4 =
     result = (parseInt(n) and 0x0F).u4
@@ -674,7 +686,7 @@ Operators
 ---------
 
 Nim allows user defined operators. An operator is any combination of the
-following characters::
+following characters:
 
        =     +     -     *     /     <     >
        @     $     ~     &     %     |
@@ -695,11 +707,26 @@ are used for other notational purposes.
 The `not` keyword is always a unary operator, `a not b` is parsed
 as `a(not b)`, not as `(a) not (b)`.
 
+Unicode Operators
+-----------------
+
+These Unicode operators are also parsed as operators:
+
+    ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓   # same priority as * (multiplication)
+    ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔             # same priority as + (addition)
+
+
+Unicode operators can be combined with non-Unicode operator
+symbols. The usual precedence extensions then apply, for example, `⊠=` is an
+assignment like operator just like `*=` is.
+
+No Unicode normalization step is performed.
+
 
 Other tokens
 ------------
 
-The following strings denote other tokens::
+The following strings denote other tokens:
 
     `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
 
@@ -1066,17 +1093,6 @@ operation                meaning
 `a %% b`                 unsigned integer modulo operation
 `a <% b`                 treat `a` and `b` as unsigned and compare
 `a <=% b`                treat `a` and `b` as unsigned and compare
-`ze(a)`                  extends the bits of `a` with zeros until it has the
-                         width of the `int` type
-`toU8(a)`                treats `a` as unsigned and converts it to an
-                         unsigned integer of 8 bits (but still the
-                         `int8` type)
-`toU16(a)`               treats `a` as unsigned and converts it to an
-                         unsigned integer of 16 bits (but still the
-                         `int16` type)
-`toU32(a)`               treats `a` as unsigned and converts it to an
-                         unsigned integer of 32 bits (but still the
-                         `int32` type)
 ======================   ======================================================
 
 `Automatic type conversion`:idx: is performed in expressions where different
@@ -1203,9 +1219,11 @@ The boolean type is named `bool`:idx: in Nim and can be one of the two
 pre-defined values `true` and `false`. Conditions in `while`,
 `if`, `elif`, `when`-statements need to be of type `bool`.
 
-This condition holds::
+This condition holds:
 
+  ```nim
   ord(false) == 0 and ord(true) == 1
+  ```
 
 The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined
 for the bool type. The `and` and `or` operators perform short-cut
@@ -1244,8 +1262,9 @@ specified. The values are ordered. Example:
   ```
 
 
-Now the following holds::
+Now the following holds:
 
+  ```nim
   ord(north) == 0
   ord(east) == 1
   ord(south) == 2
@@ -1253,6 +1272,7 @@ Now the following holds::
 
   # Also allowed:
   ord(Direction.west) == 3
+  ```
 
 The implied order is: north < east < south < west. The comparison operators can be used
 with enumeration types. Instead of `north` etc., the enum value can also
@@ -1345,6 +1365,23 @@ ambiguous, a static error will be produced.
   p value2
   ```
 
+In some cases, ambiguity of enums is resolved depending on the relation
+between the current scope and the scope the enums were defined in.
+
+  ```nim
+  # a.nim
+  type Foo* = enum abc
+
+  # b.nim
+  import a
+  type Bar = enum abc
+  echo abc is Bar # true
+
+  block:
+    type Baz = enum abc
+    echo abc is Baz # true
+  ```
+
 To implement bit fields with enums see [Bit fields].
 
 
@@ -1458,7 +1495,8 @@ it can be modified:
 
   ```nim
   var x = "123456"
-  var s: cstring = x
+  prepareMutation(x) # call `prepareMutation` before modifying the strings
+  var s: cstring = cstring(x)
   s[0] = 'u' # This is ok
   ```
 
@@ -1930,6 +1968,57 @@ Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign
     t.kind = intLit
   ```
 
+Default values for object fields
+--------------------------------
+
+Object fields are allowed to have a constant default value. The type of field can be omitted if a default value is given.
+
+```nim test
+type
+  Foo = object
+    a: int = 2
+    b: float = 3.14
+    c = "I can have a default value"
+
+  Bar = ref object
+    a: int = 2
+    b: float = 3.14
+    c = "I can have a default value"
+```
+
+The explicit initialization uses these defaults which includes an `object` created with an object construction expression or the procedure `default`; a `ref object` created with an object construction expression or the procedure `new`; an array or a tuple with a subtype which has a default created with the procedure `default`.
+
+
+```nim test
+type
+  Foo = object
+    a: int = 2
+    b = 3.0
+  Bar = ref object
+    a: int = 2
+    b = 3.0
+
+block: # created with an object construction expression
+  let x = Foo()
+  assert x.a == 2 and x.b == 3.0
+
+  let y = Bar()
+  assert y.a == 2 and y.b == 3.0
+
+block: # created with an object construction expression
+  let x = default(Foo)
+  assert x.a == 2 and x.b == 3.0
+
+  let y = default(array[1, Foo])
+  assert y[0].a == 2 and y[0].b == 3.0
+
+  let z = default(tuple[x: Foo])
+  assert z.x.a == 2 and z.x.b == 3.0
+
+block: # created with the procedure `new`
+  let y = new Bar
+  assert y.a == 2 and y.b == 3.0
+```
 
 Set type
 --------
@@ -1976,10 +2065,6 @@ dereferencing operations for reference types:
   # no need to write n[].data; in fact n[].data is highly discouraged!
   ```
 
-Automatic dereferencing can be performed for the first argument of a routine
-call, but this is an experimental feature and is described [here](
-manual_experimental.html#automatic-dereferencing).
-
 In order to simplify structural type checking, recursive tuples are not valid:
 
   ```nim
@@ -2534,13 +2619,20 @@ An expression `b` can be assigned to an expression `a` iff `a` is an
 Overload resolution
 ===================
 
-In a call `p(args)` the routine `p` that matches best is selected. If
-multiple routines match equally well, the ambiguity is reported during
-semantic analysis.
+In a call `p(args)` where `p` may refer to more than one
+candidate, it is said to be a symbol choice. Overload resolution will attempt to
+find the best candidate, thus transforming the symbol choice into a resolved symbol.
+The routine `p` that matches best is selected following a series of trials explained below. 
+In order: Catagory matching, Hierarchical Order Comparison, and finally, Complexity Analysis.
 
-Every arg in args needs to match. There are multiple different categories how an
-argument can match. Let `f` be the formal parameter's type and `a` the type
-of the argument.
+If multiple candidates match equally well after all trials have been tested, the ambiguity 
+is reported during semantic analysis.
+
+First Trial: Catagory matching
+--------------------------------
+
+Every arg in `args` needs to match and there are multiple different categories of matches.
+Let `f` be the formal parameter's type and `a` the type of the argument.
 
 1. Exact match: `a` and `f` are of the same type.
 2. Literal match: `a` is an integer literal of value `v`
@@ -2558,14 +2650,21 @@ of the argument.
 6. Conversion match: `a` is convertible to `f`, possibly via a user
    defined `converter`.
 
-These matching categories have a priority: An exact match is better than a
-literal match and that is better than a generic match etc. In the following,
-`count(p, m)` counts the number of matches of the matching category `m`
-for the routine `p`.
+Each operand may fall into one of the categories above; the operand's
+highest priority category. The list above is in order or priority.
+If a candidate has more priority matches than all other candidates, it is selected as the
+resolved symbol.
+
+For example, if a candidate with one exact match is compared to a candidate with multiple
+generic matches and zero exact matches, the candidate with an exact match will win.
+
+Below is a pseudocode interpretation of category matching, `count(p, m)` counts the number 
+of matches of the matching category `m` for the routine `p`.
 
 A routine `p` matches better than a routine `q` if the following
-algorithm returns true::
+algorithm returns true:
 
+  ```nim
   for each matching category m in ["exact match", "literal match",
                                   "generic match", "subtype match",
                                   "integral match", "conversion match"]:
@@ -2575,9 +2674,53 @@ algorithm returns true::
     else:
       return false
   return "ambiguous"
+  ```
+
+Second Trial: Hierarchical Order Comparison
+----------------------------------------------
+
+The hierarchical order of a type is analogous to its relative specificity. Consider the type defined:
+
+```nim
+type A[T] = object
+```
+
+Matching formals for this type include `T`, `object`, `A`, `A[...]` and `A[C]` where `C` is a concrete type, `A[...]`
+is a generic typeclass composition and `T` is an unconstrained generic type variable. This list is in order of 
+specificity with respect to `A` as each subsequent category narrows the set of types that are members of their match set.
+
+In this trail, the formal parameters of candidates are compared in order (1st parameter, 2nd parameter, etc.) to search for
+a candidate that has an unrivaled specificity. If such a formal parameter is found, the candidate it belongs to is chosen 
+as the resolved symbol.
 
+Third Trial: Complexity Analysis
+----------------------------------
+
+A slight clarification: While category matching digests all the formal parameters of a candidate at once (order doesn't matter),
+specificity comparison and complexity analysis operate on each formal parameter at a time. The following
+is the final trial to disambiguate a symbol choice when a pair of formal parameters have the same hierarchical order.
+
+The complexity of a type is essentially its number of modifiers and depth of shape. The definition with the *highest*
+complexity wins. Consider the following types:
+
+```nim
+type
+  A[T] = object
+  B[T, H] = object
+```
+
+Note: The below examples are not exhaustive.
+
+We shall say that:
 
-Some examples:
+1. `A[T]` has a higher complexity than `A`
+2. `var A[T]` has a higher complexity than `A[T]`
+3. `A[A[T]]` has a higher complexity than `A[T]`
+4. `B[T, H]` has a higher complexity than `A[T]` (`A` and `B` are not compatible here, but convoluted versions of this exist)
+5. `B[ptr T, H]` has a higher complexity than `B[T, H]`
+
+Some Examples
+---------------
 
   ```nim
   proc takesInt(x: int) = echo "int"
@@ -2594,7 +2737,6 @@ Some examples:
   ```
 
 
-If this algorithm returns "ambiguous" further disambiguation is performed:
 If the argument `a` matches both the parameter type `f` of `p`
 and `g` of `q` via a subtyping relation, the inheritance depth is taken
 into account:
@@ -2636,6 +2778,23 @@ matches) is preferred:
   gen(ri) # "ref T"
   ```
 
+Type variables match
+----------------------
+
+When overload resolution is considering candidates, the type variable's definition
+is not overlooked as it is used to define the formal parameter's type via variable substitution.
+
+For example:
+```nim
+type A
+proc p[T: A](param: T)
+proc p[T: object](param: T)
+```
+
+These signatures are not ambiguous for a concrete type of `A` even though the formal parameters match ("T" == "T").
+Instead `T` is treated as a variable in that (`T` ?= `T`) depending on the bound type of `T` at the time of
+overload resolution.
+
 
 Overloading based on 'var T'
 --------------------------------------
@@ -2892,9 +3051,9 @@ ref or pointer type             nil
 procedural type                 nil
 sequence                        `@[]`
 string                          `""`
-`tuple[x: A, y: B, ...]`        (default(A), default(B), ...)
+`tuple[x: A, y: B, ...]`        (zeroDefault(A), zeroDefault(B), ...)
                                 (analogous for objects)
-`array[0..., T]`                `[default(T), ...]`
+`array[0..., T]`                `[zeroDefault(T), ...]`
 `range[T]`                      default(T); this may be out of the valid range
 T = enum                        `cast[T](0)`; this may be an invalid value
 ============================    ==============================================
@@ -2990,12 +3149,25 @@ when they are declared. The only exception to this is if the `{.importc.}`
 pragma (or any of the other `importX` pragmas) is applied, in this case the
 value is expected to come from native code, typically a C/C++ `const`.
 
+Special identifier `_` (underscore)
+-----------------------------------
+
+The identifier `_` has a special meaning in declarations.
+Any definition with the name `_` will not be added to scope, meaning the
+definition is evaluated, but cannot be used. As a result the name `_` can be
+indefinitely redefined.
+
+  ```nim
+  let _ = 123
+  echo _ # error
+  let _ = 456 # compiles
+  ```
 
 Tuple unpacking
 ---------------
 
-In a `var` or `let` statement tuple unpacking can be performed. The special
-identifier `_` can be used to ignore some parts of the tuple:
+In a `var`, `let` or `const` statement tuple unpacking can be performed.
+The special identifier `_` can be used to ignore some parts of the tuple:
 
   ```nim
   proc returnsTuple(): (int, int, int) = (4, 2, 3)
@@ -3003,6 +3175,35 @@ identifier `_` can be used to ignore some parts of the tuple:
   let (x, _, z) = returnsTuple()
   ```
 
+This is treated as syntax sugar for roughly the following:
+
+  ```nim
+  let
+    tmpTuple = returnsTuple()
+    x = tmpTuple[0]
+    z = tmpTuple[2]
+  ```
+
+For `var` or `let` statements, if the value expression is a tuple literal,
+each expression is directly expanded into an assignment without the use of
+a temporary variable.
+
+  ```nim
+  let (x, y, z) = (1, 2, 3)
+  # becomes
+  let
+    x = 1
+    y = 2
+    z = 3
+  ```
+
+Tuple unpacking can also be nested:
+
+  ```nim
+  proc returnsNestedTuple(): (int, (int, int), int, int) = (4, (5, 7), 2, 3)
+
+  let (x, (_, y), _, z) = returnsNestedTuple()
+  ```
 
 
 Const section
@@ -3019,6 +3220,15 @@ A const section declares constants whose values are constant expressions:
 
 Once declared, a constant's symbol can be used as a constant expression.
 
+The value part of a constant declaration opens a new scope for each constant,
+so no symbols declared in the constant value are accessible outside of it.
+
+  ```nim
+  const foo = (var a = 1; a)
+  const bar = a # error
+  let baz = a # error
+  ```
+
 See [Constants and Constant Expressions] for details.
 
 Static statement/expression
@@ -3488,8 +3698,8 @@ Example:
   var y = if x > 8: 9 else: 10
   ```
 
-An if expression always results in a value, so the `else` part is
-required. `Elif` parts are also allowed.
+An `if` expression always results in a value, so the `else` part is
+required. `elif` parts are also allowed.
 
 When expression
 ---------------
@@ -3587,9 +3797,6 @@ type conversions to unsigned integers and between unsigned integers. The
 rationale for this is mostly better interoperability with the C Programming
 language when algorithms are ported from C to Nim.
 
-Exception: Values that are converted to an unsigned type at compile time
-are checked so that code like `byte(-1)` does not compile.
-
 **Note**: Historically the operations
 were unchecked and the conversions were sometimes checked but starting with
 the revision 1.0.4 of this document and the language implementation the
@@ -3621,6 +3828,9 @@ bit pattern of the data being cast (aside from that the size of the target type
 may differ from the source type). Casting resembles *type punning* in other
 languages or C++'s `reinterpret_cast`:cpp: and `bit_cast`:cpp: features.
 
+If the size of the target type is larger than the size of the source type,
+the remaining memory is zeroed.
+
 The addr operator
 -----------------
 The `addr` operator returns the address of an l-value. If the type of the
@@ -3688,15 +3898,6 @@ every time the function is called.
   proc foo(a: int, b: int = 47): int
   ```
 
-Just as the comma propagates the types from right to left until the
-first parameter or until a semicolon is hit, it also propagates the
-default value starting from the parameter declared with it.
-
-  ```nim
-  # Both a and b are optional with 47 as their default values.
-  proc foo(a, b: int = 47): int
-  ```
-
 Parameters can be declared mutable and so allow the proc to modify those
 arguments, by using the type modifier `var`.
 
@@ -4036,7 +4237,7 @@ the operator is in scope (including if it is private).
   ```
 
 Type bound operators are:
-`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`.
+`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`, `=wasMoved`, `=dup`.
 
 These operations can be *overridden* instead of *overloaded*. This means that
 the implementation is automatically lifted to structured types. For instance,
@@ -4057,19 +4258,19 @@ Nonoverloadable builtins
 ------------------------
 
 The following built-in procs cannot be overloaded for reasons of implementation
-simplicity (they require specialized semantic checking)::
+simplicity (they require specialized semantic checking):
 
-  declared, defined, definedInScope, compiles, sizeof,
-  is, shallowCopy, getAst, astToStr, spawn, procCall
+    declared, defined, definedInScope, compiles, sizeof,
+    is, shallowCopy, getAst, astToStr, spawn, procCall
 
 Thus, they act more like keywords than like ordinary identifiers; unlike a
 keyword however, a redefinition may `shadow`:idx: the definition in
 the [system](system.html) module.
 From this list the following should not be written in dot
 notation `x.f` since `x` cannot be type-checked before it gets passed
-to `f`::
+to `f`:
 
-  declared, defined, definedInScope, compiles, getAst, astToStr
+    declared, defined, definedInScope, compiles, getAst, astToStr
 
 
 Var parameters
@@ -4262,7 +4463,42 @@ as an example:
 Overloading of the subscript operator
 -------------------------------------
 
-The `[]` subscript operator for arrays/openarrays/sequences can be overloaded.
+The `[]` subscript operator for arrays/openarrays/sequences can be overloaded
+for any type (with some exceptions) by defining a routine with the name `[]`.
+
+  ```nim
+  type Foo = object
+    data: seq[int]
+  
+  proc `[]`(foo: Foo, i: int): int =
+    result = foo.data[i]
+  
+  let foo = Foo(data: @[1, 2, 3])
+  echo foo[1] # 2
+  ```
+
+Assignment to subscripts can also be overloaded by naming a routine `[]=`,
+which has precedence over assigning to the result of `[]`.
+
+  ```nim
+  type Foo = object
+    data: seq[int]
+  
+  proc `[]`(foo: Foo, i: int): int =
+    result = foo.data[i]
+  proc `[]=`(foo: var Foo, i: int, val: int) =
+    foo.data[i] = val
+  
+  var foo = Foo(data: @[1, 2, 3])
+  echo foo[1] # 2
+  foo[1] = 5
+  echo foo.data # @[1, 5, 3]
+  echo foo[1] # 5
+  ```
+
+Overloads of the subscript operator cannot be applied to routine or type
+symbols themselves, as this conflicts with the syntax for instantiating
+generic parameters, i.e. `foo[int](1, 2, 3)` or `Foo[int]`.
 
 
 Methods
@@ -4478,7 +4714,6 @@ Closure iterators and inline iterators have some restrictions:
    (but rarely useful) and ends the iteration.
 3. Inline iterators cannot be recursive.
 4. Neither inline nor closure iterators have the special `result` variable.
-5. Closure iterators are not supported by the JS backend.
 
 Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly
 default to being inline, but this may change in future versions of the
@@ -4687,8 +4922,8 @@ Example:
       echo "overflow!"
     except ValueError, IOError:
       echo "catch multiple exceptions!"
-    except:
-      echo "Unknown exception!"
+    except CatchableError:
+      echo "Catchable exception!"
     finally:
       close(f)
   ```
@@ -4700,9 +4935,6 @@ listed in an `except` clause, the corresponding statements are executed.
 The statements following the `except` clauses are called
 `exception handlers`:idx:.
 
-The empty `except`:idx: clause is executed if there is an exception that is
-not listed otherwise. It is similar to an `else` clause in `if` statements.
-
 If there is a `finally`:idx: clause, it is always executed after the
 exception handlers.
 
@@ -4720,11 +4952,11 @@ Try can also be used as an expression; the type of the `try` branch then
 needs to fit the types of `except` branches, but the type of the `finally`
 branch always has to be `void`:
 
-  ```nim
+  ```nim test
   from std/strutils import parseInt
 
   let x = try: parseInt("133a")
-          except: -1
+          except ValueError: -1
           finally: echo "hi"
   ```
 
@@ -4732,8 +4964,9 @@ branch always has to be `void`:
 To prevent confusing code there is a parsing limitation; if the `try`
 follows a `(` it has to be written as a one liner:
 
-  ```nim
-  let x = (try: parseInt("133a") except: -1)
+  ```nim test
+  from std/strutils import parseInt
+  let x = (try: parseInt("133a") except ValueError: -1)
   ```
 
 
@@ -4781,7 +5014,7 @@ error message from `e`, and for such situations, it is enough to use
   ```nim
   try:
     # ...
-  except:
+  except CatchableError:
     echo getCurrentExceptionMsg()
   ```
 
@@ -4896,6 +5129,38 @@ exceptions inherit from `Defect`.
 Exceptions that indicate any other runtime error that can be caught inherit from
 `system.CatchableError` (which is a subtype of `Exception`).
 
+```
+Exception
+|-- CatchableError
+|   |-- IOError
+|   |   `-- EOFError
+|   |-- OSError
+|   |-- ResourceExhaustedError
+|   `-- ValueError
+|       `-- KeyError
+`-- Defect
+    |-- AccessViolationDefect
+    |-- ArithmeticDefect
+    |   |-- DivByZeroDefect
+    |   `-- OverflowDefect
+    |-- AssertionDefect
+    |-- DeadThreadDefect
+    |-- FieldDefect
+    |-- FloatingPointDefect
+    |   |-- FloatDivByZeroDefect
+    |   |-- FloatInvalidOpDefect
+    |   |-- FloatOverflowDefect
+    |   |-- FloatUnderflowDefect
+    |   `-- InexactDefect
+    |-- IndexDefect
+    |-- NilAccessDefect
+    |-- ObjectAssignmentDefect
+    |-- ObjectConversionDefect
+    |-- OutOfMemoryDefect
+    |-- RangeDefect
+    |-- ReraiseDefect
+    `-- StackOverflowDefect
+```
 
 Imported exceptions
 -------------------
@@ -4946,8 +5211,7 @@ Effect system
 =============
 
 **Note**: The rules for effect tracking changed with the release of version
-1.6 of the Nim compiler. This section describes the new rules that are activated
-via `--experimental:strictEffects`.
+1.6 of the Nim compiler.
 
 
 Exception tracking
@@ -4970,7 +5234,7 @@ An empty `raises` list (`raises: []`) means that no exception may be raised:
     try:
       unsafeCall()
       result = true
-    except:
+    except CatchableError:
       result = false
   ```
 
@@ -4998,7 +5262,7 @@ possibly raised exceptions; the algorithm operates on `p`'s call graph:
    raise `system.Exception` (the base type of the exception hierarchy) and
    thus any exception unless `T` has an explicit `raises` list.
    However, if the call is of the form `f(...)` where `f` is a parameter of
-   the currently analyzed routine it is ignored that is marked as `.effectsOf: f`.
+   the currently analyzed routine that is marked as `.effectsOf: f`, it is ignored.
    The call is optimistically assumed to have no effect.
    Rule 2 compensates for this case.
 2. Every expression `e` of some proc type within a call that is passed to parameter
@@ -5067,9 +5331,8 @@ conservative in its effect analysis:
 
   ```nim  test = "nim c $1"  status = 1
   {.push warningAsError[Effect]: on.}
-  {.experimental: "strictEffects".}
 
-  import algorithm
+  import std/algorithm
 
   type
     MyInt = distinct int
@@ -5197,6 +5460,8 @@ To override the compiler's side effect analysis a `{.noSideEffect.}`
 **Side effects are usually inferred. The inference for side effects is
 analogous to the inference for exception tracking.**
 
+When the compiler cannot infer side effects, as is the case for imported
+functions, one can annotate them with the `sideEffect` pragma.
 
 GC safety effect
 ----------------
@@ -5262,6 +5527,7 @@ Generics are Nim's means to parametrize procs, iterators or types with
 `type parameters`:idx:. Depending on the context, the brackets are used either to
 introduce type parameters or to instantiate a generic proc, iterator, or type.
 
+
 The following example shows how a generic binary tree can be modeled:
 
   ```nim  test = "nim c $1"
@@ -5323,6 +5589,49 @@ The following example shows how a generic binary tree can be modeled:
 The `T` is called a `generic type parameter`:idx: or
 a `type variable`:idx:.
 
+
+Generic Procs
+---------------
+
+Let's consider the anatomy of a generic `proc` to agree on defined terminology.
+
+```nim
+p[T: t](arg1: f): y
+```
+
+- `p`: Callee symbol
+- `[...]`: Generic parameters
+- `T: t`: Generic constraint
+- `T`: Type variable
+- `[T: t](arg1: f): y`: Formal signature
+- `arg1: f`: Formal parameter
+- `f`: Formal parameter type
+- `y`: Formal return type
+
+The use of the word "formal" here is to denote the symbols as they are defined by the programmer,
+not as they may be at compile time contextually. Since generics may be instantiated and
+types bound, we have more than one entity to think about when generics are involved.
+
+The usage of a generic will resolve the formally defined expression into an instance of that
+expression bound to only concrete types. This process is called "instantiation".
+
+Brackets at the site of a generic's formal definition specify the "constraints" as in:
+
+```nim
+type Foo[T] = object
+proc p[H;T: Foo[H]](param: T): H
+```
+
+A constraint definition may have more than one symbol defined by separating each definition by
+a `;`. Notice how `T` is composed of `H` and the return  type of `p` is defined as `H`. When this
+generic proc is instantiated `H` will be bound to a concrete type, thus making `T` concrete and
+the return type of `p` will be bound to the same concrete type used to define `H`.
+
+Brackets at the site of usage can be used to supply concrete types to instantiate the generic in the same
+order that the symbols are defined in the constraint. Alternatively, type bindings may be inferred by the compiler
+in some situations, allowing for cleaner code.
+
+
 Is operator
 -----------
 
@@ -5352,9 +5661,9 @@ type class           matches
 ==================   ===================================================
 `object`             any object type
 `tuple`              any tuple type
-
 `enum`               any enumeration
 `proc`               any proc type
+`iterator`           any iterator type
 `ref`                any `ref` type
 `ptr`                any `ptr` type
 `var`                any `var` type
@@ -5373,7 +5682,7 @@ more complex type classes:
 
   ```nim
   # create a type class that will match all tuple and object types
-  type RecordType = tuple or object
+  type RecordType = (tuple or object)
 
   proc printFields[T: RecordType](rec: T) =
     for key, value in fieldPairs(rec):
@@ -5414,6 +5723,17 @@ as `type constraints`:idx: of the generic type parameter:
   onlyIntOrString("xy", 50) # invalid as 'T' cannot be both at the same time
   ```
 
+`proc` and `iterator` type classes also accept a calling convention pragma
+to restrict the calling convention of the matching `proc` or `iterator` type.
+
+  ```nim
+  proc onlyClosure[T: proc {.closure.}](x: T) = discard
+
+  onlyClosure(proc() = echo "hello") # valid
+  proc foo() {.nimcall.} = discard
+  onlyClosure(foo) # type mismatch
+  ```
+
 
 Implicit generics
 -----------------
@@ -5422,7 +5742,7 @@ A type class can be used directly as the parameter's type.
 
   ```nim
   # create a type class that will match all tuple and object types
-  type RecordType = tuple or object
+  type RecordType = (tuple or object)
 
   proc printFields(rec: RecordType) =
     for key, value in fieldPairs(rec):
@@ -5585,7 +5905,7 @@ at definition and the context at instantiation are considered:
   echo a == b # works!
   ```
 
-In the example, the generic `==` for tuples (as defined in the system module)
+In the example, the [generic `==` for tuples](system.html#%3D%3D%2CT%2CT_2) (as defined in the system module)
 uses the `==` operators of the tuple's components. However, the `==` for
 the `Index` type is defined *after* the `==` for tuples; yet the example
 compiles as the instantiation takes the currently defined symbols into account
@@ -5937,9 +6257,12 @@ scope is controlled by the `inject`:idx: and `gensym`:idx: pragmas:
 `gensym`'ed symbols are not exposed but `inject`'ed symbols are.
 
 The default for symbols of entity `type`, `var`, `let` and `const`
-is `gensym` and for `proc`, `iterator`, `converter`, `template`,
-`macro` is `inject`. However, if the name of the entity is passed as a
-template parameter, it is an `inject`'ed symbol:
+is `gensym`. For `proc`, `iterator`, `converter`, `template`,
+`macro`, the default is `inject`, but if a `gensym` symbol with the same name
+is defined in the same syntax-level scope, it will be `gensym` by default.
+This can be overriden by marking the routine as `inject`. 
+
+If the name of the entity is passed as a template parameter, it is an `inject`'ed symbol:
 
   ```nim
   template withFile(f, fn, mode: untyped, actions: untyped): untyped =
@@ -6550,6 +6873,8 @@ even when one version does not export some of these identifiers.
 
 The `import` statement is only allowed at the top level.
 
+String literals can be used for import/include statements.
+The compiler performs [path substitution](nimc.html#compiler-usage-commandminusline-switches) when used.
 
 Include statement
 -----------------
@@ -6738,29 +7063,80 @@ All identifiers of a module are valid from the point of declaration until
 the end of the module. Identifiers from indirectly dependent modules are *not*
 available. The `system`:idx: module is automatically imported in every module.
 
-If a module imports an identifier by two different modules, each occurrence of
-the identifier has to be qualified unless it is an overloaded procedure or
-iterator in which case the overloading resolution takes place:
+If a module imports the same identifier from two different modules, the
+identifier is considered ambiguous, which can be resolved in the following ways:
+
+* Qualifying the identifier as `module.identifier` resolves ambiguity
+  between modules. (See below for the case that the module name itself
+  is ambiguous.)
+* Calling the identifier as a routine makes overload resolution take place,
+  which resolves ambiguity in the case that one overload matches stronger
+  than the others.
+* Using the identifier in a context where the compiler can infer the type
+  of the identifier resolves ambiguity in the case that one definition
+  matches the type stronger than the others.
 
   ```nim
   # Module A
   var x*: string
+  proc foo*(a: string) =
+    echo "A: ", a
   ```
 
   ```nim
   # Module B
   var x*: int
+  proc foo*(b: int) =
+    echo "B: ", b
   ```
 
   ```nim
   # Module C
   import A, B
+
+  foo("abc") # A: abc
+  foo(123) # B: 123
+  let inferred: proc (x: string) = foo
+  foo("def") # A: def
+
   write(stdout, x) # error: x is ambiguous
   write(stdout, A.x) # no error: qualifier used
+  
+  proc bar(a: int): int = a + 1
+  assert bar(x) == x + 1 # no error: only A.x of type int matches
 
   var x = 4
   write(stdout, x) # not ambiguous: uses the module C's x
   ```
+Modules can share their name, however, when trying to qualify an identifier with the module name the compiler will fail with ambiguous identifier error. One can qualify the identifier by aliasing the module.
+
+
+```nim
+# Module A/C
+proc fb* = echo "fizz"
+```
+
+
+```nim
+# Module B/C
+proc fb* = echo "buzz"
+```
+
+
+```nim
+import A/C
+import B/C
+
+C.fb() # Error: ambiguous identifier: 'C'
+```
+
+
+```nim
+import A/C as fizz
+import B/C
+
+fizz.fb() # Works
+```
 
 
 Packages
@@ -7096,7 +7472,7 @@ echo foo() # 2
 template foo: int = 3
 ```
 
-This is mostly intended for macro generated code. 
+This is mostly intended for macro generated code.
 
 compilation option pragmas
 --------------------------
@@ -7166,7 +7542,7 @@ but are used to override the settings temporarily. Example:
   template example(): string = "https://nim-lang.org"
   {.pop.}
 
-  {.push deprecated, hint[LineTooLong]: off, used, stackTrace: off.}
+  {.push deprecated, used, stackTrace: off.}
   proc sample(): bool = true
   {.pop.}
   ```
@@ -7205,14 +7581,14 @@ and before any variable in a module that imports it.
 
 Disabling certain messages
 --------------------------
-Nim generates some warnings and hints ("line too long") that may annoy the
+Nim generates some warnings and hints that may annoy the
 user. A mechanism for disabling certain messages is provided: Each hint
 and warning message is associated with a symbol. This is the message's
 identifier, which can be used to enable or disable the message by putting it
 in brackets following the pragma:
 
   ```Nim
-  {.hint[LineTooLong]: off.} # turn off the hint about too long lines
+  {.hint[XDeclaredButNotUsed]: off.} # Turn off the hint about declared but not used symbols.
   ```
 
 This is often better than disabling all warnings at once.
@@ -7328,6 +7704,37 @@ generates:
   ```
 
 
+size pragma
+-----------
+Nim automatically determines the size of an enum.
+But when wrapping a C enum type, it needs to be of a specific size.
+The `size pragma` allows specifying the size of the enum type.
+
+  ```Nim
+  type
+    EventType* {.size: sizeof(uint32).} = enum
+      QuitEvent,
+      AppTerminating,
+      AppLowMemory
+
+  doAssert sizeof(EventType) == sizeof(uint32)
+  ```
+
+The `size pragma` can also specify the size of an `importc` incomplete object type
+so that one can get the size of it at compile time even if it was declared without fields.
+
+  ```Nim
+    type
+      AtomicFlag* {.importc: "atomic_flag", header: "<stdatomic.h>", size: 1.} = object
+
+    static:
+      # if AtomicFlag didn't have the size pragma, this code would result in a compile time error.
+      echo sizeof(AtomicFlag)
+  ```
+
+The `size pragma` accepts only the values 1, 2, 4 or 8.
+
+
 Align pragma
 ------------
 
@@ -7440,10 +7847,16 @@ Compile pragma
 The `compile` pragma can be used to compile and link a C/C++ source file
 with the project:
 
+This pragma can take three forms. The first is a simple file input:
   ```Nim
   {.compile: "myfile.cpp".}
   ```
 
+The second form is a tuple where the second arg is the output name strutils formatter:
+  ```Nim
+  {.compile: ("file.c", "$1.o").}
+  ```
+
 **Note**: Nim computes a SHA1 checksum and only recompiles the file if it
 has changed. One can use the `-f`:option: command-line option to force
 the recompilation of the file.
@@ -7878,8 +8291,8 @@ CodegenDecl pragma
 ------------------
 
 The `codegenDecl` pragma can be used to directly influence Nim's code
-generator. It receives a format string that determines how the variable
-or proc is declared in the generated code.
+generator. It receives a format string that determines how the variable,
+proc or object type is declared in the generated code.
 
 For variables, $1 in the format string represents the type of the variable,
 $2 is the name of the variable, and each appearance of $# represents $1/$2
@@ -7915,6 +8328,29 @@ will generate this code:
   __interrupt void myinterrupt()
   ```
 
+For object types, the $1 represents the name of the object type, $2 is the list of
+fields and $3 is the base type.
+
+```nim
+
+const strTemplate = """
+  struct $1 {
+    $2
+  };
+"""
+type Foo {.codegenDecl:strTemplate.} = object
+  a, b: int
+```
+
+will generate this code:
+
+
+```c
+struct Foo {
+  NI a;
+  NI b;
+};
+```
 
 `cppNonPod` pragma
 ------------------
@@ -7965,6 +8401,24 @@ used. To see if a value was provided, `defined(FooBar)` can be used.
 The syntax `-d:flag`:option: is actually just a shortcut for
 `-d:flag=true`:option:.
 
+These pragmas also accept an optional string argument for qualified
+define names.
+
+  ```nim
+  const FooBar {.intdefine: "package.FooBar".}: int = 5
+  echo FooBar
+  ```
+
+  ```cmd
+  nim c -d:package.FooBar=42 foobar.nim
+  ```
+
+This helps disambiguate define names in different packages.
+
+See also the [generic `define` pragma](manual_experimental.html#generic-nimdefine-pragma)
+for a version of these pragmas that detects the type of the define based on
+the constant value.
+
 User-defined pragmas
 ====================
 
@@ -8209,8 +8663,7 @@ is available and a literal dollar sign must be written as ``$$``.
 Bycopy pragma
 -------------
 
-The `bycopy` pragma can be applied to an object or tuple type and
-instructs the compiler to pass the type by value to procs:
+The `bycopy` pragma can be applied to an object or tuple type or a proc param. It instructs the compiler to pass the type by value to procs:
 
   ```nim
   type
@@ -8218,14 +8671,73 @@ instructs the compiler to pass the type by value to procs:
       x, y, z: float
   ```
 
-The Nim compiler automatically determines whether a parameter is passed by value or by reference based on the parameter type's size. If a parameter must be passed by value or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas.
+The Nim compiler automatically determines whether a parameter is passed by value or
+by reference based on the parameter type's size. If a parameter must be passed by value
+or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas.
+Notice params marked as `byref` takes precedence over types marked as `bycopy`.
 
 Byref pragma
 ------------
 
-The `byref` pragma can be applied to an object or tuple type and instructs
-the compiler to pass the type by reference (hidden pointer) to procs.
+The `byref` pragma can be applied to an object or tuple type or a proc param.
+When applied to a type it instructs the compiler to pass the type by reference
+(hidden pointer) to procs. When applied to a param it will take precedence, even
+if the the type was marked as `bycopy`. When an `importc` type has a `byref` pragma or
+parameters are marked as `byref` in an `importc` proc, these params translate to pointers.
+When an `importcpp` type has a `byref` pragma, these params translate to
+C++ references `&`.
 
+  ```Nim
+  {.emit: """/*TYPESECTION*/
+  typedef struct {
+    int x;
+  } CStruct;
+  """.}
+
+  {.emit: """
+  #ifdef __cplusplus
+  extern "C"
+  #endif
+  int takesCStruct(CStruct* x) {
+    return x->x;
+  }
+  """.}
+
+  type
+    CStruct {.importc, byref.} = object
+      x: cint
+
+  proc takesCStruct(x: CStruct): cint {.importc.}
+  ```
+
+  or
+
+
+  ```Nim
+  type
+    CStruct {.importc.} = object
+      x: cint
+
+  proc takesCStruct(x {.byref.}: CStruct): cint {.importc.}
+  ```
+
+  ```Nim
+  {.emit: """/*TYPESECTION*/
+  struct CppStruct {
+    int x;
+
+    int takesCppStruct(CppStruct& y) {
+      return x + y.x;
+    }
+  };
+  """.}
+
+  type
+    CppStruct {.importcpp, byref.} = object
+      x: cint
+
+  proc takesCppStruct(x, y: CppStruct): cint {.importcpp.}
+  ```
 
 Varargs pragma
 --------------
@@ -8235,7 +8747,7 @@ after the last specified parameter. Nim string values will be converted to C
 strings automatically:
 
   ```Nim
-  proc printf(formatstr: cstring) {.nodecl, varargs.}
+  proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.}
 
   printf("hallo %s", "world") # "world" will be passed as C string
   ```
@@ -8287,16 +8799,16 @@ The `dynlib` import mechanism supports a versioning scheme:
     importc, dynlib: "libtcl(|8.5|8.4|8.3).so.(1|0)".}
   ```
 
-At runtime, the dynamic library is searched for (in this order)::
+At runtime, the dynamic library is searched for (in this order):
 
-  libtcl.so.1
-  libtcl.so.0
-  libtcl8.5.so.1
-  libtcl8.5.so.0
-  libtcl8.4.so.1
-  libtcl8.4.so.0
-  libtcl8.3.so.1
-  libtcl8.3.so.0
+    libtcl.so.1
+    libtcl.so.0
+    libtcl8.5.so.1
+    libtcl8.5.so.0
+    libtcl8.4.so.1
+    libtcl8.4.so.0
+    libtcl8.3.so.1
+    libtcl8.3.so.0
 
 The `dynlib` pragma supports not only constant strings as an argument but also
 string expressions in general:
@@ -8340,27 +8852,14 @@ This is only useful if the program is compiled as a dynamic library via the
 `--app:lib`:option: command-line option.
 
 
-
 Threads
 =======
 
-To enable thread support the `--threads:on`:option: command-line switch needs to
-be used. The [system module](system.html) module then contains several threading primitives.
-See the [channels](channels_builtin.html) modules
-for the low-level thread API. There are also high-level parallelism constructs
-available. See [spawn](manual_experimental.html#parallel-amp-spawn) for
+The `--threads:on`:option: command-line switch is enabled by default. The [typedthreads module](typedthreads.html) module then contains several threading primitives. See [spawn](manual_experimental.html#parallel-amp-spawn) for
 further details.
 
-Nim's memory model for threads is quite different than that of other common
-programming languages (C, Pascal, Java): Each thread has its own (garbage
-collected) heap, and sharing of memory is restricted to global variables. This
-helps to prevent race conditions. GC efficiency is improved quite a lot,
-because the GC never has to stop other threads and see what they reference.
+The only ways to create a thread is via `spawn` or `createThread`.
 
-The only way to create a thread is via `spawn` or
-`createThread`. The invoked proc must not use `var` parameters nor must
-any of its parameters contain a `ref` or `closure` type. This enforces
-the *no heap sharing restriction*.
 
 Thread pragma
 -------------
@@ -8477,9 +8976,7 @@ model low level lockfree mechanisms:
 
 
 The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]`
-in order to support *multi lock* statements. Why these are essential is
-explained in the [lock levels](manual_experimental.md#lock-levels) section
-of experimental manual.
+in order to support *multi lock* statements.
 
 
 ### Protecting general locations
diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md
index 9dd44ab3b..da51d59ad 100644
--- a/doc/manual_experimental.md
+++ b/doc/manual_experimental.md
@@ -26,9 +26,8 @@ oneself.
 Void type
 =========
 
-The `void` type denotes the absence of any type. Parameters of
-type `void` are treated as non-existent, `void` as a return type means that
-the procedure does not return a value:
+The `void` type denotes the absence of any value, i.e. it is the type that contains no values. Consequently, no value can be provided for parameters of
+type `void`, and no value can be returned from a function with return type `void`:
 
   ```nim
   proc nothing(x, y: void): void =
@@ -65,26 +64,28 @@ However, a `void` type cannot be inferred in generic code:
 The `void` type is only valid for parameters and return types; other symbols
 cannot have the type `void`.
 
+Generic `define` pragma
+=======================
 
-Unicode Operators
-=================
-
-Under the `--experimental:unicodeOperators`:option: switch,
-these Unicode operators are also parsed as operators::
-
-  ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓   # same priority as * (multiplication)
-  ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔             # same priority as + (addition)
-
+Aside the [typed define pragmas for constants](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas),
+there is a generic `{.define.}` pragma that interprets the value of the define
+based on the type of the constant value.
 
-If enabled, Unicode operators can be combined with non-Unicode operator
-symbols. The usual precedence extensions then apply, for example, `⊠=` is an
-assignment like operator just like `*=` is.
+  ```nim
+  const foo {.define: "package.foo".} = 123
+  const bar {.define: "package.bar".} = false
+  ```
 
-No Unicode normalization step is performed.
+  ```cmd
+  nim c -d:package.foo=456 -d:package.bar foobar.nim
+  ```
 
-.. note:: Due to parser limitations one **cannot** enable this feature via a
-  pragma `{.experimental: "unicodeOperators".}` reliably.
+The following types are supported:
 
+* `string` and `cstring`
+* Signed and unsigned integer types
+* `bool`
+* Enums
 
 Top-down type inference
 =======================
@@ -122,6 +123,87 @@ would not match the type of the variable, and an error would be given.
 
 The extent of this varies, but there are some notable special cases.
 
+
+Inferred generic parameters
+---------------------------
+
+In expressions making use of generic procs or templates, the expected
+(unbound) types are often able to be inferred based on context.
+This feature has to be enabled via `{.experimental: "inferGenericTypes".}`
+
+  ```nim  test = "nim c $1"
+  {.experimental: "inferGenericTypes".}
+
+  import std/options
+
+  var x = newSeq[int](1)
+  # Do some work on 'x'...
+
+  # Works!
+  # 'x' is 'seq[int]' so 'newSeq[int]' is implied
+  x = newSeq(10)
+
+  # Works!
+  # 'T' of 'none' is bound to the 'T' of 'noneProducer', passing it along.
+  # Effectively 'none.T = noneProducer.T'
+  proc noneProducer[T](): Option[T] = none()
+  let myNone = noneProducer[int]()
+
+  # Also works
+  # 'myOtherNone' binds its 'T' to 'float' and 'noneProducer' inherits it
+  # noneProducer.T = myOtherNone.T
+  let myOtherNone: Option[float] = noneProducer()
+
+  # Works as well
+  # none.T = myOtherOtherNone.T
+  let myOtherOtherNone: Option[int] = none()
+  ```
+
+This is achieved by reducing the types on the lhs and rhs until the *lhs* is left with only types such as `T`.
+While lhs and rhs are reduced together, this does *not* mean that the *rhs* will also only be left
+with a flat type `Z`, it may be of the form `MyType[Z]`.
+
+After the types have been reduced, the types `T` are bound to the types that are left on the rhs.
+
+If bindings *cannot be inferred*, compilation will fail and manual specification is required.
+
+An example for *failing inference* can be found when passing a generic expression
+to a function/template call:
+
+  ```nim  test = "nim c $1"  status = 1
+  {.experimental: "inferGenericTypes".}
+
+  proc myProc[T](a, b: T) = discard
+
+  # Fails! Unable to infer that 'T' is supposed to be 'int'
+  myProc(newSeq[int](), newSeq(1))
+
+  # Works! Manual specification of 'T' as 'int' necessary
+  myProc(newSeq[int](), newSeq[int](1))
+  ```
+
+Combination of generic inference with the `auto` type is also unsupported:
+
+  ```nim  test = "nim c $1"  status = 1
+  {.experimental: "inferGenericTypes".}
+
+  proc produceValue[T]: auto = default(T)
+  let a: int = produceValue() # 'auto' cannot be inferred here
+  ```
+
+**Note**: The described inference does not permit the creation of overrides based on
+the return type of a procedure. It is a mapping mechanism that does not attempt to 
+perform deeper inference, nor does it modify what is a valid override.
+
+  ```nim  test = "nim c $1"  status = 1
+  # Doesn't affect the following code, it is invalid either way
+  {.experimental: "inferGenericTypes".}
+
+  proc a: int = 0
+  proc a: float = 1.0 # Fails! Invalid code and not recommended
+  ```
+
+
 Sequence literals
 -----------------
 
@@ -133,7 +215,7 @@ let x: seq[seq[float]] = @[@[1, 2, 3], @[4, 5, 6]]
 
 This behavior is tied to the `@` overloads in the `system` module,
 so overloading `@` can disable this behavior. This can be circumvented by
-specifying the `` system.`@` `` overload. 
+specifying the `` system.`@` `` overload.
 
 ```nim
 proc `@`(x: string): string = "@" & x
@@ -307,29 +389,6 @@ scope. Therefore, the following will *fail to compile:*
 This feature will likely be replaced with a better solution to remove
 the need for forward declarations.
 
-
-Automatic dereferencing
-=======================
-
-Automatic dereferencing is performed for the first argument of a routine call.
-This feature has to be enabled via `{.experimental: "implicitDeref".}`:
-
-  ```nim
-  {.experimental: "implicitDeref".}
-
-  type
-    NodeObj = object
-      # ...
-    Node = ref NodeObj
-
-  proc depth(x: NodeObj): int = ...
-
-  let n = Node()
-  echo n.depth
-  # no need to write n[].depth
-  ```
-
-
 Special Operators
 =================
 
@@ -474,28 +533,25 @@ Assuming `foo` is a macro or a template, this is roughly equivalent to:
   ```
 
 
-Symbols as template/macro calls
-===============================
+Symbols as template/macro calls (alias syntax)
+==============================================
 
-Templates and macros that take no arguments can be called as lone symbols,
-i.e. without parentheses. This is useful for repeated uses of complex
-expressions that cannot conveniently be represented as runtime values.
+Templates and macros that have no generic parameters and no required arguments
+can be called as lone symbols, i.e. without parentheses. This is useful for
+repeated uses of complex expressions that cannot conveniently be represented
+as runtime values.
 
   ```nim
   type Foo = object
     bar: int
-  
+
   var foo = Foo(bar: 10)
-  template bar: untyped = foo.bar
+  template bar: int = foo.bar
   assert bar == 10
   bar = 15
   assert bar == 15
   ```
 
-In the future, this may require more specific information on template or macro
-signatures to be used. Specializations for some applications of this may also
-be introduced to guarantee consistency and circumvent bugs.
-
 
 Not nil annotation
 ==================
@@ -504,12 +560,14 @@ Not nil annotation
 `{.experimental: "notnil".}`.
 
 All types for which `nil` is a valid value can be annotated with the
-`not nil` annotation to exclude `nil` as a valid value:
+`not nil` annotation to exclude `nil` as a valid value. Note that only local
+symbols are checked.
 
   ```nim
   {.experimental: "notnil".}
 
   type
+    TObj = object
     PObject = ref TObj not nil
     TProc = (proc (x, y: int)) not nil
 
@@ -520,8 +578,11 @@ All types for which `nil` is a valid value can be annotated with the
   p(nil)
 
   # and also this:
-  var x: PObject
-  p(x)
+  proc foo =
+    var x: PObject
+    p(x)
+
+  foo()
   ```
 
 The compiler ensures that every code path initializes variables which contain
@@ -563,8 +624,7 @@ Since version 1.4, a stricter definition of "side effect" is available.
 In addition to the existing rule that a side effect is calling a function
 with side effects, the following rule is also enforced:
 
-Any mutation to an object does count as a side effect if that object is reachable
-via a parameter that is not declared as a `var` parameter.
+A store to the heap via a `ref` or `ptr` indirection is not allowed.
 
 For example:
 
@@ -584,15 +644,12 @@ For example:
       it = it.ri
 
   func mut(n: Node) =
-    let m = n # is the statement that connected the mutation to the parameter
-    m.data = "yeah" # the mutation is here
-    # Error: 'mut' can have side effects
-    # an object reachable from 'n' is potentially mutated
-  ```
-
+    var it = n
+    while it != nil:
+      it.data = "yeah" # forbidden mutation
+      it = it.ri
 
-The algorithm behind this analysis is described in
-the [view types algorithm].
+  ```
 
 
 View types
@@ -811,9 +868,9 @@ be part of a single graph.
 Assignments like `a = b` "connect" two variables, both variables end up in the
 same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is
 much more complex than that and can involve multiple assignment targets
-and sources::
+and sources:
 
-  f(x, y) = g(a, b)
+    f(x, y) = g(a, b)
 
 connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`.
 A type based alias analysis rules out some of these combinations, for example
@@ -866,6 +923,7 @@ The concept matches if:
 
 a) all expressions within the body can be compiled for the tested type
 b) all statically evaluable boolean expressions in the body are true
+c) all type modifiers specified match their respective definitions
 
 The identifiers following the `concept` keyword represent instances of the
 currently matched type. You can apply any of the standard type modifiers such
@@ -1314,7 +1372,7 @@ to be computed dynamically.
   ```nim
   {.experimental: "dynamicBindSym".}
 
-  import macros
+  import std/macros
 
   macro callOp(opName, arg1, arg2): untyped =
     result = newCall(bindSym($opName), arg1, arg2)
@@ -1607,16 +1665,16 @@ all the arguments, but also the matched operators in reverse polish notation:
   ```
 
 This passes the expression `x + y * z - x` to the `optM` macro as
-an `nnkArgList` node containing::
+an `nnkArgList` node containing:
 
-  Arglist
-    Sym "x"
-    Sym "y"
-    Sym "z"
-    Sym "*"
-    Sym "+"
-    Sym "x"
-    Sym "-"
+    Arglist
+      Sym "x"
+      Sym "y"
+      Sym "z"
+      Sym "*"
+      Sym "+"
+      Sym "x"
+      Sym "-"
 
 (This is the reverse polish notation of `x + y * z - x`.)
 
@@ -1750,7 +1808,7 @@ the overhead of an indirection via `FlowVar[T]` to ensure correctness.
 .. note:: Currently exceptions are not propagated between `spawn`'ed tasks!
 
 This feature is likely to be removed in the future as external packages
-can have better solutions. 
+can have better solutions.
 
 
 Spawn statement
@@ -1861,100 +1919,751 @@ restrictions / changes:
   yet performed for ordinary slices outside of a `parallel` section.
 
 
+Strict definitions and `out` parameters
+=======================================
+
+With `experimental: "strictDefs"` *every* local variable must be initialized explicitly before it can be used:
+
+  ```nim
+  {.experimental: "strictDefs".}
 
-Lock levels
-===========
+  proc test =
+    var s: seq[string]
+    s.add "abc" # invalid!
 
-Lock levels are used to enforce a global locking order in order to detect
-potential deadlocks during semantic analysis. A lock level is an constant
-integer in the range 0..1_000. Lock level 0 means that no lock is acquired at
-all.
+  ```
 
-If a section of code holds a lock of level `M`, it can also acquire any
-lock of level `N < M`. Another lock of level `M` cannot be acquired. Locks
-of the same level can only be acquired *at the same time* within a
-single `locks` section:
+Needs to be written as:
 
   ```nim
-  var a, b: TLock[2]
-  var x: TLock[1]
-  # invalid locking order: TLock[1] cannot be acquired before TLock[2]:
-  {.locks: [x].}:
-    {.locks: [a].}:
-      ...
-  # valid locking order: TLock[2] acquired before TLock[1]:
-  {.locks: [a].}:
-    {.locks: [x].}:
-      ...
+  {.experimental: "strictDefs".}
 
-  # invalid locking order: TLock[2] acquired before TLock[2]:
-  {.locks: [a].}:
-    {.locks: [b].}:
-      ...
+  proc test =
+    var s: seq[string] = @[]
+    s.add "abc" # valid!
 
-  # valid locking order, locks of the same level acquired at the same time:
-  {.locks: [a, b].}:
-    ...
   ```
 
+A control flow analysis is performed in order to prove that a variable has been written to
+before it is used. Thus the following is valid:
 
-Here is how a typical multilock statement can be implemented in Nim. Note how
-the runtime check is required to ensure a global ordering for two locks `a`
-and `b` of the same lock level:
+  ```nim
+  {.experimental: "strictDefs".}
+
+  proc test(cond: bool) =
+    var s: seq[string]
+    if cond:
+      s = @["y"]
+    else:
+      s = @[]
+    s.add "abc" # valid!
+  ```
+
+In this example every path does set `s` to a value before it is used.
 
   ```nim
-  template multilock(a, b: ptr TLock; body: untyped) =
-    if cast[ByteAddress](a) < cast[ByteAddress](b):
-      pthread_mutex_lock(a)
-      pthread_mutex_lock(b)
+  {.experimental: "strictDefs".}
+
+  proc test(cond: bool) =
+    let s: seq[string]
+    if cond:
+      s = @["y"]
     else:
-      pthread_mutex_lock(b)
-      pthread_mutex_lock(a)
-    {.locks: [a, b].}:
-      try:
-        body
-      finally:
-        pthread_mutex_unlock(a)
-        pthread_mutex_unlock(b)
+      s = @[]
+  ```
+
+With `experimental: "strictDefs"`, `let` statements are allowed to not have an initial value, but every path should set `s` to a value before it is used.
+
+
+`out` parameters
+----------------
+
+An `out` parameter is like a `var` parameter but it must be written to before it can be used:
+
+  ```nim
+  proc myopen(f: out File; name: string): bool =
+    f = default(File)
+    result = open(f, name)
+  ```
+
+While it is usually the better style to use the return type in order to return results API and ABI
+considerations might make this infeasible. Like for `var T` Nim maps `out T` to a hidden pointer.
+For example POSIX's `stat` routine can be wrapped as:
+
+  ```nim
+  proc stat*(a1: cstring, a2: out Stat): cint {.importc, header: "<sys/stat.h>".}
+  ```
+
+When the implementation of a routine with output parameters is analysed, the compiler
+checks that every path before the (implicit or explicit) return does set every output
+parameter:
+
+  ```nim
+  proc p(x: out int; y: out string; cond: bool) =
+    x = 4
+    if cond:
+      y = "abc"
+    # error: not every path initializes 'y'
+  ```
+
+
+Out parameters and exception handling
+-------------------------------------
+
+The analysis should take exceptions into account (but currently does not):
+
+  ```nim
+  proc p(x: out int; y: out string; cond: bool) =
+    x = canRaise(45)
+    y = "abc" # <-- error: not every path initializes 'y'
+  ```
+
+Once the implementation takes exceptions into account it is easy enough to
+use `outParam = default(typeof(outParam))` in the beginning of the proc body.
+
+Out parameters and inheritance
+------------------------------
+
+It is not valid to pass an lvalue of a supertype to an `out T` parameter:
+
+  ```nim
+  type
+    Superclass = object of RootObj
+      a: int
+    Subclass = object of Superclass
+      s: string
+
+  proc init(x: out Superclass) =
+    x = Superclass(a: 8)
+
+  var v: Subclass
+  init v
+  use v.s # the 's' field was never initialized!
+  ```
+
+However, in the future this could be allowed and provide a better way to write object
+constructors that take inheritance into account.
+
+
+**Note**: The implementation of "strict definitions" and "out parameters" is experimental but the concept
+is solid and it is expected that eventually this mode becomes the default in later versions.
+
+
+Strict case objects
+===================
+
+With `experimental: "strictCaseObjects"` *every* field access is checked to be valid at compile-time.
+The field is within a `case` section of an `object`.
+
+  ```nim
+  {.experimental: "strictCaseObjects".}
+
+  type
+    Foo = object
+      case b: bool
+      of false:
+        s: string
+      of true:
+        x: int
+
+  var x = Foo(b: true, x: 4)
+  case x.b
+  of true:
+    echo x.x # valid
+  of false:
+    echo "no"
+
+  case x.b
+  of false:
+    echo x.x # error: field access outside of valid case branch: x.x
+  of true:
+    echo "no"
+
+  ```
+
+**Note**: The implementation of "strict case objects" is experimental but the concept
+is solid and it is expected that eventually this mode becomes the default in later versions.
+
+
+Quirky routines
+===============
+
+The default code generation strategy of exceptions under the ARC/ORC model is the so called
+`--exceptions:goto` implementation. This implementation inserts a check after every call that
+can potentially raise an exception. A typical instruction sequence for this on
+for a x86 64 bit machine looks like:
+
+  ```
+  cmp DWORD PTR [rbx], 0
+  je  .L1
+  ```
+
+This is a memory fetch followed by jump. (An ideal implementation would
+use the carry flag and a single instruction like ``jc .L1``.)
+
+This overhead might not be desired and depending on the semantics of the routine may not be required
+either.
+So it can be disabled via a `.quirky` annotation:
+
+  ```nim
+  proc wontRaise(x: int) {.quirky.} =
+    if x != 0:
+      # because of `quirky` this will continue even if `write` raised an IO exception:
+      write x
+      wontRaise(x-1)
+
+  wontRaise 10
+
+  ```
+
+If the used exception model is not `--exceptions:goto` then the `quirky` pragma has no effect and is
+ignored.
+
+The `quirky` pragma can also be be pushed in order to affect a group of routines and whether
+the compiler supports the pragma can be checked with `defined(nimHasQuirky)`:
+
+  ```nim
+  when defined(nimHasQuirky):
+    {.push quirky: on.}
+
+  proc doRaise() = raise newException(ValueError, "")
+
+  proc f(): string = "abc"
+
+  proc q(cond: bool) =
+    if cond:
+      doRaise()
+    echo f()
+
+  q(true)
+
+  when defined(nimHasQuirky):
+    {.pop.}
   ```
 
+**Warning**: The `quirky` pragma only affects code generation, no check for validity is performed!
+
+
+Threading under ARC/ORC
+=======================
+
+ARC/ORC supports a shared heap out of the box. This means that messages can be sent between
+threads without copies. However, without copying the data there is an inherent danger of
+data races. Data races are prevented at compile-time if it is enforced that
+only **isolated** subgraphs can be sent around.
+
 
-Whole routines can also be annotated with a `locks` pragma that takes a lock
-level. This then means that the routine may acquire locks of up to this level.
-This is essential so that procs can be called within a `locks` section:
+Isolation
+---------
+
+The standard library module `isolation.nim` provides a generic type `Isolated[T]` that
+captures the important notion that nothing else can reference the graph that is wrapped
+inside `Isolated[T]`. It is what a channel implementation should use in order to enforce
+the freedom of data races:
 
   ```nim
-  proc p() {.locks: 3.} = discard
+  proc send*[T](c: var Channel[T]; msg: sink Isolated[T])
+  proc recv*[T](c: var Channel[T]): T
+    ## Note: Returns T, not Isolated[T] for convenience.
+
+  proc recvIso*[T](c: var Channel[T]): Isolated[T]
+    ## remembers the data is Isolated[T].
+  ```
+
+In order to create an `Isolated` graph one has to use either `isolate` or `unsafeIsolate`.
+`unsafeIsolate` is as its name says unsafe and no checking is performed. It should be considered
+to be as dangerous as a `cast` operation.
+
 
-  var a: TLock[4]
-  {.locks: [a].}:
-    # p's locklevel (3) is strictly less than a's (4) so the call is allowed:
-    p()
+Construction must ensure that the invariant holds, namely that the wrapped `T`
+is free of external aliases into it. `isolate` ensures this invariant. It is
+inspired by Pony's `recover` construct:
+
+  ```nim
+  func isolate(x: sink T): Isolated[T] {.magic: "Isolate".}
   ```
 
 
-As usual, `locks` is an inferred effect and there is a subtype
-relation: `proc () {.locks: N.}` is a subtype of `proc () {.locks: M.}`
-iff (M <= N).
+As you can see, this is a new builtin because the check it performs on `x` is non-trivial:
 
-The `locks` pragma can also take the special value `"unknown"`. This
-is useful in the context of dynamic method dispatching. In the following
-example, the compiler can infer a lock level of 0 for the `base` case.
-However, one of the overloaded methods calls a procvar which is
-potentially locking. Thus, the lock level of calling `g.testMethod`
-cannot be inferred statically, leading to compiler warnings. By using
-`{.locks: "unknown".}`, the base method can be marked explicitly as
-having unknown lock level as well:
+If `T` does not contain a `ref` or `closure` type, it is isolated. Else the syntactic
+structure of `x` is analyzed:
+
+- Literals like `nil`, `4`, `"abc"` are isolated.
+- A local variable or a routine parameter is isolated if either of these conditions is true:
+  1. Its type is annotated with the `.sendable` pragma. Note `Isolated[T]` is annotated as
+     `.sendable`.
+  2. Its type contains the potentially dangerous `ref` and `proc {.closure}` types
+     only in places that are protected via a `.sendable` container.
+
+- An array constructor `[x...]` is isolated if every element `x` is isolated.
+- An object constructor `Obj(x...)` is isolated if every element `x` is isolated.
+- An `if` or `case` expression is isolated if all possible values the expression
+  may return are isolated.
+- A type conversion `C(x)` is isolated if `x` is isolated. Analogous for `cast`
+  expressions.
+- A function call `f(x...)` is isolated if `f` is `.noSideEffect` and for every argument `x`:
+  - `x` is isolated **or**
+  - `f`'s return type cannot *alias* `x`'s type. This is checked via a form of alias analysis as explained in the next paragraph.
+
+
+
+Alias analysis
+--------------
+
+We start with an important, simple case that must be valid: Sending the result
+of `parseJson` to a channel. Since the signature
+is `func parseJson(input: string): JsonNode` it is easy to see that JsonNode
+can never simply be a view into `input` which is a `string`.
+
+A different case is the identity function `id`, `send id(myJsonGraph)` must be
+invalid because we do not know how many aliases into `myJsonGraph` exist
+elsewhere.
+
+In general type `A` can alias type `T` if:
+
+- `A` and `T` are the same types.
+- `A` is a distinct type derived from `T`.
+- `A` is a field inside `T` if `T` is a final object type.
+- `T` is an inheritable object type. (An inherited type could always contain
+  a `field: A`).
+- `T` is a closure type. Reason: `T`'s environment can contain a field of
+  type `A`.
+- `A` is the element type of `T` if `T` is an array, sequence or pointer type.
+
+
+
+
+Sendable pragma
+---------------
+
+A container type can be marked as `.sendable`. `.sendable` declares that the type
+encapsulates a `ref` type effectively so that a variable of this container type
+can be used in an `isolate` context:
 
   ```nim
-  type SomeBase* = ref object of RootObj
-  type SomeDerived* = ref object of SomeBase
-    memberProc*: proc ()
+  type
+    Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied.
+      value: T
+
+  proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
+
+  proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} =
+    # delegate to value's sink operation
+    `=sink`(dest.value, src.value)
 
-  method testMethod(g: SomeBase) {.base, locks: "unknown".} = discard
-  method testMethod(g: SomeDerived) =
-    if g.memberProc != nil:
-      g.memberProc()
+  proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} =
+    # delegate to value's destroy operation
+    `=destroy`(dest.value)
   ```
 
-This feature may be removed in the future due to its practical difficulties.
+The `.sendable` pragma itself is an experimenal, unchecked, unsafe annotation. It is
+currently only used by `Isolated[T]`.
+
+Virtual pragma
+==============
+
+`virtual` is designed to extend or create virtual functions when targeting the cpp backend. When a proc is marked with virtual, it forward declares the proc header within the type's body.
+
+Here's an example of how to use the virtual pragma:
+
+```nim
+proc newCpp*[T](): ptr T {.importcpp: "new '*0()".}
+type
+  Foo = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+
+proc salute(self: FooPtr) {.virtual.} =
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.virtual.} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())
+
+foo.salute() # prints hello foo
+boo.salute() # prints hello boo
+booAsFoo.salute() # prints hello boo
+```
+In this example, the `salute` function is virtual in both Foo and Boo types. This allows for polymorphism.
+
+The virtual pragma also supports a special syntax to express Cpp constraints. Here's how it works:
+
+`$1` refers to the function name
+`'idx` refers to the type of the argument at the position idx. Where idx = 1 is the `this` argument.
+`#idx` refers to the argument name.
+
+The return type can be referred to as `-> '0`, but this is optional and often not needed.
+
+ ```nim
+ {.emit:"""/*TYPESECTION*/
+#include <iostream>
+  class CppPrinter {
+  public:
+
+    virtual void printConst(char* message) const {
+        std::cout << "Const Message: " << message << std::endl;
+    }
+    virtual void printConstRef(char* message, const int& flag) const {
+        std::cout << "Const Ref Message: " << message << std::endl;
+    }
+};
+""".}
+
+type
+  CppPrinter {.importcpp, inheritable.} = object
+  NimPrinter {.exportc.} = object of CppPrinter
+
+proc printConst(self: CppPrinter; message:cstring) {.importcpp.}
+CppPrinter().printConst(message)
+
+# override is optional.
+proc printConst(self: NimPrinter; message: cstring) {.virtual: "$1('2 #2) const override".} =
+  echo "NimPrinter: " & $message
+
+proc printConstRef(self: NimPrinter; message: cstring; flag:int32) {.virtual: "$1('2 #2, const '3& #3 ) const override".} =
+  echo "NimPrinterConstRef: " & $message
+
+NimPrinter().printConst(message)
+var val: int32 = 10
+NimPrinter().printConstRef(message, val)
+
+```
+
+Constructor pragma
+==================
+
+The `constructor` pragma can be used in two ways: in conjunction with `importcpp` to import a C++ constructor, and to declare constructors that operate similarly to `virtual`.
+
+Consider:
+
+```nim
+type Foo* = object
+  x: int32
+
+proc makeFoo(x: int32): Foo {.constructor.} =
+  result.x = x
+```
+
+It forward declares the constructor in the type definition. When the constructor has parameters, it also generates a default constructor. One can avoid this behaviour by using `noDecl` in a default constructor.
+
+Like `virtual`, `constructor` also supports a syntax that allows to express C++ constraints.
+
+For example:
+
+```nim
+{.emit:"""/*TYPESECTION*/
+struct CppClass {
+  int x;
+  int y;
+  CppClass(int inX, int inY) {
+    this->x = inX;
+    this->y = inY;
+  }
+  //CppClass() = default;
+};
+""".}
+
+type
+  CppClass* {.importcpp, inheritable.} = object
+    x: int32
+    y: int32
+  NimClass* = object of CppClass
+
+proc makeNimClass(x: int32): NimClass {.constructor:"NimClass('1 #1) : CppClass(0, #1)".} =
+  result.x = x
+
+# Optional: define the default constructor explicitly
+proc makeCppClass(): NimClass {.constructor: "NimClass() : CppClass(0, 0)".} =
+  result.x = 1
+```
+
+In the example above `CppClass` has a deleted default constructor. Notice how by using the constructor syntax, one can call the appropriate constructor.
+
+Notice when calling a constructor in the section of a global variable initialization, it will be called before `NimMain` meaning Nim is not fully initialized.
+
+Constructor Initializer
+=======================
+
+By default Nim initializes `importcpp` types with `{}`. This can be problematic when importing
+types with a deleted default constructor. In order to avoid this, one can specify default values for a constructor by specifying default values for the proc params in the `constructor` proc.
+
+For example:
+
+```nim
+
+{.emit: """/*TYPESECTION*/
+struct CppStruct {
+  CppStruct(int x, char* y): x(x), y(y){}
+  int x;
+  char* y;
+};
+""".}
+type
+  CppStruct {.importcpp, inheritable.} = object
+
+proc makeCppStruct(a: cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.}
+
+(proc (s: CppStruct) = echo "hello")(makeCppStruct()) 
+# If one removes a default value from the constructor and passes it to the call explicitly, the C++ compiler will complain.
+
+```
+Skip initializers in fields members
+===================================
+
+By using `noInit` in a type or field declaration, the compiler will skip the initializer. By doing so one can explicitly initialize those values in the constructor of the type owner.
+
+For example:
+
+```nim
+
+{.emit: """/*TYPESECTION*/
+  struct Foo {
+    Foo(int a){};
+  };
+  struct Boo {
+    Boo(int a){};
+  };
+
+  """.}
+
+type 
+  Foo {.importcpp.} = object
+  Boo {.importcpp, noInit.} = object
+  Test {.exportc.} = object
+    foo {.noInit.}: Foo
+    boo: Boo
+
+proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = 
+  discard
+
+proc main() = 
+  var t = makeTest()
+
+main()
+
+```
+
+Will produce: 
+
+```cpp
+
+struct Test {
+	Foo foo; 
+	Boo boo;
+  N_LIB_PRIVATE N_NOCONV(, Test)(void);
+};
+
+```
+
+Notice that without `noInit` it would produce `Foo foo {}` and `Boo boo {}`
+
+
+Member pragma
+=============
+
+Like the `constructor` and `virtual` pragmas, the `member` pragma can be used to attach a procedure to a C++ type. It's more flexible than the `virtual` pragma in the sense that it accepts not only names but also operators and destructors.
+
+For example:
+
+```nim
+proc print(s: cstring) {.importcpp: "printf(@)", header: "<stdio.h>".}
+
+type
+  Doo {.exportc.} = object
+    test: int
+
+proc memberProc(f: Doo) {.member.} = 
+  echo $f.test
+
+proc destructor(f: Doo) {.member: "~'1()", used.} = 
+  print "destructing\n"
+
+proc `==`(self, other: Doo): bool {.member: "operator==('2 const & #2) const -> '0".} = 
+  self.test == other.test
+
+let doo = Doo(test: 2)
+doo.memberProc()
+echo doo == Doo(test: 1)
+
+```
+
+Will print:
+```
+2
+false
+destructing
+destructing
+```
+
+Notice how the C++ destructor is called automatically. Also notice the double implementation of `==` as an operator in Nim but also in C++. This is useful if you need the type to match some C++ `concept` or `trait` when interoping. 
+
+A side effect of being able to declare C++ operators, is that you can now also create a
+C++ functor to have seamless interop with C++ lambdas (syntactic sugar for functors).
+
+For example:
+
+```nim
+type
+  NimFunctor = object
+    discard
+proc invoke(f: NimFunctor; n: int) {.member: "operator ()('2 #2)".} = 
+  echo "FunctorSupport!"
+
+{.experimental: "callOperator".}
+proc `()`(f: NimFunctor; n:int) {.importcpp: "#(@)" .} 
+NimFunctor()(1)
+```
+Notice we use the overload of `()` to have the same semantics in Nim, but on the `importcpp` we import the functor as a function. 
+This allows to easy interop with functions that accepts for example a `const` operator in its signature. 
+
+
+Injected symbols in generic procs and templates
+===============================================
+
+With the experimental option `openSym`, captured symbols in generic routine and
+template bodies may be replaced by symbols injected locally by templates/macros
+at instantiation time. `bind` may be used to keep the captured symbols over the
+injected ones regardless of enabling the options, but other methods like
+renaming the captured symbols should be used instead so that the code is not
+affected by context changes.
+
+Since this change may affect runtime behavior, the experimental switch
+`openSym` needs to be enabled; and a warning is given in the case where an
+injected symbol would replace a captured symbol not bound by `bind` and
+the experimental switch isn't enabled.
+
+```nim
+const value = "captured"
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  body
+
+proc old[T](): string =
+  foo(123):
+    return value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+echo old[int]() # "captured"
+
+template oldTempl(): string =
+  block:
+    foo(123):
+      value # warning: a new `value` has been injected, use `bind` or turn on `experimental:openSym`
+echo oldTempl() # "captured"
+
+{.experimental: "openSym".}
+
+proc bar[T](): string =
+  foo(123):
+    return value
+assert bar[int]() == "injected" # previously it would be "captured"
+
+proc baz[T](): string =
+  bind value
+  foo(123):
+    return value
+assert baz[int]() == "captured"
+
+template barTempl(): string =
+  block:
+    foo(123):
+      value
+assert barTempl() == "injected" # previously it would be "captured"
+
+template bazTempl(): string =
+  bind value
+  block:
+    foo(123):
+      value
+assert bazTempl() == "captured"
+```
+
+This option also generates a new node kind `nnkOpenSym` which contains
+exactly 1 `nnkSym` node. In the future this might be merged with a slightly
+modified `nnkOpenSymChoice` node but macros that want to support the
+experimental feature should still handle `nnkOpenSym`, as the node kind would
+simply not be generated as opposed to being removed.
+
+Another experimental switch `genericsOpenSym` exists that enables this behavior
+at instantiation time, meaning templates etc can enable it specifically when
+they are being called. However this does not generate `nnkOpenSym` nodes
+(unless the other switch is enabled) and so doesn't reflect the regular
+behavior of the switch.
+
+```nim
+const value = "captured"
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  {.push experimental: "genericsOpenSym".}
+  body
+  {.pop.}
+
+proc bar[T](): string =
+  foo(123):
+    return value
+echo bar[int]() # "injected"
+
+template barTempl(): string =
+  block:
+    var res: string
+    foo(123):
+      res = value
+    res
+assert barTempl() == "injected"
+```
+
+
+VTable for methods
+==================
+
+Methods now support implementations based on a VTable by using `--experimental:vtables`. Note that the option needs to enabled
+globally. The virtual method table is stored in the type info of
+an object, which is an array of function pointers.
+
+```nim
+method foo(x: Base, ...) {.base.}
+method foo(x: Derived, ...) {.base.}
+```
+
+It roughly generates a dispatcher like
+
+```nim
+proc foo_dispatch(x: Base, ...) =
+  x.typeinfo.vtable[method_index](x, ...) # method_index is the index of the sorted order of a method
+```
+
+Methods are required to be in the same module where their type has been defined.
+
+```nim
+# types.nim
+type
+  Base* = ref object
+```
+
+```nim
+import types
+
+method foo(x: Base) {.base.} = discard
+```
+
+It gives an error: method `foo` can be defined only in the same module with its type (Base).
+
+
+asmSyntax pragma
+================
+
+The `asmSyntax` pragma is used to specify target inline assembler syntax in an `asm` statement.
+
+It prevents compiling code with different of the target CC inline asm syntax, i.e. it will not allow gcc inline asm code to be compiled with vcc.
+
+```nim
+proc nothing() =
+  asm {.asmSyntax: "gcc".}"""
+    nop
+  """
+```
+
+The current C(C++) backend implementation cannot generate code for gcc and for vcc at the same time. For example, `{.asmSyntax: "vcc".}` with the ICC compiler will not generate code with intel asm syntax, even though ICC can use both gcc-like and vcc-like asm.
diff --git a/doc/manual_experimental_strictnotnil.md b/doc/manual_experimental_strictnotnil.md
index b6d8e796e..a6fa6cda8 100644
--- a/doc/manual_experimental_strictnotnil.md
+++ b/doc/manual_experimental_strictnotnil.md
@@ -74,7 +74,9 @@ local turn on/off
 ---------------------
 
 You can still turn off nil checking on function/module level by using a `{.strictNotNil: off.}` pragma.
-Note: test that/TODO for code/manual.
+
+..
+  Note: test that/TODO for code/manual.
 
 nilability state
 -----------------
@@ -101,7 +103,8 @@ Types are either nilable or non-nilable.
 When you pass a param or a default value, we use the type : for nilable types we return `MaybeNil`
 and for non-nilable `Safe`.
 
-TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually `nil` , so we should think a bit more about this section.)
+..
+  TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually `nil` , so we should think a bit more about this section.)
 
 params rules
 ------------
@@ -236,11 +239,11 @@ e.g.
   left = nil # moving out
   ```
 
+..
+  initialization of non nilable and nilable values
+  -------------------------------------------------
 
-initialization of non nilable and nilable values
--------------------------------------------------
-
-TODO
+  TODO
 
 warnings and errors
 ---------------------
diff --git a/doc/markdown_rst.md b/doc/markdown_rst.md
index c507f2511..c7977f75a 100644
--- a/doc/markdown_rst.md
+++ b/doc/markdown_rst.md
@@ -9,6 +9,8 @@ Nim-flavored Markdown and reStructuredText
 .. include:: rstcommon.rst
 .. contents::
 
+.. importdoc:: docgen.md
+
 Both `Markdown`:idx: (md) and `reStructuredText`:idx: (RST) are markup
 languages whose goal is to typeset texts with complex structure,
 formatting and references using simple plaintext representation.
@@ -22,19 +24,20 @@ Usage (to convert Markdown into HTML):
   nim md2html markdown_rst.md
   ```
 
-Output::
-  You're reading it!
+Output:
+
+    You're reading it!
 
 The `md2tex`:option: command is invoked identically to `md2html`:option:,
 but outputs a ``.tex`` file instead of ``.html``.
 
 These tools embedded into Nim compiler; the compiler can output
-the result to HTML \[#html] or Latex \[#latex].
+the result to HTML [^html] or Latex [^latex].
 
-\[#html] commands `nim doc`:cmd: for ``*.nim`` files and
+[^html]: commands `nim doc`:cmd: for ``*.nim`` files and
    `nim rst2html`:cmd: for ``*.rst`` files
 
-\[#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and
+[^latex]: commands `nim doc2tex`:cmd: for ``*.nim`` and
    `nim rst2tex`:cmd: for ``*.rst``.
 
 Full list of supported commands:
@@ -69,7 +72,7 @@ Features
 A large subset is implemented with some [limitations] and
 [additional Nim-specific features].
 
-Supported standard RST features:
+Supported common RST/Markdown features:
 
 * body elements
   + sections
@@ -80,10 +83,8 @@ Supported standard RST features:
     characters:  1. ... 2. ... *or* a. ... b. ... *or* A. ... B. ...
   + footnotes (including manually numbered, auto-numbered, auto-numbered
     with label, and auto-symbol footnotes) and citations
-  + definition lists
   + field lists
   + option lists
-  + indented literal blocks
   + quoted literal blocks
   + line blocks
   + simple tables
@@ -106,11 +107,42 @@ Supported standard RST features:
     (see [RST roles list] for description).
   + inline internal targets
 
+RST mode only features
+----------------------
+
++ RST syntax for definition lists (that is additional indentation after
+  a definition line)
++ indented literal blocks starting from ``::``
+
+Markdown-specific features
+--------------------------
+
+* Markdown tables
+* Markdown code blocks. For them the same additional arguments as for RST
+  code blocks can be provided (e.g. `test` or `number-lines`) but with
+  a one-line syntax like this:
+
+      ```nim test number-lines=10
+      echo "ok"
+      ```
+* Markdown links ``[...](...)``
+* Pandoc syntax for automatic links ``[...]``, see [Referencing] for description
+* Pandoc syntax for footnotes, including ``[^10]`` (manually numbered)
+  and ``[^someName]`` (auto-numbered with a label)
+* Markdown literal blocks indented by 4 or more spaces
+* Markdown headlines
+* Markdown block quotes
+* Markdown syntax for definition lists
+* using ``1`` as auto-enumerator in enumerated lists like RST ``#``
+  (auto-enumerator ``1`` can not be used with ``#`` in the same list)
+
 Additional Nim-specific features
 --------------------------------
 
-* directives: ``code-block`` \[cmp:Sphinx], ``title``,
-  ``index`` \[cmp:Sphinx]
+* referencing to definitions in external files, see
+  [Markup external referencing] section
+* directives: ``code-block`` [^Sphinx], ``title``,
+  ``index`` [^Sphinx]
 * predefined roles
   - ``:nim:`` (default), ``:c:`` (C programming language),
     ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#).
@@ -124,50 +156,111 @@ Additional Nim-specific features
     - ``:cmd:`` for commands and common shells syntax
     - ``:console:`` the same  for interactive sessions
       (commands should be prepended by ``$``)
-    - ``:program:`` for executable names \[cmp:Sphinx]
+    - ``:program:`` for executable names [^Sphinx]
       (one can just use ``:cmd:`` on single word)
-    - ``:option:`` for command line options \[cmp:Sphinx]
+    - ``:option:`` for command line options [^Sphinx]
   - ``:tok:``, a role for highlighting of programming language tokens
 * ***triple emphasis*** (bold and italic) using \*\*\*
 * ``:idx:`` role for \`interpreted text\` to include the link to this
   text into an index (example: [Nim index]).
 * double slash `//` in option lists serves as a prefix for any option that
-  starts from a word (without any leading symbols like `-`, `--`, `/`)::
+  starts from a word (without any leading symbols like `-`, `--`, `/`):
 
-    //compile   compile the project
-    //doc       generate documentation
+      //compile   compile the project
+      //doc       generate documentation
 
   Here the dummy `//` will disappear, while options `compile`:option:
   and `doc`:option: will be left in the final document.
+* emoji / smiley symbols
 
-\[cmp:Sphinx] similar but different from the directives of
+[^Sphinx]: similar but different from the directives of
    Python [Sphinx directives] and [Sphinx roles] extensions
 
-Extra features
---------------
+.. Note:: By default Nim has ``roSupportMarkdown`` and
+   ``roSupportRawDirective`` turned **on**.
 
-Optional additional features, by default turned on:
+.. warning:: Using Nim-specific features can cause other Markdown and
+  RST implementations to fail on your document.
 
-* emoji / smiley symbols
-* Markdown tables
-* Markdown code blocks. For them the same additional arguments as for RST
-  code blocks can be provided (e.g. `test` or `number-lines`) but with
-  a one-line syntax like this::
+Referencing
+===========
 
-    ```nim test number-lines=10
-    echo "ok"
-    ```
-* Markdown links
-* Markdown headlines
-* Markdown block quotes
-* using ``1`` as auto-enumerator in enumerated lists like RST ``#``
-  (auto-enumerator ``1`` can not be used with ``#`` in the same list)
+To be able to copy and share links Nim generates anchors for all
+main document elements:
 
-.. Note:: By default Nim has ``roSupportMarkdown`` and
-   ``roSupportRawDirective`` turned **on**.
+* headlines (including document title)
+* footnotes
+* explicitly set anchors: RST internal cross-references and
+  inline internal targets
+* Nim symbols (external referencing), see [Nim DocGen Tools Guide] for details.
+
+But direct use of those anchors have 2 problems:
+
+1. the anchors are usually mangled (e.g. spaces substituted to minus
+   signs, etc).
+2. manual usage of anchors is not checked, so it's easy to get broken
+   links inside your project if e.g. spelling has changed for a heading
+   or you use a wrong relative path to your document.
+
+That's why Nim implementation has syntax for using
+*original* labels for referencing.
+Such referencing can be either local/internal or external:
+
+* Local referencing (inside any given file) is defined by
+  RST standard or Pandoc Markdown User guide.
+* External (cross-document) referencing is a Nim-specific feature,
+  though it's not really different from local referencing by its syntax.
+
+Markup local referencing
+------------------------
+
+There are 2 syntax option available for referencing to objects
+inside any given file, e.g. for headlines:
+
+    Markdown                  RST
+
+    Some headline             Some headline
+    =============             =============
 
-.. warning:: Using Nim-specific features can cause other RST implementations
-  to fail on your document.
+    Ref. [Some headline]      Ref. `Some headline`_
+
+
+Markup external referencing
+---------------------------
+
+The syntax is the same as for local referencing, but the anchors are
+saved in ``.idx`` files, so one needs to generate them beforehand,
+and they should be loaded by an `.. importdoc::` directive.
+E.g. if we want to reference section "Some headline" in ``file1.md``
+from ``file2.md``, then ``file2.md`` may look like:
+
+```
+.. importdoc:: file1.md
+
+Ref. [Some headline]
+```
+
+```cmd
+nim md2html --index:only file1.md  # creates ``htmldocs/file1.idx``
+nim md2html file2.md               # creates ``htmldocs/file2.html``
+```
+
+To allow cross-references between any files in any order (especially, if
+circular references are present), it's strongly recommended
+to make a run for creating all the indexes first:
+
+```cmd
+nim md2html --index:only file1.md  # creates ``htmldocs/file1.idx``
+nim md2html --index:only file2.md  # creates ``htmldocs/file2.idx``
+nim md2html file1.md               # creates ``htmldocs/file1.html``
+nim md2html file2.md               # creates ``htmldocs/file2.html``
+```
+
+and then one can freely reference any objects as if these 2 documents
+are actually 1 file.
+
+Other
+=====
 
 Idiosyncrasies
 --------------
@@ -189,11 +282,11 @@ This parser has 2 modes for inline markup:
      does escape so that we can always input a single backtick ` in
      inline code. However that makes impossible to input code with
      ``\`` at the end in *single* backticks, one must use *double*
-     backticks::
+     backticks:
 
-       `\`   -- WRONG
-       ``\`` -- GOOD
-       So single backticks can always be input: `\`` will turn to ` code
+         `\`   -- WRONG
+         ``\`` -- GOOD
+         So single backticks can always be input: `\`` will turn to ` code
 
 .. Attention::
    We don't support some obviously poor design choices of Markdown (or RST).
@@ -204,11 +297,9 @@ This parser has 2 modes for inline markup:
    - interpretation of Markdown block quotes is also slightly different,
      e.g. case
 
-     ::
-
-       >>> foo
-       > bar
-       >>baz
+         >>> foo
+         > bar
+         >>baz
 
      is a single 3rd-level quote `foo bar baz` in original Markdown, while
      in Nim we naturally see it as 3rd-level quote `foo` + 1st level `bar` +
diff --git a/doc/mm.md b/doc/mm.md
index 9d1fc1955..5e0d2f3b9 100644
--- a/doc/mm.md
+++ b/doc/mm.md
@@ -22,7 +22,7 @@ Multi-paradigm Memory Management Strategies
 Nim offers multiple different memory management strategies.
 To choose the memory management strategy use the `--mm:` switch.
 
-**The recommended switch for newly written Nim code is `--mm:orc`.**
+ .. hint:: **The recommended switch for newly written Nim code is `--mm:orc`.**
 
 
 ARC/ORC
@@ -38,7 +38,7 @@ instead entire subgraphs are *moved* between threads. The Nim compiler also aggr
 optimizes away RC ops and exploits [move semantics](destructors.html#move-semantics).
 
 Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did
-to your time critical function via `--expandArc:functionName`.
+to your time critical function via `--expandArc:functionName`. Likewise, you can inspect the whole module via `--expandArc:fileName`.
 
 `--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector.
 Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but
@@ -73,17 +73,18 @@ Other MM modes
 
 Here is a comparison of the different memory management modes:
 
-================== ======== ================= ============== ===================
-Memory Management  Heap     Reference Cycles  Stop-The-World Command line switch
-================== ======== ================= ============== ===================
-ORC                Shared   Cycle Collector   No             `--mm:orc`
-ARC                Shared   Leak              No             `--mm:arc`
-RefC               Local    Cycle Collector   No             `--mm:refc`
-Mark & Sweep       Local    Cycle Collector   No             `--mm:markAndSweep`
-Boehm              Shared   Cycle Collector   Yes            `--mm:boehm`
-Go                 Shared   Cycle Collector   Yes            `--mm:go`
-None               Manual   Manual            Manual         `--mm:none`
-================== ======== ================= ============== ===================
+================== ======== ================= ============== ====== =================== ===================
+Memory Management  Heap     Reference Cycles  Stop-The-World Atomic Valgrind compatible Command line switch
+================== ======== ================= ============== ====== =================== ===================
+ORC                Shared   Cycle Collector   No             No     Yes                 `--mm:orc`
+ARC                Shared   Leak              No             No     Yes                 `--mm:arc`
+Atomic ARC         Shared   Leak              No             Yes    Yes                 `--mm:atomicArc`
+RefC               Local    Cycle Collector   No             No     No                  `--mm:refc`
+Mark & Sweep       Local    Cycle Collector   No             No     No                  `--mm:markAndSweep`
+Boehm              Shared   Cycle Collector   Yes            No     No                  `--mm:boehm`
+Go                 Shared   Cycle Collector   Yes            No     No                  `--mm:go`
+None               Manual   Manual            Manual         Manual Manual              `--mm:none`
+================== ======== ================= ============== ====== =================== ===================
 
 .. default-role:: code
 .. include:: rstcommon.rst
diff --git a/doc/nep1.md b/doc/nep1.md
index 0c62e3f9c..3d2a0cef3 100644
--- a/doc/nep1.md
+++ b/doc/nep1.md
@@ -256,36 +256,46 @@ Coding Conventions
 Conventions for multi-line statements and expressions
 -----------------------------------------------------
 
-- Tuples which are longer than one line should indent their parameters to
-  align with the parameters above it.
+- Tuples which are longer than one line should indent their parameters.
 
     ```nim
     type
-      LongTupleA = tuple[wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
-                         wordyTupleMemberThree: float]
+      LongTupleA = tuple[
+        wordyTupleMemberOne: int, wordyTupleMemberTwo: string,
+        wordyTupleMemberThree: float]
     ```
 
 - Similarly, any procedure and procedure type declarations that are longer
-  than one line should do the same thing.
+  than one line should do the same thing. Double indent may be used to
+  distinguish them from the body that follows - this applies to all constructs
+  with a body (if, while, etc).
 
     ```nim
     type
-      EventCallback = proc (timeReceived: Time, errorCode: int, event: Event,
-                            output: var string)
-
-    proc lotsOfArguments(argOne: string, argTwo: int, argThree: float,
-                         argFour: proc(), argFive: bool): int
-                        {.heyLookALongPragma.} =
+      EventCallback = proc(
+        timeReceived: Time, errorCode: int, event: Event,
+        output: var string)
+
+    proc lotsOfArguments(
+        argOne: string, argTwo: int, argThree: float,
+        argFour: proc(), argFive: bool, argSix: int
+    ): GenericType[int, string] {.heyLookALongPragma.} =
+      discard
     ```
 
-- Multi-line procedure calls should continue on the same column as the opening
-  parenthesis (like multi-line procedure declarations).
+- Multi-line procedure calls should continue indented (like multi-line procedure
+  declarations).
 
     ```nim
-    startProcess(nimExecutable, currentDirectory, compilerArguments
-                 environment, processOptions)
+    startProcess(
+      nimExecutable, currentDirectory, compilerArguments
+      environment, processOptions)
     ```
 
+Previous versions of this guide advocated vertical alignment along the opening
+brace / parenthesis - both styles are permissible with a preference for the
+current style in new code.
+
 Miscellaneous
 -------------
 
diff --git a/doc/nimc.md b/doc/nimc.md
index 1b3318ee6..38558454b 100644
--- a/doc/nimc.md
+++ b/doc/nimc.md
@@ -32,6 +32,17 @@ Compiler Usage
 
 Command-line switches
 ---------------------
+All options that take a `PATH` or `DIR` argument are subject to path substitution:
+
+- `$nim`: The global nim prefix path
+- `$lib`: The stdlib path
+- `$home` and `~`: The user's home path
+- `$config`: The directory of the module currently being compiled
+- `$projectname`: The project file's name without file extension
+- `$projectpath` and `$projectdir`: The project file's path
+- `$nimcache`: The nimcache path
+
+
 Basic command-line switches are:
 
 .. no syntax highlighting in the below included files at the moment
@@ -104,7 +115,6 @@ ExprAlwaysX
 ExtendedContext
 GCStats                          Dumps statistics about the Garbage Collector.
 GlobalVar                        Shows global variables declarations.
-LineTooLong                      Line exceeds the maximum length.
 Link                             Linking phase.
 Name
 Path                             Search paths modifications.
@@ -241,13 +251,13 @@ found an ambiguity error is produced.
 
 However before the PATH is used the current directory is checked for the
 file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the
-directory structure looks like this::
+directory structure looks like this:
 
-  $lib/x.nim
-  $lib/bar/x.nim
-  foo/x.nim
-  foo/main.nim
-  other.nim
+    $lib/x.nim
+    $lib/bar/x.nim
+    foo/x.nim
+    foo/main.nim
+    other.nim
 
 And `main` imports `x`, `foo/x` is imported. If `other` imports `x`
 then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match but ``$lib/x.nim`` is used
@@ -316,14 +326,16 @@ Another way is to make Nim invoke a cross compiler toolchain:
   nim c --cpu:arm --os:linux myproject.nim
   ```
 
-For cross compilation, the compiler invokes a C compiler named
-like `$cpu.$os.$cc` (for example arm.linux.gcc) and the configuration
-system is used to provide meaningful defaults. For example for `ARM` your
-configuration file should contain something like::
+For cross compilation, the compiler invokes a C compiler named like
+`$cpu.$os.$cc` (for example `arm.linux.gcc`) with options defined in
+`$cpu.$os.$cc.options.always`. The configuration system is used to provide
+meaningful defaults. For example, for Linux on a 32-bit ARM CPU, your
+configuration file should contain something like:
 
-  arm.linux.gcc.path = "/usr/bin"
-  arm.linux.gcc.exe = "arm-linux-gcc"
-  arm.linux.gcc.linkerexe = "arm-linux-gcc"
+    arm.linux.gcc.path = "/usr/bin"
+    arm.linux.gcc.exe = "arm-linux-gcc"
+    arm.linux.gcc.linkerexe = "arm-linux-gcc"
+    arm.linux.gcc.options.always = "-w -fmax-errors=3"
 
 Cross-compilation for Windows
 =============================
@@ -435,13 +447,14 @@ and `passL`:option: command line switches to something like:
   --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
   ```
 
-or setup a ``nim.cfg`` file like so::
+or setup a ``nim.cfg`` file like so:
 
-  #nim.cfg
-  --mm:orc
-  --d:nimAllocPagesViaMalloc
-  --passC="-I$DEVKITPRO/libnx/include"
-  --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
+    #nim.cfg
+    --mm:orc
+    --d:nimAllocPagesViaMalloc
+    --define:nimInheritHandles
+    --passC="-I$DEVKITPRO/libnx/include"
+    --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx"
 
 The devkitPro setup must be the same as the default with their new installer
 [here for Mac/Linux](https://github.com/devkitPro/pacman/releases) or
@@ -468,9 +481,35 @@ They are:
 5. nl_types. No headers for this.
 6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used.
 
+GPU Compilation
+===============
+
+Compiling for GPU computation can be achieved with `--cc:nvcc` for CUDA with nvcc, or with `--cc:hipcc` for AMD GPUs with HIP. Both compilers require building for C++ with `nim cpp`.
+
+Here's a very simple CUDA kernel example using emit, which can be compiled with `nim cpp --cc:nvcc --define:"useMalloc" hello_kernel.nim` assuming you have the CUDA toolkit installed.
+
+```nim
+{.emit: """
+__global__ void add(int a, int b) {
+  int c;
+  c = a + b;
+}
+""".}
+
+proc main() =
+  {.emit: """
+  add<<<1,1>>>(2,7);
+  """.}
+
+main()
+```
+
 DLL generation
 ==============
 
+**Note**: The same rules apply to `lib*.so` shared object files on UNIX. For better
+readability only the DLL version is described here.
+
 Nim supports the generation of DLLs. However, there must be only one
 instance of the GC per process/address space. This instance is contained in
 ``nimrtl.dll``. This means that every generated Nim DLL depends
@@ -486,9 +525,6 @@ To link against ``nimrtl.dll`` use the command:
   nim c -d:useNimRtl myprog.nim
   ```
 
-**Note**: Currently the creation of ``nimrtl.dll`` with thread support has
-never been tested and is unlikely to work!
-
 
 Additional compilation switches
 ===============================
@@ -512,7 +548,7 @@ Define                   Effect
                          This only works with `--mm:none`:option:,
                          `--mm:arc`:option: and `--mm:orc`:option:.
 `useRealtimeGC`          Enables support of Nim's GC for *soft* realtime
-                         systems. See the documentation of the [mm](mm.html)
+                         systems. See the documentation of the [refc](refc.html)
                          for further information.
 `logGC`                  Enable GC logging to stdout.
 `nodejs`                 The JS target is actually ``node.js``.
@@ -535,6 +571,13 @@ Define                   Effect
 `globalSymbols`          Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL`:c:
                          flag on Posix systems to resolve symbols in subsequently
                          loaded libraries.
+`lto`                    Enable link-time optimization in the backend compiler and
+                         linker.
+`lto_incremental`        Enable link-time optimization and additionally enable
+                         incremental linking for compilers that support it.
+                         Currently only clang and vcc.
+`strip`                  Strip debug symbols added by the backend compiler from
+                         the executable.
 ======================   =========================================================
 
 
@@ -660,11 +703,11 @@ additional flags to both the Nim compiler and the C compiler and/or linker
 to optimize the build for size. For example, the following flags can be used
 when targeting a gcc compiler:
 
-`--opt:size --passC:-flto --passL:-flto`:option:
+`--opt:size -d:lto -d:strip`:option:
 
 The `--opt:size`:option: flag instructs Nim to optimize code generation for small
-size (with the help of the C compiler), the `-flto`:option: flags enable link-time
-optimization in the compiler and linker.
+size (with the help of the C compiler), the `-d:lto`:option: flags enable link-time
+optimization in the compiler and linker, the `-d:strip`:option: strips debug symbols.
 
 Check the [Cross-compilation] section for instructions on how to compile the
 program for your target.
@@ -712,7 +755,7 @@ Nim's thread API provides a simple wrapper around more advanced
 RTOS task features. Customizing the stack size and stack guard size can
 be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`.
 
-Currently only Zephyr and FreeRTOS support these configurations.
+Currently only Zephyr, NuttX and FreeRTOS support these configurations.
 
 Nim for realtime systems
 ========================
@@ -778,20 +821,6 @@ For `let` symbols a copy is not always necessary:
   let s = varA    # may only copy a pointer if it safe to do so
   ```
 
-
-If you know what you're doing, you can also mark single-string (or sequence)
-objects as `shallow`:idx:\:
-
-  ```Nim
-  var s = "abc"
-  shallow(s) # mark 's' as a shallow string
-  var x = s  # now might not copy the string!
-  ```
-
-Usage of `shallow` is always safe once you know the string won't be modified
-anymore, similar to Ruby's `freeze`:idx:.
-
-
 The compiler optimizes string case statements: A hashing scheme is used for them
 if several different string constants are used. So code like this is reasonably
 efficient:
diff --git a/doc/nimdoc.css b/doc/nimdoc.css
index f924f5a36..0c399e4c1 100644
--- a/doc/nimdoc.css
+++ b/doc/nimdoc.css
@@ -147,6 +147,15 @@ body {
   box-sizing: border-box;

   margin-left: 1%; }

 

+@media print {

+  #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {

+    display:none;

+  }

+  .columns {

+    width:100% !important;

+  }

+}

+

 .column:first-child, .columns:first-child {

   margin-left: 0; }

 

@@ -614,8 +623,8 @@ pre {
 

 table.line-nums-table {

   border-radius: 4px;

-  border: 1px solid #cccccc;

-  background-color: ghostwhite;

+  border: 1px solid var(--border);

+  background-color: var(--secondary-background);

   border-collapse: separate;

   margin-top: 15px;

   margin-bottom: 25px; }

@@ -651,6 +660,9 @@ table {
   border-collapse: collapse;

   border-color: var(--third-background);

   border-spacing: 0;

+}

+

+table:not(.line-nums-table) {

   font-size: 0.9em;

 }

 

@@ -669,7 +681,7 @@ table th.docinfo-name {
   text-align: right;

 }

 

-table tr:hover {

+table:not(.line-nums-table) tr:hover {

   background-color: var(--third-background); }

 

 

@@ -758,9 +770,16 @@ div.topic {
 

 div.search_results {

   background-color: var(--third-background);

-  margin: 3em;

+  margin: 3vh 5vw;

   padding: 1em;

-  border: 1px solid #4d4d4d; }

+  border: 1px solid #4d4d4d;

+  position: fixed;

+  top: 10px;

+  isolation: isolate;

+  max-width: calc(100vw - 6em);

+  z-index: 1;

+  max-height: calc(100vh - 6em);

+  overflow-y: scroll;}

 

 div#global-links ul {

   margin-left: 0;

diff --git a/doc/nimfix.md b/doc/nimfix.md
deleted file mode 100644
index d105346da..000000000
--- a/doc/nimfix.md
+++ /dev/null
@@ -1,58 +0,0 @@
-.. default-role:: code
-
-=====================
-  Nimfix User Guide
-=====================
-
-:Author: Andreas Rumpf
-:Version: |nimversion|
-
-**WARNING**: Nimfix is currently beta-quality.
-
-Nimfix is a tool to help you upgrade from Nimrod (<= version 0.9.6) to
-Nim (=> version 0.10.0).
-
-It performs 3 different actions:
-
-1. It makes your code case consistent.
-2. It renames every symbol that has a deprecation rule. So if a module has a
-   rule `{.deprecated: [TFoo: Foo].}` then `TFoo` is replaced by `Foo`.
-3. It can also check that your identifiers adhere to the official style guide
-   and optionally modify them to do so (via `--styleCheck:auto`).
-
-Note that `nimfix` defaults to **overwrite** your code unless you
-use `--overwriteFiles:off`! But hey, if you do not use a version control
-system by this day and age, your project is already in big trouble.
-
-
-Installation
-------------
-
-Nimfix is part of the compiler distribution. Compile via::
-
-  nim c compiler/nimfix/nimfix.nim
-  mv compiler/nimfix/nimfix bin
-
-Or on windows::
-
-  nim c compiler\nimfix\nimfix.nim
-  move compiler\nimfix\nimfix.exe bin
-
-Usage
------
-
-Usage:
-  nimfix [options] projectfile.nim
-
-Options:
-
-  --overwriteFiles:on|off       overwrite the original nim files. DEFAULT is ON!
-  --wholeProject                overwrite every processed file.
-  --checkExtern:on|off          style check also extern names
-  --styleCheck:on|off|auto      performs style checking for identifiers
-                                and suggests an alternative spelling;
-                                'auto' corrects the spelling.
-
-In addition, all command line options of Nim are supported.
-
-
diff --git a/doc/nimgrep.md b/doc/nimgrep.md
index 8fb86a9d3..63f760051 100644
--- a/doc/nimgrep.md
+++ b/doc/nimgrep.md
@@ -77,7 +77,7 @@ That means you can always use only 1 such an option with logical OR, e.g.
 .. Note::
    If you want logical AND on patterns you should compose 1 appropriate pattern,
    possibly combined with multi-line mode `(?s)`:literal:.
-   E.g. to require that multi-line context of matches has occurences of
+   E.g. to require that multi-line context of matches has occurrences of
    **both** PAT1 and PAT2 use positive lookaheads (`(?=PAT)`:literal:):
      ```cmd
      nimgrep --inContext:'(?s)(?=.*PAT1)(?=.*PAT2)'
diff --git a/doc/nimgrep_cmdline.txt b/doc/nimgrep_cmdline.txt
index 73f29f524..6f6887bc4 100644
--- a/doc/nimgrep_cmdline.txt
+++ b/doc/nimgrep_cmdline.txt
@@ -1,12 +1,17 @@
 
 Usage:
 
-* To search::
-    nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-]
-* To replace::
-    nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/-
-* To list file names::
-    nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*]
+* To search:
+
+      nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-]
+
+* To replace:
+
+      nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/-
+
+* To list file names:
+
+      nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*]
 
 Positional arguments, from left to right:
 1) PATTERN is either Regex (default) or Peg if `--peg` is specified.
@@ -42,10 +47,11 @@ Options:
                       to abort any time without touching the file
   --filenames         just list filenames. Provide a PATTERN to find it in
                       the filenames (not in the contents of a file) or run
-                      with empty pattern to just list all files::
-                        nimgrep --filenames               # In current dir
-                        nimgrep --filenames "" DIRECTORY
-                          # Note empty pattern "", lists all files in DIRECTORY
+                      with empty pattern to just list all files:
+
+                          nimgrep --filenames               # In current dir
+                          nimgrep --filenames "" DIRECTORY
+                           # Note empty pattern "", lists all files in DIRECTORY
 * Interprete patterns:
   --peg               PATTERN and PAT are Peg
   --re                PATTERN and PAT are regular expressions (default)
diff --git a/doc/niminst.md b/doc/niminst.md
index b1bbade14..cc399c57a 100644
--- a/doc/niminst.md
+++ b/doc/niminst.md
@@ -72,11 +72,11 @@ Key                    description
 Many sections support the `files` key. Listed filenames
 can be separated by semicolon or the `files` key can be repeated. Wildcards
 in filenames are supported. If it is a directory name, all files in the
-directory are used::
+directory are used:
 
-  [Config]
-  Files: "configDir"
-  Files: "otherconfig/*.conf;otherconfig/*.cfg"
+    [Config]
+    Files: "configDir"
+    Files: "otherconfig/*.conf;otherconfig/*.cfg"
 
 
 Config section
diff --git a/doc/nims.md b/doc/nims.md
index ecda526fb..987cc2096 100644
--- a/doc/nims.md
+++ b/doc/nims.md
@@ -61,45 +61,44 @@ Standard library modules
 
 At least the following standard library modules are available:
 
-* [macros](macros.html)
-* [os](os.html)
-* [strutils](strutils.html)
-* [math](math.html)
-* [distros](distros.html)
-* [sugar](sugar.html)
 * [algorithm](algorithm.html)
 * [base64](base64.html)
 * [bitops](bitops.html)
 * [chains](chains.html)
 * [colors](colors.html)
 * [complex](complex.html)
+* [distros](distros.html)
+* [std/editdistance](editdistance.html)
 * [htmlgen](htmlgen.html)
+* [htmlparser](htmlparser.html)
 * [httpcore](httpcore.html)
+* [json](json.html)
 * [lenientops](lenientops.html)
-* [mersenne](mersenne.html)
+* [macros](macros.html)
+* [math](math.html)
 * [options](options.html)
+* [os](os.html)
+* [parsecfg](parsecfg.html)
+* [parsecsv](parsecsv.html)
+* [parsejson](parsejson.html)
+* [parsesql](parsesql.html)
 * [parseutils](parseutils.html)
 * [punycode](punycode.html)
-* [random](punycode.html)
+* [random](random.html)
+* [ropes](ropes.html)
+* [std/setutils](setutils.html)
 * [stats](stats.html)
 * [strformat](strformat.html)
 * [strmisc](strmisc.html)
 * [strscans](strscans.html)
+* [strtabs](strtabs.html)
+* [strutils](strutils.html)
+* [sugar](sugar.html)
 * [unicode](unicode.html)
+* [unidecode](unidecode.html)
 * [uri](uri.html)
-* [std/editdistance](editdistance.html)
 * [std/wordwrap](wordwrap.html)
-* [std/sums](sums.html)
-* [parsecsv](parsecsv.html)
-* [parsecfg](parsecfg.html)
-* [parsesql](parsesql.html)
 * [xmlparser](xmlparser.html)
-* [htmlparser](htmlparser.html)
-* [ropes](ropes.html)
-* [json](json.html)
-* [parsejson](parsejson.html)
-* [strtabs](strtabs.html)
-* [unidecode](unidecode.html)
 
 In addition to the standard Nim syntax ([system](system.html) module),
 NimScripts support the procs and templates defined in the
@@ -126,6 +125,8 @@ Here are few examples of using the `switch` proc:
   switch("define", "release")
   # command-line: --forceBuild
   switch("forceBuild")
+  # command-line: --hint[Conf]:off or --hint:Conf:off
+  switch("hint", "[Conf]:off")
   ```
 
 NimScripts also support `--`:option: templates for convenience, which look
@@ -213,7 +214,7 @@ ends with ``.nims``:
   echo "hello world"
   ```
 
-Use `#!/usr/bin/env -S nim --hints:off` to disable hints.
+Use `#!/usr/bin/env -S nim e --hints:off` to disable hints and relax the file extension constraint.
 
 
 Benefits
@@ -331,7 +332,7 @@ Evolving Scripting language
 
 NimScript evolves together with Nim,
 [occasionally new features might become available on NimScript](
-https://github.com/nim-lang/Nim/pulls?utf8=%E2%9C%93&q=nimscript),
+https://github.com/nim-lang/Nim/pulls?q=nimscript+is%3Amerged),
 adapted from compiled Nim or added as new features on both.
 
 Scripting Language with a Package Manager
diff --git a/doc/nimsuggest.md b/doc/nimsuggest.md
index 97cb2b1fa..3d076a6f5 100644
--- a/doc/nimsuggest.md
+++ b/doc/nimsuggest.md
@@ -49,7 +49,7 @@ via sockets is more reasonable so that is the default. It listens to port 6000
 by default.
 
 Nimsuggest is basically a frontend for the nim compiler so `--path`:option: flags and
-[config files](https://nim-lang.org/docs/nimc.html#compiler-usage-configuration-files)
+[config files](nimc.html#compiler-usage-configuration-files)
 can be used to specify additional dependencies like 
 `nimsuggest --stdin --debug --path:"dependencies" myproject.nim`:cmd:.
 
diff --git a/doc/packaging.md b/doc/packaging.md
index 9a1cc0f6e..b742bef28 100644
--- a/doc/packaging.md
+++ b/doc/packaging.md
@@ -69,8 +69,11 @@ Hints on the build process:
 
 What to install:
 
-- The expected stdlib location is /usr/lib/nim
-- Global configuration files under /etc/nim
+- The expected stdlib location is `/usr/lib/nim/lib`, previously it was just `/usr/lib/nim`
+- `nimdoc.css` and `nimdoc.cls` from the `doc` folder should go into `/usr/lib/nim/doc/`
+- `tools/debug/nim-gdb.py` should go into `/usr/lib/nim/tools/`
+- `tools/dochack/dochack.js` should be installed to `/usr/lib/nim/tools/dochack/`
+- Global configuration files under `/etc/nim`
 - Optionally: manpages, documentation, shell completion
 - When installing documentation, .idx files are not required
 - The "compiler" directory contains compiler sources and should not be part of the compiler binary package
diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt
index 99d9d784c..0a8fd8187 100644
--- a/doc/pegdocs.txt
+++ b/doc/pegdocs.txt
@@ -131,44 +131,44 @@ notation           meaning
 Supported PEG grammar
 ---------------------
 
-The PEG parser implements this grammar (written in PEG syntax)::
-
-  # Example grammar of PEG in PEG syntax.
-  # Comments start with '#'.
-  # First symbol is the start symbol.
-
-  grammar <- rule* / expr
-
-  identifier <- [A-Za-z][A-Za-z0-9_]*
-  charsetchar <- "\\" . / [^\]]
-  charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
-  stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
-                            "'" ("\\" . / [^'])* "'")
-  builtin <- "\\" identifier / [^\13\10]
-
-  comment <- '#' @ \n
-  ig <- (\s / comment)* # things to ignore
-
-  rule <- identifier \s* "<-" expr ig
-  identNoArrow <- identifier !(\s* "<-")
-  prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
-  literal <- ig identifier? '$' '^'? [0-9]+ / '$' / '^' /
-             ig identNoArrow /
-             ig charset /
-             ig stringlit /
-             ig builtin /
-             ig '.' /
-             ig '_' /
-             (ig "(" expr ig ")") /
-             (ig "{" expr? ig "}")
-  postfixOpr <- ig '?' / ig '*' / ig '+'
-  primary <- prefixOpr* (literal postfixOpr*)
-
-  # Concatenation has higher priority than choice:
-  # ``a b / c`` means ``(a b) / c``
-
-  seqExpr <- primary+
-  expr <- seqExpr (ig "/" expr)*
+The PEG parser implements this grammar (written in PEG syntax):
+
+    # Example grammar of PEG in PEG syntax.
+    # Comments start with '#'.
+    # First symbol is the start symbol.
+
+    grammar <- rule* / expr
+
+    identifier <- [A-Za-z][A-Za-z0-9_]*
+    charsetchar <- "\\" . / [^\]]
+    charset <- "[" "^"? (charsetchar ("-" charsetchar)?)+ "]"
+    stringlit <- identifier? ("\"" ("\\" . / [^"])* "\"" /
+                              "'" ("\\" . / [^'])* "'")
+    builtin <- "\\" identifier / [^\13\10]
+
+    comment <- '#' @ \n
+    ig <- (\s / comment)* # things to ignore
+
+    rule <- identifier \s* "<-" expr ig
+    identNoArrow <- identifier !(\s* "<-")
+    prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@'
+    literal <- ig identifier? '$' '^'? [0-9]+ / '$' / '^' /
+               ig identNoArrow /
+               ig charset /
+               ig stringlit /
+               ig builtin /
+               ig '.' /
+               ig '_' /
+               (ig "(" expr ig ")") /
+               (ig "{" expr? ig "}")
+    postfixOpr <- ig '?' / ig '*' / ig '+'
+    primary <- prefixOpr* (literal postfixOpr*)
+
+    # Concatenation has higher priority than choice:
+    # ``a b / c`` means ``(a b) / c``
+
+    seqExpr <- primary+
+    expr <- seqExpr (ig "/" expr)*
 
 
 **Note**: As a special syntactic extension if the whole PEG is only a single
diff --git a/doc/readme.txt b/doc/readme.txt
index 7b1a445b5..1157752b9 100644
--- a/doc/readme.txt
+++ b/doc/readme.txt
@@ -3,5 +3,5 @@ Nim's documentation system
 ============================
 
 This folder contains Nim's documentation. The documentation
-is written in a format called *reStructuredText*, a markup language that reads
+is written in a format called *Markdown*, a markup language that reads
 like ASCII and can be converted to HTML automatically!
diff --git a/doc/refc.md b/doc/refc.md
index 6e4672ac5..4023748e6 100644
--- a/doc/refc.md
+++ b/doc/refc.md
@@ -138,6 +138,7 @@ The garbage collector won't try to free them, you need to call their respective
 when you are done with them or they will leak.
 
 
+
 Heap dump
 =========
 
diff --git a/doc/regexprs.txt b/doc/regexprs.txt
index f5544cc28..fa7f9d24a 100644
--- a/doc/regexprs.txt
+++ b/doc/regexprs.txt
@@ -56,9 +56,9 @@ backslashes are interpreted by the regular expression engine:
 A regular expression is a pattern that is matched against a subject string
 from left to right. Most characters stand for themselves in a pattern, and
 match the corresponding characters in the subject. As a trivial example,
-the pattern::
+the pattern:
 
-  The quick brown fox
+    The quick brown fox
 
 matches a portion of a subject string that is identical to itself.
 The power of regular expressions comes from the ability to include
@@ -130,7 +130,7 @@ in patterns in a visible manner. There is no restriction on the appearance of
 non-printing characters, apart from the binary zero that terminates a pattern,
 but when a pattern is being prepared by text editing, it is usually easier to
 use one of the following escape sequences than the binary character it
-represents::
+represents:
 
 ==============     ============================================================
 character          meaning
@@ -246,7 +246,7 @@ The fourth use of backslash is for certain `simple assertions`:idx:. An
 assertion specifies a condition that has to be met at a particular point in
 a match, without consuming any characters from the subject string. The use of
 subpatterns for more complicated assertions is described below. The
-backslashed assertions are::
+backslashed assertions are:
 
 ==============     ============================================================
 assertion          meaning
diff --git a/doc/rstcommon.rst b/doc/rstcommon.rst
index 7ecc7b8cb..e9cc615db 100644
--- a/doc/rstcommon.rst
+++ b/doc/rstcommon.rst
@@ -1,9 +1,9 @@
 ..
   Usage of this file:
-     Add this in the beginning of *.rst file::
+     Add this in the beginning of *.rst file:
 
-       .. default-role:: code
-       .. include:: rstcommon.rst
+         .. default-role:: code
+         .. include:: rstcommon.rst
 
      It's the current trick for brevity and compatibility with both Github and
      rst2html.py, considering that Github cannot highlight Nim in
diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt
index 4aac75191..35b1fc023 100644
--- a/doc/sets_fragment.txt
+++ b/doc/sets_fragment.txt
@@ -5,24 +5,25 @@ only be an ordinal type of a certain size, namely:
 * `uint8`/`byte`-`uint16`
 * `char`
 * `enum`
+* Ordinal subrange types, i.e. `range[-10..10]`
 
-or equivalent. For signed integers the set's base type is defined to be in the
-range `0 .. MaxSetElements-1` where `MaxSetElements` is currently always
-2^16.
+or equivalent. When constructing a set with signed integer literals, the set's
+base type is defined to be in the range `0 .. DefaultSetElements-1` where
+`DefaultSetElements` is currently always 2^8. The maximum range length for the
+base type of a set is `MaxSetElements` which is currently always 2^16. Types
+with a bigger range length are coerced into the range `0 .. MaxSetElements-1`.
 
 The reason is that sets are implemented as high performance bit vectors.
 Attempting to declare a set with a larger type will result in an error:
 
 ```nim
-
   var s: set[int64] # Error: set is too large; use `std/sets` for ordinal types
                     # with more than 2^16 elements
-
 ```
 
 
 **Note:** Nim also offers [hash sets](sets.html) (which you need to import
-with `import sets`), which have no such restrictions.
+with `import std/sets`), which have no such restrictions.
 
 Sets can be constructed via the set constructor: `{}` is the empty set. The
 empty set is type compatible with any concrete set type. The constructor
@@ -38,6 +39,14 @@ can also be used to include elements (and ranges of elements):
                            # from '0' to '9'
   ```
 
+The module [`std/setutils`](setutils.html) provides a way to initialize a set from an iterable:
+
+```nim
+import std/setutils
+
+let uniqueChars = myString.toSet
+```
+
 These operations are supported by sets:
 
 ==================    ========================================================
diff --git a/doc/testament.md b/doc/testament.md
index 812b10984..0ff3591ac 100644
--- a/doc/testament.md
+++ b/doc/testament.md
@@ -17,7 +17,7 @@ so can be useful to run your tests, even the most complex ones.
 Test files location
 ===================
 
-By default, Testament looks for test files on ``"./tests/*.nim"``.
+By default, Testament looks for test files on ``"./tests/category/*.nim"``.
 You can overwrite this pattern glob using `pattern <glob>`:option:.
 The default working directory path can be changed using
 `--directory:"folder/subfolder/"`:option:.
@@ -27,24 +27,31 @@ You can change that using `--nim:"folder/subfolder/nim"`:option:.
 Running JavaScript tests with `--targets:"js"`:option: requires
 a working NodeJS on `PATH`.
 
+Commands
+========
+
+p|pat|pattern <glob>        run all the tests matching the given pattern
+all                         run all tests inside of category folders
+c|cat|category <category>   run all the tests of a certain category
+r|run <test>                run single test file
+html                        generate testresults.html from the database
+
 
 Options
 =======
 
---print                   Also print results to the console
---simulate                See what tests would be run but don't run them
-                          (for debugging)
---failing                 Only show failing/ignored tests
---targets:"c cpp js objc"
-                          Run tests for specified targets (default: c)
---nim:path                Use a particular nim executable (default: $PATH/nim)
---directory:dir           Change to directory dir before reading the tests
-                          or doing anything else.
+--print                   print results to the console
+--verbose                 print commands (compiling and running tests)
+--simulate                see what tests would be run but don't run them (for debugging)
+--failing                 only show failing/ignored tests
+--targets:"c cpp js objc" run tests for specified targets (default: c)
+--nim:path                use a particular nim executable (default: $PATH/nim)
+--directory:dir           Change to directory dir before reading the tests or doing anything else.
 --colors:on|off           Turn messages coloring on|off.
---backendLogging:on|off   Disable or enable backend logging.
-                          By default, turned on.
---skipFrom:file           Read tests to skip from ``file`` - one test per
-                          line, # comments ignored
+--backendLogging:on|off   Disable or enable backend logging. By default turned on.
+--megatest:on|off         Enable or disable megatest. Default is on.
+--valgrind:on|off         Enable or disable valgrind support. Default is on.
+--skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored
 
 
 Running a single test
@@ -54,18 +61,21 @@ This is a minimal example to understand the basics,
 not very useful for production, but easy to understand:
 
   ```console
-  $ mkdir tests
-  $ echo "assert 42 == 42" > tests/test0.nim
-  $ testament run test0.nim
-  PASS: tests/test0.nim C                                    ( 0.2 sec)
-  $ testament r test0
-  PASS: tests/test0.nim C                                    ( 0.2 sec)
+  $ mkdir -p tests/category
+  $ echo "assert 42 == 42" > tests/category/test0.nim
+  $ testament run tests/category/test0.nim
+  PASS: tests/category/test0.nim c                           ( 0.2 sec)
+  $ testament r tests/category/test0
+  PASS: tests/category/test0.nim C                           ( 0.2 sec)
   ```
 
 
 Running all tests from a directory
 ==================================
 
+ This will run all tests in the top level tests/ directory. NOTE: these
+ tests are skipped by `testament all`.
+ 
   ```console
   $ testament pattern "tests/*.nim"
   ```
@@ -102,8 +112,21 @@ Example "template" **to edit** and write a Testament unittest:
     #   "run": expect successful compilation and execution
     #   "reject": expect failed compilation. The "reject" action can catch
     #             {.error.} pragmas but not {.fatal.} pragmas because
+    #             {.error.} calls are expected to originate from the test-file, 
+    #             and can explicitly be specified using the "file", "line" and
+    #             "column" options.
     #             {.fatal.} pragmas guarantee that compilation will be aborted.
     action: "run"
+    
+    # For testing failed compilations you can specify the expected origin of the 
+    # compilation error.
+    # With the "file", "line" and "column" options you can define the file, 
+    # line and column that a compilation-error should have originated from.
+    # Use only with action: "reject" as it expects a failed compilation.
+    # Requires errormsg or msg to be defined.
+    # file: ""
+    # line: ""
+    # column: ""
 
     # The exit code that the test is expected to return. Typically, the default
     # value of 0 is fine. Note that if the test will be run by valgrind, then
@@ -117,13 +140,14 @@ Example "template" **to edit** and write a Testament unittest:
     output: ""
     outputsub: ""
 
-    # Whether to sort the output lines before comparing them to the desired
-    # output.
+    # Whether to sort the compiler output lines before comparing them to the 
+    # expected output.
     sortoutput: true
 
-    # Each line in the string given here appears in the same order in the
-    # compiler output, but there may be more lines that appear before, after, or
-    # in between them.
+    # Provide a `nimout` string to assert that the compiler during compilation
+    # prints the defined lines to the standard out.
+    # The lines must match in order, but there may be more lines that appear 
+    # before, after, or in between them. 
     nimout: '''
   a very long,
   multi-line
@@ -150,6 +174,9 @@ Example "template" **to edit** and write a Testament unittest:
     #   "leaks": run the test with Valgrind, but do not check for memory leaks
     valgrind: false   # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only).
 
+    # Checks that the specified piece of C-code is within the generated C-code.
+    ccodecheck: "'Assert error message'"
+
     # Command the test should use to run. If left out or an empty string is
     # provided, the command is taken to be:
     # "nim $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file"
@@ -186,7 +213,7 @@ Example "template" **to edit** and write a Testament unittest:
 * As you can see the "Spec" is just a `discard """ """`.
 * Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine.
 * This is not the full spec of Testament, check [the Testament Spec on GitHub,
-  see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L315).
+  see parseSpec()](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L317).
 * Nim itself uses Testament, so [there are plenty of test examples](
   https://github.com/nim-lang/Nim/tree/devel/tests).
 * Has some built-in CI compatibility, like Azure Pipelines, etc.
@@ -273,6 +300,38 @@ Expected to fail:
   assert not_defined == "not_defined", "not_defined is not defined"
   ```
 
+Expected to fail with error thrown from another file:
+```nim
+# test.nim
+discard """
+  action: "reject"
+  errorMsg: "I break"
+  file: "breakPragma.nim"
+"""
+import ./breakPragma
+
+proc x() {.justDo.} = discard
+
+# breakPragma.nim
+import std/macros
+
+macro justDo*(procDef: typed): untyped =
+  error("I break")
+  return procDef
+```
+
+Expecting generated C to contain a given piece of code:
+
+  ```nim
+  discard """
+    # Checks that the string "Assert error message" is in the generated 
+    # C code.
+    ccodecheck: "'Assert error message'"
+  """
+  assert 42 == 42, "Assert error message"
+  ```
+
+
 Non-Zero exit code:
 
   ```nim
diff --git a/doc/tools.md b/doc/tools.md
index 6849103f9..baf7ce386 100644
--- a/doc/tools.md
+++ b/doc/tools.md
@@ -7,6 +7,10 @@ Tools available with Nim
 
 The standard distribution ships with the following tools:
 
+- | [atlas](atlas.html)
+  | `atlas`:cmd: is a simple package cloner tool. It manages an isolated workspace that
+    contains projects and dependencies.
+
 - | [Hot code reloading](hcr.html)
   | The "Hot code reloading" feature is built into the compiler but has its own
     document explaining how it works.
@@ -33,10 +37,9 @@ The standard distribution ships with the following tools:
   | `nimpretty`:cmd: is a Nim source code beautifier,
     to format code according to the official style guide.
 
-- | [testament](https://nim-lang.github.io/Nim/testament.html)
+- | [testament](testament.html)
   | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests,
     is used for the development of Nim itself, offers process isolation for your tests,
     it can generate statistics about test cases, supports multiple targets (C, JS, etc),
-    [simulated Dry-Runs](https://en.wikipedia.org/wiki/Dry_run_(testing)),
     has logging, can generate HTML reports, skip tests from a file, and more,
     so can be useful to run your tests, even the most complex ones.
diff --git a/doc/tut1.md b/doc/tut1.md
index c66ff7337..2e83effa3 100644
--- a/doc/tut1.md
+++ b/doc/tut1.md
@@ -45,24 +45,28 @@ We start the tour with a modified "hello world" program:
   ```
 
 
-Save this code to the file "greetings.nim". Now compile and run it::
-
+Save this code to the file "greetings.nim". Now compile and run it:
+  ```cmd
   nim compile --run greetings.nim
+  ```
 
 With the ``--run`` [switch](nimc.html#compiler-usage-commandminusline-switches) Nim
 executes the file automatically after compilation. You can give your program
-command-line arguments by appending them after the filename::
-
+command-line arguments by appending them after the filename:
+  ```cmd
   nim compile --run greetings.nim arg1 arg2
+  ```
 
-Commonly used commands and switches have abbreviations, so you can also use::
-
+Commonly used commands and switches have abbreviations, so you can also use:
+  ```cmd
   nim c -r greetings.nim
+  ```
 
 This is a **debug version**.
-To compile a release version use::
-
+To compile a release version use:
+  ```cmd
   nim c -d:release greetings.nim
+  ```
 
 By default, the Nim compiler generates a large number of runtime checks
 aiming for your debugging pleasure. With ``-d:release`` some checks are
diff --git a/doc/tut2.md b/doc/tut2.md
index 3c858c64e..1b59288d5 100644
--- a/doc/tut2.md
+++ b/doc/tut2.md
@@ -166,7 +166,7 @@ An example:
   n.strVal = ""
   ```
 
-As can been seen from the example, an advantage to an object hierarchy is that
+As can be seen from the example, an advantage to an object hierarchy is that
 no conversion between different object types is needed. Yet, access to invalid
 object fields raises an exception.
 
@@ -393,7 +393,7 @@ The `try` statement handles exceptions:
       echo "could not convert string to integer"
     except IOError:
       echo "IO error!"
-    except:
+    except CatchableError:
       echo "Unknown exception!"
       # reraise the unknown exception:
       raise
@@ -425,7 +425,7 @@ module. Example:
   ```nim
   try:
     doSomethingHere()
-  except:
+  except CatchableError:
     let
       e = getCurrentException()
       msg = getCurrentExceptionMsg()
diff --git a/doc/tut3.md b/doc/tut3.md
index 958d35791..3a55d4790 100644
--- a/doc/tut3.md
+++ b/doc/tut3.md
@@ -322,6 +322,36 @@ used to get this output.
     raise newException(AssertionDefect, $a & " != " & $b)
   ```
 
+
+Going further
+-------------
+
+It is possible to create more complex macros by combining different
+`NimNode` symbols with `quote do:` expressions. For example, you may
+use `newStmtList` to build your macro iteratively, and `ident` in cases
+in which you wish to create an identifier from a string, as shown below.
+
+  ```nim
+  import std/macros
+
+  macro createProcedures() =
+    result = newStmtList()
+
+    for i in 0..<10:
+      let name = ident("myProc" & $i)
+      let content = newLit("I am procedure number #" & $i)
+
+      result.add quote do:
+        proc `name`() =
+          echo `content`
+
+  createProcedures()
+  myProc7()
+  ```
+
+The call to `myProc7` will echo `I am procedure number #7`.
+
+
 With Power Comes Responsibility
 -------------------------------
 
@@ -366,7 +396,7 @@ recommended way. But still `strformat` is a good example for a
 practical use case for a macro that is slightly more complex than the
 `assert` macro.
 
-[Strformat](https://github.com/nim-lang/Nim/blob/5845716df8c96157a047c2bd6bcdd795a7a2b9b1/lib/pure/strformat.nim#L280)
+[Strformat](https://github.com/nim-lang/Nim/blob/devel/lib/pure/strformat.nim)
 
 Ast Pattern Matching
 --------------------
@@ -375,7 +405,7 @@ Ast Pattern Matching is a macro library to aid in writing complex
 macros. This can be seen as a good example of how to repurpose the
 Nim syntax tree with new semantics.
 
-[Ast Pattern Matching](https://github.com/krux02/ast-pattern-matching)
+[Ast Pattern Matching](https://github.com/nim-lang/ast-pattern-matching)
 
 OpenGL Sandbox
 --------------
diff --git a/koch.nim b/koch.nim
index e3e2cb098..77bc2299f 100644
--- a/koch.nim
+++ b/koch.nim
@@ -1,17 +1,22 @@
 #
 #
 #         Maintenance program for Nim
-#        (c) Copyright 2017 Andreas Rumpf
+#        (c) Copyright 2024 Andreas Rumpf
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
 #
-#    See doc/koch.txt for documentation.
+#    See doc/koch.md for documentation.
 #
 
 const
-  NimbleStableCommit = "d13f3b8ce288b4dc8c34c219a4e050aaeaf43fc9" # master
-  # examples of possible values: #head, #ea82b54, 1.2.3
+  # examples of possible values for repos: Head, ea82b54
+  NimbleStableCommit = "4fb6f8e6c33963f6f510fe82d09ad2a61b5e4265" # 0.16.1
+  AtlasStableCommit = "5faec3e9a33afe99a7d22377dd1b45a5391f5504"
+  ChecksumsStableCommit = "bd9bf4eaea124bf8d01e08f92ac1b14c6879d8d3"
+  SatStableCommit = "faf1617f44d7632ee9601ebc13887644925dcc01"
+
+  # examples of possible values for fusion: #head, #ea82b54, 1.2.3
   FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014"
   HeadHash = "#head"
 when not defined(windows):
@@ -34,6 +39,9 @@ import std/[os, strutils, parseopt, osproc]
   # If this fails with: `Error: cannot open file: std/os`, see
   # https://github.com/nim-lang/Nim/pull/14291 for explanation + how to fix.
 
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
 import tools / kochdocs
 import tools / deps
 
@@ -44,7 +52,7 @@ const
 +-----------------------------------------------------------------+
 |         Maintenance program for Nim                             |
 |             Version $1|
-|             (c) 2017 Andreas Rumpf                              |
+|             (c) 2024 Andreas Rumpf                              |
 +-----------------------------------------------------------------+
 Build time: $2, $3
 
@@ -59,6 +67,7 @@ Options:
   --nim:path               use specified path for nim binary
   --localdocs[:path]       only build local documentations. If a path is not
                            specified (or empty), the default is used.
+  --skipIntegrityCheck     skips integrity check when booting the compiler
 Possible Commands:
   boot [options]           bootstraps with given command line options
   distrohelper [bindir]    helper for distro packagers
@@ -67,6 +76,8 @@ Possible Commands:
                            e.g. nimble)
                            doesn't require network connectivity
   nimble                   builds the Nimble tool
+  atlas                    builds the Atlas tool
+  checksums                installs the checksums dependency
   fusion                   installs fusion via Nimble
 
 Boot options:
@@ -76,9 +87,12 @@ Boot options:
   -d:leanCompiler          produce a compiler without JS codegen or
                            documentation generator in order to use less RAM
                            for bootstrapping
+  -d:nimHasLibFFI          adds FFI support for allowing compile-time VM to
+                           interface with native functions (experimental,
+                           requires prior `koch installdeps libffi`)
 
 Commands for core developers:
-  runCI                    runs continuous integration (CI), e.g. from travis
+  runCI                    runs continuous integration (CI), e.g. from Github Actions
   docs [options]           generates the full documentation
   csource -d:danger        builds the C sources for installation
   pdf                      builds the PDF documentation
@@ -89,9 +103,6 @@ Commands for core developers:
   tests [options]          run the testsuite (run a subset of tests by
                            specifying a category, e.g. `tests cat async`)
   temp options             creates a temporary compiler for testing
-Web options:
-  --googleAnalytics:UA-... add the given google analytics code to the docs. To
-                           build the official docs, use UA-48159761-1
 """
 
 let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed e.g.: `nim c -o:koch_debug koch.nim`
@@ -142,15 +153,29 @@ proc csource(args: string) =
 proc bundleC2nim(args: string) =
   cloneDependency(distDir, "https://github.com/nim-lang/c2nim.git")
   nimCompile("dist/c2nim/c2nim",
-             options = "--noNimblePath --useVersion:1.6 --path:. " & args)
+             options = "--noNimblePath --path:. " & args)
 
 proc bundleNimbleExe(latest: bool, args: string) =
   let commit = if latest: "HEAD" else: NimbleStableCommit
   cloneDependency(distDir, "https://github.com/nim-lang/nimble.git",
                   commit = commit, allowBundled = true)
+  cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/checksums.git",
+                commit = ChecksumsStableCommit, allowBundled = true) # or copy it from dist?
+  cloneDependency(distDir / "nimble" / distDir, "https://github.com/nim-lang/sat.git",
+                commit = SatStableCommit, allowBundled = true)
   # installer.ini expects it under $nim/bin
   nimCompile("dist/nimble/src/nimble.nim",
-             options = "-d:release --mm:refc --useVersion:1.6 --noNimblePath " & args)
+             options = "-d:release -d:nimNimbleBootstrap --noNimblePath " & args)
+
+proc bundleAtlasExe(latest: bool, args: string) =
+  let commit = if latest: "HEAD" else: AtlasStableCommit
+  cloneDependency(distDir, "https://github.com/nim-lang/atlas.git",
+                  commit = commit, allowBundled = true)
+  cloneDependency(distDir / "atlas" / distDir, "https://github.com/nim-lang/sat.git",
+                commit = SatStableCommit, allowBundled = true)
+  # installer.ini expects it under $nim/bin
+  nimCompile("dist/atlas/src/atlas.nim",
+             options = "-d:release --noNimblePath -d:nimAtlasBootstrap " & args)
 
 proc bundleNimsuggest(args: string) =
   nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim",
@@ -181,8 +206,14 @@ proc bundleWinTools(args: string) =
     nimCompile(r"tools\downloader.nim",
                options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui " & args)
 
+proc bundleChecksums(latest: bool) =
+  let commit = if latest: "HEAD" else: ChecksumsStableCommit
+  cloneDependency(distDir, "https://github.com/nim-lang/checksums.git", commit, allowBundled = true)
+
 proc zip(latest: bool; args: string) =
+  bundleChecksums(latest)
   bundleNimbleExe(latest, args)
+  bundleAtlasExe(latest, args)
   bundleNimsuggest(args)
   bundleNimpretty(args)
   bundleWinTools(args)
@@ -226,12 +257,17 @@ proc buildTools(args: string = "") =
       "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args,
       outputName = "nim_dbg")
 
-  nimCompileFold("Compile atlas", "tools/atlas/atlas.nim", options = "-d:release " & args,
-      outputName = "atlas")
-
+proc testTools(args: string = "") =
+  nimCompileFold("Compile nimgrep", "tools/nimgrep.nim",
+                 options = "-d:release " & args)
+  when defined(windows): buildVccTool(args)
+  bundleNimpretty(args)
+  nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args)
 
 proc nsis(latest: bool; args: string) =
+  bundleChecksums(latest)
   bundleNimbleExe(latest, args)
+  bundleAtlasExe(latest, args)
   bundleNimsuggest(args)
   bundleWinTools(args)
   # make sure we have generated the niminst executables:
@@ -251,6 +287,22 @@ proc install(args: string) =
   geninstall()
   exec("sh ./install.sh $#" % args)
 
+proc installDeps(dep: string, commit = "") =
+  # the hashes/urls are version controlled here, so can be changed seamlessly
+  # and tied to a nim release (mimicking git submodules)
+  var commit = commit
+  case dep
+  of "tinyc":
+    if commit.len == 0: commit = "916cc2f94818a8a382dd8d4b8420978816c1dfb3"
+    cloneDependency(distDir, "https://github.com/timotheecour/nim-tinyc-archive", commit)
+  of "libffi":
+    # technically a nimble package, however to play nicely with --noNimblePath,
+    # let's just clone it wholesale:
+    if commit.len == 0: commit = "bb2bdaf1a29a4bff6fbd8ae4695877cbb3ec783e"
+    cloneDependency(distDir, "https://github.com/Araq/libffi", commit)
+  else: doAssert false, "unsupported: " & dep
+  # xxx: also add linenoise, niminst etc, refs https://github.com/nim-lang/RFCs/issues/206
+
 # -------------- boot ---------------------------------------------------------
 
 proc findStartNim: string =
@@ -278,7 +330,7 @@ proc thVersion(i: int): string =
 
 template doUseCpp(): bool = getEnv("NIM_COMPILE_TO_CPP", "false") == "true"
 
-proc boot(args: string) =
+proc boot(args: string, skipIntegrityCheck: bool) =
   ## bootstrapping is a process that involves 3 steps:
   ## 1. use csourcesAny to produce nim1.exe. This nim1.exe is buggy but
   ## rock solid for building a Nim compiler. It shouldn't be used for anything else.
@@ -293,13 +345,16 @@ proc boot(args: string) =
   let smartNimcache = (if "release" in args or "danger" in args: "nimcache/r_" else: "nimcache/d_") &
                       hostOS & "_" & hostCPU
 
+  bundleChecksums(false)
+
+  let usingLibFFI = "nimHasLibFFI" in args
+  if usingLibFFI and not dirExists("dist/libffi"):
+    installDeps("libffi")
+
   let nimStart = findStartNim().quoteShell()
-  for i in 0..2:
-    # Nim versions < (1, 1) expect Nim's exception type to have a 'raiseId' field for
-    # C++ interop. Later Nim versions do this differently and removed the 'raiseId' field.
-    # Thus we always bootstrap the first iteration with "c" and not with "cpp" as
-    # a workaround.
-    let defaultCommand = if useCpp and i > 0: "cpp" else: "c"
+  let times = 2 - ord(skipIntegrityCheck)
+  for i in 0..times:
+    let defaultCommand = if useCpp: "cpp" else: "c"
     let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: ""
     echo "iteration: ", i+1
     var extraOption = ""
@@ -307,6 +362,10 @@ proc boot(args: string) =
     if i == 0:
       nimi = nimStart
       extraOption.add " --skipUserCfg --skipParentCfg -d:nimKochBootstrap"
+
+      # --noNimblePath precludes nimble packages as dependencies to the compiler,
+      # so libffi is not "installed as a nimble package"
+      if usingLibFFI: extraOption.add " --path:./dist"
         # The configs are skipped for bootstrap
         # (1st iteration) to prevent newer flags from breaking bootstrap phase.
       let ret = execCmdEx(nimStart & " --version")
@@ -329,7 +388,9 @@ proc boot(args: string) =
       return
     copyExe(output, (i+1).thVersion)
   copyExe(output, finalDest)
-  when not defined(windows): echo "[Warning] executables are still not equal"
+  when not defined(windows):
+    if not skipIntegrityCheck:
+      echo "[Warning] executables are still not equal"
 
 # -------------- clean --------------------------------------------------------
 
@@ -447,6 +508,8 @@ proc temp(args: string) =
       result[1].add " " & quoteShell(args[i])
       inc i
 
+  bundleChecksums(false)
+
   let d = getAppDir()
   let output = d / "compiler" / "nim".exe
   let finalDest = d / "bin" / "nim_temp".exe
@@ -516,17 +579,6 @@ proc hostInfo(): string =
   "hostOS: $1, hostCPU: $2, int: $3, float: $4, cpuEndian: $5, cwd: $6" %
     [hostOS, hostCPU, $int.sizeof, $float.sizeof, $cpuEndian, getCurrentDir()]
 
-proc installDeps(dep: string, commit = "") =
-  # the hashes/urls are version controlled here, so can be changed seamlessly
-  # and tied to a nim release (mimicking git submodules)
-  var commit = commit
-  case dep
-  of "tinyc":
-    if commit.len == 0: commit = "916cc2f94818a8a382dd8d4b8420978816c1dfb3"
-    cloneDependency(distDir, "https://github.com/timotheecour/nim-tinyc-archive", commit)
-  else: doAssert false, "unsupported: " & dep
-  # xxx: also add linenoise, niminst etc, refs https://github.com/nim-lang/RFCs/issues/206
-
 proc runCI(cmd: string) =
   doAssert cmd.len == 0, cmd # avoid silently ignoring
   echo "runCI: ", cmd
@@ -534,7 +586,7 @@ proc runCI(cmd: string) =
   # boot without -d:nimHasLibFFI to make sure this still works
   # `--lib:lib` is needed for bootstrap on openbsd, for reasons described in
   # https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd).
-  kochExecFold("Boot in release mode", "boot -d:release -d:nimStrictMode --lib:lib")
+  kochExecFold("Boot Nim ORC", "boot -d:release -d:nimStrictMode --lib:lib")
 
   when false: # debugging: when you need to run only 1 test in CI, use something like this:
     execFold("debugging test", "nim r tests/stdlib/tosproc.nim")
@@ -543,12 +595,14 @@ proc runCI(cmd: string) =
   ## build nimble early on to enable remainder to depend on it if needed
   kochExecFold("Build Nimble", "nimble")
 
+  execFold("Install smtp", "nimble install smtp -y")
+
   let batchParam = "--batch:$1" % "NIM_TESTAMENT_BATCH".getEnv("_")
   if getEnv("NIM_TEST_PACKAGES", "0") == "1":
     nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release")
     execFold("Test selected Nimble packages", "testament $# pcat nimble-packages" % batchParam)
   else:
-    buildTools()
+    testTools()
 
     for a in "zip opengl sdl1 jester@#head".split:
       let buildDeps = "build"/"deps" # xxx factor pending https://github.com/timotheecour/Nim/issues/616
@@ -568,11 +622,11 @@ proc runCI(cmd: string) =
 
     block: # nimHasLibFFI:
       when defined(posix): # windows can be handled in future PR's
-        execFold("nimble install -y libffi", "nimble install -y libffi")
+        installDeps("libffi")
         const nimFFI = "bin/nim.ctffi"
         # no need to bootstrap with koch boot (would be slower)
         let backend = if doUseCpp(): "cpp" else: "c"
-        execFold("build with -d:nimHasLibFFI", "nim $1 -d:release -d:nimHasLibFFI -o:$2 compiler/nim.nim" % [backend, nimFFI])
+        execFold("build with -d:nimHasLibFFI", "nim $1 -d:release --noNimblePath -d:nimHasLibFFI --path:./dist -o:$2 compiler/nim.nim" % [backend, nimFFI])
         execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend])
 
     execFold("Run nimdoc tests", "nim r nimdoc/tester")
@@ -586,11 +640,8 @@ proc runCI(cmd: string) =
       execFold("build nimsuggest_testing", "nim c -o:bin/nimsuggest_testing -d:release nimsuggest/nimsuggest")
       execFold("Run nimsuggest tests", "nim r nimsuggest/tester")
 
-    execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls")
+    kochExecFold("Testing booting in refc", "boot -d:release --mm:refc -d:nimStrictMode --lib:lib")
 
-  when not defined(bsd):
-    # the BSDs are overwhelmed already, so only run this test on the other machines:
-    kochExecFold("Boot Nim ORC", "boot -d:release --mm:orc --lib:lib")
 
 proc testUnixInstall(cmdLineRest: string) =
   csource("-d:danger" & cmdLineRest)
@@ -661,6 +712,7 @@ when isMainModule:
     latest = false
     localDocsOnly = false
     localDocsOut = ""
+    skipIntegrityCheck = false
   while true:
     op.next()
     case op.kind
@@ -674,12 +726,14 @@ when isMainModule:
         localDocsOnly = true
         if op.val.len > 0:
           localDocsOut = op.val.absolutePath
+      of "skipintegritycheck":
+        skipIntegrityCheck = true
       else: showHelp(success = false)
     of cmdArgument:
       case normalize(op.key)
-      of "boot": boot(op.cmdLineRest)
+      of "boot": boot(op.cmdLineRest, skipIntegrityCheck)
       of "clean": clean(op.cmdLineRest)
-      of "doc", "docs": buildDocs(op.cmdLineRest, localDocsOnly, localDocsOut)
+      of "doc", "docs": buildDocs(op.cmdLineRest & " --d:nimPreviewSlimSystem " & paCode, localDocsOnly, localDocsOut)
       of "doc0", "docs0":
         # undocumented command for Araq-the-merciful:
         buildDocs(op.cmdLineRest & gaCode)
@@ -699,6 +753,7 @@ when isMainModule:
       of "xtemp": xtemp(op.cmdLineRest)
       of "wintools": bundleWinTools(op.cmdLineRest)
       of "nimble": bundleNimbleExe(latest, op.cmdLineRest)
+      of "atlas": bundleAtlasExe(latest, op.cmdLineRest)
       of "nimsuggest": bundleNimsuggest(op.cmdLineRest)
       # toolsNoNimble is kept for backward compatibility with build scripts
       of "toolsnonimble", "toolsnoexternal":
@@ -706,6 +761,9 @@ when isMainModule:
       of "tools":
         buildTools(op.cmdLineRest)
         bundleNimbleExe(latest, op.cmdLineRest)
+        bundleAtlasExe(latest, op.cmdLineRest)
+      of "checksums":
+        bundleChecksums(latest)
       of "pushcsource":
         quit "use this instead: https://github.com/nim-lang/csources_v1/blob/master/push_c_code.nim"
       of "valgrind": valgrind(op.cmdLineRest)
diff --git a/lib/core/hotcodereloading.nim b/lib/core/hotcodereloading.nim
index 73f38402d..3a876885c 100644
--- a/lib/core/hotcodereloading.nim
+++ b/lib/core/hotcodereloading.nim
@@ -11,7 +11,7 @@
 
 when defined(hotcodereloading):
   import
-    macros
+    std/macros
 
   template beforeCodeReload*(body: untyped) =
     hcrAddEventHandler(true, proc = body) {.executeOnReload.}
diff --git a/lib/core/locks.nim b/lib/core/locks.nim
index 92967b9db..523727479 100644
--- a/lib/core/locks.nim
+++ b/lib/core/locks.nim
@@ -18,8 +18,7 @@ when not compileOption("threads") and not defined(nimdoc):
   when false: # fix #12330
     {.error: "Locks requires --threads:on option.".}
 
-const insideRLocksModule = false
-include "system/syslocks"
+import std/private/syslocks
 
 type
   Lock* = SysLock ## Nim lock; whether this is re-entrant
@@ -38,7 +37,7 @@ proc initLock*(lock: var Lock) {.inline.} =
   when not defined(js):
     initSysLock(lock)
 
-proc deinitLock*(lock: var Lock) {.inline.} =
+proc deinitLock*(lock: Lock) {.inline.} =
   ## Frees the resources associated with the lock.
   deinitSys(lock)
 
@@ -61,7 +60,7 @@ proc initCond*(cond: var Cond) {.inline.} =
   ## Initializes the given condition variable.
   initSysCond(cond)
 
-proc deinitCond*(cond: var Cond) {.inline.} =
+proc deinitCond*(cond: Cond) {.inline.} =
   ## Frees the resources associated with the condition variable.
   deinitSysCond(cond)
 
diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim
index d4b103793..39999fa11 100644
--- a/lib/core/macrocache.nim
+++ b/lib/core/macrocache.nim
@@ -127,6 +127,19 @@ proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} =
       mySeq.add(newLit(42))
       assert mySeq[0].intVal == 42
 
+proc `[]`*(s: CacheSeq; i: BackwardsIndex): NimNode =
+  ## Returns the `i`th last value from `s`.
+  runnableExamples:
+    import std/macros
+
+    const mySeq = CacheSeq"backTest"
+    static:
+      mySeq &= newLit(42)
+      mySeq &= newLit(7)
+      assert mySeq[^1].intVal == 7  # Last item
+      assert mySeq[^2].intVal == 42 # Second last item
+  s[s.len - int(i)]
+
 iterator items*(s: CacheSeq): NimNode =
   ## Iterates over each item in `s`.
   runnableExamples:
@@ -181,6 +194,32 @@ proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} =
       # get the NimNode back
       assert mcTable["toAdd"].kind == nnkStmtList
 
+proc hasKey*(t: CacheTable; key: string): bool =
+  ## Returns true if `key` is in the table `t`.
+  ##
+  ## See also:
+  ## * [contains proc][contains(CacheTable, string)] for use with the `in` operator
+  runnableExamples:
+    import std/macros
+    const mcTable = CacheTable"hasKeyEx"
+    static:
+      assert not mcTable.hasKey("foo")
+      mcTable["foo"] = newEmptyNode()
+      # Will now be true since we inserted a value
+      assert mcTable.hasKey("foo")
+  discard "Implemented in vmops"
+
+proc contains*(t: CacheTable; key: string): bool {.inline.} =
+  ## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator.
+  runnableExamples:
+    import std/macros
+    const mcTable = CacheTable"containsEx"
+    static:
+      mcTable["foo"] = newEmptyNode()
+      # Will be true since we gave it a value before
+      assert "foo" in mcTable
+  t.hasKey(key)
+
 proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".}
 proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".}
 
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 24901180e..7646b165c 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -23,6 +23,8 @@ when defined(nimPreviewSlimSystem):
 
 ## .. include:: ../../doc/astspec.txt
 
+## .. importdoc:: system.nim
+
 # If you look for the implementation of the magic symbol
 # ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`.
 
@@ -75,11 +77,11 @@ type
     nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy,
     nnkRecList, nnkRecCase, nnkRecWhen,
     nnkRefTy, nnkPtrTy, nnkVarTy,
-    nnkConstTy, nnkMutableTy,
+    nnkConstTy, nnkOutTy,
     nnkDistinctTy,
     nnkProcTy,
     nnkIteratorTy,         # iterator type
-    nnkSharedTy,           # 'shared T'
+    nnkSinkAsgn,
     nnkEnumTy,
     nnkEnumFieldDef,
     nnkArgList, nnkPattern
@@ -91,6 +93,8 @@ type
     nnkFuncDef,
     nnkTupleConstr,
     nnkError,  ## erroneous AST node
+    nnkModuleRef, nnkReplayAction, nnkNilRodNode ## internal IC nodes
+    nnkOpenSym
 
   NimNodeKinds* = set[NimNodeKind]
   NimTypeKind* = enum  # some types are no longer used, see ast.nim
@@ -125,6 +129,10 @@ type
 
   TNimSymKinds* {.deprecated.} = set[NimSymKind]
 
+const
+  nnkMutableTy* {.deprecated.} = nnkOutTy
+  nnkSharedTy* {.deprecated.} = nnkSinkAsgn
+
 type
   NimIdent* {.deprecated.} = object of RootObj
     ## Represents a Nim identifier in the AST. **Note**: This is only
@@ -139,8 +147,9 @@ type
 
 const
   nnkLiterals* = {nnkCharLit..nnkNilLit}
+  # see matching set CallNodes below
   nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
-                   nnkCallStrLit}
+                   nnkCallStrLit, nnkHiddenCallConv}
   nnkPragmaCallKinds = {nnkExprColonExpr, nnkCall, nnkCallStrLit}
 
 {.push warnings: off.}
@@ -201,7 +210,7 @@ template `or`*(x, y: NimNode): NimNode =
   ## Evaluate `x` and when it is not an empty node, return
   ## it. Otherwise evaluate to `y`. Can be used to chain several
   ## expressions to get the first expression that is not empty.
-  ##   ```
+  ##   ```nim
   ##   let node = mightBeEmpty() or mightAlsoBeEmpty() or fallbackNode
   ##   ```
 
@@ -212,12 +221,12 @@ template `or`*(x, y: NimNode): NimNode =
     y
 
 proc add*(father, child: NimNode): NimNode {.magic: "NAdd", discardable,
-  noSideEffect, locks: 0.}
+  noSideEffect.}
   ## Adds the `child` to the `father` node. Returns the
   ## father node so that calls can be nested.
 
 proc add*(father: NimNode, children: varargs[NimNode]): NimNode {.
-  magic: "NAddMultiple", discardable, noSideEffect, locks: 0.}
+  magic: "NAddMultiple", discardable, noSideEffect.}
   ## Adds each child of `children` to the `father` node.
   ## Returns the `father` node so that calls can be nested.
 
@@ -270,7 +279,7 @@ when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform)
     ## note that code transformations are implementation dependent and subject to change.
     ## See an example in `tests/macros/tmacros_various.nim`.
 
-proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.}
+proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect, deprecated.}
   ## Accepts a node of kind `nnkSym` and returns its owner's symbol.
   ## The meaning of 'owner' depends on `sym`'s `NimSymKind` and declaration
   ## context. For top level declarations this is an `nskModule` symbol,
@@ -340,8 +349,7 @@ proc getTypeImpl*(n: NimNode): NimNode {.magic: "NGetType", noSideEffect.} =
       newLit(x.getTypeImpl.repr)
     let t = """
 object
-  arr: array[0 .. 3, float32]
-"""
+  arr: array[0 .. 3, float32]"""
     doAssert(dumpTypeImpl(a) == t)
     doAssert(dumpTypeImpl(b) == t)
     doAssert(dumpTypeImpl(c) == t)
@@ -420,7 +428,12 @@ proc copyNimTree*(n: NimNode): NimNode {.magic: "NCopyNimTree", noSideEffect.} =
       let x = 12
       echo x
 
-proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign.}
+when defined(nimHasNoReturnError):
+  {.pragma: errorNoReturn, noreturn.}
+else:
+  {.pragma: errorNoReturn.}
+
+proc error*(msg: string, n: NimNode = nil) {.magic: "NError", benign, errorNoReturn.}
   ## Writes an error message at compile time. The optional `n: NimNode`
   ## parameter is used as the source for file and line number information in
   ## the compilation error message.
@@ -529,6 +542,22 @@ proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.}
   ## Copy lineinfo from `info`.
 
+proc setLine(arg: NimNode, line: uint16) {.magic: "NLineInfo", noSideEffect.}
+proc setColumn(arg: NimNode, column: int16) {.magic: "NLineInfo", noSideEffect.}
+proc setFile(arg: NimNode, file: string) {.magic: "NLineInfo", noSideEffect.}
+
+proc setLineInfo*(arg: NimNode, file: string, line: int, column: int) =
+  ## Sets the line info on the NimNode. The file needs to exists, but can be a
+  ## relative path. If you want to attach line info to a block using `quote`
+  ## you'll need to add the line information after the quote block.
+  arg.setFile(file)
+  arg.setLine(line.uint16)
+  arg.setColumn(column.int16)
+
+proc setLineInfo*(arg: NimNode, lineInfo: LineInfo) =
+  ## See `setLineInfo proc<#setLineInfo,NimNode,string,int,int>`_
+  setLineInfo(arg, lineInfo.filename, lineInfo.line, lineInfo.column)
+
 proc lineInfoObj*(n: NimNode): LineInfo =
   ## Returns `LineInfo` of `n`, using absolute path for `filename`.
   result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn)
@@ -567,7 +596,7 @@ proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEf
   ## Obtains the AST nodes returned from a macro or template invocation.
   ## See also `genasts.genAst`.
   ## Example:
-  ##   ```
+  ##   ```nim
   ##   macro FooMacro() =
   ##     var ast = getAst(BarTemplate())
   ##   ```
@@ -925,6 +954,8 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent
     discard # same as nil node in this representation
   of nnkCharLit .. nnkInt64Lit:
     res.add(" " & $n.intVal)
+  of nnkUIntLit .. nnkUInt64Lit:
+    res.add(" " & $cast[uint64](n.intVal))
   of nnkFloatLit .. nnkFloat64Lit:
     res.add(" " & $n.floatVal)
   of nnkStrLit .. nnkTripleStrLit, nnkCommentStmt, nnkIdent, nnkSym:
@@ -1024,7 +1055,7 @@ macro dumpTree*(s: untyped): untyped = echo s.treeRepr
   ## a certain expression/statement.
   ##
   ## For example:
-  ##   ```
+  ##   ```nim
   ##   dumpTree:
   ##     echo "Hello, World!"
   ##   ```
@@ -1048,7 +1079,7 @@ macro dumpLisp*(s: untyped): untyped = echo s.lispRepr(indented = true)
   ## a certain expression/statement.
   ##
   ## For example:
-  ##   ```
+  ##   ```nim
   ##   dumpLisp:
   ##     echo "Hello, World!"
   ##   ```
@@ -1071,7 +1102,7 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr
   ## outputs and then copying the snippets into the macro for modification.
   ##
   ## For example:
-  ##   ```
+  ##   ```nim
   ##   dumpAstGen:
   ##     echo "Hello, World!"
   ##   ```
@@ -1154,7 +1185,7 @@ proc newIdentDefs*(name, kind: NimNode;
   ## `let` or `var` blocks may have an empty `kind` node if the
   ## identifier is being assigned a value. Example:
   ##
-  ##   ```
+  ##   ```nim
   ##   var varSection = newNimNode(nnkVarSection).add(
   ##     newIdentDefs(ident("a"), ident("string")),
   ##     newIdentDefs(ident("b"), newEmptyNode(), newLit(3)))
@@ -1165,7 +1196,7 @@ proc newIdentDefs*(name, kind: NimNode;
   ##
   ## If you need to create multiple identifiers you need to use the lower level
   ## `newNimNode`:
-  ##   ```
+  ##   ```nim
   ##   result = newNimNode(nnkIdentDefs).add(
   ##     ident("a"), ident("b"), ident("c"), ident("string"),
   ##       newStrLitNode("Hello"))
@@ -1184,8 +1215,8 @@ const
   RoutineNodes* = {nnkProcDef, nnkFuncDef, nnkMethodDef, nnkDo, nnkLambda,
                    nnkIteratorDef, nnkTemplateDef, nnkConverterDef, nnkMacroDef}
   AtomicNodes* = {nnkNone..nnkNilLit}
-  CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
-    nnkCallStrLit, nnkHiddenCallConv}
+  # see matching set nnkCallKinds above
+  CallNodes* = nnkCallKinds
 
 proc expectKind*(n: NimNode; k: set[NimNodeKind]) =
   ## Checks that `n` is of kind `k`. If this is not the case,
@@ -1216,7 +1247,7 @@ proc newProc*(name = newEmptyNode();
 
 proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode =
   ## Constructor for `if` statements.
-  ##   ```
+  ##   ```nim
   ##   newIfStmt(
   ##     (Ident, StmtList),
   ##     ...
@@ -1233,7 +1264,7 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode],
 
   ## Creates a new enum. `name` must be an ident. Fields are allowed to be
   ## either idents or EnumFieldDef:
-  ##   ```
+  ##   ```nim
   ##   newEnum(
   ##     name    = ident("Colors"),
   ##     fields  = [ident("Blue"), ident("Red")],
@@ -1378,7 +1409,7 @@ proc `$`*(node: NimNode): string =
     result = node.basename.strVal & "*"
   of nnkStrLit..nnkTripleStrLit, nnkCommentStmt, nnkSym, nnkIdent:
     result = node.strVal
-  of nnkOpenSymChoice, nnkClosedSymChoice:
+  of nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
     result = $node[0]
   of nnkAccQuoted:
     result = ""
@@ -1404,7 +1435,7 @@ iterator children*(n: NimNode): NimNode {.inline.} =
 
 template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} =
   ## Find the first child node matching condition (or nil).
-  ##   ```
+  ##   ```nim
   ##   var res = findChild(n, it.kind == nnkPostfix and
   ##                          it.basename.ident == ident"foo")
   ##   ```
@@ -1499,11 +1530,10 @@ proc boolVal*(n: NimNode): bool {.noSideEffect.} =
   if n.kind == nnkIntLit: n.intVal != 0
   else: n == bindSym"true" # hacky solution for now
 
-when defined(nimMacrosGetNodeId):
-  proc nodeID*(n: NimNode): int {.magic: "NodeId".}
-    ## Returns the id of `n`, when the compiler has been compiled
-    ## with the flag `-d:useNodeids`, otherwise returns `-1`. This
-    ## proc is for the purpose to debug the compiler only.
+proc nodeID*(n: NimNode): int {.magic: "NodeId".}
+  ## Returns the id of `n`, when the compiler has been compiled
+  ## with the flag `-d:useNodeids`, otherwise returns `-1`. This
+  ## proc is for the purpose to debug the compiler only.
 
 macro expandMacros*(body: typed): untyped =
   ## Expands one level of macro - useful for debugging.
@@ -1512,7 +1542,7 @@ macro expandMacros*(body: typed): untyped =
   ##
   ## For instance,
   ##
-  ##   ```
+  ##   ```nim
   ##   import std/[sugar, macros]
   ##
   ##   let
@@ -1567,10 +1597,10 @@ proc customPragmaNode(n: NimNode): NimNode =
     let impl = n.getImpl()
     if impl.kind in RoutineNodes:
       return impl.pragma
-    elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr:
+    elif impl.kind in {nnkIdentDefs, nnkConstDef} and impl[0].kind == nnkPragmaExpr:
       return impl[0][1]
     else:
-      let timpl = typ.getImpl()
+      let timpl = getImpl(if typ.kind == nnkBracketExpr: typ[0] else: typ)
       if timpl.len>0 and timpl[0].len>1:
         return timpl[0][1]
       else:
@@ -1578,11 +1608,9 @@ proc customPragmaNode(n: NimNode): NimNode =
 
   if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}:
     let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1])
-    let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0])
-    var typDef = getImpl(
-      if typInst.kind in {nnkVarTy, nnkBracketExpr}: typInst[0]
-      else: typInst
-    )
+    var typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0])
+    while typInst.kind in {nnkVarTy, nnkBracketExpr}: typInst = typInst[0]
+    var typDef = getImpl(typInst)
     while typDef != nil:
       typDef.expectKind(nnkTypeDef)
       let typ = typDef[2].extractTypeImpl()
@@ -1630,7 +1658,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped =
   ##
   ## See also `getCustomPragmaVal`_.
   ##
-  ##   ```
+  ##   ```nim
   ##   template myAttr() {.pragma.}
   ##   type
   ##     MyObj = object
@@ -1655,7 +1683,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped =
   ##
   ## See also `hasCustomPragma`_.
   ##
-  ##   ```
+  ##   ```nim
   ##   template serializationKey(key: string) {.pragma.}
   ##   type
   ##     MyObj {.serializationKey: "mo".} = object
@@ -1751,7 +1779,7 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode =
   ## runnableExamples in `a`, stopping at the first child that is neither.
   ## Example:
   ##
-  ##   ```
+  ##   ```nim
   ##   import std/macros
   ##   macro transf(a): untyped =
   ##     result = quote do:
diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim
index 0444b9a83..8cb0cef05 100644
--- a/lib/core/rlocks.nim
+++ b/lib/core/rlocks.nim
@@ -16,8 +16,7 @@ when not compileOption("threads") and not defined(nimdoc):
     # so they can replace each other seamlessly.
     {.error: "Rlocks requires --threads:on option.".}
 
-const insideRLocksModule = true
-include "system/syslocks"
+import std/private/syslocks
 
 type
   RLock* = SysLock ## Nim lock, re-entrant
@@ -32,7 +31,7 @@ proc initRLock*(lock: var RLock) {.inline.} =
   else:
     initSysLock(lock)
 
-proc deinitRLock*(lock: var RLock) {.inline.} =
+proc deinitRLock*(lock: RLock) {.inline.} =
   ## Frees the resources associated with the lock.
   deinitSys(lock)
 
diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim
index df2e66226..f2fee91c4 100644
--- a/lib/core/typeinfo.nim
+++ b/lib/core/typeinfo.nim
@@ -91,7 +91,7 @@ type
       rawTypePtr: pointer
 
   ppointer = ptr pointer
-  pbyteArray = ptr array[0xffff, int8]
+  pbyteArray = ptr array[0xffff, uint8]
 
 when not defined(gcDestructors):
   type
@@ -129,20 +129,22 @@ when not defined(gcDestructors):
 else:
   proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
   proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
-  proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+  proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+    importCompilerProc.}
+  proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
     importCompilerProc.}
 
-template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b)
+template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b)
 
 proc getDiscriminant(aa: pointer, n: ptr TNimNode): int =
   assert(n.kind == nkCase)
   var d: int
-  let a = cast[ByteAddress](aa)
+  let a = cast[int](aa)
   case n.typ.size
-  of 1: d = ze(cast[ptr int8](a +% n.offset)[])
-  of 2: d = ze(cast[ptr int16](a +% n.offset)[])
-  of 4: d = int(cast[ptr int32](a +% n.offset)[])
-  of 8: d = int(cast[ptr int64](a +% n.offset)[])
+  of 1: d = int(cast[ptr uint8](a +% n.offset)[])
+  of 2: d = int(cast[ptr uint16](a +% n.offset)[])
+  of 4: d = int(cast[ptr uint32](a +% n.offset)[])
+  of 8: d = int(cast[ptr uint64](a +% n.offset)[])
   else: assert(false)
   return d
 
@@ -159,16 +161,6 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} =
   result.value = value
   result.rawType = rawType
 
-when declared(system.VarSlot):
-  proc toAny*(x: VarSlot): Any {.inline.} =
-    ## Constructs an `Any` object from a variable slot `x`.
-    ## This captures `x`'s address, so `x` can be modified with its
-    ## `Any` wrapper! The caller needs to ensure that the wrapper
-    ## **does not** live longer than `x`!
-    ## This is provided for easier reflection capabilities of a debugger.
-    result.value = x.address
-    result.rawType = x.typ
-
 proc toAny*[T](x: var T): Any {.inline.} =
   ## Constructs an `Any` object from `x`. This captures `x`'s address, so
   ## `x` can be modified with its `Any` wrapper! The caller needs to ensure
@@ -221,7 +213,8 @@ proc extendSeq*(x: Any) =
     var s = cast[ptr NimSeqV2Reimpl](x.value)
     let elem = x.rawType.base
     if s.p == nil or s.p.cap < s.len+1:
-      s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align))
+      s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAddUninit(s.len, s.p, 1, elem.size, elem.align))
+    zeroNewElements(s.len, s.p, 1, elem.size, elem.align)
     inc s.len
   else:
     var y = cast[ptr PGenSeq](x.value)[]
@@ -484,8 +477,8 @@ proc getBiggestInt*(x: Any): BiggestInt =
   of tyChar: result = BiggestInt(cast[ptr char](x.value)[])
   of tyEnum, tySet:
     case t.size
-    of 1: result = ze64(cast[ptr int8](x.value)[])
-    of 2: result = ze64(cast[ptr int16](x.value)[])
+    of 1: result = int64(cast[ptr uint8](x.value)[])
+    of 2: result = int64(cast[ptr uint16](x.value)[])
     of 4: result = BiggestInt(cast[ptr int32](x.value)[])
     of 8: result = BiggestInt(cast[ptr int64](x.value)[])
     else: assert false
@@ -509,8 +502,8 @@ proc setBiggestInt*(x: Any, y: BiggestInt) =
   of tyChar: cast[ptr char](x.value)[] = chr(y.int)
   of tyEnum, tySet:
     case t.size
-    of 1: cast[ptr int8](x.value)[] = toU8(y.int)
-    of 2: cast[ptr int16](x.value)[] = toU16(y.int)
+    of 1: cast[ptr uint8](x.value)[] = uint8(y.int)
+    of 2: cast[ptr uint16](x.value)[] = uint16(y.int)
     of 4: cast[ptr int32](x.value)[] = int32(y)
     of 8: cast[ptr int64](x.value)[] = y
     else: assert false
@@ -691,14 +684,14 @@ iterator elements*(x: Any): int =
   # "typ.slots.len" field is for sets the "first" field
   var u: int64
   case typ.size
-  of 1: u = ze64(cast[ptr int8](p)[])
-  of 2: u = ze64(cast[ptr int16](p)[])
-  of 4: u = ze64(cast[ptr int32](p)[])
+  of 1: u = int64(cast[ptr uint8](p)[])
+  of 2: u = int64(cast[ptr uint16](p)[])
+  of 4: u = int64(cast[ptr uint32](p)[])
   of 8: u = cast[ptr int64](p)[]
   else:
     let a = cast[pbyteArray](p)
     for i in 0 .. typ.size*8-1:
-      if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0:
+      if (int(a[i div 8]) and (1 shl (i mod 8))) != 0:
         yield i + typ.node.len
   if typ.size <= 8:
     for i in 0..sizeof(int64)*8-1:
@@ -727,4 +720,4 @@ proc inclSetElement*(x: Any, elem: int) =
     a[] = a[] or (1'i64 shl e)
   else:
     var a = cast[pbyteArray](p)
-    a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7)))
+    a[e shr 3] = a[e shr 3] or uint8(1 shl (e and 7))
diff --git a/lib/deprecated/pure/future.nim b/lib/deprecated/pure/future.nim
index 3f7b63a30..0e06161f2 100644
--- a/lib/deprecated/pure/future.nim
+++ b/lib/deprecated/pure/future.nim
@@ -2,5 +2,5 @@
 
 {.deprecated: "Use the new 'sugar' module instead".}
 
-import sugar
+import std/sugar
 export sugar
diff --git a/lib/pure/mersenne.nim b/lib/deprecated/pure/mersenne.nim
index 37c5085b1..37c5085b1 100644
--- a/lib/pure/mersenne.nim
+++ b/lib/deprecated/pure/mersenne.nim
diff --git a/lib/deprecated/pure/ospaths.nim b/lib/deprecated/pure/ospaths.nim
index b57839d1a..43fcb17cc 100644
--- a/lib/deprecated/pure/ospaths.nim
+++ b/lib/deprecated/pure/ospaths.nim
@@ -11,7 +11,7 @@
 
 {.deprecated: "use `std/os` instead".}
 
-import os
+import std/os
 export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode,
   doslikeFileSystem, CurDir, ParDir, DirSep, AltSep, PathSep, FileSystemCaseSensitive,
   ExeExt, ScriptExt, DynlibFormat, ExtSep, joinPath, `/`, splitPath, parentDir,
diff --git a/lib/pure/oswalkdir.nim b/lib/deprecated/pure/oswalkdir.nim
index 866f9ed70..57a2cb81d 100644
--- a/lib/pure/oswalkdir.nim
+++ b/lib/deprecated/pure/oswalkdir.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-## This module is deprecated, `import os` instead.
-{.deprecated: "import os.nim instead".}
-import os
+## This module is deprecated, `import std/os` instead.
+{.deprecated: "import 'std/os' instead".}
+import std/os
 export PathComponent, walkDir, walkDirRec
diff --git a/lib/deprecated/pure/securehash.nim b/lib/deprecated/pure/securehash.nim
deleted file mode 100644
index b4749ad75..000000000
--- a/lib/deprecated/pure/securehash.nim
+++ /dev/null
@@ -1,6 +0,0 @@
-## This module is a deprecated alias for the `sha1` module. Deprecated since 0.18.1.
-
-{.deprecated: "use `std/sha1` instead".}
-
-import "../std/sha1"
-export sha1
diff --git a/lib/std/sums.nim b/lib/deprecated/pure/sums.nim
index a6ce1b85d..a6ce1b85d 100644
--- a/lib/std/sums.nim
+++ b/lib/deprecated/pure/sums.nim
diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim
index 4ca5eb319..669e9f613 100644
--- a/lib/experimental/diff.nim
+++ b/lib/experimental/diff.nim
@@ -43,7 +43,7 @@ jkl"""
 # "An O(ND) Difference Algorithm and its Variations" by Eugene Myers
 # Algorithmica Vol. 1 No. 2, 1986, p 251.
 
-import tables, strutils
+import std/[tables, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
diff --git a/lib/genode/alloc.nim b/lib/genode/alloc.nim
index 3ddd3074b..24fb9954e 100644
--- a/lib/genode/alloc.nim
+++ b/lib/genode/alloc.nim
@@ -15,7 +15,7 @@ when not defined(genode):
   {.error: "Genode only module".}
 
 when not declared(GenodeEnv):
-  include genode/env
+  import genode/env
 
 type RamDataspaceCapability {.
   importcpp: "Genode::Ram_dataspace_capability", pure.} = object
@@ -111,7 +111,7 @@ proc osDeallocPages(p: pointer; size: int) =
       if m.attachment == p:
         if m.size != size:
           echo "cannot partially detach dataspace"
-          quit -1
+          rawQuit -1
         runtimeEnv.detachAddress m.attachment
         runtimeEnv.freeDataspace m.ds
         m[] = Map()
diff --git a/lib/genode/constructibles.nim b/lib/genode/constructibles.nim
new file mode 100644
index 000000000..3a4a646e0
--- /dev/null
+++ b/lib/genode/constructibles.nim
@@ -0,0 +1,21 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+type Constructible*[T] {.
+  importcpp: "Genode::Constructible",
+  header: "<util/reconstructible.h>", byref, pure.} = object
+
+proc construct*[T](x: Constructible[T]) {.importcpp.}
+  ## Construct a constructible C++ object.
+
+proc destruct*[T](x: Constructible[T]) {.importcpp.}
+  ## Destruct a constructible C++ object.
+
+proc constructed*[T](x: Constructible[T]): bool {.importcpp.}
+  ## Test if an object is constructed.
diff --git a/lib/genode/entrypoints.nim b/lib/genode/entrypoints.nim
new file mode 100644
index 000000000..0bf5e0e0e
--- /dev/null
+++ b/lib/genode/entrypoints.nim
@@ -0,0 +1,22 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Entrypoint <https://genode.org/documentation/genode-foundations/21.05/functional_specification/Entrypoint.html>`
+## for a description of Entrypoints.
+
+type
+  EntrypointObj {.
+    importcpp: "Genode::Entrypoint",
+    header: "<base/entrypoint.h>",
+    pure.} = object
+  Entrypoint* = ptr EntrypointObj
+    ## Opaque Entrypoint object.
+
+proc ep*(env: GenodeEnv): Entrypoint {.importcpp: "(&#->ep())".}
+  ## Access the entrypoint associated with `env`.
diff --git a/lib/genode/env.nim b/lib/genode/env.nim
index ef4a25883..babe2a8a0 100644
--- a/lib/genode/env.nim
+++ b/lib/genode/env.nim
@@ -17,13 +17,13 @@
 #
 
 when not defined(genode):
-  {.error: "Genode only include".}
+  {.error: "Genode only module".}
 
 type
-  GenodeEnvObj {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
-  GenodeEnvPtr = ptr GenodeEnvObj
+  GenodeEnvObj* {.importcpp: "Genode::Env", header: "<base/env.h>", pure.} = object
+  GenodeEnvPtr* = ptr GenodeEnvObj
 
-const runtimeEnvSym = "nim_runtime_env"
+const runtimeEnvSym* = "nim_runtime_env"
 
 when not defined(nimscript):
-  var runtimeEnv {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
+  var runtimeEnv* {.importcpp: runtimeEnvSym.}: GenodeEnvPtr
diff --git a/lib/genode/signals.nim b/lib/genode/signals.nim
new file mode 100644
index 000000000..7d1875730
--- /dev/null
+++ b/lib/genode/signals.nim
@@ -0,0 +1,77 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Emery Hemingway
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## See `Genode Foundations - Asynchronous notifications <https://genode.org/documentation/genode-foundations/21.05/architecture/Inter-component_communication.html#Asynchronous_notifications>`
+## for a description of Genode signals.
+
+when not defined(genode) or defined(nimdoc):
+  {.error: "Genode only module".}
+
+import ./entrypoints, ./constructibles
+
+export ep # Entrypoint accessor on GenodeEnv
+
+type
+  SignalContextCapability* {.
+    importcpp: "Genode::Signal_context_capability",
+    header: "<base/signal.h>", pure.} = object
+    ## Capability to an asynchronous signal context.
+
+proc isValid*(cap: SignalContextCapability): bool {.importcpp: "#.valid()".}
+  ## Call the Genode core to check if this `SignalContextCapability` is valid.
+  # TODO: RpcEffect
+
+type
+  HandlerProc = proc () {.closure, gcsafe.}
+
+  SignalHandlerBase {.
+    importcpp: "Nim::SignalHandler",
+    header: "genode_cpp/signals.h",
+    pure.} = object
+
+  SignalHandlerCpp = Constructible[SignalHandlerBase]
+
+  SignalHandlerObj = object
+    cpp: SignalHandlerCpp
+    cb: HandlerProc
+      ## Signal handling procedure called during dispatch.
+
+  SignalHandler* = ref SignalHandlerObj
+    ## Nim object enclosing a Genode signal handler.
+
+proc construct(cpp: SignalHandlerCpp; ep: Entrypoint; sh: SignalHandler) {.importcpp.}
+
+proc cap(cpp: SignalHandlerCpp): SignalContextCapability {.importcpp: "#->cap()".}
+
+proc newSignalHandler*(ep: Entrypoint; cb: HandlerProc): SignalHandler =
+  ## Create a new signal handler. A label is recommended for
+  ## debugging purposes. A signal handler will not be garbage
+  ## collected until after it has been dissolved.
+  result = SignalHandler(cb: cb)
+  result.cpp.construct(ep, result)
+  GCref result
+
+proc dissolve*(sig: SignalHandler) =
+  ## Dissolve signal dispatcher from entrypoint.
+  # TODO: =destroy?
+  destruct sig.cpp
+  sig.cb = nil # lose the callback
+  GCunref sig
+
+proc cap*(sig: SignalHandler): SignalContextCapability =
+  ## Signal context capability. Can be delegated to external components.
+  sig.cpp.cap
+
+proc submit*(cap: SignalContextCapability) {.
+    importcpp: "Genode::Signal_transmitter(#).submit()".}
+  ## Submit a signal to a context capability.
+
+proc nimHandleSignal(p: pointer) {.exportc.} =
+  ## C symbol invoked by entrypoint during signal dispatch.
+  cast[SignalHandler](p).cb()
diff --git a/lib/genode_cpp/signals.h b/lib/genode_cpp/signals.h
new file mode 100644
index 000000000..fa3975d38
--- /dev/null
+++ b/lib/genode_cpp/signals.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *           Nim's Runtime Library
+ *       (c) Copyright 2022 Emery Hemingway
+ *
+ *   See the file "copying.txt", included in this
+ *   distribution, for details about the copyright.
+ *
+ */
+
+#ifndef _NIM_SIGNALS_H_
+#define _NIM_SIGNALS_H_
+
+#include <libc/component.h>
+#include <base/signal.h>
+#include <util/reconstructible.h>
+
+// Symbol for calling back into Nim
+extern "C" void nimHandleSignal(void *arg);
+
+namespace Nim { struct SignalHandler; }
+
+struct Nim::SignalHandler
+{
+	// Pointer to the Nim handler object.
+	void *arg;
+
+	void handle_signal() {
+		Libc::with_libc([this] () { nimHandleSignal(arg); }); }
+
+	Genode::Signal_handler<SignalHandler> handler;
+
+	SignalHandler(Genode::Entrypoint *ep, void *arg)
+	: arg(arg), handler(*ep, *this, &SignalHandler::handle_signal) { }
+
+	Genode::Signal_context_capability cap() { return handler; }
+};
+
+#endif
diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim
deleted file mode 100644
index 9a98cb9c5..000000000
--- a/lib/impure/db_mysql.nim
+++ /dev/null
@@ -1,425 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `mySQL`:idx: database wrapper. The same interface is
-## implemented for other databases too.
-##
-## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_postgres <db_postgres.html>`_.
-##
-## Parameter substitution
-## ======================
-##
-## All `db_*` modules support the same form of parameter substitution.
-## That is, using the `?` (question mark) to signify the place where a
-## value should be placed. For example:
-##   ```
-##   sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##   ```
-##
-## Examples
-## ========
-##
-## Opening a connection to a database
-## ----------------------------------
-##
-##   ```
-##   import std/db_mysql
-##   let db = open("localhost", "user", "password", "dbname")
-##   db.close()
-##   ```
-##
-## Creating a table
-## ----------------
-##
-##   ```
-##   db.exec(sql"DROP TABLE IF EXISTS myTable")
-##   db.exec(sql("""CREATE TABLE myTable (
-##                    id integer,
-##                    name varchar(50) not null)"""))
-##   ```
-##
-## Inserting data
-## --------------
-##
-##   ```
-##   db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##           "Dominik")
-##   ```
-##
-## Larger example
-## --------------
-##
-##   ```
-##   import std/[db_mysql, math]
-##
-##   let theDb = open("localhost", "nim", "nim", "test")
-##
-##   theDb.exec(sql"Drop table if exists myTestTbl")
-##   theDb.exec(sql("create table myTestTbl (" &
-##       " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
-##       " Name  VARCHAR(50) NOT NULL, " &
-##       " i     INT(11), " &
-##       " f     DECIMAL(18,10))"))
-##
-##   theDb.exec(sql"START TRANSACTION")
-##   for i in 1..1000:
-##     theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##           "Item#" & $i, i, sqrt(i.float))
-##   theDb.exec(sql"COMMIT")
-##
-##   for x in theDb.fastRows(sql"select * from myTestTbl"):
-##     echo x
-##
-##   let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##           "Item#1001", 1001, sqrt(1001.0))
-##   echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
-##
-##   theDb.close()
-##   ```
-
-
-import strutils, mysql
-
-import db_common
-export db_common
-
-import std/private/since
-
-type
-  DbConn* = distinct PMySQL ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = object ## a handle that can be used to get a row's
-                       ## column text on demand
-    row: cstringArray
-    len: int
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises a DbError exception.
-  var e: ref DbError
-  new(e)
-  e.msg = $mysql.error(PMySQL db)
-  raise e
-
-when false:
-  proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
-    var stmt = mysql_stmt_init(db)
-    if stmt == nil: dbError(db)
-    if mysql_stmt_prepare(stmt, query, len(query)) != 0:
-      dbError(db)
-    var
-      binding: seq[MYSQL_BIND]
-    discard mysql_stmt_close(stmt)
-
-proc dbQuote*(s: string): string =
-  ## DB quotes the string. Note that this doesn't escape `%` and `_`.
-  result = newStringOfCap(s.len + 2)
-  result.add "'"
-  for c in items(s):
-    # see https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#mysql-escaping
-    case c
-    of '\0': result.add "\\0"
-    of '\b': result.add "\\b"
-    of '\t': result.add "\\t"
-    of '\l': result.add "\\n"
-    of '\r': result.add "\\r"
-    of '\x1a': result.add "\\Z"
-    of '"': result.add "\\\""
-    of '\'': result.add "\\'"
-    of '\\': result.add "\\\\"
-    else: result.add c
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
-
-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.
-  var q = dbFormat(query, args)
-  return mysql.real_query(PMySQL db, q.cstring, q.len) == 0'i32
-
-proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
-  var q = dbFormat(query, args)
-  if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db)
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query and raises EDB if not successful.
-  var q = dbFormat(query, args)
-  if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
-  if row != nil:
-    while mysql.fetch_row(sqlres) != nil: discard
-  mysql.freeResult(sqlres)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous.  Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## Breaking the fastRows() iterator during a loop will cause the next
-  ## database query to raise an `EDb` exception `Commands out of sync`.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var
-      L = int(mysql.numFields(sqlres))
-      row: cstringArray
-      result: Row
-      backup: Row
-    newSeq(result, L)
-    while true:
-      row = mysql.fetch_row(sqlres)
-      if row == nil: break
-      for i in 0..L-1:
-        setLen(result[i], 0)
-        result[i].add row[i]
-      yield result
-    properFreeResult(sqlres, row)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## Same as fastRows but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within the iterator body.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    let L = int(mysql.numFields(sqlres))
-    var row: cstringArray
-    while true:
-      row = mysql.fetch_row(sqlres)
-      if row == nil: break
-      yield InstantRow(row: row, len: L)
-    properFreeResult(sqlres, row)
-
-proc setTypeName(t: var DbType; f: PFIELD) =
-  t.name = $f.name
-  t.maxReprLen = Natural(f.max_length)
-  if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
-  case f.ftype
-  of TYPE_DECIMAL:
-    t.kind = dbDecimal
-  of TYPE_TINY:
-    t.kind = dbInt
-    t.size = 1
-  of TYPE_SHORT:
-    t.kind = dbInt
-    t.size = 2
-  of TYPE_LONG:
-    t.kind = dbInt
-    t.size = 4
-  of TYPE_FLOAT:
-    t.kind = dbFloat
-    t.size = 4
-  of TYPE_DOUBLE:
-    t.kind = dbFloat
-    t.size = 8
-  of TYPE_NULL:
-    t.kind = dbNull
-  of TYPE_TIMESTAMP:
-    t.kind = dbTimestamp
-  of TYPE_LONGLONG:
-    t.kind = dbInt
-    t.size = 8
-  of TYPE_INT24:
-    t.kind = dbInt
-    t.size = 3
-  of TYPE_DATE:
-    t.kind = dbDate
-  of TYPE_TIME:
-    t.kind = dbTime
-  of TYPE_DATETIME:
-    t.kind = dbDatetime
-  of TYPE_YEAR:
-    t.kind = dbDate
-  of TYPE_NEWDATE:
-    t.kind = dbDate
-  of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
-    t.kind = dbVarchar
-  of TYPE_BIT:
-    t.kind = dbBit
-  of TYPE_NEWDECIMAL:
-    t.kind = dbDecimal
-  of TYPE_ENUM: t.kind = dbEnum
-  of TYPE_SET: t.kind = dbSet
-  of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
-     TYPE_BLOB: t.kind = dbBlob
-  of TYPE_GEOMETRY:
-    t.kind = dbGeometry
-
-proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
-  setLen(columns, L)
-  for i in 0..<L:
-    let fp = mysql.fetch_field_direct(res, cint(i))
-    setTypeName(columns[i].typ, fp)
-    columns[i].name = $fp.name
-    columns[i].tableName = $fp.table
-    columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
-    #columns[i].foreignKey = there is no such thing in mysql
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
-                      args: varargs[string, `$`]): InstantRow =
-  ## Same as fastRows but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within the iterator body.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    let L = int(mysql.numFields(sqlres))
-    setColumnInfo(columns, sqlres, L)
-    var row: cstringArray
-    while true:
-      row = mysql.fetch_row(sqlres)
-      if row == nil: break
-      yield InstantRow(row: row, len: L)
-    properFreeResult(sqlres, row)
-
-
-proc `[]`*(row: InstantRow, col: int): string {.inline.} =
-  ## Returns text for given column of the row.
-  $row.row[col]
-
-proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
-  ## Return cstring of given column of the row
-  row.row[index]
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## Returns number of columns in the row.
-  row.len
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var L = int(mysql.numFields(sqlres))
-    result = newRow(L)
-    var row = mysql.fetch_row(sqlres)
-    if row != nil:
-      for i in 0..L-1:
-        setLen(result[i], 0)
-        add(result[i], row[i])
-    properFreeResult(sqlres, row)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the whole result dataset.
-  result = @[]
-  rawExec(db, query, args)
-  var sqlres = mysql.useResult(PMySQL db)
-  if sqlres != nil:
-    var L = int(mysql.numFields(sqlres))
-    var row: cstringArray
-    var j = 0
-    while true:
-      row = mysql.fetch_row(sqlres)
-      if row == nil: break
-      setLen(result, j+1)
-      newSeq(result[j], L)
-      for i in 0..L-1:
-        result[j][i] = $row[i]
-      inc(j)
-    mysql.freeResult(sqlres)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, query, args)): yield r
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  result = getRow(db, query, args)[0]
-
-proc tryInsertId*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  var q = dbFormat(query, args)
-  if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32:
-    result = -1'i64
-  else:
-    result = mysql.insertId(PMySQL db)
-
-proc insertId*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
-                args: varargs[string, `$`]): int64
-               {.tags: [WriteDbEffect], raises: [], since: (1, 3).} =
-  ## same as tryInsertID
-  tryInsertID(db, query, args)
-
-proc insert*(db: DbConn, query: SqlQuery, pkName: string,
-             args: varargs[string, `$`]): int64
-            {.tags: [WriteDbEffect], since: (1, 3).} =
-  ## same as insertId
-  result = tryInsert(db, query,pkName, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-                       tags: [ReadDbEffect, WriteDbEffect].} =
-  ## runs the query (typically "UPDATE") and returns the
-  ## number of affected rows
-  rawExec(db, query, args)
-  result = mysql.affectedRows(PMySQL db)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## closes the database connection.
-  if PMySQL(db) != nil: mysql.close(PMySQL db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  var res = mysql.init(nil)
-  if res == nil: dbError("could not open database connection")
-  let
-    colonPos = connection.find(':')
-    host = if colonPos < 0: connection
-           else: substr(connection, 0, colonPos-1)
-    port: int32 = if colonPos < 0: 0'i32
-                  else: substr(connection, colonPos+1).parseInt.int32
-  if mysql.realConnect(res, host.cstring, user, password, database,
-                       port, nil, 0) == nil:
-    var errmsg = $mysql.error(res)
-    mysql.close(res)
-    dbError(errmsg)
-  result = DbConn(res)
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  result = mysql.set_character_set(PMySQL connection, encoding) == 0
diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim
deleted file mode 100644
index da1b1e9b5..000000000
--- a/lib/impure/db_odbc.nim
+++ /dev/null
@@ -1,532 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Nim Contributors
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `ODBC` database wrapper.
-##
-## This is the same interface that is implemented for other databases.
-##
-## This has NOT yet been (extensively) tested against ODBC drivers for
-## Teradata, Oracle, Sybase, MSSqlvSvr, et. al.  databases.
-##
-## Currently all queries are ANSI calls, not Unicode.
-##
-## See also: `db_postgres <db_postgres.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_mysql <db_mysql.html>`_.
-##
-## Parameter substitution
-## ======================
-##
-## All `db_*` modules support the same form of parameter substitution.
-## That is, using the `?` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-##
-## Examples
-## ========
-##
-## Opening a connection to a database
-## ----------------------------------
-##
-## .. code-block:: Nim
-##     import std/db_odbc
-##     var db = open("localhost", "user", "password", "dbname")
-##     db.close()
-##
-## Creating a table
-## ----------------
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## --------------
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Andreas")
-##
-## Large example
-## -------------
-##
-## .. code-block:: Nim
-##
-##  import std/[db_odbc, math]
-##
-##  var theDb = open("localhost", "nim", "nim", "test")
-##
-##  theDb.exec(sql"Drop table if exists myTestTbl")
-##  theDb.exec(sql("create table myTestTbl (" &
-##      " Id    INT(11)     NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
-##      " Name  VARCHAR(50) NOT NULL, " &
-##      " i     INT(11), " &
-##      " f     DECIMAL(18,10))"))
-##
-##  theDb.exec(sql"START TRANSACTION")
-##  for i in 1..1000:
-##    theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#" & $i, i, sqrt(i.float))
-##  theDb.exec(sql"COMMIT")
-##
-##  for x in theDb.fastRows(sql"select * from myTestTbl"):
-##    echo x
-##
-##  let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
-##          "Item#1001", 1001, sqrt(1001.0))
-##  echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
-##
-##  theDb.close()
-
-import strutils, odbcsql
-import db_common
-export db_common
-
-import std/private/since
-
-type
-  OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
-  DbConn* = OdbcConnTyp    ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = tuple[row: seq[string], len: int]  ## a handle that can be
-                                                    ## used to get a row's
-                                                    ## column text on demand
-
-var
-  buf: array[0..4096, char]
-
-proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
-          tags: [WriteDbEffect], raises: [].} =
-  try:
-    discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
-    sqlres = nil
-  except: discard
-
-proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
-          tags: [ReadDbEffect], raises: [].} =
-  ## Returns ODBC error information
-  var
-    sqlState: array[0..512, char]
-    nativeErr: array[0..512, char]
-    errMsg: array[0..512, char]
-    retSz: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  try:
-    sqlState[0] = '\0'
-    nativeErr[0] = '\0'
-    errMsg[0] = '\0'
-    res = SQLErr(db.env, db.hDb, db.stmt,
-              cast[PSQLCHAR](sqlState.addr),
-              cast[PSQLCHAR](nativeErr.addr),
-              cast[PSQLCHAR](errMsg.addr),
-              511.TSqlSmallInt, retSz.addr)
-  except:
-    discard
-  return (res.int, $(addr sqlState), $(addr nativeErr), $(addr errMsg))
-
-proc dbError*(db: var DbConn) {.
-          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
-  ## Raises an `[DbError]` exception with ODBC error information
-  var
-    e: ref DbError
-    ss, ne, msg: string = ""
-    isAnError = false
-    res: int = 0
-    prevSs = ""
-  while true:
-    prevSs = ss
-    (res, ss, ne, msg) = db.getErrInfo()
-    if prevSs == ss:
-      break
-    # sqlState of 00000 is not an error
-    elif ss == "00000":
-      break
-    elif ss == "01000":
-      echo "\nWarning: ", ss, " ", msg
-      continue
-    else:
-      isAnError = true
-      echo "\nError: ", ss, " ", msg
-  if isAnError:
-    new(e)
-    e.msg = "ODBC Error"
-    if db.stmt != nil:
-      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-    properFreeResult(SQL_HANDLE_DBC, db.hDb)
-    properFreeResult(SQL_HANDLE_ENV, db.env)
-    raise e
-
-proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
-  ## Wrapper that raises `EDb` if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA
-  if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db)
-
-proc sqlGetDBMS(db: var DbConn): string {.
-        tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
-  ## Returns the ODBC SQL_DBMS_NAME string
-  const
-    SQL_DBMS_NAME = 17.SqlUSmallInt
-  var
-    sz: TSqlSmallInt = 0
-  buf[0] = '\0'
-  try:
-    db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
-                        4095.TSqlSmallInt, sz.addr))
-  except: discard
-  return $(addr buf)
-
-proc dbQuote*(s: string): string {.noSideEffect.} =
-  ## DB quotes the string.
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
-                  noSideEffect.} =
-  ## Replace any `?` placeholders with `args`,
-  ## and quotes the arguments
-  result = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      add(result, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(result, c)
-
-proc prepareFetch(db: var DbConn, query: SqlQuery,
-                args: varargs[string, `$`]): TSqlSmallInt {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  # Prepare a statement, execute it and fetch the data to the driver
-  # ready for retrieval of the data
-  # Used internally by iterators and retrieval procs
-  # requires calling
-  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  # when finished
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  db.sqlCheck(SQLExecute(db.stmt))
-  result = SQLFetch(db.stmt)
-  db.sqlCheck(result)
-
-proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
-                args: varargs[string, `$`]) {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  # Prepare a statement, execute it and fetch the data to the driver
-  # ready for retrieval of the data
-  # Used internally by iterators and retrieval procs
-  # requires calling
-  #      properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  # when finished
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  db.sqlCheck(SQLFetch(db.stmt))
-
-proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Tries to execute the query and returns true if successful, false otherwise.
-  var
-    res:TSqlSmallInt = -1
-  try:
-    db.prepareFetchDirect(query, args)
-    var
-      rCnt:TSqlLen = -1
-    res = SQLRowCount(db.stmt, rCnt)
-    properFreeResult(SQL_HANDLE_STMT, db.stmt)
-    if res != SQL_SUCCESS: dbError(db)
-  except: discard
-  return res == SQL_SUCCESS
-
-proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  db.prepareFetchDirect(query, args)
-
-proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query and raises EDB if not successful.
-  db.prepareFetchDirect(query, args)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-
-proc newRow(L: int): Row {.noSideEFfect.} =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-iterator fastRows*(db: var DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.
-                tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous.  Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## Breaking the fastRows() iterator during a loop may cause a driver error
-  ## for subsequent queries
-  ##
-  ## Rows are retrieved from the server at each iteration.
-  var
-    rowRes: Row
-    sz: TSqlLen = 0
-    cCnt: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    discard
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095, sz.addr))
-        rowRes[colId-1] = $(addr buf)
-      yield rowRes
-      res = SQLFetch(db.stmt)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-iterator instantRows*(db: var DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                {.tags: [ReadDbEffect, WriteDbEffect].} =
-  ## Same as fastRows but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within the iterator body.
-  var
-    rowRes: Row = @[]
-    sz: TSqlLen = 0
-    cCnt: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    discard
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095, sz.addr))
-        rowRes[colId-1] = $(addr buf)
-      yield (row: rowRes, len: cCnt.int)
-      res = SQLFetch(db.stmt)
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-proc `[]`*(row: InstantRow, col: int): string {.inline.} =
-  ## Returns text for given column of the row
-  $row.row[col]
-
-proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
-  ## Return cstring of given column of the row
-  row.row[index].cstring
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## Returns number of columns in the row
-  row.len
-
-proc getRow*(db: var DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.
-          tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  var
-    rowRes: Row
-    sz: TSqlLen = 0
-    cCnt: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    result = @[]
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    for colId in 1..cCnt:
-      buf[0] = '\0'
-      db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                               cast[cstring](buf.addr), 4095, sz.addr))
-      rowRes[colId-1] = $(addr buf)
-    res = SQLFetch(db.stmt)
-    result = rowRes
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-proc getAllRows*(db: var DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.
-           tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
-  ## Executes the query and returns the whole result dataset.
-  var
-    rows: seq[Row] = @[]
-    rowRes: Row
-    sz: TSqlLen = 0
-    cCnt: TSqlSmallInt = 0
-    res: TSqlSmallInt = 0
-  res = db.prepareFetch(query, args)
-  if res == SQL_NO_DATA:
-    result = @[]
-  elif res == SQL_SUCCESS:
-    res = SQLNumResultCols(db.stmt, cCnt)
-    rowRes = newRow(cCnt)
-    rowRes.setLen(max(cCnt,0))
-    while res == SQL_SUCCESS:
-      for colId in 1..cCnt:
-        buf[0] = '\0'
-        db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
-                                 cast[cstring](buf.addr), 4095, sz.addr))
-        rowRes[colId-1] = $(addr buf)
-      rows.add(rowRes)
-      res = SQLFetch(db.stmt)
-    result = rows
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  db.sqlCheck(res)
-
-iterator rows*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.
-         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Same as `fastRows`, but slower and safe.
-  ##
-  ## This retrieves ALL rows into memory before
-  ## iterating through the rows.
-  ## Large dataset queries will impact on memory usage.
-  for r in items(getAllRows(db, query, args)): yield r
-
-proc getValue*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.
-           tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  result = ""
-  try:
-    result = getRow(db, query, args)[0]
-  except: discard
-
-proc tryInsertId*(db: var DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.
-            tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  if not tryExec(db, query, args):
-    result = -1'i64
-  else:
-    result = -1'i64
-    try:
-      case sqlGetDBMS(db).toLower():
-      of "postgresql":
-        result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
-      of "mysql":
-        result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
-      of "sqlite":
-        result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
-      of "microsoft sql server":
-        result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
-      of "oracle":
-        result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
-      else: result = -1'i64
-    except: discard
-
-proc insertId*(db: var DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.
-         tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc tryInsert*(db: var DbConn, query: SqlQuery,pkName: string,
-                args: varargs[string, `$`]): int64
-               {.tags: [ReadDbEffect, WriteDbEffect], raises: [], since: (1, 3).} =
-  ## same as tryInsertID
-  tryInsertID(db, query, args)
-
-proc insert*(db: var DbConn, query: SqlQuery, pkName: string,
-             args: varargs[string, `$`]): int64 
-            {.tags: [ReadDbEffect, WriteDbEffect], since: (1, 3).} =
-  ## same as insertId
-  result = tryInsert(db, query,pkName, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: var DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-             tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Runs the query (typically "UPDATE") and returns the
-  ## number of affected rows
-  result = -1
-  db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle))
-  var q = dbFormat(query, args)
-  db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
-  rawExec(db, query, args)
-  var rCnt:TSqlLen = -1
-  db.sqlCheck(SQLRowCount(db.hDb, rCnt))
-  properFreeResult(SQL_HANDLE_STMT, db.stmt)
-  result = rCnt.int64
-
-proc close*(db: var DbConn) {.
-      tags: [WriteDbEffect], raises: [].} =
-  ## Closes the database connection.
-  if db.hDb != nil:
-    try:
-      var res = SQLDisconnect(db.hDb)
-      if db.stmt != nil:
-        res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
-      res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
-      res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
-      db = (hDb: nil, env: nil, stmt: nil)
-    except:
-      discard
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Opens a database connection.
-  ##
-  ## Raises `EDb` if the connection could not be established.
-  ##
-  ## Currently the database parameter is ignored,
-  ## but included to match `open()` in the other db_xxxxx library modules.
-  var
-    val = SQL_OV_ODBC3
-    resLen = 0
-  result = (hDb: nil, env: nil, stmt: nil)
-  # allocate environment handle
-  var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
-  if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
-  res = SQLSetEnvAttr(result.env,
-                      SQL_ATTR_ODBC_VERSION.TSqlInteger,
-                      cast[SqlPointer](val), resLen.TSqlInteger)
-  if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
-  # allocate hDb handle
-  res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
-  if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
-
-  # Connect: connection = dsn str,
-  res = SQLConnect(result.hDb,
-                  connection.PSQLCHAR , connection.len.TSqlSmallInt,
-                  user.PSQLCHAR, user.len.TSqlSmallInt,
-                  password.PSQLCHAR, password.len.TSqlSmallInt)
-  if res != SQL_SUCCESS:
-    result.dbError()
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
-  ## Currently not implemented for ODBC.
-  ##
-  ## Sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  ##result = set_character_set(connection, encoding) == 0
-  dbError("setEncoding() is currently not implemented by the db_odbc module")
diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim
deleted file mode 100644
index e629b7945..000000000
--- a/lib/impure/db_postgres.nim
+++ /dev/null
@@ -1,647 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `PostgreSQL`:idx: database wrapper. This interface
-## is implemented for other databases also.
-##
-## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
-## `db_mysql <db_mysql.html>`_.
-##
-## Parameter substitution
-## ======================
-##
-## All `db_*` modules support the same form of parameter substitution.
-## That is, using the `?` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##     sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
-##
-## **Note**: There are two approaches to parameter substitution support by
-## this module.
-##
-## 1. `SqlQuery` using `?, ?, ?, ...` (same as all the `db_*` modules)
-##
-## 2. `SqlPrepared` using `$1, $2, $3, ...`
-##
-## .. code-block:: Nim
-##   prepare(db, "myExampleInsert",
-##           sql"""INSERT INTO myTable
-##                 (colA, colB, colC)
-##                 VALUES ($1, $2, $3)""",
-##           3)
-##
-##
-## Unix Socket
-## ===========
-##
-## Using Unix sockets instead of TCP connection can
-## `improve performance up to 30% ~ 175% for some operations <https://momjian.us/main/blogs/pgblog/2012.html#June_6_2012>`_.
-##
-## To use Unix sockets with `db_postgres`, change the server address to the socket file path:
-##
-## .. code-block:: Nim
-##   import std/db_postgres ## Change "localhost" or "127.0.0.1" to the socket file path
-##   let db = db_postgres.open("/run/postgresql", "user", "password", "database")
-##   echo db.getAllRows(sql"SELECT version();")
-##   db.close()
-##
-## The socket file path is operating system specific and distribution specific,
-## additional configuration may or may not be needed on your `postgresql.conf`.
-## The Postgres server must be on the same computer and only works for Unix-like operating systems.
-##
-##
-## Examples
-## ========
-##
-## Opening a connection to a database
-## ----------------------------------
-##
-## .. code-block:: Nim
-##     import std/db_postgres
-##     let db = open("localhost", "user", "password", "dbname")
-##     db.close()
-##
-## Creating a table
-## ----------------
-##
-## .. code-block:: Nim
-##      db.exec(sql"DROP TABLE IF EXISTS myTable")
-##      db.exec(sql("""CREATE TABLE myTable (
-##                       id integer,
-##                       name varchar(50) not null)"""))
-##
-## Inserting data
-## --------------
-##
-## .. code-block:: Nim
-##     db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-##             "Dominik")
-import strutils, postgres
-
-import db_common
-export db_common
-
-import std/private/since
-
-type
-  DbConn* = PPGconn    ## encapsulates a database connection
-  Row* = seq[string]   ## a row of a dataset. NULL database values will be
-                       ## converted to nil.
-  InstantRow* = object ## a handle that can be
-    res: PPGresult     ## used to get a row's
-  SqlPrepared* = distinct string ## a identifier for the prepared queries
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## raises a DbError exception.
-  var e: ref DbError
-  new(e)
-  e.msg = $pqErrorMessage(db)
-  raise e
-
-proc dbQuote*(s: string): string =
-  ## DB quotes the string.
-  result = "'"
-  for c in items(s):
-    case c
-    of '\'': add(result, "''")
-    of '\0': add(result, "\\0")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  result = ""
-  var a = 0
-  if args.len > 0 and not string(formatstr).contains("?"):
-    dbError("""parameter substitution expects "?" """)
-  if args.len == 0:
-    return string(formatstr)
-  else:
-    for c in items(string(formatstr)):
-      if c == '?':
-        add(result, dbQuote(args[a]))
-        inc(a)
-      else:
-        add(result, c)
-
-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.
-  var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil,
-                        nil, nil, 0)
-  result = pqresultStatus(res) == PGRES_COMMAND_OK
-  pqclear(res)
-
-proc tryExec*(db: DbConn, stmtName: SqlPrepared,
-              args: varargs[string, `$`]): bool {.tags: [
-              ReadDbEffect, WriteDbEffect].} =
-  ## tries to execute the query and returns true if successful, false otherwise.
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  result = pqresultStatus(res) == PGRES_COMMAND_OK
-  pqclear(res)
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## executes the query and raises EDB if not successful.
-  var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil,
-                        nil, nil, 0)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  pqclear(res)
-
-proc exec*(db: DbConn, stmtName: SqlPrepared,
-          args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  pqclear(res)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-proc setupQuery(db: DbConn, query: SqlQuery,
-                args: varargs[string]): PPGresult =
-  result = pqexec(db, dbFormat(query, args).cstring)
-  if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-
-proc setupQuery(db: DbConn, stmtName: SqlPrepared,
-                 args: varargs[string]): PPGresult =
-  var arr = allocCStringArray(args)
-  result = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
-                          nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
-
-proc setupSingeRowQuery(db: DbConn, query: SqlQuery,
-                        args: varargs[string]) =
-  if pqsendquery(db, dbFormat(query, args).cstring) != 1:
-    dbError(db)
-  if pqSetSingleRowMode(db) != 1:
-    dbError(db)
-
-proc setupSingeRowQuery(db: DbConn, stmtName: SqlPrepared,
-                       args: varargs[string]) =
-  var arr = allocCStringArray(args)
-  if pqsendqueryprepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) != 1:
-    dbError(db)
-  if pqSetSingleRowMode(db) != 1:
-    dbError(db)
-  deallocCStringArray(arr)
-
-proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
-              nParams: int): SqlPrepared =
-  ## Creates a new `SqlPrepared` statement. Parameter substitution is done
-  ## via `$1`, `$2`, `$3`, etc.
-  if nParams > 0 and not string(query).contains("$1"):
-    dbError("parameter substitution expects \"$1\"")
-  var res = pqprepare(db, stmtName, query.cstring, int32(nParams), nil)
-  if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  return SqlPrepared(stmtName)
-
-proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
-  for col in 0'i32..cols-1:
-    setLen(r[col], 0)
-    let x = pqgetvalue(res, line, col)
-    if x.isNil:
-      r[col] = ""
-    else:
-      add(r[col], x)
-
-template fetchRows(db: DbConn): untyped =
-  var res: PPGresult = nil
-  while true:
-    res = pqgetresult(db)
-    if res == nil:
-      break
-    let status = pqresultStatus(res)
-    if status == PGRES_TUPLES_OK:
-      discard
-    elif status != PGRES_SINGLE_TUPLE:
-      dbError(db)
-    else:
-      let L = pqNfields(res)
-      var result = newRow(L)
-      setRow(res, result, 0, L)
-      yield result
-    pqclear(res)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the query and iterates over the result dataset. This is very
-  ## fast, but potentially dangerous: If the for-loop-body executes another
-  ## query, the results can be undefined. For Postgres it is safe though.
-  setupSingeRowQuery(db, query, args)
-  fetchRows(db)
-
-iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## executes the query and iterates over the result dataset. This is very
-  ## fast, but potentially dangerous: If the for-loop-body executes another
-  ## query, the results can be undefined. For Postgres it is safe though.
-  setupSingeRowQuery(db, stmtName, args)
-  fetchRows(db)
-
-template fetchinstantRows(db: DbConn): untyped =
-  var res: PPGresult = nil
-  while true:
-    res = pqgetresult(db)
-    if res == nil:
-      break
-    let status = pqresultStatus(res)
-    if status == PGRES_TUPLES_OK:
-     discard
-    elif status != PGRES_SINGLE_TUPLE:
-      dbError(db)
-    else:
-      yield InstantRow(res: res)
-    pqclear(res)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within iterator body.
-  setupSingeRowQuery(db, query, args)
-  fetchinstantRows(db)
-
-iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## same as fastRows but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within iterator body.
-  setupSingeRowQuery(db, stmtName, args)
-  fetchinstantRows(db)
-
-proc getColumnType(res: PPGresult, col: int) : DbType =
-  ## returns DbType for given column in the row
-  ## defined in pg_type.h file in the postgres source code
-  ## Wire representation for types: http://www.npgsql.org/dev/types.html
-  var oid = pqftype(res, int32(col))
-  ## The integer returned is the internal OID number of the type
-  case oid
-  of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool")
-  of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea")
-
-  of 21:   return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2)
-  of 23:   return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4)
-  of 20:   return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8)
-  of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")
-  of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit")
-
-  of 18:   return DbType(kind: DbTypeKind.dbFixedChar, name: "char")
-  of 19:   return DbType(kind: DbTypeKind.dbFixedChar, name: "name")
-  of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar")
-
-  of 25:   return DbType(kind: DbTypeKind.dbVarchar, name: "text")
-  of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar")
-  of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring")
-
-  of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4")
-  of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8")
-
-  of 790:  return DbType(kind: DbTypeKind.dbDecimal, name: "money")
-  of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric")
-
-  of 704:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval")
-  of 702:  return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime")
-  of 703:  return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime")
-  of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date")
-  of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time")
-  of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp")
-  of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz")
-  of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval")
-  of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz")
-
-  of 114:  return DbType(kind: DbTypeKind.dbJson, name: "json")
-  of 142:  return DbType(kind: DbTypeKind.dbXml, name: "xml")
-  of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb")
-
-  of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point")
-  of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg")
-  of 602: return DbType(kind: DbTypeKind.dbPath, name: "path")
-  of 603: return DbType(kind: DbTypeKind.dbBox, name: "box")
-  of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon")
-  of 628: return DbType(kind: DbTypeKind.dbLine, name: "line")
-  of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")
-
-  of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr")
-  of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr")
-  of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet")
-
-  of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid")
-  of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector")
-  of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery")
-  of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot")
-
-  of 27:   return DbType(kind: DbTypeKind.dbComposite, name: "tid")
-  of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor")
-  of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record")
-  of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range")
-  of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange")
-  of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange")
-  of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange")
-  of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange")
-  of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range")
-
-  of 22:   return DbType(kind: DbTypeKind.dbArray, name: "int2vector")
-  of 30:   return DbType(kind: DbTypeKind.dbArray, name: "oidvector")
-  of 143:  return DbType(kind: DbTypeKind.dbArray, name: "xml[]")
-  of 199:  return DbType(kind: DbTypeKind.dbArray, name: "json[]")
-  of 629:  return DbType(kind: DbTypeKind.dbArray, name: "line[]")
-  of 651:  return DbType(kind: DbTypeKind.dbArray, name: "cidr[]")
-  of 719:  return DbType(kind: DbTypeKind.dbArray, name: "circle[]")
-  of 791:  return DbType(kind: DbTypeKind.dbArray, name: "money[]")
-  of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]")
-  of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]")
-  of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]")
-  of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]")
-  of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]")
-  of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]")
-  of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]")
-  of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]")
-  of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]")
-  of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]")
-  of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]")
-  of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]")
-  of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]")
-  of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]")
-  of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]")
-  of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]")
-  of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]")
-  of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]")
-  of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]")
-  of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]")
-  of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]")
-  of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]")
-  of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]")
-  of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]")
-  of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]")
-  of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]")
-  of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]")
-  of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]")
-  of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]")
-  of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]")
-  of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]")
-  of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]")
-  of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]")
-  of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]")
-  of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]")
-  of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]")
-  of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]")
-  of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]")
-  of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]")
-  of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]")
-  of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]")
-  of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]")
-  of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]")
-  of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]")
-  of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]")
-  of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]")
-  of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]")
-  of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]")
-  of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]")
-  of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]")
-  of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]")
-  of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]")
-
-  of 705:  return DbType(kind: DbTypeKind.dbUnknown, name: "unknown")
-  else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced.
-
-proc setColumnInfo(columns: var DbColumns; res: PPGresult, L: int32) =
-  setLen(columns, L)
-  for i in 0'i32..<L:
-    columns[i].name = $pqfname(res, i)
-    columns[i].typ = getColumnType(res, i)
-    columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched.
-                                               ## Query the system table pg_class to determine exactly which table is referenced.
-    #columns[i].primaryKey = libpq does not have a function for that
-    #columns[i].foreignKey = libpq does not have a function for that
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  setupSingeRowQuery(db, query, args)
-  var res: PPGresult = nil
-  var colsObtained = false
-  while true:
-    res = pqgetresult(db)
-    if not colsObtained:
-      setColumnInfo(columns, res, pqnfields(res))
-      colsObtained = true
-    if res == nil:
-      break
-    let status = pqresultStatus(res)
-    if status == PGRES_TUPLES_OK:
-      discard
-    elif status != PGRES_SINGLE_TUPLE:
-      dbError(db)
-    else:
-      yield InstantRow(res: res)
-    pqclear(res)
-
-proc `[]`*(row: InstantRow; col: int): string {.inline.} =
-  ## returns text for given column of the row
-  $pqgetvalue(row.res, int32(0), int32(col))
-
-proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
-  ## Return cstring of given column of the row
-  pqgetvalue(row.res, int32(0), int32(index))
-
-proc len*(row: InstantRow): int {.inline.} =
-  ## returns number of columns in the row
-  int(pqNfields(row.res))
-
-proc getRow(res: PPGresult): Row =
-  let L = pqnfields(res)
-  result = newRow(L)
-  if pqntuples(res) > 0:
-    setRow(res, result, 0, L)
-  pqclear(res)
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a Row with empty strings for each column.
-  let res = setupQuery(db, query, args)
-  getRow(res)
-
-proc getRow*(db: DbConn, stmtName: SqlPrepared,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  let res = setupQuery(db, stmtName, args)
-  getRow(res)
-
-proc getAllRows(res: PPGresult): seq[Row] =
-  let N = pqntuples(res)
-  let L = pqnfields(res)
-  result = newSeqOfCap[Row](N)
-  var row = newRow(L)
-  for i in 0'i32..N-1:
-    setRow(res, row, i, L)
-    result.add(row)
-  pqclear(res)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.
-                 tags: [ReadDbEffect].} =
-  ## executes the query and returns the whole result dataset.
-  let res = setupQuery(db, query, args)
-  getAllRows(res)
-
-proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
-                 args: varargs[string, `$`]): seq[Row] {.tags:
-                 [ReadDbEffect].} =
-  ## executes the prepared query and returns the whole result dataset.
-  let res = setupQuery(db, stmtName, args)
-  getAllRows(res)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, query, args)): yield r
-
-iterator rows*(db: DbConn, stmtName: SqlPrepared,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## same as `fastRows`, but slower and safe.
-  for r in items(getAllRows(db, stmtName, args)): yield r
-
-proc getValue(res: PPGresult): string =
-  if pqntuples(res) > 0:
-    var x = pqgetvalue(res, 0, 0)
-    result = if isNil(x): "" else: $x
-  else:
-    result = ""
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.
-               tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  let res = setupQuery(db, query, args)
-  getValue(res)
-
-proc getValue*(db: DbConn, stmtName: SqlPrepared,
-               args: varargs[string, `$`]): string {.
-               tags: [ReadDbEffect].} =
-  ## executes the query and returns the first column of the first row of the
-  ## result dataset. Returns "" if the dataset contains no rows or the database
-  ## value is NULL.
-  let res = setupQuery(db, stmtName, args)
-  getValue(res)
-
-proc tryInsertID*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64 {.
-                  tags: [WriteDbEffect].}=
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error. For Postgre this adds
-  ## `RETURNING id` to the query, so it only works if your primary key is
-  ## named `id`.
-  var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
-    args), 0, 0)
-  if not isNil(x):
-    result = parseBiggestInt($x)
-  else:
-    result = -1
-
-proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.
-               tags: [WriteDbEffect].} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row. For Postgre this adds
-  ## `RETURNING id` to the query, so it only works if your primary key is
-  ## named `id`.
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc tryInsert*(db: DbConn, query: SqlQuery,pkName: string,
-                args: varargs[string, `$`]): int64
-               {.tags: [WriteDbEffect], since: (1, 3).}=
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING " & pkName),
-    args), 0, 0)
-  if not isNil(x):
-    result = parseBiggestInt($x)
-  else:
-    result = -1
-
-proc insert*(db: DbConn, query: SqlQuery, pkName: string,
-             args: varargs[string, `$`]): int64
-            {.tags: [WriteDbEffect], since: (1, 3).} =
-  ## executes the query (typically "INSERT") and returns the
-  ## generated ID
-  result = tryInsert(db, query, pkName, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.tags: [
-                       ReadDbEffect, WriteDbEffect].} =
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  var q = dbFormat(query, args)
-  var res = pqExec(db, q.cstring)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($pqcmdTuples(res))
-  pqclear(res)
-
-proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
-                       args: varargs[string, `$`]): int64 {.tags: [
-                       ReadDbEffect, WriteDbEffect].} =
-  ## executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  var arr = allocCStringArray(args)
-  var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
-                           nil, nil, 0)
-  deallocCStringArray(arr)
-  if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
-  result = parseBiggestInt($pqcmdTuples(res))
-  pqclear(res)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## closes the database connection.
-  if db != nil: pqfinish(db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## opens a database connection. Raises `EDb` if the connection could not
-  ## be established.
-  ##
-  ## Clients can also use Postgres keyword/value connection strings to
-  ## connect.
-  ##
-  ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##      con = open("", "", "", "host=localhost port=5432 dbname=mydb")
-  ##
-  ## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
-  ## for more information.
-  let
-    colonPos = connection.find(':')
-    host = if colonPos < 0: connection
-           else: substr(connection, 0, colonPos-1)
-    port = if colonPos < 0: ""
-           else: substr(connection, colonPos+1)
-  result = pqsetdbLogin(host.cstring, port.cstring, nil, nil, database, user, password)
-  if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## sets the encoding of a database connection, returns true for
-  ## success, false for failure.
-  return pqsetClientEncoding(connection, encoding) == 0
-
-
-# Tests are in ../../tests/untestable/tpostgres.
diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim
deleted file mode 100644
index 5e648d097..000000000
--- a/lib/impure/db_sqlite.nim
+++ /dev/null
@@ -1,945 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## A higher level `SQLite`:idx: database wrapper. This interface
-## is implemented for other databases too.
-##
-## Basic usage
-## ===========
-##
-## The basic flow of using this module is:
-##
-## 1. Open database connection
-## 2. Execute SQL query
-## 3. Close database connection
-##
-## Parameter substitution
-## ----------------------
-##
-## All `db_*` modules support the same form of parameter substitution.
-## That is, using the `?` (question mark) to signify the place where a
-## value should be placed. For example:
-##
-## .. code-block:: Nim
-##
-##    sql"INSERT INTO my_table (colA, colB, colC) VALUES (?, ?, ?)"
-##
-## Opening a connection to a database
-## ----------------------------------
-##
-## .. code-block:: Nim
-##
-##    import std/db_sqlite
-##
-##    # user, password, database name can be empty.
-##    # These params are not used on db_sqlite module.
-##    let db = open("mytest.db", "", "", "")
-##    db.close()
-##
-## Creating a table
-## ----------------
-##
-## .. code-block:: Nim
-##
-##    db.exec(sql"DROP TABLE IF EXISTS my_table")
-##    db.exec(sql"""CREATE TABLE my_table (
-##                     id   INTEGER,
-##                     name VARCHAR(50) NOT NULL
-##                  )""")
-##
-## Inserting data
-## --------------
-##
-## .. code-block:: Nim
-##
-##    db.exec(sql"INSERT INTO my_table (id, name) VALUES (0, ?)",
-##            "Jack")
-##
-## Larger example
-## --------------
-##
-## .. code-block:: nim
-##
-##    import std/[db_sqlite, math]
-##
-##    let db = open("mytest.db", "", "", "")
-##
-##    db.exec(sql"DROP TABLE IF EXISTS my_table")
-##    db.exec(sql"""CREATE TABLE my_table (
-##                     id    INTEGER PRIMARY KEY,
-##                     name  VARCHAR(50) NOT NULL,
-##                     i     INT(11),
-##                     f     DECIMAL(18, 10)
-##                  )""")
-##
-##    db.exec(sql"BEGIN")
-##    for i in 1..1000:
-##      db.exec(sql"INSERT INTO my_table (name, i, f) VALUES (?, ?, ?)",
-##              "Item#" & $i, i, sqrt(i.float))
-##    db.exec(sql"COMMIT")
-##
-##    for x in db.fastRows(sql"SELECT * FROM my_table"):
-##      echo x
-##
-##    let id = db.tryInsertId(sql"""INSERT INTO my_table (name, i, f)
-##                                  VALUES (?, ?, ?)""",
-##                            "Item#1001", 1001, sqrt(1001.0))
-##    echo "Inserted item: ", db.getValue(sql"SELECT name FROM my_table WHERE id=?", id)
-##
-##    db.close()
-##
-## Storing binary data example
-##----------------------------
-##
-## .. code-block:: nim
-##
-##   import std/random
-##
-##   ## Generate random float datas
-##   var orig = newSeq[float64](150)
-##   randomize()
-##   for x in orig.mitems:
-##     x = rand(1.0)/10.0
-##
-##   let db = open("mysqlite.db", "", "", "")
-##   block: ## Create database
-##     ## Binary datas needs to be of type BLOB in SQLite
-##     let createTableStr = sql"""CREATE TABLE test(
-##       id INTEGER NOT NULL PRIMARY KEY,
-##       data BLOB
-##     )
-##     """
-##     db.exec(createTableStr)
-##
-##   block: ## Insert data
-##     var id = 1
-##     ## Data needs to be converted to seq[byte] to be interpreted as binary by bindParams
-##     var dbuf = newSeq[byte](orig.len*sizeof(float64))
-##     copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len)
-##
-##     ## Use prepared statement to insert binary data into database
-##     var insertStmt = db.prepare("INSERT INTO test (id, data) VALUES (?, ?)")
-##     insertStmt.bindParams(id, dbuf)
-##     let bres = db.tryExec(insertStmt)
-##     ## Check insert
-##     doAssert(bres)
-##     # Destroy statement
-##     finalize(insertStmt)
-##
-##   block: ## Use getValue to select data
-##     var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
-##     ## Calculate sequence size from buffer size
-##     let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
-##     ## Copy binary string data in dataTest into a seq
-##     var res: seq[float64] = newSeq[float64](seqSize)
-##     copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len)
-##
-##     ## Check datas obtained is identical
-##     doAssert res == orig
-##
-##   db.close()
-##
-##
-## Note
-## ====
-## This module does not implement any ORM features such as mapping the types from the schema.
-## Instead, a `seq[string]` is returned for each row.
-##
-## The reasoning is as follows:
-## 1. it's close to what many DBs offer natively (`char**`:c:)
-## 2. it hides the number of types that the DB supports
-##    (int? int64? decimal up to 10 places? geo coords?)
-## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query)
-##
-## See also
-## ========
-##
-## * `db_odbc module <db_odbc.html>`_ for ODBC database wrapper
-## * `db_mysql module <db_mysql.html>`_ for MySQL database wrapper
-## * `db_postgres module <db_postgres.html>`_ for PostgreSQL database wrapper
-
-{.experimental: "codeReordering".}
-
-import sqlite3, macros
-
-import db_common
-export db_common
-
-import std/private/[since, dbutils]
-when defined(nimPreviewSlimSystem):
-  import std/assertions
-
-type
-  DbConn* = PSqlite3  ## Encapsulates a database connection.
-  Row* = seq[string]  ## A row of a dataset. `NULL` database values will be
-                      ## converted to an empty string.
-  InstantRow* = PStmt ## A handle that can be used to get a row's column
-                      ## text on demand.
-  SqlPrepared* = distinct PStmt ## a identifier for the prepared queries
-
-proc dbError*(db: DbConn) {.noreturn.} =
-  ## Raises a `DbError` exception.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    if not db.tryExec(sql"SELECT * FROM not_exist_table"):
-  ##      dbError(db)
-  ##    db.close()
-  var e: ref DbError
-  new(e)
-  e.msg = $sqlite3.errmsg(db)
-  raise e
-
-proc dbQuote*(s: string): string =
-  ## Escapes the `'` (single quote) char to `''`.
-  ## Because single quote is used for defining `VARCHAR` in SQL.
-  runnableExamples:
-    doAssert dbQuote("'") == "''''"
-    doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'"
-
-  result = "'"
-  for c in items(s):
-    if c == '\'': add(result, "''")
-    else: add(result, c)
-  add(result, '\'')
-
-proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
-  dbFormatImpl(formatstr, dbQuote, args)
-
-proc prepare*(db: DbConn; q: string): SqlPrepared {.since: (1, 3).} =
-  ## Creates a new `SqlPrepared` statement.
-  if prepare_v2(db, q, q.len.cint,result.PStmt, nil) != SQLITE_OK:
-    discard finalize(result.PStmt)
-    dbError(db)
-
-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.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    if not db.tryExec(sql"SELECT * FROM my_table"):
-  ##      dbError(db)
-  ##    db.close()
-  assert(not db.isNil, "Database not connected.")
-  var q = dbFormat(query, args)
-  var stmt: sqlite3.PStmt
-  if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
-    let x = step(stmt)
-    if x in {SQLITE_DONE, SQLITE_ROW}:
-      result = finalize(stmt) == SQLITE_OK
-    else:
-      discard finalize(stmt)
-      result = false
-
-proc tryExec*(db: DbConn, stmtName: SqlPrepared): bool {.
-              tags: [ReadDbEffect, WriteDbEffect].} =
-    let x = step(stmtName.PStmt)
-    if x in {SQLITE_DONE, SQLITE_ROW}:
-      result = true
-    else:
-      discard finalize(stmtName.PStmt)
-      result = false
-
-proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`])  {.
-  tags: [ReadDbEffect, WriteDbEffect].} =
-  ## Executes the query and raises a `DbError` exception if not successful.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    try:
-  ##      db.exec(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
-  ##              1, "item#1")
-  ##    except:
-  ##      stderr.writeLine(getCurrentExceptionMsg())
-  ##    finally:
-  ##      db.close()
-  if not tryExec(db, query, args): dbError(db)
-
-proc newRow(L: int): Row =
-  newSeq(result, L)
-  for i in 0..L-1: result[i] = ""
-
-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.cstring, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
-
-proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3).} =
-  assert(not db.isNil, "Database not connected.")
-  result = stmtName
-
-proc setRow(stmt: PStmt, r: var Row, cols: cint) =
-  for col in 0'i32..cols-1:
-    let cb = column_bytes(stmt, col)
-    setLen(r[col], cb) # set capacity
-    if column_type(stmt, col) == SQLITE_BLOB:
-      copyMem(addr(r[col][0]), column_blob(stmt, col), cb)
-    else:
-      setLen(r[col], 0)
-      let x = column_text(stmt, col)
-      if not isNil(x): add(r[col], x)
-
-iterator fastRows*(db: DbConn, query: SqlQuery,
-                   args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Executes the query and iterates over the result dataset.
-  ##
-  ## This is very fast, but potentially dangerous. Use this iterator only
-  ## if you require **ALL** the rows.
-  ##
-  ## **Note:** Breaking the `fastRows()` iterator during a loop will cause the
-  ## next database query to raise a `DbError` exception `unable to close due
-  ## to ...`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    for row in db.fastRows(sql"SELECT id, name FROM my_table"):
-  ##      echo row
-  ##
-  ##    # Output:
-  ##    # @["1", "item#1"]
-  ##    # @["2", "item#2"]
-  ##
-  ##    db.close()
-  var stmt = setupQuery(db, query, args)
-  var L = (column_count(stmt))
-  var result = newRow(L)
-  try:
-    while step(stmt) == SQLITE_ROW:
-      setRow(stmt, result, L)
-      yield result
-  finally:
-    if finalize(stmt) != SQLITE_OK: dbError(db)
-
-iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row
-                  {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
-  discard setupQuery(db, stmtName)
-  var L = (column_count(stmtName.PStmt))
-  var result = newRow(L)
-  try:
-    while step(stmtName.PStmt) == SQLITE_ROW:
-      setRow(stmtName.PStmt, result, L)
-      yield result
-  except:
-    dbError(db)
-
-iterator instantRows*(db: DbConn, query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_
-  ## but returns a handle that can be used to get column text
-  ## on demand using `[]`. Returned handle is valid only within the iterator body.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    for row in db.instantRows(sql"SELECT * FROM my_table"):
-  ##      echo "id:" & row[0]
-  ##      echo "name:" & row[1]
-  ##      echo "length:" & $len(row)
-  ##
-  ##    # Output:
-  ##    # id:1
-  ##    # name:item#1
-  ##    # length:2
-  ##    # id:2
-  ##    # name:item#2
-  ##    # length:2
-  ##
-  ##    db.close()
-  var stmt = setupQuery(db, query, args)
-  try:
-    while step(stmt) == SQLITE_ROW:
-      yield stmt
-  finally:
-    if finalize(stmt) != SQLITE_OK: dbError(db)
-
-iterator instantRows*(db: DbConn, stmtName: SqlPrepared): InstantRow
-                      {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
-  var stmt = setupQuery(db, stmtName).PStmt
-  try:
-    while step(stmt) == SQLITE_ROW:
-      yield stmt
-  except:
-    dbError(db)
-
-proc toTypeKind(t: var DbType; x: int32) =
-  case x
-  of SQLITE_INTEGER:
-    t.kind = dbInt
-    t.size = 8
-  of SQLITE_FLOAT:
-    t.kind = dbFloat
-    t.size = 8
-  of SQLITE_BLOB: t.kind = dbBlob
-  of SQLITE_NULL: t.kind = dbNull
-  of SQLITE_TEXT: t.kind = dbVarchar
-  else: t.kind = dbUnknown
-
-proc setColumns(columns: var DbColumns; x: PStmt) =
-  let L = column_count(x)
-  setLen(columns, L)
-  for i in 0'i32 ..< L:
-    columns[i].name = $column_name(x, i)
-    columns[i].typ.name = $column_decltype(x, i)
-    toTypeKind(columns[i].typ, column_type(x, i))
-    columns[i].tableName = $column_table_name(x, i)
-
-iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
-                      args: varargs[string, `$`]): InstantRow
-                      {.tags: [ReadDbEffect].} =
-  ## Similar to `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_,
-  ## but sets information about columns to `columns`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    var columns: DbColumns
-  ##    for row in db.instantRows(columns, sql"SELECT * FROM my_table"):
-  ##      discard
-  ##    echo columns[0]
-  ##
-  ##    # Output:
-  ##    # (name: "id", tableName: "my_table", typ: (kind: dbNull,
-  ##    # notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0,
-  ##    # scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false,
-  ##    # foreignKey: false)
-  ##
-  ##    db.close()
-  var stmt = setupQuery(db, query, args)
-  setColumns(columns, stmt)
-  try:
-    while step(stmt) == SQLITE_ROW:
-      yield stmt
-  finally:
-    if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
-  ## Returns text for given column of the row.
-  ##
-  ## See also:
-  ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
-  ##   example code
-  $column_text(row, col)
-
-proc unsafeColumnAt*(row: InstantRow, index: int32): cstring {.inline.} =
-  ## Returns cstring for given column of the row.
-  ##
-  ## See also:
-  ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
-  ##   example code
-  column_text(row, index)
-
-proc len*(row: InstantRow): int32 {.inline.} =
-  ## Returns number of columns in a row.
-  ##
-  ## See also:
-  ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
-  ##   example code
-  column_count(row)
-
-proc getRow*(db: DbConn, query: SqlQuery,
-             args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Retrieves a single row. If the query doesn't return any rows, this proc
-  ## will return a `Row` with empty strings for each column.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    doAssert db.getRow(sql"SELECT id, name FROM my_table"
-  ##                       ) == Row(@["1", "item#1"])
-  ##    doAssert db.getRow(sql"SELECT id, name FROM my_table WHERE id = ?",
-  ##                       2) == Row(@["2", "item#2"])
-  ##
-  ##    # Returns empty.
-  ##    doAssert db.getRow(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
-  ##                       3, "item#3") == @[]
-  ##    doAssert db.getRow(sql"DELETE FROM my_table WHERE id = ?", 3) == @[]
-  ##    doAssert db.getRow(sql"UPDATE my_table SET name = 'ITEM#1' WHERE id = ?",
-  ##                       1) == @[]
-  ##    db.close()
-  var stmt = setupQuery(db, query, args)
-  var L = (column_count(stmt))
-  result = newRow(L)
-  if step(stmt) == SQLITE_ROW:
-    setRow(stmt, result, L)
-  if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc getAllRows*(db: DbConn, query: SqlQuery,
-                 args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
-  ## Executes the query and returns the whole result dataset.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    doAssert db.getAllRows(sql"SELECT id, name FROM my_table") == @[Row(@["1", "item#1"]), Row(@["2", "item#2"])]
-  ##    db.close()
-  result = @[]
-  for r in fastRows(db, query, args):
-    result.add(r)
-
-proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row]
-                {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
-  result = @[]
-  for r in fastRows(db, stmtName):
-    result.add(r)
-
-iterator rows*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
-  ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_,
-  ## but slower and safe.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    for row in db.rows(sql"SELECT id, name FROM my_table"):
-  ##      echo row
-  ##
-  ##    ## Output:
-  ##    ## @["1", "item#1"]
-  ##    ## @["2", "item#2"]
-  ##
-  ##    db.close()
-  for r in fastRows(db, query, args): yield r
-
-iterator rows*(db: DbConn, stmtName: SqlPrepared): Row
-              {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
-  for r in fastRows(db, stmtName): yield r
-
-proc getValue*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
-  ## Executes the query and returns the first column of the first row of the
-  ## result dataset. Returns `""` if the dataset contains no rows or the database
-  ## value is `NULL`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    doAssert db.getValue(sql"SELECT name FROM my_table WHERE id = ?",
-  ##                         2) == "item#2"
-  ##    doAssert db.getValue(sql"SELECT id, name FROM my_table") == "1"
-  ##    doAssert db.getValue(sql"SELECT name, id FROM my_table") == "item#1"
-  ##
-  ##    db.close()
-  var stmt = setupQuery(db, query, args)
-  if step(stmt) == SQLITE_ROW:
-    let cb = column_bytes(stmt, 0)
-    if cb == 0:
-      result = ""
-    else:
-      if column_type(stmt, 0) == SQLITE_BLOB:
-        result.setLen(cb)
-        copyMem(addr(result[0]), column_blob(stmt, 0), cb)
-      else:
-        result = newStringOfCap(cb)
-        add(result, column_text(stmt, 0))
-  else:
-    result = ""
-  if finalize(stmt) != SQLITE_OK: dbError(db)
-
-proc getValue*(db: DbConn,  stmtName: SqlPrepared): string
-              {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
-  var stmt = setupQuery(db, stmtName).PStmt
-  if step(stmt) == SQLITE_ROW:
-    let cb = column_bytes(stmt, 0)
-    if cb == 0:
-      result = ""
-    else:
-      if column_type(stmt, 0) == SQLITE_BLOB:
-        result.setLen(cb)
-        copyMem(addr(result[0]), column_blob(stmt, 0), cb)
-      else:
-        result = newStringOfCap(cb)
-        add(result, column_text(stmt, 0))
-  else:
-    result = ""
-
-proc tryInsertID*(db: DbConn, query: SqlQuery,
-                  args: varargs[string, `$`]): int64
-                  {.tags: [WriteDbEffect], raises: [DbError].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row or -1 in case of an error.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")
-  ##
-  ##    doAssert db.tryInsertID(sql"INSERT INTO not_exist_table (id, name) VALUES (?, ?)",
-  ##                            1, "item#1") == -1
-  ##    db.close()
-  assert(not db.isNil, "Database not connected.")
-  var q = dbFormat(query, args)
-  var stmt: sqlite3.PStmt
-  result = -1
-  if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
-    if step(stmt) == SQLITE_DONE:
-      result = last_insert_rowid(db)
-    if finalize(stmt) != SQLITE_OK:
-      result = -1
-  else:
-    discard finalize(stmt)
-
-proc insertID*(db: DbConn, query: SqlQuery,
-               args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
-  ## Executes the query (typically "INSERT") and returns the
-  ## generated ID for the row.
-  ##
-  ## Raises a `DbError` exception when failed to insert row.
-  ## For Postgre this adds `RETURNING id` to the query, so it only works
-  ## if your primary key is named `id`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")
-  ##
-  ##    for i in 0..2:
-  ##      let id = db.insertID(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", i, "item#" & $i)
-  ##      echo "LoopIndex = ", i, ", InsertID = ", id
-  ##
-  ##    # Output:
-  ##    # LoopIndex = 0, InsertID = 1
-  ##    # LoopIndex = 1, InsertID = 2
-  ##    # LoopIndex = 2, InsertID = 3
-  ##
-  ##    db.close()
-  result = tryInsertID(db, query, args)
-  if result < 0: dbError(db)
-
-proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
-                args: varargs[string, `$`]): int64
-               {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} =
-  ## same as tryInsertID
-  tryInsertID(db, query, args)
-
-proc insert*(db: DbConn, query: SqlQuery, pkName: string,
-             args: varargs[string, `$`]): int64
-            {.tags: [WriteDbEffect], since: (1, 3).} =
-  ## same as insertId
-  result = tryInsert(db, query, pkName, args)
-  if result < 0: dbError(db)
-
-proc execAffectedRows*(db: DbConn, query: SqlQuery,
-                       args: varargs[string, `$`]): int64 {.
-                       tags: [ReadDbEffect, WriteDbEffect].} =
-  ## Executes the query (typically "UPDATE") and returns the
-  ## number of affected rows.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##
-  ##    # Records of my_table:
-  ##    # | id | name     |
-  ##    # |----|----------|
-  ##    # |  1 | item#1   |
-  ##    # |  2 | item#2   |
-  ##
-  ##    doAssert db.execAffectedRows(sql"UPDATE my_table SET name = 'TEST'") == 2
-  ##
-  ##    db.close()
-  exec(db, query, args)
-  result = changes(db)
-
-proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64
-                      {.tags: [ReadDbEffect, WriteDbEffect],since: (1, 3).} =
-  exec(db, stmtName)
-  result = changes(db)
-
-proc close*(db: DbConn) {.tags: [DbEffect].} =
-  ## Closes the database connection.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    let db = open("mytest.db", "", "", "")
-  ##    db.close()
-  if sqlite3.close(db) != SQLITE_OK: dbError(db)
-
-proc open*(connection, user, password, database: string): DbConn {.
-  tags: [DbEffect].} =
-  ## Opens a database connection. Raises a `DbError` exception if the connection
-  ## could not be established.
-  ##
-  ## **Note:** Only the `connection` parameter is used for `sqlite`.
-  ##
-  ## **Examples:**
-  ##
-  ## .. code-block:: Nim
-  ##
-  ##    try:
-  ##      let db = open("mytest.db", "", "", "")
-  ##      ## do something...
-  ##      ## db.getAllRows(sql"SELECT * FROM my_table")
-  ##      db.close()
-  ##    except:
-  ##      stderr.writeLine(getCurrentExceptionMsg())
-  var db: DbConn
-  if sqlite3.open(connection, db) == SQLITE_OK:
-    result = db
-  else:
-    dbError(db)
-
-proc setEncoding*(connection: DbConn, encoding: string): bool {.
-  tags: [DbEffect].} =
-  ## Sets the encoding of a database connection, returns `true` for
-  ## success, `false` for failure.
-  ##
-  ## **Note:** The encoding cannot be changed once it's been set.
-  ## According to SQLite3 documentation, any attempt to change
-  ## the encoding after the database is created will be silently
-  ## ignored.
-  exec(connection, sql"PRAGMA encoding = ?", [encoding])
-  result = connection.getValue(sql"PRAGMA encoding") == encoding
-
-proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} =
-  discard finalize(sqlPrepared.PStmt)
-
-template dbBindParamError*(paramIdx: int, val: varargs[untyped]) =
-  ## Raises a `DbError` exception.
-  var e: ref DbError
-  new(e)
-  e.msg = "error binding param in position " & $paramIdx
-  raise e
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int32) {.since: (1, 3).} =
-  ## Binds a int32  to the specified paramIndex.
-  if bind_int(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
-    dbBindParamError(paramIdx, val)
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int64) {.since: (1, 3).} =
-  ## Binds a int64  to the specified paramIndex.
-  if bind_int64(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
-    dbBindParamError(paramIdx, val)
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int) {.since: (1, 3).} =
-  ## Binds a int  to the specified paramIndex.
-  when sizeof(int) == 8:
-    bindParam(ps, paramIdx, val.int64)
-  else:
-    bindParam(ps, paramIdx, val.int32)
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int, val: float64) {.since: (1, 3).} =
-  ## Binds a 64bit float to the specified paramIndex.
-  if bind_double(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
-    dbBindParamError(paramIdx, val)
-
-proc bindNull*(ps: SqlPrepared, paramIdx: int) {.since: (1, 3).} =
-  ## Sets the bindparam at the specified paramIndex to null
-  ## (default behaviour by sqlite).
-  if bind_null(ps.PStmt, paramIdx.int32) != SQLITE_OK:
-    dbBindParamError(paramIdx)
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int, val: string, copy = true) {.since: (1, 3).} =
-  ## Binds a string to the specified paramIndex.
-  ## if copy is true then SQLite makes its own private copy of the data immediately
-  if bind_text(ps.PStmt, paramIdx.int32, val.cstring, val.len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
-    dbBindParamError(paramIdx, val)
-
-proc bindParam*(ps: SqlPrepared, paramIdx: int,val: openArray[byte], copy = true) {.since: (1, 3).} =
-  ## binds a blob to the specified paramIndex.
-  ## if copy is true then SQLite makes its own private copy of the data immediately
-  let len = val.len
-  if bind_blob(ps.PStmt, paramIdx.int32, val[0].unsafeAddr, len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
-    dbBindParamError(paramIdx, val)
-
-macro bindParams*(ps: SqlPrepared, params: varargs[untyped]): untyped {.since: (1, 3).} =
-  let bindParam = bindSym("bindParam", brOpen)
-  let bindNull = bindSym("bindNull")
-  let preparedStatement = genSym()
-  result = newStmtList()
-  # Store `ps` in a temporary variable. This prevents `ps` from being evaluated every call.
-  result.add newNimNode(nnkLetSection).add(newIdentDefs(preparedStatement, newEmptyNode(), ps))
-  for idx, param in params:
-    if param.kind != nnkNilLit:
-      result.add newCall(bindParam, preparedStatement, newIntLitNode idx + 1, param)
-    else:
-      result.add newCall(bindNull, preparedStatement, newIntLitNode idx + 1)
-
-macro untypedLen(args: varargs[untyped]): int =
-  newLit(args.len)
-
-template exec*(db: DbConn, stmtName: SqlPrepared,
-          args: varargs[typed]): untyped =
-  when untypedLen(args) > 0:
-    if reset(stmtName.PStmt) != SQLITE_OK:
-      dbError(db)
-    if clear_bindings(stmtName.PStmt) != SQLITE_OK:
-      dbError(db)
-    stmtName.bindParams(args)
-  if not tryExec(db, stmtName): dbError(db)
-
-when not defined(testing) and isMainModule:
-  var db = open(":memory:", "", "", "")
-  exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
-  exec(db, sql"insert into tbl1 values('hello!',10)", [])
-  exec(db, sql"insert into tbl1 values('goodbye', 20)", [])
-  var p1 = db.prepare "create table tbl2(one varchar(10), two smallint)"
-  exec(db, p1)
-  finalize(p1)
-  var p2 = db.prepare "insert into tbl2 values('hello!',10)"
-  exec(db, p2)
-  finalize(p2)
-  var p3 = db.prepare "insert into tbl2 values('goodbye', 20)"
-  exec(db, p3)
-  finalize(p3)
-  #db.query("create table tbl1(one varchar(10), two smallint)")
-  #db.query("insert into tbl1 values('hello!',10)")
-  #db.query("insert into tbl1 values('goodbye', 20)")
-  for r in db.rows(sql"select * from tbl1", []):
-    echo(r[0], r[1])
-  for r in db.instantRows(sql"select * from tbl1", []):
-    echo(r[0], r[1])
-  var p4 =  db.prepare "select * from tbl2"
-  for r in db.rows(p4):
-    echo(r[0], r[1])
-  finalize(p4)
-  var i5 = 0
-  var p5 =  db.prepare "select * from tbl2"
-  for r in db.instantRows(p5):
-    inc i5
-    echo(r[0], r[1])
-  assert i5 == 2
-  finalize(p5)
-
-  for r in db.rows(sql"select * from tbl2", []):
-    echo(r[0], r[1])
-  for r in db.instantRows(sql"select * from tbl2", []):
-    echo(r[0], r[1])
-  var p6 = db.prepare "select * from tbl2 where one = ? "
-  p6.bindParams("goodbye")
-  var rowsP3 = 0
-  for r in db.rows(p6):
-    rowsP3 = 1
-    echo(r[0], r[1])
-  assert rowsP3 == 1
-  finalize(p6)
-
-  var p7 = db.prepare "select * from tbl2 where two=?"
-  p7.bindParams(20'i32)
-  when sizeof(int) == 4:
-    p7.bindParams(20)
-  var rowsP = 0
-  for r in db.rows(p7):
-    rowsP = 1
-    echo(r[0], r[1])
-  assert rowsP == 1
-  finalize(p7)
-
-  exec(db, sql"CREATE TABLE photos(ID INTEGER PRIMARY KEY AUTOINCREMENT, photo BLOB)")
-  var p8 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
-  var d = "abcdefghijklmnopqrstuvwxyz"
-  p8.bindParams(1'i32, "abcdefghijklmnopqrstuvwxyz")
-  exec(db, p8)
-  finalize(p8)
-  var p10 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
-  p10.bindParams(2'i32,nil)
-  exec(db, p10)
-  exec( db, p10, 3, nil)
-  finalize(p10)
-  for r in db.rows(sql"select * from photos where ID = 1", []):
-    assert r[1].len == d.len
-    assert r[1] == d
-  var i6 = 0
-  for r in db.rows(sql"select * from photos where ID = 3", []):
-    i6 = 1
-  assert i6 == 1
-  var p9 = db.prepare("select * from photos where PHOTO is ?")
-  p9.bindParams(nil)
-  var rowsP2 = 0
-  for r in db.rows(p9):
-    rowsP2 = 1
-    echo(r[0], repr r[1])
-  assert rowsP2 == 1
-  finalize(p9)
-
-  db_sqlite.close(db)
diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim
index e9dd49df0..39d238055 100644
--- a/lib/impure/nre.nim
+++ b/lib/impure/nre.nim
@@ -20,15 +20,17 @@ when defined(js):
 ## search the internet for a wide variety of third-party documentation and
 ## tools.
 ##
-## **Note**: If you love `sequtils.toSeq` we have bad news for you. This
-## library doesn't work with it due to documented compiler limitations. As
-## a workaround, use this:
+## .. warning:: If you love `sequtils.toSeq` we have bad news for you. This
+##   library doesn't work with it due to documented compiler limitations. As
+##   a workaround, use this:
 runnableExamples:
   # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`:
   import std/sequtils
   iterator iota(n: int): int =
     for i in 0..<n: yield i
   assert sequtils.toSeq(iota(3)) == @[0, 1, 2]
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
 ## Licencing
 ## ---------
 ##
@@ -59,12 +61,12 @@ runnableExamples:
   assert find("uxabc", re"(?<=x|y)ab", start = 1).get.captures[-1] == "ab"
   assert find("uxabc", re"ab", start = 3).isNone
 
-from pcre import nil
+from std/pcre import nil
 import nre/private/util
-import tables
-from strutils import `%`
-import options
-from unicode import runeLenAt
+import std/tables
+from std/strutils import `%`
+import std/options
+from std/unicode import runeLenAt
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -215,9 +217,11 @@ type
     ## code.
 
 proc destroyRegex(pattern: Regex) =
+  `=destroy`(pattern.pattern)
   pcre.free_substring(cast[cstring](pattern.pcreObj))
   if pattern.pcreExtra != nil:
     pcre.free_study(pattern.pcreExtra)
+  `=destroy`(pattern.captureNameToId)
 
 proc getinfo[T](pattern: Regex, opt: cint): T =
   let retcode = pcre.fullinfo(pattern.pcreObj, pattern.pcreExtra, opt, addr result)
diff --git a/lib/impure/nre/private/util.nim b/lib/impure/nre/private/util.nim
index d227dcba3..ed8420776 100644
--- a/lib/impure/nre/private/util.nim
+++ b/lib/impure/nre/private/util.nim
@@ -1,5 +1,5 @@
 ## INTERNAL FILE FOR USE ONLY BY nre.nim.
-import tables
+import std/tables
 
 const Ident = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
 const StartIdent = Ident - {'0'..'9'}
diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim
index adc0e212d..f4fc26380 100644
--- a/lib/impure/rdstdin.nim
+++ b/lib/impure/rdstdin.nim
@@ -22,7 +22,11 @@ runnableExamples("-r:off"):
     if line.len > 0: echo line
   echo "exiting"
 
+
 when defined(windows):
+  when defined(nimPreviewSlimSystem):
+    import std/syncio
+
   proc readLineFromStdin*(prompt: string): string {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
     ## Reads a line from stdin.
@@ -51,7 +55,7 @@ elif defined(genode):
     stdin.readLine(line)
 
 else:
-  import linenoise
+  import std/linenoise
 
   proc readLineFromStdin*(prompt: string, line: var string): bool {.
                           tags: [ReadIOEffect, WriteIOEffect].} =
diff --git a/lib/impure/re.nim b/lib/impure/re.nim
index 8b9de0c68..053c6ab55 100644
--- a/lib/impure/re.nim
+++ b/lib/impure/re.nim
@@ -17,6 +17,10 @@ when defined(js):
 ## C library. This means that your application will depend on the PCRE
 ## library's licence when using this module, which should not be a problem
 ## though.
+##
+## .. note:: There are also alternative nimble packages such as [tinyre](https://github.com/khchen/tinyre)
+##   and [regex](https://github.com/nitely/nim-regex).
+##
 ## PCRE's licence follows:
 ##
 ## .. include:: ../../doc/regexprs.txt
@@ -32,7 +36,7 @@ runnableExamples:
     # can't match start of string since we're starting at 1
 
 import
-  pcre, strutils, rtarrays
+  std/[pcre, strutils, rtarrays]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -61,10 +65,16 @@ type
     ## is raised if the pattern is no valid regular expression.
 
 when defined(gcDestructors):
-  proc `=destroy`(x: var RegexDesc) =
-    pcre.free_substring(cast[cstring](x.h))
-    if not isNil(x.e):
-      pcre.free_study(x.e)
+  when defined(nimAllowNonVarDestructor):
+    proc `=destroy`(x: RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
+  else:
+    proc `=destroy`(x: var RegexDesc) =
+      pcre.free_substring(cast[cstring](x.h))
+      if not isNil(x.e):
+        pcre.free_study(x.e)
 
 proc raiseInvalidRegex(msg: string) {.noinline, noreturn.} =
   var e: ref RegexError
@@ -436,7 +446,7 @@ iterator findAll*(buf: cstring, pattern: Regex, start = 0, bufSize: int): string
 
 proc findAll*(s: string, pattern: Regex, start = 0): seq[string] {.inline.} =
   ## returns all matching `substrings` of `s` that match `pattern`.
-  ## If it does not match, @[] is returned.
+  ## If it does not match, `@[]` is returned.
   result = @[]
   for x in findAll(s, pattern, start): result.add x
 
@@ -450,7 +460,7 @@ template `=~` *(s: string, pattern: Regex): untyped =
       elif line =~ re"\s*(\#.*)": # matches a comment
         # note that the implicit `matches` array is different from 1st branch
         result = $(matches[0],)
-      else: doAssert false
+      else: raiseAssert "unreachable"
       doAssert not declared(matches)
     doAssert parse("NAME = LENA") == """("NAME", "LENA")"""
     doAssert parse("   # comment ... ") == """("# comment ... ",)"""
diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim
index 8f91e1acd..9b043f3e5 100644
--- a/lib/js/asyncjs.nim
+++ b/lib/js/asyncjs.nim
@@ -17,37 +17,42 @@
 ##
 ## This is roughly equivalent to the `async` keyword in JavaScript code.
 ##
-## .. code-block:: nim
-##  proc loadGame(name: string): Future[Game] {.async.} =
-##    # code
+##   ```nim
+##   proc loadGame(name: string): Future[Game] {.async.} =
+##     # code
+##   ```
 ##
 ## should be equivalent to
 ##
-## .. code-block:: javascript
+##   ```javascript
 ##   async function loadGame(name) {
 ##     // code
 ##   }
+##   ```
 ##
 ## A call to an asynchronous procedure usually needs `await` to wait for
 ## the completion of the `Future`.
 ##
-## .. code-block:: nim
+##   ```nim
 ##   var game = await loadGame(name)
+##   ```
 ##
 ## Often, you might work with callback-based API-s. You can wrap them with
 ## asynchronous procedures using promises and `newPromise`:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   proc loadGame(name: string): Future[Game] =
 ##     var promise = newPromise() do (resolve: proc(response: Game)):
 ##       cbBasedLoadGame(name) do (game: Game):
 ##         resolve(game)
 ##     return promise
+##   ```
 ##
 ## Forward definitions work properly, you just need to always add the `{.async.}` pragma:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   proc loadGame(name: string): Future[Game] {.async.}
+##   ```
 ##
 ## JavaScript compatibility
 ## ========================
@@ -57,7 +62,7 @@
 ## If you need to use this module with older versions of JavaScript, you can
 ## use a tool that backports the resulting JavaScript code, as babel.
 
-# xxx code-block:: javascript above gives `LanguageXNotSupported` warning.
+# xxx code: javascript above gives `LanguageXNotSupported` warning.
 
 when not defined(js) and not defined(nimsuggest):
   {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".}
@@ -85,6 +90,8 @@ proc replaceReturn(node: var NimNode) =
       node[z] = nnkReturnStmt.newTree(value)
     elif son.kind == nnkAsgn and son[0].kind == nnkIdent and $son[0] == "result":
       node[z] = nnkAsgn.newTree(son[0], nnkCall.newTree(jsResolve, son[1]))
+    elif son.kind in RoutineNodes:
+      discard
     else:
       replaceReturn(son)
     inc z
@@ -95,10 +102,18 @@ proc isFutureVoid(node: NimNode): bool =
            node[1].kind == nnkIdent and $node[1] == "void"
 
 proc generateJsasync(arg: NimNode): NimNode =
-  if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
+  if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo, nnkProcTy}:
       error("Cannot transform this node kind into an async proc." &
             " proc/method definition or lambda node expected.")
 
+  # Transform type X = proc (): something {.async.}
+  # into      type X = proc (): Future[something]
+  if arg.kind == nnkProcTy:
+    result = arg
+    if arg[0][0].kind == nnkEmpty:
+      result[0][0] = quote do: Future[void]
+    return result
+
   result = arg
   var isVoid = false
   let jsResolve = ident("jsResolve")
@@ -228,7 +243,7 @@ since (1, 5, 1):
     else:
       type A = impl(onSuccess(default(T)))
     var ret: A
-    asm "`ret` = `future`.then(`onSuccess`, `onReject`)"
+    {.emit: "`ret` = `future`.then(`onSuccess`, `onReject`);".}
     return ret
 
   proc catch*[T](future: Future[T], onReject: OnReject): Future[void] =
@@ -251,4 +266,4 @@ since (1, 5, 1):
 
       discard main()
 
-    asm "`result` = `future`.catch(`onReject`)"
+    {.emit: "`result` = `future`.catch(`onReject`);".}
diff --git a/lib/js/dom.nim b/lib/js/dom.nim
index 9859e95ae..be2a34db1 100644
--- a/lib/js/dom.nim
+++ b/lib/js/dom.nim
@@ -9,6 +9,37 @@
 
 ## Declaration of the Document Object Model for the `JavaScript backend
 ## <backends.html#backends-the-javascript-target>`_.
+##
+##
+## Document Ready
+## --------------
+##
+## * Basic example of a document ready:
+runnableExamples"-b:js -r:off":
+  proc example(e: Event) = echo "Document is ready"
+  document.addEventListener("DOMContentLoaded", example)  # You can also use "load" event.
+## * This example runs 5 seconds after the document ready:
+runnableExamples"-b:js -r:off":
+  proc example() = echo "5 seconds after document ready"
+  proc domReady(e: Event) = discard setTimeout(example, 5_000) # Document is ready.
+  document.addEventListener("DOMContentLoaded", domReady)
+## Document onUnload
+## -----------------
+##
+## * Simple example of how to implement code that runs when the page unloads:
+runnableExamples"-b:js -r:off":
+  proc example(e: Event) = echo "Document is unloaded"
+  document.addEventListener("unload", example)  # You can also use "beforeunload".
+## Document Autorefresh
+## --------------------
+##
+## * Minimal example of a document autorefresh:
+runnableExamples"-b:js -r:off":
+  proc example() = window.location.reload()
+  discard setTimeout(example, 5_000)
+## - For more examples, see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
+
+
 import std/private/since
 when not defined(js):
   {.error: "This module only works on the JavaScript platform".}
@@ -17,8 +48,7 @@ const
   DomApiVersion* = 3 ## the version of DOM API we try to follow. No guarantees though.
 
 type
-  EventTarget* = ref EventTargetObj
-  EventTargetObj {.importc.} = object of RootObj
+  EventTarget* {.importc.} = ref object of RootObj
     onabort*: proc (event: Event) {.closure.}
     onblur*: proc (event: Event) {.closure.}
     onchange*: proc (event: Event) {.closure.}
@@ -129,8 +159,7 @@ type
 
   Storage* {.importc.} = ref object
 
-  Window* = ref WindowObj
-  WindowObj {.importc.} = object of EventTargetObj
+  Window* {.importc.} = ref object of EventTarget
     document*: Document
     event*: Event
     history*: History
@@ -159,11 +188,9 @@ type
     sessionStorage*: Storage
     parent*: Window
 
-  Frame* = ref FrameObj
-  FrameObj {.importc.} = object of WindowObj
+  Frame* {.importc.} = ref object of Window
 
-  ClassList* = ref ClassListObj
-  ClassListObj {.importc.} = object of RootObj
+  ClassList* {.importc.} = ref object of RootObj
 
   NodeType* = enum
     ElementNode = 1,
@@ -179,8 +206,7 @@ type
     DocumentFragmentNode,
     NotationNode
 
-  Node* = ref NodeObj
-  NodeObj {.importc.} = object of EventTargetObj
+  Node* {.importc.} = ref object of EventTarget
     attributes*: seq[Node]
     childNodes*: seq[Node]
     children*: seq[Node]
@@ -204,8 +230,7 @@ type
     parentElement*: Element
     isConnected*: bool
 
-  Document* = ref DocumentObj
-  DocumentObj {.importc.} = object of NodeObj
+  Document* {.importc.} = ref object of Node
     activeElement*: Element
     documentElement*: Element
     alinkColor*: cstring
@@ -232,8 +257,7 @@ type
     links*: seq[LinkElement]
     fonts*: FontFaceSet
 
-  Element* = ref ElementObj
-  ElementObj {.importc.} = object of NodeObj
+  Element* {.importc.} = ref object of Node
     className*: cstring
     classList*: ClassList
     checked*: bool
@@ -254,8 +278,7 @@ type
     offsetLeft*: int
     offsetTop*: int
 
-  ValidityState* = ref ValidityStateObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ValidityState>`_
-  ValidityStateObj {.importc.} = object
+  ValidityState* {.importc.} = ref object ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ValidityState>`_
     badInput*: bool
     customError*: bool
     patternMismatch*: bool
@@ -268,25 +291,21 @@ type
     valid*: bool
     valueMissing*: bool
 
-  Blob* = ref BlobObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Blob>`_
-  BlobObj {.importc.} = object of RootObj
+  Blob* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Blob>`_
     size*: int
     `type`*: cstring
 
-  File* = ref FileObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/File>`_
-  FileObj {.importc.} = object of Blob
+  File* {.importc.} = ref object of Blob ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/File>`_
     lastModified*: int
     name*: cstring
 
-  TextAreaElement* = ref TextAreaElementObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement>`_
-  TextAreaElementObj {.importc.} = object of Element
+  TextAreaElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLTextAreaElement>`_
     value*: cstring
     selectionStart*, selectionEnd*: int
     selectionDirection*: cstring
     rows*, cols*: int
 
-  InputElement* = ref InputElementObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement>`_
-  InputElementObj {.importc.} = object of Element
+  InputElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement>`_
     # Properties related to the parent form
     formAction*: cstring
     formEncType*: cstring
@@ -338,15 +357,13 @@ type
     valueAsDate*: cstring
     valueAsNumber*: float
 
-  LinkElement* = ref LinkObj
-  LinkObj {.importc.} = object of ElementObj
+  LinkElement* {.importc.} = ref object of Element
     target*: cstring
     text*: cstring
     x*: int
     y*: int
 
-  EmbedElement* = ref EmbedObj
-  EmbedObj {.importc.} = object of ElementObj
+  EmbedElement* {.importc.} = ref object of Element
     height*: int
     hspace*: int
     src*: cstring
@@ -354,21 +371,18 @@ type
     `type`*: cstring
     vspace*: int
 
-  AnchorElement* = ref AnchorObj
-  AnchorObj {.importc.} = object of ElementObj
+  AnchorElement* {.importc.} = ref object of Element
     text*: cstring
     x*, y*: int
 
-  OptionElement* = ref OptionObj
-  OptionObj {.importc.} = object of ElementObj
+  OptionElement* {.importc.} = ref object of Element
     defaultSelected*: bool
     selected*: bool
     selectedIndex*: int
     text*: cstring
     value*: cstring
 
-  FormElement* = ref FormObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement>`_
-  FormObj {.importc.} = object of ElementObj
+  FormElement* {.importc.} = ref object of Element ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement>`_
     acceptCharset*: cstring
     action*: cstring
     autocomplete*: cstring
@@ -380,8 +394,7 @@ type
     noValidate*: bool
     target*: cstring
 
-  ImageElement* = ref ImageObj
-  ImageObj {.importc.} = object of ElementObj
+  ImageElement* {.importc.} = ref object of Element
     border*: int
     complete*: bool
     height*: int
@@ -391,8 +404,7 @@ type
     vspace*: int
     width*: int
 
-  Style* = ref StyleObj
-  StyleObj {.importc.} = object of RootObj
+  Style* {.importc.} = ref object of RootObj
     alignContent*: cstring
     alignItems*: cstring
     alignSelf*: cstring
@@ -768,8 +780,7 @@ type
     AtTarget,
     BubblingPhase
 
-  Event* = ref EventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Event>`_
-  EventObj {.importc.} = object of RootObj
+  Event* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/Event>`_
     bubbles*: bool
     cancelBubble*: bool
     cancelable*: bool
@@ -781,13 +792,11 @@ type
     `type`*: cstring
     isTrusted*: bool
 
-  UIEvent* = ref UIEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/UIEvent>`_
-  UIEventObj {.importc.} = object of Event
+  UIEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/UIEvent>`_
     detail*: int64
     view*: Window
 
-  KeyboardEvent* = ref KeyboardEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent>`_
-  KeyboardEventObj {.importc.} = object of UIEvent
+  KeyboardEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent>`_
     altKey*, ctrlKey*, metaKey*, shiftKey*: bool
     code*: cstring
     isComposing*: bool
@@ -1149,8 +1158,7 @@ type
     FourthButton = 8,
     FifthButton = 16
 
-  MouseEvent* = ref MouseEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent>`_
-  MouseEventObj {.importc.} = object of UIEvent
+  MouseEvent* {.importc.} = ref object of UIEvent ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent>`_
     altKey*, ctrlKey*, metaKey*, shiftKey*: bool
     button*: int
     buttons*: int
@@ -1167,13 +1175,11 @@ type
     File = "file",
     String = "string"
 
-  DataTransferItem* = ref DataTransferItemObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem>`_
-  DataTransferItemObj {.importc.} = object of RootObj
+  DataTransferItem* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem>`_
     kind*: cstring
     `type`*: cstring
 
-  DataTransfer* = ref DataTransferObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer>`_
-  DataTransferObj {.importc.} = object of RootObj
+  DataTransfer* {.importc.} = ref object of RootObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer>`_
     dropEffect*: cstring
     effectAllowed*: cstring
     files*: seq[Element]
@@ -1215,8 +1221,7 @@ type
     ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent>`_
     clipboardData*: DataTransfer
 
-  StorageEvent* = ref StorageEventObj ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent>`_
-  StorageEventObj {.importc.} = object of Event
+  StorageEvent* {.importc.} = ref object of Event ## see `docs<https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent>`_
     key*: cstring
     newValue*, oldValue*: cstring
     storageArea*: Storage
@@ -1225,8 +1230,7 @@ type
   TouchList* {.importc.} = ref object of RootObj
     length*: int
 
-  Touch* = ref TouchObj
-  TouchObj {.importc.} = object of RootObj
+  Touch* {.importc.} = ref object of RootObj
     identifier*: int
     screenX*, screenY*, clientX*, clientY*, pageX*, pageY*: int
     target*: Element
@@ -1234,12 +1238,10 @@ type
     rotationAngle*: int
     force*: float
 
-  TouchEvent* = ref TouchEventObj
-  TouchEventObj {.importc.} = object of UIEvent
+  TouchEvent* {.importc.} = ref object of UIEvent
     changedTouches*, targetTouches*, touches*: seq[Touch]
 
-  Location* = ref LocationObj
-  LocationObj {.importc.} = object of RootObj
+  Location* {.importc.} = ref object of RootObj
     hash*: cstring
     host*: cstring
     hostname*: cstring
@@ -1250,12 +1252,10 @@ type
     search*: cstring
     origin*: cstring
 
-  History* = ref HistoryObj
-  HistoryObj {.importc.} = object of RootObj
+  History* {.importc.} = ref object of RootObj
     length*: int
 
-  Navigator* = ref NavigatorObj
-  NavigatorObj {.importc.} = object of RootObj
+  Navigator* {.importc.} = ref object of RootObj
     appCodeName*: cstring
     appName*: cstring
     appVersion*: cstring
@@ -1293,8 +1293,7 @@ type
   ToolBar* = LocationBar
   StatusBar* = LocationBar
 
-  Screen = ref ScreenObj
-  ScreenObj {.importc.} = object of RootObj
+  Screen* {.importc.} = ref object of RootObj
     availHeight*: int
     availWidth*: int
     colorDepth*: int
@@ -1324,8 +1323,7 @@ type
     `block`*: cstring
     inline*: cstring
 
-  MediaQueryList* = ref MediaQueryListObj
-  MediaQueryListObj {.importc.} = object of EventTargetObj
+  MediaQueryList* {.importc.} = ref object of EventTarget
     matches*: bool
     media*: cstring
 
@@ -1335,35 +1333,31 @@ since (1, 3):
       ## DOM Parser object (defined on browser only, may not be on NodeJS).
       ## * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser
       ##
-      ## .. code-block:: nim
+      ##   ```nim
       ##   let prsr = newDomParser()
       ##   discard prsr.parseFromString("<html><marquee>Hello World</marquee></html>".cstring, "text/html".cstring)
+      ##   ```
 
-    DomException* = ref DOMExceptionObj
+    DomException* {.importc.} = ref object
       ## The DOMException interface represents an abnormal event (called an exception)
       ## which occurs as a result of calling a method or accessing a property of a web API.
       ## Each exception has a name, which is a short "CamelCase" style string identifying
       ## the error or abnormal condition.
       ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException
 
-    DOMExceptionObj {.importc.} = object
-
-    FileReader* = ref FileReaderObj
+    FileReader* {.importc.} = ref object of EventTarget
       ## The FileReader object lets web applications asynchronously read the contents of files
       ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify
       ## the file or data to read.
       ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader
 
-    FileReaderObj {.importc.} = object of EventTargetObj
-
     FileReaderState* = distinct range[0'u16..2'u16]
     RootNodeOptions* = object of RootObj
       composed*: bool
     DocumentOrShadowRoot* {.importc.} = object of RootObj
       activeElement*: Element
       # styleSheets*: StyleSheetList
-    ShadowRoot* = ref ShadowRootObj
-    ShadowRootObj {.importc.} = object of DocumentOrShadowRoot
+    ShadowRoot* {.importc.} = ref object of DocumentOrShadowRoot
       delegatesFocus*: bool
       host*: Element
       innerHTML*: cstring
@@ -1372,8 +1366,7 @@ since (1, 3):
       mode*: cstring
       delegatesFocus*: bool
 
-    HTMLSlotElement* = ref HTMLSlotElementObj
-    HTMLSlotElementObj {.importc.} = object of RootObj
+    HTMLSlotElement* {.importc.} = ref object of RootObj
       name*: cstring
     SlotOptions* = object of RootObj
       flatten*: bool
@@ -1430,7 +1423,7 @@ when defined(nodejs):
         parent.childNodes[i] = newNode
         return
       inc i
-    doAssert false, "old node not in node list"
+    raiseAssert "old node not in node list"
 
   proc removeChild*(parent, child: Node) =
     child.parentNode = nil
@@ -1440,7 +1433,7 @@ when defined(nodejs):
         parent.childNodes.delete(i)
         return
       inc i
-    doAssert false, "old node not in node list"
+    raiseAssert "old node not in node list"
 
   proc insertBefore*(parent, newNode, before: Node) =
     appendChild(parent, newNode)
@@ -1452,7 +1445,7 @@ when defined(nodejs):
         parent.childNodes[i-1] = newNode
         return
       inc i
-    #doAssert false, "before not in node list"
+    #raiseAssert "before not in node list"
 
   proc createElement*(d: Document, identifier: cstring): Element =
     new(result)
@@ -1510,7 +1503,7 @@ proc confirm*(w: Window, msg: cstring): bool
 proc disableExternalCapture*(w: Window)
 proc enableExternalCapture*(w: Window)
 proc find*(w: Window, text: cstring, caseSensitive = false,
-           backwards = false)
+           backwards = false): bool
 proc focus*(w: Window)
 proc forward*(w: Window)
 proc getComputedStyle*(w: Window, e: Node, pe: Node = nil): Style
@@ -1662,7 +1655,7 @@ proc item*(list: TouchList, i: int): Touch
 proc clearData*(dt: DataTransfer, format: cstring)
 proc getData*(dt: DataTransfer, format: cstring): cstring
 proc setData*(dt: DataTransfer, format: cstring, data: cstring)
-proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int64, yOffset: int64)
+proc setDragImage*(dt: DataTransfer, img: Element, xOffset: int, yOffset: int)
 
 # DataTransferItem "methods"
 proc getAsFile*(dti: DataTransferItem): File
@@ -1689,7 +1682,6 @@ proc `$`*(s: Selection): string = $(s.toString())
 # Storage "methods"
 proc getItem*(s: Storage, key: cstring): cstring
 proc setItem*(s: Storage, key, value: cstring)
-proc hasItem*(s: Storage, key: cstring): bool
 proc clear*(s: Storage)
 proc removeItem*(s: Storage, key: cstring)
 
@@ -1839,3 +1831,11 @@ since (1, 7):
 
   proc matches*(self: Node; cssSelector: cstring): bool {.importjs: "(#.$1(#) || false)".}
     ## https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
+
+
+since (2, 1):
+  type VisualViewport* {.importc.} = ref object of EventTarget
+    offsetLeft*, offsetTop*, pageLeft*, pageTop*, width*, height*, scale*: float
+    onResize*, onScroll*: proc (event: Event) {.closure.}
+
+  func visualViewport*(self: Window): VisualViewport {.importjs: "#.$1", nodecl.}
diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim
index 781e8fd57..be353875c 100644
--- a/lib/js/jscore.nim
+++ b/lib/js/jscore.nim
@@ -13,7 +13,7 @@
 ## specific requirements and solely targets JavaScript, you should be using
 ## the relevant functions in the `math`, `json`, and `times` stdlib
 ## modules instead.
-import std/private/since
+import std/private/[since, jsutils]
 
 when not defined(js):
   {.error: "This module only works on the JavaScript platform".}
@@ -74,9 +74,16 @@ proc parse*(d: DateLib, s: cstring): int {.importcpp.}
 proc newDate*(): DateTime {.
   importcpp: "new Date()".}
 
-proc newDate*(date: int|int64|string): DateTime {.
+proc newDate*(date: int|string): DateTime {.
   importcpp: "new Date(#)".}
 
+whenJsNoBigInt64:
+  proc newDate*(date: int64): DateTime {.
+    importcpp: "new Date(#)".}
+do:
+  proc newDate*(date: int64): DateTime {.
+    importcpp: "new Date(Number(#))".}
+
 proc newDate*(year, month, day, hours, minutes,
              seconds, milliseconds: int): DateTime {.
   importcpp: "new Date(#,#,#,#,#,#,#)".}
@@ -88,20 +95,27 @@ proc getMilliseconds*(d: DateTime): int {.importcpp.}
 proc getMinutes*(d: DateTime): int {.importcpp.}
 proc getMonth*(d: DateTime): int {.importcpp.}
 proc getSeconds*(d: DateTime): int {.importcpp.}
-proc getYear*(d: DateTime): int {.importcpp.}
 proc getTime*(d: DateTime): int {.importcpp.}
-proc toString*(d: DateTime): cstring {.importcpp.}
+proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
 proc getUTCDate*(d: DateTime): int {.importcpp.}
+proc getUTCDay*(d: DateTime): int {.importcpp.}
 proc getUTCFullYear*(d: DateTime): int {.importcpp.}
 proc getUTCHours*(d: DateTime): int {.importcpp.}
 proc getUTCMilliseconds*(d: DateTime): int {.importcpp.}
 proc getUTCMinutes*(d: DateTime): int {.importcpp.}
 proc getUTCMonth*(d: DateTime): int {.importcpp.}
 proc getUTCSeconds*(d: DateTime): int {.importcpp.}
-proc getUTCDay*(d: DateTime): int {.importcpp.}
-proc getTimezoneOffset*(d: DateTime): int {.importcpp.}
+proc getYear*(d: DateTime): int {.importcpp.}
+
 proc setFullYear*(d: DateTime, year: int) {.importcpp.}
 
+func toDateString*(d: DateTime): cstring {.importcpp.}
+func toISOString*(d: DateTime): cstring {.importcpp.}
+func toJSON*(d: DateTime): cstring {.importcpp.}
+proc toString*(d: DateTime): cstring {.importcpp.}
+func toTimeString*(d: DateTime): cstring {.importcpp.}
+func toUTCString*(d: DateTime): cstring {.importcpp.}
+
 #JSON library
 proc stringify*(l: JsonLib, s: JsRoot): cstring {.importcpp.}
 proc parse*(l: JsonLib, s: cstring): JsRoot {.importcpp.}
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index aca4fc292..d50d58ae5 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -227,36 +227,40 @@ macro `.`*(obj: JsObject, field: untyped): JsObject =
     assert obj.a.to(int) == 20
   if validJsName($field):
     let importString = "#." & $field
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject): JsObject
-        {.importjs: `importString`, gensym.}
-      helper(`obj`)
+      proc `helperName`(o: JsObject): JsObject
+        {.importjs: `importString`.}
+      `helperName`(`obj`)
   else:
     if not mangledNames.hasKey($field):
       mangledNames[$field] = mangleJsName($field)
     let importString = "#." & mangledNames[$field]
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject): JsObject
-        {.importjs: `importString`, gensym.}
-      helper(`obj`)
+      proc `helperName`(o: JsObject): JsObject
+        {.importjs: `importString`.}
+      `helperName`(`obj`)
 
 macro `.=`*(obj: JsObject, field, value: untyped): untyped =
   ## Experimental dot accessor (set) for type JsObject.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
   if validJsName($field):
     let importString = "#." & $field & " = #"
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject, v: auto)
-        {.importjs: `importString`, gensym.}
-      helper(`obj`, `value`)
+      proc `helperName`(o: JsObject, v: auto)
+        {.importjs: `importString`.}
+      `helperName`(`obj`, `value`)
   else:
     if not mangledNames.hasKey($field):
       mangledNames[$field] = mangleJsName($field)
     let importString = "#." & mangledNames[$field] & " = #"
+    let helperName = genSym(nskProc, "helper")
     result = quote do:
-      proc helper(o: JsObject, v: auto)
-        {.importjs: `importString`, gensym.}
-      helper(`obj`, `value`)
+      proc `helperName`(o: JsObject, v: auto)
+        {.importjs: `importString`.}
+      `helperName`(`obj`, `value`)
 
 macro `.()`*(obj: JsObject,
              field: untyped,
@@ -268,15 +272,14 @@ macro `.()`*(obj: JsObject,
   ## so be careful when using this.)
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  # Let's get back to the console example:
-  ##  var console {.importc, nodecl.}: JsObject
-  ##  let res = console.log("I return undefined!")
-  ##  console.log(res) # This prints undefined, as console.log always returns
-  ##                   # undefined. Thus one has to be careful, when using
-  ##                   # JsObject calls.
+  ##   ```nim
+  ##   # Let's get back to the console example:
+  ##   var console {.importc, nodecl.}: JsObject
+  ##   let res = console.log("I return undefined!")
+  ##   console.log(res) # This prints undefined, as console.log always returns
+  ##                    # undefined. Thus one has to be careful, when using
+  ##                    # JsObject calls.
+  ##   ```
   var importString: string
   if validJsName($field):
     importString = "#." & $field & "(@)"
@@ -284,10 +287,11 @@ macro `.()`*(obj: JsObject,
     if not mangledNames.hasKey($field):
       mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field] & "(@)"
-  result = quote:
-    proc helper(o: JsObject): JsObject
-      {.importjs: `importString`, gensym, discardable.}
-    helper(`obj`)
+  let helperName = genSym(nskProc, "helper")
+  result = quote do:
+    proc `helperName`(o: JsObject): JsObject
+      {.importjs: `importString`, discardable.}
+    `helperName`(`obj`)
   for idx in 0 ..< args.len:
     let paramName = newIdentNode("param" & $idx)
     result[0][3].add newIdentDefs(paramName, newIdentNode("JsObject"))
@@ -304,10 +308,11 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V],
     if not mangledNames.hasKey($field):
       mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field]
+  let helperName = genSym(nskProc, "helper")
   result = quote do:
-    proc helper(o: type(`obj`)): `obj`.V
-      {.importjs: `importString`, gensym.}
-    helper(`obj`)
+    proc `helperName`(o: type(`obj`)): `obj`.V
+      {.importjs: `importString`.}
+    `helperName`(`obj`)
 
 macro `.=`*[K: cstring, V](obj: JsAssoc[K, V],
                                     field: untyped,
@@ -321,10 +326,11 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V],
     if not mangledNames.hasKey($field):
       mangledNames[$field] = mangleJsName($field)
     importString = "#." & mangledNames[$field] & " = #"
+  let helperName = genSym(nskProc, "helper")
   result = quote do:
-    proc helper(o: type(`obj`), v: `obj`.V)
-      {.importjs: `importString`, gensym.}
-    helper(`obj`, `value`)
+    proc `helperName`(o: type(`obj`), v: `obj`.V)
+      {.importjs: `importString`.}
+    `helperName`(`obj`, `value`)
 
 macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V],
                                            field: untyped,
@@ -407,21 +413,20 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto =
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
+  ##   # Let's say we have a type with a ton of fields, where some fields do not
+  ##   # need to be set, and we do not want those fields to be set to `nil`:
+  ##   type
+  ##     ExtremelyHugeType = ref object
+  ##       a, b, c, d, e, f, g: int
+  ##       h, i, j, k, l: cstring
+  ##       # And even more fields ...
   ##
-  ##  # Let's say we have a type with a ton of fields, where some fields do not
-  ##  # need to be set, and we do not want those fields to be set to `nil`:
-  ##  type
-  ##    ExtremelyHugeType = ref object
-  ##      a, b, c, d, e, f, g: int
-  ##      h, i, j, k, l: cstring
-  ##      # And even more fields ...
-  ##
-  ##  let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }
-  ##
-  ##  # This generates roughly the same JavaScript as:
-  ##  {.emit: "var obj = {a: 1, k: "foo", d: 42};".}
+  ##   let obj = ExtremelyHugeType{ a: 1, k: "foo".cstring, d: 42 }
   ##
+  ##   # This generates roughly the same JavaScript as:
+  ##   {.emit: "var obj = {a: 1, k: "foo", d: 42};".}
+  ##   ```
   let a = ident"a"
   var body = quote do:
     var `a` {.noinit.}: `typ`
@@ -463,7 +468,7 @@ proc replaceSyms(n: NimNode): NimNode =
     for i in 0..<n.len:
       result[i] = replaceSyms(n[i])
 
-macro bindMethod*(procedure: typed): auto =
+macro bindMethod*(procedure: typed): auto {.deprecated: "Don't use it with closures".} =
   ## Takes the name of a procedure and wraps it into a lambda missing the first
   ## argument, which passes the JavaScript builtin `this` as the first
   ## argument to the procedure. Returns the resulting lambda.
@@ -471,24 +476,25 @@ macro bindMethod*(procedure: typed): auto =
   ## Example:
   ##
   ## We want to generate roughly this JavaScript:
-  ##
-  ## .. code-block:: js
-  ##  var obj = {a: 10};
-  ##  obj.someMethod = function() {
-  ##    return this.a + 42;
-  ##  };
+  ##   ```js
+  ##   var obj = {a: 10};
+  ##   obj.someMethod = function() {
+  ##     return this.a + 42;
+  ##   };
+  ##   ```
   ##
   ## We can achieve this using the `bindMethod` macro:
   ##
-  ## .. code-block:: nim
-  ##  let obj = JsObject{ a: 10 }
-  ##  proc someMethodImpl(that: JsObject): int =
-  ##    that.a.to(int) + 42
-  ##  obj.someMethod = bindMethod someMethodImpl
+  ##   ```nim
+  ##   let obj = JsObject{ a: 10 }
+  ##   proc someMethodImpl(that: JsObject): int =
+  ##     that.a.to(int) + 42
+  ##   obj.someMethod = bindMethod someMethodImpl
   ##
-  ##  # Alternatively:
-  ##  obj.someMethod = bindMethod
-  ##    proc(that: JsObject): int = that.a.to(int) + 42
+  ##   # Alternatively:
+  ##   obj.someMethod = bindMethod
+  ##     proc(that: JsObject): int = that.a.to(int) + 42
+  ##   ```
   if not (procedure.kind == nnkSym or procedure.kind == nnkLambda):
     error("Argument has to be a proc or a symbol corresponding to a proc.")
   var
diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim
index 19888aaa9..2d931eb20 100644
--- a/lib/js/jsre.nim
+++ b/lib/js/jsre.nim
@@ -34,6 +34,9 @@ func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.com
 func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".}
   ## Returns a new string with some or all matches of a pattern replaced by given replacement
 
+func replace*(pattern: cstring, self: RegExp, cb: proc (args: varargs[cstring]): cstring): cstring {.importcpp.}
+  ## Returns a new string with some or all matches of a pattern replaced by given callback function
+
 func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".}
   ## Divides a string into an ordered list of substrings and returns the array
 
@@ -55,7 +58,7 @@ func contains*(pattern: cstring; self: RegExp): bool =
     assert jsregex in r"abc"
     assert jsregex notin r"abcd"
     assert "xabc".contains jsregex
-  asm "`result` = `self`.test(`pattern`);"
+  {.emit: "`result` = `self`.test(`pattern`);".}
 
 func startsWith*(pattern: cstring; self: RegExp): bool =
   ## Tests if string starts with given RegExp
@@ -88,5 +91,7 @@ runnableExamples:
   assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring]
   jsregex.compile(r"[lw]", r"i")
   assert "hello world".replace(jsregex,"X") == "heXlo world"
+  jsregex.compile(r"([a-z])\1*", r"g")
+  assert "abbcccdddd".replace(jsregex, proc (m: varargs[cstring]): cstring = ($m[0] & $(m.len)).cstring) == "a1b2c3d4"
   let digitsRegex: RegExp = newRegExp(r"\d")
   assert "foo".match(digitsRegex) == @[]
diff --git a/lib/nimbase.h b/lib/nimbase.h
index e4dec0dd2..cf0c8002b 100644
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -10,14 +10,12 @@
 /* compiler symbols:
 __BORLANDC__
 _MSC_VER
-__WATCOMC__
-__LCC__
 __GNUC__
-__DMC__
-__POCC__
 __TINYC__
 __clang__
 __AVR__
+__arm__
+__EMSCRIPTEN__
 */
 
 
@@ -89,11 +87,8 @@ __AVR__
 #endif
 
 /* calling convention mess ----------------------------------------------- */
-#if defined(__GNUC__) || defined(__LCC__) || defined(__POCC__) \
-                      || defined(__TINYC__)
+#if defined(__GNUC__) || defined(__TINYC__)
   /* these should support C99's inline */
-  /* the test for __POCC__ has to come before the test for _MSC_VER,
-     because PellesC defines _MSC_VER too. This is brain-dead. */
 #  define N_INLINE(rettype, name) inline rettype name
 #elif defined(__BORLANDC__) || defined(_MSC_VER)
 /* Borland's compiler is really STRANGE here; note that the __fastcall
@@ -101,21 +96,13 @@ __AVR__
    the return type, so we do not handle this mess in the code generator
    but rather here. */
 #  define N_INLINE(rettype, name) __inline rettype name
-#elif defined(__DMC__)
-#  define N_INLINE(rettype, name) inline rettype name
-#elif defined(__WATCOMC__)
-#  define N_INLINE(rettype, name) __inline rettype name
 #else /* others are less picky: */
 #  define N_INLINE(rettype, name) rettype __inline name
 #endif
 
 #define N_INLINE_PTR(rettype, name) rettype (*name)
 
-#if defined(__POCC__)
-#  define NIM_CONST /* PCC is really picky with const modifiers */
-#  undef _MSC_VER /* Yeah, right PCC defines _MSC_VER even if it is
-                     not that compatible. Well done. */
-#elif defined(__cplusplus)
+#if defined(__cplusplus)
 #  define NIM_CONST /* C++ is picky with const modifiers */
 #else
 #  define NIM_CONST  const
@@ -125,12 +112,17 @@ __AVR__
   NIM_THREADVAR declaration based on
   http://stackoverflow.com/questions/18298280/how-to-declare-a-variable-as-thread-local-portably
 */
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
+#if defined _WIN32
+#  if defined _MSC_VER || defined __BORLANDC__
+#    define NIM_THREADVAR __declspec(thread)
+#  else
+#    define NIM_THREADVAR __thread
+#  endif
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
 #  define NIM_THREADVAR _Thread_local
 #elif defined _WIN32 && ( \
        defined _MSC_VER || \
        defined __ICL || \
-       defined __DMC__ || \
        defined __BORLANDC__ )
 #  define NIM_THREADVAR __declspec(thread)
 #elif defined(__TINYC__) || defined(__GENODE__)
@@ -149,8 +141,7 @@ __AVR__
 #endif
 
 /* --------------- how int64 constants should be declared: ----------- */
-#if defined(__GNUC__) || defined(__LCC__) || \
-    defined(__POCC__) || defined(__DMC__) || defined(_MSC_VER)
+#if defined(__GNUC__) || defined(_MSC_VER)
 #  define IL64(x) x##LL
 #else /* works only without LL */
 #  define IL64(x) ((NI64)x)
@@ -188,12 +179,13 @@ __AVR__
 #  define N_THISCALL_PTR(rettype, name) rettype (__thiscall *name)
 #  define N_SAFECALL_PTR(rettype, name) rettype (__stdcall *name)
 
-#  ifdef __cplusplus
-#    define N_LIB_EXPORT  NIM_EXTERNC __declspec(dllexport)
+#  ifdef __EMSCRIPTEN__
+#    define N_LIB_EXPORT  NIM_EXTERNC __declspec(dllexport) __attribute__((used))
+#    define N_LIB_EXPORT_VAR  __declspec(dllexport) __attribute__((used))
 #  else
 #    define N_LIB_EXPORT  NIM_EXTERNC __declspec(dllexport)
+#    define N_LIB_EXPORT_VAR  __declspec(dllexport)
 #  endif
-#  define N_LIB_EXPORT_VAR  __declspec(dllexport)
 #  define N_LIB_IMPORT  extern __declspec(dllimport)
 #else
 #  define N_LIB_PRIVATE __attribute__((visibility("hidden")))
@@ -222,8 +214,13 @@ __AVR__
 #    define N_FASTCALL_PTR(rettype, name) rettype (*name)
 #    define N_SAFECALL_PTR(rettype, name) rettype (*name)
 #  endif
-#  define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default")))
-#  define N_LIB_EXPORT_VAR  __attribute__((visibility("default")))
+#  ifdef __EMSCRIPTEN__
+#    define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default"), used))
+#    define N_LIB_EXPORT_VAR  __attribute__((visibility("default"), used))
+#  else
+#    define N_LIB_EXPORT NIM_EXTERNC __attribute__((visibility("default")))
+#    define N_LIB_EXPORT_VAR  __attribute__((visibility("default")))
+#  endif
 #  define N_LIB_IMPORT  extern
 #endif
 
@@ -241,8 +238,7 @@ __AVR__
 
 #define N_NOINLINE_PTR(rettype, name) rettype (*name)
 
-#if defined(__BORLANDC__) || defined(__WATCOMC__) || \
-    defined(__POCC__) || defined(_MSC_VER) || defined(WIN32) || defined(_WIN32)
+#if defined(__BORLANDC__) || defined(_MSC_VER) || defined(WIN32) || defined(_WIN32)
 /* these compilers have a fastcall so use it: */
 #  ifdef __TINYC__
 #    define N_NIMCALL(rettype, name) rettype __attribute((__fastcall)) name
@@ -277,11 +273,15 @@ __AVR__
 #elif defined(__cplusplus)
 #define NIM_STATIC_ASSERT(x, msg) static_assert((x), msg)
 #else
-#define NIM_STATIC_ASSERT(x, msg) typedef int NIM_STATIC_ASSERT_AUX[(x) ? 1 : -1];
+#define _NIM_STATIC_ASSERT_FINAL(x, append_name) typedef int NIM_STATIC_ASSERT_AUX ## append_name[(x) ? 1 : -1];
+#define _NIM_STATIC_ASSERT_STAGE_3(x, line)      _NIM_STATIC_ASSERT_FINAL(x, _AT_LINE_##line)
+#define _NIM_STATIC_ASSERT_STAGE_2(x, line)      _NIM_STATIC_ASSERT_STAGE_3(x, line)
+#define NIM_STATIC_ASSERT(x, msg)                _NIM_STATIC_ASSERT_STAGE_2(x,__LINE__)
 // On failure, your C compiler will say something like:
-//   "error: 'NIM_STATIC_ASSERT_AUX' declared as an array with a negative size"
-// we could use a better fallback to also show line number, using:
-// http://www.pixelbeat.org/programming/gcc/static_assert.html
+//   "error: 'NIM_STATIC_ASSERT_AUX_AT_LINE_XXX' declared as an array with a negative size"
+// Adding the line number helps to avoid redefinitions which are not allowed in
+// old GCC versions, however the order of evaluation for __LINE__ is a little tricky,
+// hence all the helper macros. See https://stackoverflow.com/a/3385694 for more info.
 #endif
 
 /* C99 compiler? */
@@ -290,8 +290,7 @@ __AVR__
 #endif
 
 /* Known compiler with stdint.h that doesn't fit the general pattern? */
-#if defined(__LCC__) || defined(__DMC__) || defined(__POCC__) || \
-  defined(__AVR__) || (defined(__cplusplus) && (__cplusplus < 201103))
+#if defined(__AVR__) || (defined(__cplusplus) && (__cplusplus < 201103))
 #  define HAVE_STDINT_H
 #endif
 
@@ -351,8 +350,7 @@ NIM_STATIC_ASSERT(CHAR_BIT == 8, "");
                               the generated code does not rely on it anymore */
 #endif
 
-#if defined(__BORLANDC__) || defined(__DMC__) \
-   || defined(__WATCOMC__) || defined(_MSC_VER)
+#if defined(__BORLANDC__) || defined(_MSC_VER)
 typedef signed char NI8;
 typedef signed short int NI16;
 typedef signed int NI32;
@@ -478,7 +476,9 @@ typedef char* NCSTRING;
   } name = {{length, (NI) ((NU)length | NIM_STRLIT_FLAG)}, str}
 
 /* declared size of a sequence/variable length array: */
-#if defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER)
+#if defined(__cplusplus) && defined(__clang__)
+#  define SEQ_DECL_SIZE 1
+#elif defined(__GNUC__) || defined(_MSC_VER)
 #  define SEQ_DECL_SIZE /* empty is correct! */
 #else
 #  define SEQ_DECL_SIZE 1000000
@@ -585,9 +585,16 @@ NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "P
   #define nimMulInt64(a, b, res) __builtin_smulll_overflow(a, b, (long long int*)res)
 
   #if NIM_INTBITS == 32
-    #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
-    #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
-    #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
+    #if defined(__arm__) && defined(__GNUC__)
+      /* arm-none-eabi-gcc targets defines int32_t as long int */
+      #define nimAddInt(a, b, res) __builtin_saddl_overflow(a, b, res)
+      #define nimSubInt(a, b, res) __builtin_ssubl_overflow(a, b, res)
+      #define nimMulInt(a, b, res) __builtin_smull_overflow(a, b, res)
+    #else
+      #define nimAddInt(a, b, res) __builtin_sadd_overflow(a, b, res)
+      #define nimSubInt(a, b, res) __builtin_ssub_overflow(a, b, res)
+      #define nimMulInt(a, b, res) __builtin_smul_overflow(a, b, res)
+    #endif
   #else
     /* map it to the 'long long' variant */
     #define nimAddInt(a, b, res) __builtin_saddll_overflow(a, b, (long long int*)res)
diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim
index b35ecf8df..e87bb2413 100644
--- a/lib/nimhcr.nim
+++ b/lib/nimhcr.nim
@@ -200,6 +200,9 @@ batchable: false
 #     block. Perhaps something can be done about this - some way of re-allocating
 #     the state and transferring the old...
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 when not defined(js) and (defined(hotcodereloading) or
                           defined(createNimHcr) or
                           defined(testNimHcr)):
@@ -216,7 +219,7 @@ when defined(createNimHcr):
   when system.appType != "lib":
     {.error: "This file has to be compiled as a library!".}
 
-  import os, tables, sets, times, strutils, reservedmem, dynlib
+  import std/[os, tables, sets, times, strutils, reservedmem, dynlib]
 
   template trace(args: varargs[untyped]) =
     when defined(testNimHcr) or defined(traceHcr):
@@ -302,7 +305,7 @@ when defined(createNimHcr):
       hash: string
       gen: int
       lastModification: Time
-      handlers: seq[tuple[isBefore: bool, cb: proc ()]]
+      handlers: seq[tuple[isBefore: bool, cb: proc () {.nimcall.}]]
 
   proc newModuleDesc(): ModuleDesc =
     result.procs = initTable[string, ProcSym]()
@@ -554,8 +557,12 @@ when defined(createNimHcr):
     # Future versions of NIMHCR won't use the GC, because all globals and the
     # metadata needed to access them will be placed in shared memory, so they
     # can be manipulated from external programs without reloading.
-    GC_disable()
-    defer: GC_enable()
+    when declared(GC_disable):
+      GC_disable()
+      defer: GC_enable()
+    elif declared(GC_disableOrc):
+      GC_disableOrc()
+      defer: GC_enableOrc()
 
     inc(generation)
     trace "HCR RELOADING: ", generation
@@ -595,7 +602,7 @@ when defined(createNimHcr):
       hashToModuleMap.del(modules[name].hash)
       modules.del(name)
 
-  proc hcrAddEventHandler*(isBefore: bool, cb: proc ()) {.nimhcr.} =
+  proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) {.nimhcr.} =
     modules[currentModule].handlers.add(
       (isBefore: isBefore, cb: cb))
 
@@ -646,7 +653,7 @@ elif defined(hotcodereloading) or defined(testNimHcr):
 
     proc hcrPerformCodeReload*() {.nimhcr.}
 
-    proc hcrAddEventHandler*(isBefore: bool, cb: proc ()) {.nimhcr.}
+    proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) {.nimhcr.}
 
     proc hcrMarkGlobals*() {.raises: [], nimhcr, nimcall, gcsafe.}
 
@@ -658,7 +665,7 @@ elif defined(hotcodereloading) or defined(testNimHcr):
       # TODO
       false
 
-    proc hcrAddEventHandler*(isBefore: bool, cb: proc ()) =
+    proc hcrAddEventHandler*(isBefore: bool, cb: proc () {.nimcall.}) =
       # TODO
       discard
 
diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim
index 93349b287..a2fb6ce60 100644
--- a/lib/nimrtl.nim
+++ b/lib/nimrtl.nim
@@ -36,5 +36,5 @@ when not defined(createNimRtl):
   {.error: "This file has to be compiled with '-d:createNimRtl'".}
 
 import
-  parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
-  os, osproc, times, cstrutils
+  std/[parseutils, strutils, parseopt, parsecfg, strtabs, unicode, pegs, ropes,
+  os, osproc, times, cstrutils]
diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim
index b85e37983..0a41d85b5 100644
--- a/lib/packages/docutils/dochelpers.nim
+++ b/lib/packages/docutils/dochelpers.nim
@@ -14,6 +14,7 @@
 ## matches it with `generated`, produced from `PNode` by ``docgen.rst``.
 
 import rstast
+import std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio]
@@ -35,6 +36,12 @@ type
                                ## name-type seq, e.g. for proc
     outType*: string           ## result type, e.g. for proc
 
+proc `$`*(s: LangSymbol): string =  # for debug
+  ("(symkind=$1, symTypeKind=$2, name=$3, generics=$4, isGroup=$5, " &
+   "parametersProvided=$6, parameters=$7, outType=$8)") % [
+      s.symKind, s.symTypeKind , s.name, s.generics, $s.isGroup,
+      $s.parametersProvided, $s.parameters, s.outType]
+
 func nimIdentBackticksNormalize*(s: string): string =
   ## Normalizes the string `s` as a Nim identifier.
   ##
@@ -71,22 +78,31 @@ func nimIdentBackticksNormalize*(s: string): string =
     else: discard  # just omit '`' or ' '
   if j != s.len: setLen(result, j)
 
+proc langSymbolGroup*(kind: string, name: string): LangSymbol =
+  if kind notin ["proc", "func", "macro", "method", "iterator",
+                 "template", "converter"]:
+    raise newException(ValueError, "unknown symbol kind $1" % [kind])
+  result = LangSymbol(symKind: kind, name: name, isGroup: true)
+
 proc toLangSymbol*(linkText: PRstNode): LangSymbol =
   ## Parses `linkText` into a more structured form using a state machine.
   ##
   ## This proc is designed to allow link syntax with operators even
-  ## without escaped backticks inside::
+  ## without escaped backticks inside:
   ##   
-  ##   `proc *`_
-  ##   `proc []`_
+  ##     `proc *`_
+  ##     `proc []`_
   ##
   ## This proc should be kept in sync with the `renderTypes` proc from
   ## ``compiler/typesrenderer.nim``.
-  assert linkText.kind in {rnRstRef, rnInner}
+  template fail(msg: string) =
+    raise newException(ValueError, msg)
+  if linkText.kind notin {rnRstRef, rnInner}:
+    fail("toLangSymbol: wrong input kind " & $linkText.kind)
 
   const NimDefs = ["proc", "func", "macro", "method", "iterator",
                    "template", "converter", "const", "type", "var",
-                   "enum", "object", "tuple"]
+                   "enum", "object", "tuple", "module"]
   template resolveSymKind(x: string) =
     if x in ["enum", "object", "tuple"]:
       result.symKind = "type"
@@ -109,11 +125,11 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol =
   template flushIdent() =
     if curIdent != "":
       case state
-      of inBeginning:  doAssert false, "incorrect state inBeginning"
+      of inBeginning:  fail("incorrect state inBeginning")
       of afterSymKind:  resolveSymKind curIdent
-      of beforeSymbolName:  doAssert false, "incorrect state beforeSymbolName"
+      of beforeSymbolName:  fail("incorrect state beforeSymbolName")
       of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize
-      of afterSymbolName: doAssert false, "incorrect state afterSymbolName"
+      of afterSymbolName: fail("incorrect state afterSymbolName")
       of genericsPar: result.generics = curIdent
       of parameterName: result.parameters.add (curIdent, "")
       of parameterType:
@@ -273,7 +289,7 @@ proc match*(generated: LangSymbol, docLink: LangSymbol): bool =
         if g.`type` == d.name:
           onlyType = true  # only types, not names, are provided in `docLink`
       if onlyType:
-        result = g.`type` == d.name:
+        result = g.`type` == d.name
       else:
         if d.`type` != "":
           result = g.`type` == d.`type`
diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim
index 4e62031eb..f8376f46c 100644
--- a/lib/packages/docutils/highlite.nim
+++ b/lib/packages/docutils/highlite.nim
@@ -13,7 +13,7 @@
 ##
 ## You can use this to build your own syntax highlighting, check this example:
 ##
-## .. code:: Nim
+##   ```Nim
 ##   let code = """for x in $int.high: echo x.ord mod 2 == 0"""
 ##   var toknizr: GeneralTokenizer
 ##   initGeneralTokenizer(toknizr, code)
@@ -31,19 +31,20 @@
 ##     else:
 ##       echo toknizr.kind # All the kinds of tokens can be processed here.
 ##       echo substr(code, toknizr.start, toknizr.length + toknizr.start - 1)
+##   ```
 ##
 ## The proc `getSourceLanguage` can get the language `enum` from a string:
-##
-## .. code:: Nim
+##   ```Nim
 ##   for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l)
+##   ```
 ##
 ## There is also a `Cmd` pseudo-language supported, which is a simple generic
 ## shell/cmdline tokenizer (UNIX shell/Powershell/Windows Command):
 ## no escaping, no programming language constructs besides variable definition
 ## at the beginning of line. It supports these operators:
-##
-## .. code:: Cmd
-##    &  &&  |  ||  (  )  ''  ""  ;  # for comments
+##   ```Cmd
+##   &  &&  |  ||  (  )  ''  ""  ;  # for comments
+##   ```
 ##
 ## Instead of escaping always use quotes like here
 ## `nimgrep --ext:'nim|nims' file.name`:cmd: shows how to input ``|``.
@@ -56,11 +57,11 @@
 ## as program output.
 
 import
-  strutils
-from algorithm import binarySearch
+  std/strutils
+from std/algorithm import binarySearch
 
 when defined(nimPreviewSlimSystem):
-  import std/assertions
+  import std/[assertions, syncio]
 
 
 type
@@ -323,17 +324,18 @@ proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) =
       pos = nimNumber(g, pos)
     of '\'':
       inc(pos)
-      g.kind = gtCharLit
-      while true:
-        case g.buf[pos]
-        of '\0', '\r', '\n':
-          break
-        of '\'':
-          inc(pos)
-          break
-        of '\\':
-          inc(pos, 2)
-        else: inc(pos)
+      if g.kind != gtPunctuation:
+        g.kind = gtCharLit
+        while true:
+          case g.buf[pos]
+          of '\0', '\r', '\n':
+            break
+          of '\'':
+            inc(pos)
+            break
+          of '\\':
+            inc(pos, 2)
+          else: inc(pos)
     of '\"':
       inc(pos)
       if (g.buf[pos] == '\"') and (g.buf[pos + 1] == '\"'):
@@ -497,6 +499,9 @@ proc clikeNextToken(g: var GeneralTokenizer, keywords: openArray[string],
           of '\0':
             break
           else: inc(pos)
+      else:
+        g.kind = gtOperator
+        while g.buf[pos] in OpChars: inc(pos)
     of '#':
       inc(pos)
       if hasPreprocessor in flags:
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 3c95c9ef0..706c50689 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -21,8 +21,10 @@
 ## turned on by passing ``options:`` [RstParseOptions] to [proc rstParse].
 
 import
-  os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils,
-  std/private/miscdollars, tables, strscans
+  std/[os, strutils, enumutils, algorithm, lists, sequtils,
+  tables, strscans]
+import dochelpers, rstidx, rstast
+import std/private/miscdollars
 from highlite import SourceLanguage, getSourceLanguage
 
 when defined(nimPreviewSlimSystem):
@@ -40,7 +42,7 @@ type
     roNimFile                 ## set for Nim files where default interpreted
                               ## text role should be :nim:
     roSandboxDisabled         ## this option enables certain options
-                              ## (e.g. raw, include)
+                              ## (e.g. raw, include, importdoc)
                               ## which are disabled by default as they can
                               ## enable users to read arbitrary data and
                               ## perform XSS if the parser is used in a web
@@ -73,11 +75,17 @@ type
     mwUnsupportedLanguage = "language '$1' not supported",
     mwUnsupportedField = "field '$1' not supported",
     mwRstStyle = "RST style: $1",
+    mwUnusedImportdoc = "importdoc for '$1' is not used",
     meSandboxedDirective = "disabled directive: '$1'",
 
   MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
                        arg: string) {.closure, gcsafe.} ## what to do in case of an error
   FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}
+  FindRefFileHandler* =
+    proc (targetRelPath: string):
+         tuple[targetPath: string, linkRelPath: string] {.closure, gcsafe.}
+    ## returns where .html or .idx file should be found by its relative path;
+    ## `linkRelPath` is a prefix to be added before a link anchor from such file
 
 proc rstnodeToRefname*(n: PRstNode): string
 proc addNodes*(n: PRstNode): string
@@ -333,7 +341,8 @@ type
     arInternalRst,  ## For automatically generated RST anchors (from
                     ## headings, footnotes, inline internal targets):
                     ## case-insensitive, 1-space-significant (by RST spec)
-    arNim   ## For anchors generated by ``docgen.rst``: Nim-style case
+    arExternalRst,  ## For external .nim doc comments or .rst/.md
+    arNim   ## For anchors generated by ``docgen.nim``: Nim-style case
             ## sensitivity, etc. (see `proc normalizeNimName`_ for details)
     arHyperlink,  ## For links with manually set anchors in
                   ## form `text <pagename.html#anchor>`_
@@ -343,17 +352,22 @@ type
     footnoteAnchor = "footnote anchor",
     headlineAnchor = "implicitly-generated headline anchor"
   AnchorSubst = object
-    info: TLineInfo         # where the anchor was defined
+    info: TLineInfo         # the file where the anchor was defined
     priority: int
     case kind: range[arInternalRst .. arNim]
     of arInternalRst:
       anchorType: RstAnchorKind
       target: PRstNode
+    of arExternalRst:
+      anchorTypeExt: RstAnchorKind
+      refnameExt: string
     of arNim:
+      module: FileIndex     # anchor's module (generally not the same as file)
       tooltip: string       # displayed tooltip for Nim-generated anchors
       langSym: LangSymbol
       refname: string     # A reference name that will be inserted directly
                           # into HTML/Latex.
+      external: bool
   AnchorSubstTable = Table[string, seq[AnchorSubst]]
                          # use `seq` to account for duplicate anchors
   FootnoteType = enum
@@ -366,12 +380,18 @@ type
     kind: FootnoteType  # discriminator
     number: int         # valid for fnManualNumber (always) and fnAutoNumber,
                         # fnAutoNumberLabel after resolveSubs is called
-    autoNumIdx: int     # order of occurence: fnAutoNumber, fnAutoNumberLabel
-    autoSymIdx: int     # order of occurence: fnAutoSymbol
+    autoNumIdx: int     # order of occurrence: fnAutoNumber, fnAutoNumberLabel
+    autoSymIdx: int     # order of occurrence: fnAutoSymbol
     label: string       # valid for fnAutoNumberLabel
   RstFileTable* = object
     filenameToIdx*: Table[string, FileIndex]
     idxToFilename*: seq[string]
+  ImportdocInfo = object
+    used: bool             # was this import used?
+    fromInfo: TLineInfo    # place of `.. importdoc::` directive
+    idxPath: string        # full path to ``.idx`` file
+    linkRelPath: string    # prefix before target anchor
+    title: string          # document title obtained from ``.idx``
   RstSharedState = object
     options*: RstParseOptions   # parsing options
     hLevels: LevelMap           # hierarchy of heading styles
@@ -393,12 +413,17 @@ type
     footnotes: seq[FootnoteSubst] # correspondence b/w footnote label,
                                   # number, order of occurrence
     msgHandler: MsgHandler      # How to handle errors.
-    findFile: FindFileHandler   # How to find files.
+    findFile: FindFileHandler   # How to find files for include.
+    findRefFile: FindRefFileHandler
+                                # How to find files imported by importdoc.
     filenames*: RstFileTable    # map file name <-> FileIndex (for storing
                                 # file names for warnings after 1st stage)
     currFileIdx*: FileIndex     # current index in `filenames`
     tocPart*: seq[PRstNode]     # all the headings of a document
     hasToc*: bool
+    idxImports*: Table[string, ImportdocInfo]
+                                # map `importdoc`ed filename -> it's info
+    nimFileImported*: bool      # Was any ``.nim`` module `importdoc`ed ?
 
   PRstSharedState* = ref RstSharedState
   ManualAnchor = object
@@ -419,6 +444,7 @@ type
                                 ## because RST can have >1 alias per 1 anchor
 
   EParseError* = object of ValueError
+  SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.}
 
 const
   LineRstInit* = 1  ## Initial line number for standalone RST text
@@ -452,6 +478,9 @@ proc defaultFindFile*(filename: string): string =
   if fileExists(filename): result = filename
   else: result = ""
 
+proc defaultFindRefFile*(filename: string): (string, string) =
+  (filename, "")
+
 proc defaultRole(options: RstParseOptions): string =
   if roNimFile in options: "nim" else: "literal"
 
@@ -492,12 +521,19 @@ proc getFilename(filenames: RstFileTable, fid: FileIndex): string =
         $fid.int, $(filenames.len - 1)])
   result = filenames.idxToFilename[fid.int]
 
+proc getFilename(s: PRstSharedState, subst: AnchorSubst): string =
+  getFilename(s.filenames, subst.info.fileIndex)
+
+proc getModule(s: PRstSharedState, subst: AnchorSubst): string =
+  result = getFilename(s.filenames, subst.module)
+
 proc currFilename(s: PRstSharedState): string =
   getFilename(s.filenames, s.currFileIdx)
 
 proc newRstSharedState*(options: RstParseOptions,
                         filename: string,
                         findFile: FindFileHandler,
+                        findRefFile: FindRefFileHandler,
                         msgHandler: MsgHandler,
                         hasToc: bool): PRstSharedState =
   let r = defaultRole(options)
@@ -507,6 +543,9 @@ proc newRstSharedState*(options: RstParseOptions,
       options: options,
       msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler,
       findFile: if not isNil(findFile): findFile else: defaultFindFile,
+      findRefFile:
+        if not isNil(findRefFile): findRefFile
+        else: defaultFindRefFile,
       hasToc: hasToc
   )
   setCurrFilename(result, filename)
@@ -525,6 +564,14 @@ proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
 proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) =
   s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg)
 
+proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string;
+                line, col: int) =
+  s.msgHandler(s.currFilename, line, col, msgKind, arg)
+
+proc rstMessage(s: PRstSharedState, filename: string, msgKind: MsgKind,
+                arg: string) =
+  s.msgHandler(filename, LineRstInit, ColRstInit, msgKind, arg)
+
 proc rstMessage*(filenames: RstFileTable, f: MsgHandler,
                  info: TLineInfo, msgKind: MsgKind, arg: string) =
   ## Print warnings using `info`, i.e. in 2nd-pass warnings for
@@ -541,6 +588,31 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) =
                              p.col + currentTok(p).col, msgKind,
                              currentTok(p).symbol)
 
+# Functions `isPureRst` & `stopOrWarn` address differences between
+# Markdown and RST:
+# * Markdown always tries to continue working. If it is really impossible
+#   to parse a markup element, its proc just returns `nil` and parsing
+#   continues for it as for normal text paragraph.
+#   The downside is that real mistakes/typos are often silently ignored.
+#   The same applies to legacy `RstMarkdown` mode for nimforum.
+# * RST really signals errors. The downside is that it's more intrusive -
+#   the user must escape special syntax with \ explicitly.
+#
+# TODO: we need to apply this strategy to all markup elements eventually.
+
+func isPureRst(p: RstParser): bool = roSupportMarkdown notin p.s.options
+func isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options
+func isMd(p: RstParser): bool = roPreferMarkdown in p.s.options
+func isMd(s: PRstSharedState): bool = roPreferMarkdown in s.options
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg)
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string, line, col: int) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg, line, col)
+
 proc currInd(p: RstParser): int =
   result = p.indentStack[high(p.indentStack)]
 
@@ -756,6 +828,14 @@ proc internalRefPriority(k: RstAnchorKind): int =
   of footnoteAnchor: result = 4
   of headlineAnchor: result = 3
 
+proc `$`(subst: AnchorSubst): string =  # for debug
+  let s =
+    case subst.kind
+    of arInternalRst: "type=" & $subst.anchorType
+    of arExternalRst: "type=" & $subst.anchorTypeExt
+    of arNim: "langsym=" & $subst.langSym
+  result = "(kind=$1, priority=$2, $3)" % [$subst.kind, $subst.priority, s]
+
 proc addAnchorRst(p: var RstParser, name: string, target: PRstNode,
                   anchorType: RstAnchorKind) =
   ## Associates node `target` (which has field `anchor`) with an
@@ -771,31 +851,49 @@ proc addAnchorRst(p: var RstParser, name: string, target: PRstNode,
                     info: prevLineInfo(p), anchorType: anchorType))
   p.curAnchors.setLen 0
 
-proc addAnchorNim*(s: var PRstSharedState, refn: string, tooltip: string,
+proc addAnchorExtRst(s: var PRstSharedState, key: string, refn: string,
+                  anchorType: RstAnchorKind, info: TLineInfo) =
+  let name = key.toLowerAscii
+  let prio = internalRefPriority(anchorType)
+  s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add(
+      AnchorSubst(kind: arExternalRst, refnameExt: refn, priority: prio,
+                  info: info,
+                  anchorTypeExt: anchorType))
+
+proc addAnchorNim*(s: var PRstSharedState, external: bool, refn: string, tooltip: string,
                    langSym: LangSymbol, priority: int,
-                   info: TLineInfo) =
+                   info: TLineInfo, module: FileIndex) =
   ## Adds an anchor `refn`, which follows
   ## the rule `arNim` (i.e. a symbol in ``*.nim`` file)
   s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add(
-      AnchorSubst(kind: arNim, refname: refn, langSym: langSym,
+      AnchorSubst(kind: arNim, external: external, refname: refn, langSym: langSym,
                   tooltip: tooltip, priority: priority,
                   info: info))
 
 proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode,
                        info: TLineInfo):
                       seq[AnchorSubst] =
-  let langSym = toLangSymbol(signature)
+  var langSym: LangSymbol
+  try:
+    langSym = toLangSymbol(signature)
+  except ValueError:  # parsing failed, not a Nim symbol
+    return
   let substitutions = s.anchors.getOrDefault(langSym.name,
                                              newSeq[AnchorSubst]())
   if substitutions.len == 0:
     return
-  # map symKind (like "proc") -> found symbols/groups:
-  var found: Table[string, seq[AnchorSubst]]
-  for s in substitutions:
-    if s.kind == arNim:
-      if match(s.langSym, langSym):
-        found.mgetOrPut(s.langSym.symKind, newSeq[AnchorSubst]()).add s
-  for symKind, sList in found:
+  # logic to select only groups instead of concrete symbols
+  # with overloads, note that the same symbol can be defined
+  # in multiple modules and `importdoc`ed:
+  type GroupKey = tuple[symKind: string, origModule: string]
+  # map (symKind, file) (like "proc", "os.nim") -> found symbols/groups:
+  var found: Table[GroupKey, seq[AnchorSubst]]
+  for subst in substitutions:
+    if subst.kind == arNim:
+      if match(subst.langSym, langSym):
+        let key: GroupKey = (subst.langSym.symKind, getModule(s, subst))
+        found.mgetOrPut(key, newSeq[AnchorSubst]()).add subst
+  for key, sList in found:
     if sList.len == 1:
       result.add sList[0]
     else:  # > 1, there are overloads, potential ambiguity in this `symKind`
@@ -812,14 +910,16 @@ proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode,
             result.add s
             foundGroup = true
             break
-        doAssert foundGroup, "docgen has not generated the group"
+        doAssert(foundGroup,
+                 "docgen has not generated the group for $1 (file $2)" % [
+                 langSym.name, getModule(s, sList[0]) ])
 
 proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo):
                       seq[AnchorSubst] =
   let name = linkText.toLowerAscii
   let substitutions = s.anchors.getOrDefault(name, newSeq[AnchorSubst]())
   for s in substitutions:
-    if s.kind == arInternalRst:
+    if s.kind in {arInternalRst, arExternalRst}:
       result.add s
 
 proc addFootnoteNumManual(p: var RstParser, num: int) =
@@ -1426,7 +1526,7 @@ proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode =
     result = nil
   else:
     result = newRstNode(rnFieldList)
-  while currentTok(p).kind != tkIndent:
+  while currentTok(p).kind notin {tkIndent, tkEof}:
     if currentTok(p).kind == tkWhite:
       inc p.idx
     else:
@@ -1503,6 +1603,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
   else:
     args = nil
   var n = newLeaf("")
+  var isFirstLine = true
   while true:
     if currentTok(p).kind == tkEof:
       rstMessage(p, meMissingClosing,
@@ -1514,7 +1615,8 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
       inc p.idx, 2
       break
     elif currentTok(p).kind == tkIndent:
-      n.text.add "\n"
+      if not isFirstLine:
+        n.text.add "\n"
       if currentTok(p).ival > baseCol:
         n.text.add " ".repeat(currentTok(p).ival - baseCol)
       elif currentTok(p).ival < baseCol:
@@ -1524,6 +1626,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
     else:
       n.text.add(currentTok(p).symbol)
       inc p.idx
+    isFirstLine = false
   result.sons[0] = args
   if result.sons[2] == nil:
     var lb = newRstNode(rnLiteralBlock)
@@ -1597,7 +1700,7 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
   else:
     result = false
 
-proc getFootnoteType(label: PRstNode): (FootnoteType, int) =
+proc getRstFootnoteType(label: PRstNode): (FootnoteType, int) =
   if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and
       label.sons[0].text == "#":
     if label.sons.len == 1:
@@ -1610,12 +1713,23 @@ proc getFootnoteType(label: PRstNode): (FootnoteType, int) =
   elif label.len == 1 and label.sons[0].kind == rnLeaf:
     try:
       result = (fnManualNumber, parseInt(label.sons[0].text))
-    except:
+    except ValueError:
       result = (fnCitation, -1)
   else:
     result = (fnCitation, -1)
 
-proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode =
+proc getMdFootnoteType(label: PRstNode): (FootnoteType, int) =
+  try:
+    result = (fnManualNumber, parseInt(label.sons[0].text))
+  except ValueError:
+    result = (fnAutoNumberLabel, -1)
+
+proc getFootnoteType(s: PRstSharedState, label: PRstNode): (FootnoteType, int) =
+  ## Returns footnote/citation type and manual number (if present).
+  if isMd(s): getMdFootnoteType(label)
+  else: getRstFootnoteType(label)
+
+proc parseRstFootnoteName(p: var RstParser, reference: bool): PRstNode =
   ## parse footnote/citation label. Precondition: start at `[`.
   ## Label text should be valid ref. name symbol, otherwise nil is returned.
   var i = p.idx + 1
@@ -1645,6 +1759,41 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode =
     inc i
   p.idx = i
 
+proc isMdFootnoteName(p: RstParser, reference: bool): bool =
+  ## Pandoc Markdown footnote extension.
+  let j = p.idx
+  result = p.tok[j].symbol == "[" and p.tok[j+1].symbol == "^" and
+           p.tok[j+2].kind == tkWord
+
+proc parseMdFootnoteName(p: var RstParser, reference: bool): PRstNode =
+  if isMdFootnoteName(p, reference):
+    result = newRstNode(rnInner)
+    var j = p.idx + 2
+    while p.tok[j].kind in {tkWord, tkOther} or
+        validRefnamePunct(p.tok[j].symbol):
+      result.add newLeaf(p.tok[j].symbol)
+      inc j
+    if j == p.idx + 2:
+      return nil
+    if p.tok[j].symbol == "]":
+      if reference:
+        p.idx = j + 1  # skip ]
+      else:
+        if p.tok[j+1].symbol == ":":
+          p.idx = j + 2  # skip ]:
+        else:
+          result = nil
+    else:
+      result = nil
+  else:
+    result = nil
+
+proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode =
+  if isMd(p): parseMdFootnoteName(p, reference)
+  else:
+    if isInlineMarkupStart(p, "["): parseRstFootnoteName(p, reference)
+    else: nil
+
 proc isMarkdownCodeBlock(p: RstParser, idx: int): bool =
   let tok = p.tok[idx]
   template allowedSymbol: bool =
@@ -1711,16 +1860,12 @@ proc parseInline(p: var RstParser, father: PRstNode) =
       var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1))
       parseUntil(p, n, "|", false)
       father.add(n)
-    elif roSupportMarkdown in p.s.options and
-        currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and
-        parseMarkdownLink(p, father):
-      discard "parseMarkdownLink already processed it"
-    elif isInlineMarkupStart(p, "[") and nextTok(p).symbol != "[" and
+    elif currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and
          (n = parseFootnoteName(p, reference=true); n != nil):
       var nn = newRstNode(rnFootnoteRef)
       nn.info = lineInfo(p, saveIdx+1)
       nn.add n
-      let (fnType, _) = getFootnoteType(n)
+      let (fnType, _) = getFootnoteType(p.s, n)
       case fnType
       of fnAutoSymbol:
         p.s.lineFootnoteSymRef.add lineInfo(p)
@@ -1728,6 +1873,10 @@ proc parseInline(p: var RstParser, father: PRstNode) =
         p.s.lineFootnoteNumRef.add lineInfo(p)
       else: discard
       father.add(nn)
+    elif roSupportMarkdown in p.s.options and
+        currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and
+        parseMarkdownLink(p, father):
+      discard "parseMarkdownLink already processed it"
     else:
       if roSupportSmilies in p.s.options:
         let n = parseSmiley(p)
@@ -1865,8 +2014,26 @@ proc getMdBlockIndent(p: RstParser): int =
     else:
       result = nextIndent                 # allow parsing next lines [case.3]
 
-template isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options
-template isMd(p: RstParser): bool = roPreferMarkdown in p.s.options
+proc indFollows(p: RstParser): bool =
+  result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p)
+
+proc parseBlockContent(p: var RstParser, father: var PRstNode,
+                       contentParser: SectionParser): bool {.gcsafe.} =
+  ## parse the final content part of explicit markup blocks (directives,
+  ## footnotes, etc). Returns true if succeeded.
+  if currentTok(p).kind != tkIndent or indFollows(p):
+    let blockIndent = getWrappableIndent(p)
+    pushInd(p, blockIndent)
+    let content = contentParser(p)
+    popInd(p)
+    father.add content
+    result = true
+
+proc parseSectionWrapper(p: var RstParser): PRstNode =
+  result = newRstNode(rnInner)
+  parseSection(p, result)
+  while result.kind == rnInner and result.len == 1:
+    result = result.sons[0]
 
 proc parseField(p: var RstParser): PRstNode =
   ## Returns a parsed rnField node.
@@ -2052,17 +2219,20 @@ proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool =
     while p.tok[i].kind notin {tkEof, tkIndent}:
       headlineLen += p.tok[i].symbol.len
       inc i
-    result = p.tok[adornmentIdx].symbol.len >= headlineLen and
-         headlineLen != 0
-    if result:
-      result = result and p.tok[i].kind == tkIndent and
-         p.tok[i+1].kind == tkAdornment and
-         p.tok[i+1].symbol == p.tok[adornmentIdx].symbol
-      if not result:
-        failure = "(underline '" & p.tok[i+1].symbol & "' does not match " &
-            "overline '" & p.tok[adornmentIdx].symbol & "')"
-    else:
-      failure = "(overline '" & p.tok[adornmentIdx].symbol & "' is too short)"
+    if p.tok[i].kind == tkIndent and
+       p.tok[i+1].kind == tkAdornment and
+       p.tok[i+1].symbol[0] == p.tok[adornmentIdx].symbol[0]:
+      result = p.tok[adornmentIdx].symbol.len >= headlineLen and
+           headlineLen != 0
+      if result:
+        result = p.tok[i+1].symbol == p.tok[adornmentIdx].symbol
+        if not result:
+          failure = "(underline '" & p.tok[i+1].symbol & "' does not match " &
+              "overline '" & p.tok[adornmentIdx].symbol & "')"
+      else:
+        failure = "(overline '" & p.tok[adornmentIdx].symbol & "' is too short)"
+    else:  # it's not overline/underline section, not reporting error
+      return false
   if not result:
     rstMessage(p, meNewSectionExpected, failure)
 
@@ -2172,7 +2342,7 @@ proc whichSection(p: RstParser): RstNodeKind =
     # for punctuation sequences that can be both tkAdornment and tkPunct
     if isMarkdownCodeBlock(p):
       return rnCodeBlock
-    elif currentTok(p).symbol == "::":
+    elif isRst(p) and currentTok(p).symbol == "::":
       return rnLiteralBlock
     elif currentTok(p).symbol == ".."  and
        nextTok(p).kind in {tkWhite, tkIndent}:
@@ -2189,16 +2359,19 @@ proc whichSection(p: RstParser): RstNodeKind =
       result = rnLineBlock
     elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p):
       result = rnMarkdownBlockQuote
-    elif match(p, p.idx + 1, "i") and isAdornmentHeadline(p, p.idx):
+    elif (match(p, p.idx + 1, "i") and not match(p, p.idx + 2, "I")) and
+         isAdornmentHeadline(p, p.idx):
       result = rnOverline
     else:
-      result = rnLeaf
+      result = rnParagraph
   of tkPunct:
     if isMarkdownHeadline(p):
       result = rnMarkdownHeadline
     elif roSupportMarkdown in p.s.options and predNL(p) and
         match(p, p.idx, "| w") and findPipe(p, p.idx+3):
       result = rnMarkdownTable
+    elif isMd(p) and isMdFootnoteName(p, reference=false):
+      result = rnFootnote
     elif currentTok(p).symbol == "|" and isLineBlock(p):
       result = rnLineBlock
     elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p):
@@ -2362,8 +2535,10 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
     of tkIndent:
       if nextTok(p).kind == tkIndent:
         inc p.idx
-        break
-      elif currentTok(p).ival == currInd(p):
+        break  # blank line breaks paragraph for both Md & Rst
+      elif currentTok(p).ival == currInd(p) or (
+          isMd(p) and currentTok(p).ival > currInd(p)):
+          # (Md allows adding additional indentation inside paragraphs)
         inc p.idx
         case whichSection(p)
         of rnParagraph, rnLeaf, rnHeadline, rnMarkdownHeadline,
@@ -2373,11 +2548,14 @@ proc parseParagraph(p: var RstParser, result: PRstNode) =
           result.addIfNotNil(parseLineBlock(p))
         of rnMarkdownBlockQuote:
           result.addIfNotNil(parseMarkdownBlockQuote(p))
-        else: break
+        else:
+          dec p.idx  # allow subsequent block to be parsed as another section
+          break
       else:
         break
     of tkPunct:
-      if (let literalBlockKind = whichRstLiteralBlock(p);
+      if isRst(p) and (
+          let literalBlockKind = whichRstLiteralBlock(p);
           literalBlockKind != lbNone):
         result.add newLeaf(":")
         inc p.idx            # skip '::'
@@ -2515,11 +2693,11 @@ proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int =
 proc checkColumns(p: RstParser, cols: RstCols) =
   var i = p.idx
   if p.tok[i].symbol[0] != '=':
-    rstMessage(p, mwRstStyle,
+    stopOrWarn(p, meIllformedTable,
                "only tables with `=` columns specification are allowed")
   for col in 0 ..< cols.len:
     if tokEnd(p, i) != cols[col].stop:
-      rstMessage(p, meIllformedTable,
+      stopOrWarn(p, meIllformedTable,
                  "end of table column #$1 should end at position $2" % [
                    $(col+1), $(cols[col].stop+ColRstOffset)],
                  p.tok[i].line, tokEnd(p, i))
@@ -2528,12 +2706,12 @@ proc checkColumns(p: RstParser, cols: RstCols) =
       if p.tok[i].kind == tkWhite:
         inc i
       if p.tok[i].kind notin {tkIndent, tkEof}:
-        rstMessage(p, meIllformedTable, "extraneous column specification")
+        stopOrWarn(p, meIllformedTable, "extraneous column specification")
     elif p.tok[i].kind == tkWhite:
       inc i
     else:
-      rstMessage(p, meIllformedTable, "no enough table columns",
-                 p.tok[i].line, p.tok[i].col)
+      stopOrWarn(p, meIllformedTable,
+                 "no enough table columns", p.tok[i].line, p.tok[i].col)
 
 proc getSpans(p: RstParser, nextLine: int,
               cols: RstCols, unitedCols: RstCols): seq[int] =
@@ -2588,17 +2766,18 @@ proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNo
       if tokEnd(p) <= colEnd(nCell):
         if tokStart(p) < colStart(nCell):
           if currentTok(p).kind != tkWhite:
-            rstMessage(p, meIllformedTable,
+            stopOrWarn(p, meIllformedTable,
                        "this word crosses table column from the left")
-          else:
-            inc p.idx
+            row[nCell].add(currentTok(p).symbol)
         else:
           row[nCell].add(currentTok(p).symbol)
-          inc p.idx
+        inc p.idx
       else:
         if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite:
-          rstMessage(p, meIllformedTable,
+          stopOrWarn(p, meIllformedTable,
                      "this word crosses table column from the right")
+          row[nCell].add(currentTok(p).symbol)
+          inc p.idx
         inc nCell
     if currentTok(p).kind == tkIndent: inc p.idx
     if tokEnd(p) <= colEnd(0): break
@@ -2761,7 +2940,7 @@ proc parseOptionList(p: var RstParser): PRstNode =
       break
 
 proc parseMdDefinitionList(p: var RstParser): PRstNode =
-  ## Parses (Pandoc/kramdown/PHPextra) Mardkown definition lists.
+  ## Parses (Pandoc/kramdown/PHPextra) Markdown definition lists.
   result = newRstNodeA(p, rnMdDefList)
   let termCol = currentTok(p).col
   while true:
@@ -2896,13 +3075,13 @@ proc parseEnumList(p: var RstParser): PRstNode =
       let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol
       # check that it's in sequence: enumerator == next(prevEnum)
       if "n" in wildcards[w]:  # arabic numeral
-        let prevEnumI = try: parseInt(prevEnum) except: 1
+        let prevEnumI = try: parseInt(prevEnum) except ValueError: 1
         if enumerator in autoEnums:
           if prevAE != "" and enumerator != prevAE:
             break
           prevAE = enumerator
           curEnum = prevEnumI + 1
-        else: curEnum = (try: parseInt(enumerator) except: 1)
+        else: curEnum = (try: parseInt(enumerator) except ValueError: 1)
         if curEnum - prevEnumI != 1:
           break
         prevEnum = enumerator
@@ -2917,6 +3096,57 @@ proc parseEnumList(p: var RstParser): PRstNode =
     else:
       break
 
+proc prefix(ftnType: FootnoteType): string =
+  case ftnType
+  of fnManualNumber: result = "footnote-"
+  of fnAutoNumber: result = "footnoteauto-"
+  of fnAutoNumberLabel: result = "footnote-"
+  of fnAutoSymbol: result = "footnotesym-"
+  of fnCitation: result = "citation-"
+
+proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} =
+  ## Parses footnotes and citations, always returns 2 sons:
+  ##
+  ## 1) footnote label, always containing rnInner with 1 or more sons
+  ## 2) footnote body, which may be nil
+  var label: PRstNode
+  if isRst(p):
+    inc p.idx  # skip space after `..`
+  label = parseFootnoteName(p, reference=false)
+  if label == nil:
+    if isRst(p):
+      dec p.idx
+    return nil
+  result = newRstNode(rnFootnote)
+  result.add label
+  let (fnType, i) = getFootnoteType(p.s, label)
+  var name = ""
+  var anchor = fnType.prefix
+  case fnType
+  of fnManualNumber:
+    addFootnoteNumManual(p, i)
+    anchor.add $i
+  of fnAutoNumber, fnAutoNumberLabel:
+    name = rstnodeToRefname(label)
+    addFootnoteNumAuto(p, name)
+    if fnType == fnAutoNumberLabel:
+      anchor.add name
+    else:  # fnAutoNumber
+      result.order = p.s.lineFootnoteNum.len
+      anchor.add $result.order
+  of fnAutoSymbol:
+    addFootnoteSymAuto(p)
+    result.order = p.s.lineFootnoteSym.len
+    anchor.add $p.s.lineFootnoteSym.len
+  of fnCitation:
+    anchor.add rstnodeToRefname(label)
+  addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor)
+  result.anchor = anchor
+  if currentTok(p).kind == tkWhite: inc p.idx
+  discard parseBlockContent(p, result, parseSectionWrapper)
+  if result.len < 2:
+    result.add nil
+
 proc sonKind(father: PRstNode, i: int): RstNodeKind =
   result = rnLeaf
   if i < father.len: result = father.sons[i].kind
@@ -2932,11 +3162,11 @@ proc parseSection(p: var RstParser, result: PRstNode) =
       elif currentTok(p).ival > currInd(p):
         if roPreferMarkdown in p.s.options:  # Markdown => normal paragraphs
           if currentTok(p).ival - currInd(p) >= 4:
-            rstMessage(p, mwRstStyle,
-                       "Markdown indented code not implemented")
-          pushInd(p, currentTok(p).ival)
-          parseSection(p, result)
-          popInd(p)
+            result.add parseLiteralBlock(p)
+          else:
+            pushInd(p, currentTok(p).ival)
+            parseSection(p, result)
+            popInd(p)
         else:  # RST mode => block quotes
           pushInd(p, currentTok(p).ival)
           var a = newRstNodeA(p, rnBlockQuote)
@@ -2959,6 +3189,7 @@ proc parseSection(p: var RstParser, result: PRstNode) =
     of rnLineBlock: a = parseLineBlock(p)
     of rnMarkdownBlockQuote: a = parseMarkdownBlockQuote(p)
     of rnDirective: a = parseDotDot(p)
+    of rnFootnote: a = parseFootnote(p)
     of rnEnumList: a = parseEnumList(p)
     of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)")
     of rnParagraph: discard
@@ -2984,12 +3215,6 @@ proc parseSection(p: var RstParser, result: PRstNode) =
     result.sons[0] = newRstNode(rnInner, result.sons[0].sons,
                                 anchor=result.sons[0].anchor)
 
-proc parseSectionWrapper(p: var RstParser): PRstNode =
-  result = newRstNode(rnInner)
-  parseSection(p, result)
-  while result.kind == rnInner and result.len == 1:
-    result = result.sons[0]
-
 proc parseDoc(p: var RstParser): PRstNode =
   result = parseSectionWrapper(p)
   if currentTok(p).kind != tkEof:
@@ -2999,7 +3224,6 @@ type
   DirFlag = enum
     hasArg, hasOptions, argIsFile, argIsWord
   DirFlags = set[DirFlag]
-  SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.}
 
 proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode =
   ## Parses arguments and options for a directive block.
@@ -3042,21 +3266,6 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode
       popInd(p)
   result.add(options)
 
-proc indFollows(p: RstParser): bool =
-  result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p)
-
-proc parseBlockContent(p: var RstParser, father: var PRstNode,
-                       contentParser: SectionParser): bool {.gcsafe.} =
-  ## parse the final content part of explicit markup blocks (directives,
-  ## footnotes, etc). Returns true if succeeded.
-  if currentTok(p).kind != tkIndent or indFollows(p):
-    let blockIndent = getWrappableIndent(p)
-    pushInd(p, blockIndent)
-    let content = contentParser(p)
-    popInd(p)
-    father.add content
-    result = true
-
 proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags,
                     contentParser: SectionParser): PRstNode =
   ## A helper proc that does main work for specific directive procs.
@@ -3248,6 +3457,15 @@ proc dirRaw(p: var RstParser): PRstNode =
   else:
     dirRawAux(p, result, rnRaw, parseSectionWrapper)
 
+proc dirImportdoc(p: var RstParser): PRstNode =
+  result = parseDirective(p, rnDirective, {}, parseLiteralBlock)
+  assert result.sons[2].kind == rnLiteralBlock
+  assert result.sons[2].sons[0].kind == rnLeaf
+  let filenames: seq[string] = split(result.sons[2].sons[0].text, seps = {','})
+  proc rmSpaces(s: string): string = s.split.join("")
+  for origFilename in filenames:
+    p.s.idxImports[origFilename.rmSpaces] = ImportdocInfo(fromInfo: lineInfo(p))
+
 proc selectDir(p: var RstParser, d: string): PRstNode =
   result = nil
   let tok = p.tok[p.idx-2] # report on directive in ".. directive::"
@@ -3268,6 +3486,7 @@ proc selectDir(p: var RstParser, d: string): PRstNode =
   of "hint": result = dirAdmonition(p, d)
   of "image": result = dirImage(p)
   of "important": result = dirAdmonition(p, d)
+  of "importdoc": result = dirImportdoc(p)
   of "include": result = dirInclude(p)
   of "index": result = dirIndex(p)
   of "note": result = dirAdmonition(p, d)
@@ -3283,54 +3502,6 @@ proc selectDir(p: var RstParser, d: string): PRstNode =
   else:
     rstMessage(p, meInvalidDirective, d, tok.line, tok.col)
 
-proc prefix(ftnType: FootnoteType): string =
-  case ftnType
-  of fnManualNumber: result = "footnote-"
-  of fnAutoNumber: result = "footnoteauto-"
-  of fnAutoNumberLabel: result = "footnote-"
-  of fnAutoSymbol: result = "footnotesym-"
-  of fnCitation: result = "citation-"
-
-proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} =
-  ## Parses footnotes and citations, always returns 2 sons:
-  ##
-  ## 1) footnote label, always containing rnInner with 1 or more sons
-  ## 2) footnote body, which may be nil
-  inc p.idx
-  let label = parseFootnoteName(p, reference=false)
-  if label == nil:
-    dec p.idx
-    return nil
-  result = newRstNode(rnFootnote)
-  result.add label
-  let (fnType, i) = getFootnoteType(label)
-  var name = ""
-  var anchor = fnType.prefix
-  case fnType
-  of fnManualNumber:
-    addFootnoteNumManual(p, i)
-    anchor.add $i
-  of fnAutoNumber, fnAutoNumberLabel:
-    name = rstnodeToRefname(label)
-    addFootnoteNumAuto(p, name)
-    if fnType == fnAutoNumberLabel:
-      anchor.add name
-    else:  # fnAutoNumber
-      result.order = p.s.lineFootnoteNum.len
-      anchor.add $result.order
-  of fnAutoSymbol:
-    addFootnoteSymAuto(p)
-    result.order = p.s.lineFootnoteSym.len
-    anchor.add $p.s.lineFootnoteSym.len
-  of fnCitation:
-    anchor.add rstnodeToRefname(label)
-  addAnchorRst(p, anchor, target = result, anchorType = footnoteAnchor)
-  result.anchor = anchor
-  if currentTok(p).kind == tkWhite: inc p.idx
-  discard parseBlockContent(p, result, parseSectionWrapper)
-  if result.len < 2:
-    result.add nil
-
 proc parseDotDot(p: var RstParser): PRstNode =
   # parse "explicit markup blocks"
   result = nil
@@ -3398,77 +3569,200 @@ proc rstParsePass1*(fragment: string,
   getTokens(fragment, p.tok)
   result = parseDoc(p)
 
-proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) =
+proc extractLinkEnd(x: string): string =
+  ## From links like `path/to/file.html#/%` extract `file.html#/%`.
+  let i = find(x, '#')
+  let last =
+    if i >= 0: i
+    else: x.len - 1
+  let j = rfind(x, '/', start=0, last=last)
+  if j >= 0:
+    result = x[j+1 .. ^1]
+  else:
+    result = x
+
+proc loadIdxFile(s: var PRstSharedState, origFilename: string) =
+  doAssert roSandboxDisabled in s.options
+  var info: TLineInfo
+  info.fileIndex = addFilename(s, origFilename)
+  var (dir, basename, ext) = origFilename.splitFile
+  if ext notin [".md", ".rst", ".nim", ""]:
+    rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo,
+               meCannotOpenFile, origFilename & ": unknown extension")
+  let idxFilename = dir / basename & ".idx"
+  let (idxPath, linkRelPath) = s.findRefFile(idxFilename)
+  s.idxImports[origFilename].linkRelPath = linkRelPath
+  var
+    fileEntries: seq[IndexEntry]
+    title: IndexEntry
+  try:
+    (fileEntries, title) = parseIdxFile(idxPath)
+  except IOError:
+    rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo,
+               meCannotOpenFile, idxPath)
+  except ValueError as e:
+    s.msgHandler(idxPath, LineRstInit, ColRstInit, meInvalidField, e.msg)
+
+  var isMarkup = false  # for sanity check to avoid mixing .md <-> .nim
+  for entry in fileEntries:
+    # Though target .idx already has inside it the path to HTML relative
+    # project's root, we won't rely on it and use `linkRelPath` instead.
+    let refn = extractLinkEnd(entry.link)
+    # select either markup (rst/md) or Nim cases:
+    if entry.kind in {ieMarkupTitle, ieNimTitle}:
+      s.idxImports[origFilename].title = entry.keyword
+    case entry.kind
+    of ieIdxRole, ieHeading, ieMarkupTitle:
+      if ext == ".nim" and entry.kind == ieMarkupTitle:
+        rstMessage(s, idxPath, meInvalidField,
+                   $ieMarkupTitle & " in supposedly .nim-derived file")
+      if entry.kind == ieMarkupTitle:
+        isMarkup = true
+      info.line = entry.line.uint16
+      addAnchorExtRst(s, key = entry.keyword, refn = refn,
+                      anchorType = headlineAnchor, info=info)
+    of ieNim, ieNimGroup, ieNimTitle:
+      if ext in [".md", ".rst"] or isMarkup:
+        rstMessage(s, idxPath, meInvalidField,
+                   $entry.kind & " in supposedly markup-derived file")
+      s.nimFileImported = true
+      var langSym: LangSymbol
+      if entry.kind in {ieNim, ieNimTitle}:
+        var q: RstParser
+        initParser(q, s)
+        info.line = entry.line.uint16
+        setLen(q.tok, 0)
+        q.idx = 0
+        getTokens(entry.linkTitle, q.tok)
+        var sons = newSeq[PRstNode](q.tok.len)
+        for i in 0 ..< q.tok.len: sons[i] = newLeaf(q.tok[i].symbol)
+        let linkTitle = newRstNode(rnInner, sons)
+        langSym = linkTitle.toLangSymbol
+      else:  # entry.kind == ieNimGroup
+        langSym = langSymbolGroup(kind=entry.linkTitle, name=entry.keyword)
+      addAnchorNim(s, external = true, refn = refn, tooltip = entry.linkDesc,
+                   langSym = langSym, priority = -4, # lowest
+                   info = info, module = info.fileIndex)
+  doAssert s.idxImports[origFilename].title != ""
+
+proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) =
   ## Records titles in node `mainNode` and orders footnotes.
   countTitles(s, mainNode)
   fixHeadlines(s)
   orderFootnotes(s)
+  if importdoc:
+    for origFilename in s.idxImports.keys:
+      loadIdxFile(s, origFilename)
 
 proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode =
-    # Associate this link alias with its target and change node kind to
-    # rnHyperlink or rnInternalRef appropriately.
-    var desc, alias: PRstNode
-    if n.kind == rnPandocRef:  # link like [desc][alias]
-      desc = n.sons[0]
-      alias = n.sons[1]
-    else:  # n.kind == rnRstRef, link like `desc=alias`_
-      desc = n
-      alias = n
-    type LinkDef = object
-      ar: AnchorRule
-      priority: int
-      tooltip: string
-      target: PRstNode
-      info: TLineInfo
-    proc cmp(x, y: LinkDef): int =
-      result = cmp(x.priority, y.priority)
-      if result == 0:
-        result = cmp(x.target, y.target)
-    var foundLinks: seq[LinkDef]
-    let refn = rstnodeToRefname(alias)
-    var hyperlinks = findRef(s, refn)
-    for y in hyperlinks:
-      foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind),
-                             target: y.value, info: y.info,
-                             tooltip: "(" & $y.kind & ")")
-    let substRst = findMainAnchorRst(s, alias.addNodes, n.info)
-    for subst in substRst:
-      foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority,
-                             target: newLeaf(subst.target.anchor),
-                             info: subst.info,
-                             tooltip: "(" & $subst.anchorType & ")")
-    # find anchors automatically generated from Nim symbols
-    if roNimFile in s.options:
-      let substNim = findMainAnchorNim(s, signature=alias, n.info)
-      for subst in substNim:
-        foundLinks.add LinkDef(ar: arNim, priority: subst.priority,
-                               target: newLeaf(subst.refname),
-                               info: subst.info, tooltip: subst.tooltip)
-    foundLinks.sort(cmp = cmp, order = Descending)
-    let aliasStr = addNodes(alias)
-    if foundLinks.len >= 1:
-      let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink
-                 elif foundLinks[0].ar == arNim: rnNimdocRef
-                 else: rnInternalRef
-      result = newRstNode(kind)
-      result.sons = @[newRstNode(rnInner, desc.sons), foundLinks[0].target]
-      if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip
-      if foundLinks.len > 1:  # report ambiguous link
-        var targets = newSeq[string]()
-        for l in foundLinks:
-          var t = "    "
-          if s.filenames.len > 1:
-            t.add getFilename(s.filenames, l.info.fileIndex)
-          let n = l.info.line
-          let c = l.info.col + ColRstOffset
-          t.add "($1, $2): $3" % [$n, $c, l.tooltip]
-          targets.add t
-        rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink,
-                   "`$1`\n  clash:\n$2" % [
-                     aliasStr, targets.join("\n")])
-    else:  # nothing found
-      result = n
-      rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr)
+  # Associate this link alias with its target and change node kind to
+  # rnHyperlink or rnInternalRef appropriately.
+  var desc, alias: PRstNode
+  if n.kind == rnPandocRef:  # link like [desc][alias]
+    desc = n.sons[0]
+    alias = n.sons[1]
+  else:  # n.kind == rnRstRef, link like `desc=alias`_
+    desc = n
+    alias = n
+  type LinkDef = object
+    ar: AnchorRule
+    priority: int
+    tooltip: string
+    target: PRstNode
+    info: TLineInfo
+    externFilename: string
+      # when external anchor: origin filename where anchor was defined
+    isTitle: bool
+  proc cmp(x, y: LinkDef): int =
+    result = cmp(x.priority, y.priority)
+    if result == 0:
+      result = cmp(x.target, y.target)
+  var foundLinks: seq[LinkDef]
+  let refn = rstnodeToRefname(alias)
+  var hyperlinks = findRef(s, refn)
+  for y in hyperlinks:
+    foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind),
+                           target: y.value, info: y.info,
+                           tooltip: "(" & $y.kind & ")")
+  let substRst = findMainAnchorRst(s, alias.addNodes, n.info)
+  template getExternFilename(subst: AnchorSubst): string =
+    if subst.kind == arExternalRst or
+        (subst.kind == arNim and subst.external):
+      getFilename(s, subst)
+    else: ""
+  for subst in substRst:
+    var refname, fullRefname: string
+    if subst.kind == arInternalRst:
+      refname = subst.target.anchor
+      fullRefname = refname
+    else:  # arExternalRst
+      refname = subst.refnameExt
+      fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath &
+                      "/" & refname
+    let anchorType =
+      if subst.kind == arInternalRst: subst.anchorType
+      else: subst.anchorTypeExt  # arExternalRst
+    foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority,
+                           target: newLeaf(fullRefname),
+                           info: subst.info,
+                           externFilename: getExternFilename(subst),
+                           isTitle: isDocumentationTitle(refname),
+                           tooltip: "(" & $anchorType & ")")
+  # find anchors automatically generated from Nim symbols
+  if roNimFile in s.options or s.nimFileImported:
+    let substNim = findMainAnchorNim(s, signature=alias, n.info)
+    for subst in substNim:
+      let fullRefname =
+        if subst.external:
+          s.idxImports[getFilename(s, subst)].linkRelPath &
+              "/" & subst.refname
+        else: subst.refname
+      foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority,
+                             target: newLeaf(fullRefname),
+                             externFilename: getExternFilename(subst),
+                             isTitle: isDocumentationTitle(subst.refname),
+                             info: subst.info, tooltip: subst.tooltip)
+  foundLinks.sort(cmp = cmp, order = Descending)
+  let aliasStr = addNodes(alias)
+  if foundLinks.len >= 1:
+    if foundLinks[0].externFilename != "":
+      s.idxImports[foundLinks[0].externFilename].used = true
+    let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink
+               elif foundLinks[0].ar == arNim:
+                 if foundLinks[0].externFilename == "": rnNimdocRef
+                 else: rnHyperlink
+               else: rnInternalRef
+    result = newRstNode(kind)
+    let documentName =  # filename without ext for `.nim`, title for `.md`
+      if foundLinks[0].ar == arNim:
+        changeFileExt(foundLinks[0].externFilename.extractFilename, "")
+      elif foundLinks[0].externFilename != "":
+        s.idxImports[foundLinks[0].externFilename].title
+      else: foundLinks[0].externFilename.extractFilename
+    let linkText =
+      if foundLinks[0].externFilename != "":
+        if foundLinks[0].isTitle: newLeaf(addNodes(desc))
+        else: newLeaf(documentName & ": " & addNodes(desc))
+      else:
+        newRstNode(rnInner, desc.sons)
+    result.sons = @[linkText, foundLinks[0].target]
+    if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip
+    if foundLinks.len > 1:  # report ambiguous link
+      var targets = newSeq[string]()
+      for l in foundLinks:
+        var t = "    "
+        if s.filenames.len > 1:
+          t.add getFilename(s.filenames, l.info.fileIndex)
+        let n = l.info.line
+        let c = l.info.col + ColRstOffset
+        t.add "($1, $2): $3" % [$n, $c, l.tooltip]
+        targets.add t
+      rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink,
+                 "`$1`\n  clash:\n$2" % [
+                   aliasStr, targets.join("\n")])
+  else:  # nothing found
+    result = n
+    rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, aliasStr)
 
 proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
   ## Makes pass 2 of RST parsing.
@@ -3491,7 +3785,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
   of rnRstRef, rnPandocRef:
     result = resolveLink(s, n)
   of rnFootnote:
-    var (fnType, num) = getFootnoteType(n.sons[0])
+    var (fnType, num) = getFootnoteType(s, n.sons[0])
     case fnType
     of fnManualNumber, fnCitation:
       discard "no need to alter fixed text"
@@ -3509,7 +3803,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
       n.sons[0].sons[0].text = sym
     n.sons[1] = resolveSubs(s, n.sons[1])
   of rnFootnoteRef:
-    var (fnType, num) = getFootnoteType(n.sons[0])
+    var (fnType, num) = getFootnoteType(s, n.sons[0])
     template addLabel(number: int | string) =
       var nn = newRstNode(rnInner)
       nn.add newLeaf($number)
@@ -3565,20 +3859,28 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
           inc i
       result.sons = newSons
 
+proc completePass2*(s: PRstSharedState) =
+  for (filename, importdocInfo) in s.idxImports.pairs:
+    if not importdocInfo.used:
+      rstMessage(s.filenames, s.msgHandler, importdocInfo.fromInfo,
+                 mwUnusedImportdoc, filename)
+
 proc rstParse*(text, filename: string,
                line, column: int,
                options: RstParseOptions,
                findFile: FindFileHandler = nil,
+               findRefFile: FindRefFileHandler = nil,
                msgHandler: MsgHandler = nil):
               tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] =
   ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`,
   ## note that 2nd tuple element should be fed to `initRstGenerator`
   ## argument `filenames` (it is being filled here at least with `filename`
   ## and possibly with other files from RST ``.. include::`` statement).
-  var sharedState = newRstSharedState(options, filename, findFile,
+  var sharedState = newRstSharedState(options, filename, findFile, findRefFile,
                                       msgHandler, hasToc=false)
   let unresolved = rstParsePass1(text, line, column, sharedState)
   preparePass2(sharedState, unresolved)
   result.node = resolveSubs(sharedState, unresolved)
+  completePass2(sharedState)
   result.filenames = sharedState.filenames
   result.hasToc = sharedState.hasToc
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index e85bbfb98..2bbb0d0b8 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -9,7 +9,7 @@
 
 ## This module implements an AST for the `reStructuredText`:idx: parser.
 
-import strutils, json
+import std/[strutils, json]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -377,13 +377,13 @@ proc renderRstToJsonNode(node: PRstNode): JsonNode =
 
 proc renderRstToJson*(node: PRstNode): string =
   ## Writes the given RST node as JSON that is in the form
-  ## ::
-  ##   {
-  ##     "kind":string node.kind,
-  ##     "text":optional string node.text,
-  ##     "level":optional int node.level,
-  ##     "sons":optional node array
-  ##   }
+  ##
+  ##     {
+  ##       "kind":string node.kind,
+  ##       "text":optional string node.text,
+  ##       "level":optional int node.level,
+  ##       "sons":optional node array
+  ##     }
   renderRstToJsonNode(node).pretty
 
 proc renderRstToText*(node: PRstNode): string =
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index f5ff9aa03..7fc0ac03a 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -39,9 +39,10 @@
 ##   No backreferences are generated since finding all references of a footnote
 ##   can be done by simply searching for ``[footnoteName]``.
 
-import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
-  algorithm, parseutils, std/strbasics
+import std/[strutils, os, hashes, strtabs, tables, sequtils,
+  algorithm, parseutils, strbasics]
 
+import rstast, rst, rstidx, highlite
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio, formatfloat]
@@ -59,7 +60,7 @@ type
     outLatex            # output is Latex
 
   MetaEnum* = enum
-    metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
+    metaNone, metaTitleRaw, metaTitle, metaSubtitle, metaAuthor, metaVersion
 
   EscapeMode* = enum  # in Latex text inside options [] and URLs is
                       # escaped slightly differently than in normal text
@@ -88,7 +89,7 @@ type
     ## for hyperlinks. See renderIndexTerm proc for details.
     id*: int               ## A counter useful for generating IDs.
     onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int;
-                          content: string)
+                          content: string) {.gcsafe.}
     escMode*: EscapeMode
     curQuotationDepth: int
 
@@ -153,12 +154,12 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   import packages/docutils/rstgen
   ##
   ##   var gen: RstGenerator
   ##   gen.initRstGenerator(outHtml, defaultConfig(), "filename", {})
+  ##   ```
   g.config = config
   g.target = target
   g.tocPart = @[]
@@ -283,19 +284,18 @@ proc dispA(target: OutputTarget, dest: var string,
 proc `or`(x, y: string): string {.inline.} =
   result = if x.len == 0: y else: x
 
-proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
+proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string) {.gcsafe.}
   ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration.
   ##
   ## Before using this proc you need to initialise a ``RstGenerator`` with
   ## ``initRstGenerator`` and parse a rst file with ``rstParse`` from the
   ## `packages/docutils/rst module <rst.html>`_. Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   # ...configure gen and rst vars...
   ##   var generatedHtml = ""
   ##   renderRstToOut(gen, rst, generatedHtml)
   ##   echo generatedHtml
+  ##   ```
 
 proc renderAux(d: PDoc, n: PRstNode, result: var string) =
   for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
@@ -322,31 +322,8 @@ proc renderAux(d: PDoc, n: PRstNode, html, tex: string, result: var string) =
 
 # ---------------- index handling --------------------------------------------
 
-proc quoteIndexColumn(text: string): string =
-  ## Returns a safe version of `text` for serialization to the ``.idx`` file.
-  ##
-  ## The returned version can be put without worries in a line based tab
-  ## separated column text file. The following character sequence replacements
-  ## will be performed for that goal:
-  ##
-  ## * ``"\\"`` => ``"\\\\"``
-  ## * ``"\n"`` => ``"\\n"``
-  ## * ``"\t"`` => ``"\\t"``
-  result = newStringOfCap(text.len + 3)
-  for c in text:
-    case c
-    of '\\': result.add "\\"
-    of '\L': result.add "\\n"
-    of '\C': discard
-    of '\t': result.add "\\t"
-    else: result.add c
-
-proc unquoteIndexColumn(text: string): string =
-  ## Returns the unquoted version generated by ``quoteIndexColumn``.
-  result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
-
-proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string,
-                   linkTitle, linkDesc = "") =
+proc setIndexTerm*(d: var RstGenerator; k: IndexEntryKind, htmlFile, id, term: string,
+                   linkTitle, linkDesc = "", line = 0) =
   ## Adds a `term` to the index using the specified hyperlink identifier.
   ##
   ## A new entry will be added to the index using the format
@@ -369,21 +346,8 @@ proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string,
   ## <#writeIndexFile,RstGenerator,string>`_. The purpose of the index is
   ## documented in the `docgen tools guide
   ## <docgen.html#related-options-index-switch>`_.
-  var
-    entry = term
-    isTitle = false
-  entry.add('\t')
-  entry.add(htmlFile)
-  if id.len > 0:
-    entry.add('#')
-    entry.add(id)
-  else:
-    isTitle = true
-  if linkTitle.len > 0 or linkDesc.len > 0:
-    entry.add('\t' & linkTitle.quoteIndexColumn)
-    entry.add('\t' & linkDesc.quoteIndexColumn)
-  entry.add("\n")
-
+  let (entry, isTitle) = formatIndexEntry(k, htmlFile, id, term,
+                                          linkTitle, linkDesc, line)
   if isTitle: d.theIndex.insert(entry)
   else: d.theIndex.add(entry)
 
@@ -396,6 +360,15 @@ proc hash(n: PRstNode): int =
       result = result !& hash(n.sons[i])
     result = !$result
 
+proc htmlFileRelPath(d: PDoc): string =
+  if d.outDir.len == 0:
+    # /foo/bar/zoo.nim -> zoo.html
+    changeFileExt(extractFilename(d.filename), HtmlExt)
+  else: # d is initialized in docgen.nim
+    # outDir   = /foo              -\
+    # destFile = /foo/bar/zoo.html -|-> bar/zoo.html
+    d.destFile.relativePath(d.outDir, '/')
+
 proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
   ## Renders the string decorated within \`foobar\`\:idx\: markers.
   ##
@@ -412,17 +385,12 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
 
   var term = ""
   renderAux(d, n, term)
-  setIndexTerm(d, changeFileExt(extractFilename(d.filename), HtmlExt), id, term, d.currentSection)
+  setIndexTerm(d, ieIdxRole,
+  htmlFileRelPath(d), id, term, d.currentSection)
   dispA(d.target, result, "<span id=\"$1\">$2</span>", "\\nimindexterm{$1}{$2}",
         [id, term])
 
 type
-  IndexEntry* = object
-    keyword*: string
-    link*: string
-    linkTitle*: string ## contains a prettier text for the href
-    linkDesc*: string ## the title attribute of the final href
-
   IndexedDocs* = Table[IndexEntry, seq[IndexEntry]] ## \
     ## Contains the index sequences for doc types.
     ##
@@ -433,21 +401,6 @@ type
     ## The value indexed by this IndexEntry is a sequence with the real index
     ## entries found in the ``.idx`` file.
 
-proc cmp(a, b: IndexEntry): int =
-  ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
-  result = cmpIgnoreStyle(a.keyword, b.keyword)
-  if result == 0:
-    result = cmpIgnoreStyle(a.link, b.link)
-
-proc hash(x: IndexEntry): Hash =
-  ## Returns the hash for the combined fields of the type.
-  ##
-  ## The hash is computed as the chained hash of the individual string hashes.
-  result = x.keyword.hash !& x.link.hash
-  result = result !& x.linkTitle.hash
-  result = result !& x.linkDesc.hash
-  result = !$result
-
 when defined(gcDestructors):
   template `<-`(a, b: var IndexEntry) = a = move(b)
 else:
@@ -456,6 +409,7 @@ else:
     shallowCopy a.link, b.link
     shallowCopy a.linkTitle, b.linkTitle
     shallowCopy a.linkDesc, b.linkDesc
+    shallowCopy a.module, b.module
 
 proc sortIndex(a: var openArray[IndexEntry]) =
   # we use shellsort here; fast and simple
@@ -495,16 +449,20 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
   result = "<dl>"
   var i = 0
   while i < symbols.len:
-    let keyword = symbols[i].keyword
+    let keyword = esc(outHtml, symbols[i].keyword)
     let cleanedKeyword = keyword.escapeLink
     result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><dd><ul class=\"simple\">\n",
                 [keyword, cleanedKeyword])
     var j = i
-    while j < symbols.len and keyword == symbols[j].keyword:
+    while j < symbols.len and symbols[i].keyword == symbols[j].keyword:
       let
         url = symbols[j].link.escapeLink
-        text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url
-        desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: ""
+        module = symbols[j].module
+        text =
+          if symbols[j].linkTitle.len > 0:
+            esc(outHtml, module & ": " & symbols[j].linkTitle)
+          else: url
+        desc = symbols[j].linkDesc
       if desc.len > 0:
         result.addf("""<li><a class="reference external"
           title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
@@ -518,13 +476,6 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
     i = j
   result.add("</dl>")
 
-proc isDocumentationTitle(hyperlink: string): bool =
-  ## Returns true if the hyperlink is actually a documentation title.
-  ##
-  ## Documentation titles lack the hash. See `mergeIndexes()
-  ## <#mergeIndexes,string>`_ for a more detailed explanation.
-  result = hyperlink.find('#') < 0
-
 proc stripTocLevel(s: string): tuple[level: int, text: string] =
   ## Returns the *level* of the toc along with the text without it.
   for c in 0 ..< s.len:
@@ -558,17 +509,15 @@ proc generateDocumentationToc(entries: seq[IndexEntry]): string =
     level = 1
   levels.newSeq(entries.len)
   for entry in entries:
-    let (rawLevel, rawText) = stripTocLevel(entry.linkTitle or entry.keyword)
+    let (rawLevel, rawText) = stripTocLevel(entry.linkTitle)
     if rawLevel < 1:
       # This is a normal symbol, push it *inside* one level from the last one.
       levels[L].level = level + 1
-      # Also, ignore the linkTitle and use directly the keyword.
-      levels[L].text = entry.keyword
     else:
       # The level did change, update the level indicator.
       level = rawLevel
       levels[L].level = rawLevel
-      levels[L].text = rawText
+    levels[L].text = rawText
     inc L
 
   # Now generate hierarchical lists based on the precalculated levels.
@@ -599,7 +548,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
   for title in titles:
     let tocList = generateDocumentationToc(docs.getOrDefault(title))
     result.add("<ul><li><a href=\"" &
-      title.link & "\">" & title.keyword & "</a>\n" & tocList & "</li></ul>\n")
+      title.link & "\">" & title.linkTitle & "</a>\n" & tocList & "</li></ul>\n")
 
 proc generateDocumentationJumps(docs: IndexedDocs): string =
   ## Returns a plain list of hyperlinks to documentation TOCs in HTML.
@@ -611,7 +560,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string =
 
   var chunks: seq[string] = @[]
   for title in titles:
-    chunks.add("<a href=\"" & title.link & "\">" & title.keyword & "</a>")
+    chunks.add("<a href=\"" & title.link & "\">" & title.linkTitle & "</a>")
 
   result.add(chunks.join(", ") & ".<br/>")
 
@@ -640,39 +589,12 @@ proc readIndexDir*(dir: string):
   # Scan index files and build the list of symbols.
   for path in walkDirRec(dir):
     if path.endsWith(IndexExt):
-      var
-        fileEntries: seq[IndexEntry]
-        title: IndexEntry
-        f = 0
-      newSeq(fileEntries, 500)
-      setLen(fileEntries, 0)
-      for line in lines(path):
-        let s = line.find('\t')
-        if s < 0: continue
-        setLen(fileEntries, f+1)
-        fileEntries[f].keyword = line.substr(0, s-1)
-        fileEntries[f].link = line.substr(s+1)
-        # See if we detect a title, a link without a `#foobar` trailing part.
-        if title.keyword.len == 0 and fileEntries[f].link.isDocumentationTitle:
-          title.keyword = fileEntries[f].keyword
-          title.link = fileEntries[f].link
-
-        if fileEntries[f].link.find('\t') > 0:
-          let extraCols = fileEntries[f].link.split('\t')
-          fileEntries[f].link = extraCols[0]
-          assert extraCols.len == 3
-          fileEntries[f].linkTitle = extraCols[1].unquoteIndexColumn
-          fileEntries[f].linkDesc = extraCols[2].unquoteIndexColumn
-        else:
-          fileEntries[f].linkTitle = ""
-          fileEntries[f].linkDesc = ""
-        inc f
+      var (fileEntries, title) = parseIdxFile(path)
       # Depending on type add this to the list of symbols or table of APIs.
-      if title.keyword.len == 0:
-        for i in 0 ..< f:
-          # Don't add to symbols TOC entries (they start with a whitespace).
-          let toc = fileEntries[i].linkTitle
-          if toc.len > 0 and toc[0] == ' ':
+
+      if title.kind == ieNimTitle:
+        for i in 0 ..< fileEntries.len:
+          if fileEntries[i].kind != ieNim:
             continue
           # Ok, non TOC entry, add it.
           setLen(result.symbols, L + 1)
@@ -688,9 +610,17 @@ proc readIndexDir*(dir: string):
             result.modules.add(x.changeFileExt(""))
       else:
         # Generate the symbolic anchor for index quickjumps.
-        title.linkTitle = "doc_toc_" & $result.docs.len
+        title.aux = "doc_toc_" & $result.docs.len
         result.docs[title] = fileEntries
 
+      for i in 0 ..< fileEntries.len:
+        if fileEntries[i].kind != ieIdxRole:
+          continue
+
+        setLen(result.symbols, L + 1)
+        result.symbols[L] = fileEntries[i]
+        inc L
+
 proc mergeIndexes*(dir: string): string =
   ## Merges all index files in `dir` and returns the generated index as HTML.
   ##
@@ -748,24 +678,6 @@ proc mergeIndexes*(dir: string): string =
 
 # ----------------------------------------------------------------------------
 
-proc stripTocHtml(s: string): string =
-  ## Ugly quick hack to remove HTML tags from TOC titles.
-  ##
-  ## A TocEntry.header field already contains rendered HTML tags. Instead of
-  ## implementing a proper version of renderRstToOut() which recursively
-  ## renders an rst tree to plain text, we simply remove text found between
-  ## angled brackets. Given the limited possibilities of rst inside TOC titles
-  ## this should be enough.
-  result = s
-  var first = result.find('<')
-  while first >= 0:
-    let last = result.find('>', first)
-    if last < 0:
-      # Abort, since we didn't found a closing angled bracket.
-      return
-    result.delete(first, last)
-    first = result.find('<', first)
-
 proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
   var tmp = ""
   for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
@@ -786,19 +698,12 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
 
   # Generate index entry using spaces to indicate TOC level for the output HTML.
   assert n.level >= 0
-  let
-    htmlFileRelPath = if d.outDir.len == 0:
-                        # /foo/bar/zoo.nim -> zoo.html
-                        changeFileExt(extractFilename(d.filename), HtmlExt)
-                      else: # d is initialized in docgen.nim
-                        # outDir   = /foo              -\
-                        # destFile = /foo/bar/zoo.html -|-> bar/zoo.html
-                        d.destFile.relativePath(d.outDir, '/')
-  setIndexTerm(d, htmlFileRelPath, n.anchor, tmp.stripTocHtml,
-    spaces(max(0, n.level)) & tmp)
+  setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor,
+               term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp)
 
 proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
   if n.level == 0 and d.meta[metaTitle].len == 0:
+    d.meta[metaTitleRaw] = n.addNodes
     for i in countup(0, len(n)-1):
       renderRstToOut(d, n.sons[i], d.meta[metaTitle])
     d.currentSection = d.meta[metaTitle]
@@ -814,6 +719,8 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
     dispA(d.target, result, "<h$1$2><center>$3</center></h$1>",
                    "\\rstov$4[$5]{$3}$2\n", [$n.level,
                    n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName])
+    setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor,
+                 term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp)
 
 proc renderTocEntry(d: PDoc, n: PRstNode, result: var string) =
   var header = ""
@@ -1028,7 +935,7 @@ proc renderCodeLang*(result: var string, lang: SourceLanguage, code: string,
 proc renderNimCode*(result: var string, code: string, target: OutputTarget) =
   renderCodeLang(result, langNim, code, target)
 
-proc renderCode(d: PDoc, n: PRstNode, result: var string) =
+proc renderCode(d: PDoc, n: PRstNode, result: var string) {.gcsafe.} =
   ## Renders a code (code block or inline code), appending it to `result`.
   ##
   ## If the code block uses the ``number-lines`` option, a table will be
@@ -1198,6 +1105,18 @@ proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string,
       "\\hyperlink{$2}{$1} (p.~\\pageref{$2})",
       [textStr, linkStr, nimDocStr, tooltipStr])
 
+proc traverseForIndex*(d: PDoc, n: PRstNode) =
+  ## A version of [renderRstToOut] that only fills entries for ``.idx`` files.
+  var discarded: string
+  if n == nil: return
+  case n.kind
+  of rnIdx: renderIndexTerm(d, n, discarded)
+  of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, discarded)
+  of rnOverline: renderOverline(d, n, discarded)
+  else:
+    for i in 0 ..< len(n):
+      traverseForIndex(d, n.sons[i])
+
 proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   if n == nil: return
   case n.kind
@@ -1256,7 +1175,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
     renderAux(d, n, "<div class=\"option-list-description\">$1</div>",
         " $1\n", result)
   of rnOption, rnOptionString, rnOptionArgument:
-    doAssert false, "renderRstToOut"
+    raiseAssert "renderRstToOut"
   of rnLiteralBlock:
     renderAux(d, n, "<pre$2>$1</pre>\n",
                     "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result)
@@ -1452,6 +1371,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   of rnTitle:
     d.meta[metaTitle] = ""
     renderRstToOut(d, n.sons[0], d.meta[metaTitle])
+    d.meta[metaTitleRaw] = n.sons[0].addNodes
 
 # -----------------------------------------------------------------------------
 
@@ -1592,7 +1512,7 @@ $content
 
 proc rstToHtml*(s: string, options: RstParseOptions,
                 config: StringTableRef,
-                msgHandler: MsgHandler = rst.defaultMsgHandler): string =
+                msgHandler: MsgHandler = rst.defaultMsgHandler): string {.gcsafe.} =
   ## Converts an input rst string into embeddable HTML.
   ##
   ## This convenience proc parses any input string using rst markup (it doesn't
@@ -1602,12 +1522,13 @@ proc rstToHtml*(s: string, options: RstParseOptions,
   ## work. For an explanation of the ``config`` parameter see the
   ## ``initRstGenerator`` proc. Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   import packages/docutils/rstgen, strtabs
   ##
   ##   echo rstToHtml("*Hello* **world**!", {},
   ##     newStringTable(modeStyleInsensitive))
   ##   # --> <em>Hello</em> <strong>world</strong>!
+  ##   ```
   ##
   ## If you need to allow the rst ``include`` directive or tweak the generated
   ## output you have to create your own ``RstGenerator`` with
@@ -1616,11 +1537,13 @@ proc rstToHtml*(s: string, options: RstParseOptions,
   proc myFindFile(filename: string): string =
     # we don't find any files in online mode:
     result = ""
+  proc myFindRefFile(filename: string): (string, string) =
+    result = ("", "")
 
   const filen = "input"
   let (rst, filenames, t) = rstParse(s, filen,
                                      line=LineRstInit, column=ColRstInit,
-                                     options, myFindFile, msgHandler)
+                                     options, myFindFile, myFindRefFile, msgHandler)
   var d: RstGenerator
   initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler,
                    filenames, hasToc = t)
diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim
new file mode 100644
index 000000000..1472d28fd
--- /dev/null
+++ b/lib/packages/docutils/rstidx.nim
@@ -0,0 +1,141 @@
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+
+## Nim `idx`:idx: file format related definitions.
+
+import std/[strutils, syncio, hashes]
+from std/os import splitFile
+
+type
+  IndexEntryKind* = enum ## discriminator tag
+    ieMarkupTitle = "markupTitle"
+                           ## RST/Markdown title, text in `keyword` +
+                           ## HTML text in `linkTitle`
+    ieNimTitle = "nimTitle"
+                           ## Nim title
+    ieHeading = "heading"  ## RST/Markdown markup heading, escaped
+    ieIdxRole = "idx"      ## RST :idx: definition, escaped
+    ieNim = "nim"          ## Nim symbol, unescaped
+    ieNimGroup = "nimgrp"  ## Nim overload group, unescaped
+  IndexEntry* = object
+    kind*: IndexEntryKind  ## 0.
+    keyword*: string       ## 1.
+    link*: string          ## 2.
+    linkTitle*: string     ## 3. contains a prettier text for the href
+    linkDesc*: string      ## 4. the title attribute of the final href
+    line*: int             ## 5.
+    module*: string        ## origin file, NOT a field in ``.idx`` file
+    aux*: string           ## auxuliary field, NOT a field in ``.idx`` file
+
+proc isDocumentationTitle*(hyperlink: string): bool =
+  ## Returns true if the hyperlink is actually a documentation title.
+  ##
+  ## Documentation titles lack the hash. See `mergeIndexes()
+  ## <#mergeIndexes,string>`_ for a more detailed explanation.
+  result = hyperlink.find('#') < 0
+
+proc `$`*(e: IndexEntry): string =
+  """("$1", "$2", "$3", "$4", $5)""" % [
+      e.keyword, e.link, e.linkTitle, e.linkDesc, $e.line]
+
+proc quoteIndexColumn(text: string): string =
+  ## Returns a safe version of `text` for serialization to the ``.idx`` file.
+  ##
+  ## The returned version can be put without worries in a line based tab
+  ## separated column text file. The following character sequence replacements
+  ## will be performed for that goal:
+  ##
+  ## * ``"\\"`` => ``"\\\\"``
+  ## * ``"\n"`` => ``"\\n"``
+  ## * ``"\t"`` => ``"\\t"``
+  result = newStringOfCap(text.len + 3)
+  for c in text:
+    case c
+    of '\\': result.add "\\"
+    of '\L': result.add "\\n"
+    of '\C': discard
+    of '\t': result.add "\\t"
+    else: result.add c
+
+proc unquoteIndexColumn*(text: string): string =
+  ## Returns the unquoted version generated by ``quoteIndexColumn``.
+  result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
+
+proc formatIndexEntry*(kind: IndexEntryKind; htmlFile, id, term, linkTitle,
+                       linkDesc: string, line: int):
+                      tuple[entry: string, isTitle: bool] =
+  result.entry = $kind
+  result.entry.add('\t')
+  result.entry.add term
+  result.entry.add('\t')
+  result.entry.add(htmlFile)
+  if id.len > 0:
+    result.entry.add('#')
+    result.entry.add(id)
+    result.isTitle = false
+  else:
+    result.isTitle = true
+  result.entry.add('\t' & linkTitle.quoteIndexColumn)
+  result.entry.add('\t' & linkDesc.quoteIndexColumn)
+  result.entry.add('\t' & $line)
+  result.entry.add("\n")
+
+proc parseIndexEntryKind(s: string): IndexEntryKind =
+  result = case s:
+    of "nim": ieNim
+    of "nimgrp": ieNimGroup
+    of "heading": ieHeading
+    of "idx": ieIdxRole
+    of "nimTitle": ieNimTitle
+    of "markupTitle": ieMarkupTitle
+    else: raise newException(ValueError, "unknown index entry value $1" % [s])
+
+proc parseIdxFile*(path: string):
+    tuple[fileEntries: seq[IndexEntry], title: IndexEntry] =
+  var
+    f = 0
+  newSeq(result.fileEntries, 500)
+  setLen(result.fileEntries, 0)
+  let (_, base, _) = path.splitFile
+  for line in lines(path):
+    let s = line.find('\t')
+    if s < 0: continue
+    setLen(result.fileEntries, f+1)
+    let cols = line.split('\t')
+    result.fileEntries[f].kind = parseIndexEntryKind(cols[0])
+    result.fileEntries[f].keyword = cols[1]
+    result.fileEntries[f].link = cols[2]
+    if result.fileEntries[f].kind == ieIdxRole:
+      result.fileEntries[f].module = base
+    else:
+      if result.title.keyword.len == 0:
+        result.fileEntries[f].module = base
+      else:
+        result.fileEntries[f].module = result.title.keyword
+
+    result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn
+    result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn
+    result.fileEntries[f].line = parseInt(cols[5])
+
+    if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}:
+      result.title = result.fileEntries[f]
+    inc f
+
+proc cmp*(a, b: IndexEntry): int =
+  ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`.
+  result = cmpIgnoreStyle(a.keyword, b.keyword)
+  if result == 0:
+    result = cmpIgnoreStyle(a.link, b.link)
+
+proc hash*(x: IndexEntry): Hash =
+  ## Returns the hash for the combined fields of the type.
+  ##
+  ## The hash is computed as the chained hash of the individual string hashes.
+  result = x.keyword.hash !& x.link.hash
+  result = result !& x.linkTitle.hash
+  result = result !& x.linkDesc.hash
+  result = !$result
diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim
index 7ee062e4f..007488354 100644
--- a/lib/posix/epoll.nim
+++ b/lib/posix/epoll.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-from posix import SocketHandle
+from std/posix import SocketHandle
 
 const
   EPOLLIN* = 0x00000001
diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim
index db698c59c..575accc18 100644
--- a/lib/posix/inotify.nim
+++ b/lib/posix/inotify.nim
@@ -7,16 +7,20 @@
 #    distribution, for details about the copyright.
 #
 
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
 # Get the platform-dependent flags.
 # Structure describing an inotify event.
 type
   InotifyEvent* {.pure, final, importc: "struct inotify_event",
-                  header: "<sys/inotify.h>".} = object ## An Inotify event.
+                  header: "<sys/inotify.h>",
+                  completeStruct.} = object            ## An Inotify event.
     wd* {.importc: "wd".}: FileHandle                  ## Watch descriptor.
     mask* {.importc: "mask".}: uint32                  ## Watch mask.
     cookie* {.importc: "cookie".}: uint32              ## Cookie to synchronize two events.
     len* {.importc: "len".}: uint32                    ## Length (including NULs) of name.
-    name* {.importc: "name".}: char                    ## Name.
+    name* {.importc: "name".}: UncheckedArray[char]    ## Name.
 
 # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH.
 const
@@ -63,7 +67,8 @@ proc inotify_init*(): FileHandle {.cdecl, importc: "inotify_init",
 
 proc inotify_init1*(flags: cint): FileHandle {.cdecl, importc: "inotify_init1",
     header: "<sys/inotify.h>".}
-  ## Create and initialize inotify instance.
+  ## Like `inotify_init<#inotify_init>`_ ,
+  ## but has a flags argument that provides access to some extra functionality.
 
 proc inotify_add_watch*(fd: cint; name: cstring; mask: uint32): cint {.cdecl,
     importc: "inotify_add_watch", header: "<sys/inotify.h>".}
@@ -75,11 +80,18 @@ proc inotify_rm_watch*(fd: cint; wd: cint): cint {.cdecl,
 
 iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent =
   ## Abstract the packed buffer interface to yield event object pointers.
-  ##
-  ## .. code-block:: Nim
-  ##   var evs = newSeq[byte](8192)        # Already did inotify_init+add_watch
-  ##   while (let n = read(fd, evs[0].addr, 8192); n) > 0:     # read forever
-  ##     for e in inotify_events(evs[0].addr, n): echo e[].len # echo name lens
+  runnableExamples("-r:off"):
+    when defined(linux):
+       import std/posix  # needed for FileHandle read procedure
+       const MaxWatches = 8192
+
+       let inotifyFd = inotify_init()  # create new inotify instance and get it's FileHandle
+       let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE)  # Add new watch
+
+       var events: array[MaxWatches, byte]  # event buffer
+       while (let n = read(inotifyFd, addr events, MaxWatches); n) > 0:  # blocks until any events have been read
+         for e in inotify_events(addr events, n):
+           echo (e[].wd, e[].mask, cast[cstring](addr e[].name))    # echo watch id, mask, and name value of each event
   var ev: ptr InotifyEvent = cast[ptr InotifyEvent](evs)
   var n = n
   while n > 0:
@@ -90,8 +102,10 @@ iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent =
 
 runnableExamples:
   when defined(linux):
-    let inoty: FileHandle = inotify_init()           ## Create 1 Inotify.
-    doAssert inoty >= 0                              ## Check for errors (FileHandle is alias to cint).
-    let watchdoge: cint = inotify_add_watch(inoty, ".", IN_ALL_EVENTS) ## Add directory to watchdog.
-    doAssert watchdoge >= 0                          ## Check for errors.
-    doAssert inotify_rm_watch(inoty, watchdoge) >= 0 ## Remove directory from the watchdog
+    let inotifyFd = inotify_init()  # create and get new inotify FileHandle
+    doAssert inotifyFd >= 0         # check for errors
+
+    let wd = inotifyFd.inotify_add_watch("/tmp", IN_CREATE or IN_DELETE)  # Add new watch
+    doAssert wd >= 0                 # check for errors
+
+    discard inotifyFd.inotify_rm_watch(wd) # remove watch
diff --git a/lib/posix/kqueue.nim b/lib/posix/kqueue.nim
index c83ae33ea..2450cdb42 100644
--- a/lib/posix/kqueue.nim
+++ b/lib/posix/kqueue.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-from posix import Timespec
+from std/posix import Timespec
 
 when defined(macosx) or defined(freebsd) or defined(openbsd) or
      defined(dragonfly):
diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim
index 2d6702e87..29fd4288d 100644
--- a/lib/posix/linux.nim
+++ b/lib/posix/linux.nim
@@ -1,4 +1,4 @@
-import posix
+import std/posix
 
 ## Flags of `clone` syscall.
 ## See `clone syscall manual
@@ -29,7 +29,7 @@ const
   CLONE_NEWPID* = 0x20000000'i32
   CLONE_NEWNET* = 0x40000000'i32
   CLONE_IO* = 0x80000000'i32
-  CLONE_STOPPED* {.deprecated.} = 0x02000000'i32
+
 
 # fn should be of type proc (a2: pointer) {.cdecl.}
 proc clone*(fn: pointer; child_stack: pointer; flags: cint;
diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim
index 4ebae4361..fbe945df3 100644
--- a/lib/posix/posix.nim
+++ b/lib/posix/posix.nim
@@ -195,8 +195,29 @@ proc open*(a1: cstring, a2: cint, mode: Mode | cint = 0.Mode): cint {.inline.} =
 
 proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {.
   importc, header: "<fcntl.h>".}
-proc posix_fallocate*(a1: cint, a2, a3: Off): cint {.
-  importc, header: "<fcntl.h>".}
+
+proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "<unistd.h>".}
+when defined(osx):              # 2001 POSIX evidently does not concern Apple
+  type FStore {.importc: "fstore_t", header: "<fcntl.h>", bycopy.} = object
+    fst_flags: uint32           ## IN: flags word
+    fst_posmode: cint           ## IN: indicates offset field
+    fst_offset,                 ## IN: start of the region
+      fst_length,               ## IN: size of the region
+      fst_bytesalloc: Off       ## OUT: number of bytes allocated
+  var F_PEOFPOSMODE {.importc, header: "<fcntl.h>".}: cint
+  var F_ALLOCATEALL {.importc, header: "<fcntl.h>".}: uint32
+  var F_PREALLOCATE {.importc, header: "<fcntl.h>".}: cint
+  proc posix_fallocate*(a1: cint, a2, a3: Off): cint =
+    var fst = FStore(fst_flags: F_ALLOCATEALL, fst_posmode: F_PEOFPOSMODE,
+                     fst_offset: a2, fst_length: a3)
+    # Must also call ftruncate to match what POSIX does. Unlike posix_fallocate,
+    # this can shrink files.  Could guard w/getFileSize, but caller likely knows
+    # present size & has no good reason to call this unless it is growing.
+    if fcntl(a1, F_PREALLOCATE, fst.addr) != cint(-1): ftruncate(a1, a2 + a3)
+    else: cint(-1)
+else:
+  proc posix_fallocate*(a1: cint, a2, a3: Off): cint {.
+    importc, header: "<fcntl.h>".}
 
 when not defined(haiku) and not defined(openbsd):
   proc fmtmsg*(a1: int, a2: cstring, a3: cint,
@@ -249,26 +270,52 @@ proc setlocale*(a1: cint, a2: cstring): cstring {.
 proc strfmon*(a1: cstring, a2: int, a3: cstring): int {.varargs,
    importc, header: "<monetary.h>".}
 
-when not defined(nintendoswitch):
-  proc mq_close*(a1: Mqd): cint {.importc, header: "<mqueue.h>".}
-  proc mq_getattr*(a1: Mqd, a2: ptr MqAttr): cint {.
-    importc, header: "<mqueue.h>".}
-  proc mq_notify*(a1: Mqd, a2: ptr SigEvent): cint {.
+when not (defined(nintendoswitch) or defined(macos) or defined(macosx)):
+  proc mq_notify*(mqdes: Mqd, event: ptr SigEvent): cint {.
     importc, header: "<mqueue.h>".}
-  proc mq_open*(a1: cstring, a2: cint): Mqd {.
+
+  proc mq_open*(name: cstring, flags: cint): Mqd {.
     varargs, importc, header: "<mqueue.h>".}
-  proc mq_receive*(a1: Mqd, a2: cstring, a3: int, a4: var int): int {.
-    importc, header: "<mqueue.h>".}
-  proc mq_send*(a1: Mqd, a2: cstring, a3: int, a4: int): cint {.
+
+  proc mq_close*(mqdes: Mqd): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_receive*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: var cuint
+  ): int {.importc, header: "<mqueue.h>".}
+
+  proc mq_timedreceive*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint,
+    timeout: ptr Timespec
+  ): int {.importc, header: "<mqueue.h>".}
+
+  proc mq_send*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint
+  ): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_timedsend*(
+    mqdes: Mqd,
+    buffer: cstring,
+    length: csize_t,
+    priority: cuint,
+    timeout: ptr Timespec
+  ): cint {.importc, header: "<mqueue.h>".}
+
+  proc mq_getattr*(mqdes: Mqd, attribute: ptr MqAttr): cint {.
     importc, header: "<mqueue.h>".}
-  proc mq_setattr*(a1: Mqd, a2, a3: ptr MqAttr): cint {.
+
+  proc mq_setattr*(mqdes: Mqd, newAttribute, oldAttribute: ptr MqAttr): cint {.
     importc, header: "<mqueue.h>".}
 
-  proc mq_timedreceive*(a1: Mqd, a2: cstring, a3: int, a4: int,
-                        a5: ptr Timespec): int {.importc, header: "<mqueue.h>".}
-  proc mq_timedsend*(a1: Mqd, a2: cstring, a3: int, a4: int,
-                     a5: ptr Timespec): cint {.importc, header: "<mqueue.h>".}
-  proc mq_unlink*(a1: cstring): cint {.importc, header: "<mqueue.h>".}
+  proc mq_unlink*(mqdes: cstring): cint {.importc, header: "<mqueue.h>".}
 
 
 proc getpwnam*(a1: cstring): ptr Passwd {.importc, header: "<pwd.h>".}
@@ -485,7 +532,6 @@ proc fpathconf*(a1, a2: cint): int {.importc, header: "<unistd.h>".}
 proc fsync*(a1: cint): cint {.importc, header: "<unistd.h>".}
  ## synchronize a file's buffer cache to the storage device
 
-proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "<unistd.h>".}
 proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "<unistd.h>", sideEffect.}
 proc getuid*(): Uid {.importc, header: "<unistd.h>", sideEffect.}
  ## returns the real user ID of the calling process
@@ -538,7 +584,8 @@ proc pread*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
 proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {.
   importc, header: "<unistd.h>".}
 proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "<unistd.h>".}
-proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
+when not defined(nintendoswitch):
+  proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
 proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl",
       header: "<sys/ioctl.h>", varargs, tags: [WriteIOEffect].}
   ## A system call for device-specific input/output operations and other
@@ -557,7 +604,10 @@ proc setsid*(): Pid {.importc, header: "<unistd.h>".}
 proc setuid*(a1: Uid): cint {.importc, header: "<unistd.h>".}
 proc sleep*(a1: cint): cint {.importc, header: "<unistd.h>".}
 proc swab*(a1, a2: pointer, a3: int) {.importc, header: "<unistd.h>".}
-proc symlink*(a1, a2: cstring): cint {.importc, header: "<unistd.h>".}
+when not defined(nintendoswitch):
+  proc symlink*(a1, a2: cstring): cint {.importc, header: "<unistd.h>".}
+else:
+  proc symlink*(a1, a2: cstring): cint = -1
 proc sync*() {.importc, header: "<unistd.h>".}
 proc sysconf*(a1: cint): int {.importc, header: "<unistd.h>".}
 proc tcgetpgrp*(a1: cint): Pid {.importc, header: "<unistd.h>".}
@@ -908,7 +958,7 @@ proc `==`*(x, y: SocketHandle): bool {.borrow.}
 proc accept*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen): SocketHandle {.
   importc, header: "<sys/socket.h>", sideEffect.}
 
-when defined(linux) or defined(bsd):
+when defined(linux) or defined(bsd) or defined(nuttx):
   proc accept4*(a1: SocketHandle, a2: ptr SockAddr, a3: ptr SockLen,
                 flags: cint): SocketHandle {.importc, header: "<sys/socket.h>".}
 
@@ -1093,11 +1143,11 @@ template onSignal*(signals: varargs[cint], body: untyped) =
   ## scope.
   ##
   ## Example:
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   from std/posix import SIGINT, SIGTERM, onSignal
   ##   onSignal(SIGINT, SIGTERM):
   ##     echo "bye from signal ", sig
+  ##   ```
 
   for s in signals:
     handle_signal(s,
@@ -1114,12 +1164,12 @@ type
   ## The getrlimit() and setrlimit() system calls get and set resource limits respectively.
   ## Each resource has an associated soft and hard limit, as defined by the RLimit structure
 
-proc setrlimit*(resource: cint, rlp: var RLimit): cint
-      {.importc: "setrlimit",header: "<sys/resource.h>".}
+proc setrlimit*(resource: cint, rlp: var RLimit): cint {.
+  importc: "setrlimit", header: "<sys/resource.h>".}
   ## The setrlimit() system calls sets resource limits.
 
-proc getrlimit*(resource: cint, rlp: var RLimit): cint
-      {.importc: "getrlimit",header: "<sys/resource.h>".}
+proc getrlimit*(resource: cint, rlp: var RLimit): cint {.
+  importc: "getrlimit", header: "<sys/resource.h>".}
   ## The getrlimit() system call gets resource limits.
 
 when defined(nimHasStyleChecks):
diff --git a/lib/posix/posix_haiku.nim b/lib/posix/posix_haiku.nim
index d626b2106..32a6d24e2 100644
--- a/lib/posix/posix_haiku.nim
+++ b/lib/posix/posix_haiku.nim
@@ -304,7 +304,7 @@ type
   Stack* {.importc: "stack_t",
             header: "<signal.h>", final, pure.} = object ## stack_t
     ss_sp*: pointer  ## Stack base or pointer.
-    ss_size*: csize  ## Stack size.
+    ss_size*: csize_t ## Stack size.
     ss_flags*: cint  ## Flags.
 
   SigInfo* {.importc: "siginfo_t",
@@ -404,7 +404,7 @@ type
   IOVec* {.importc: "struct iovec", pure, final,
             header: "<sys/uio.h>".} = object ## struct iovec
     iov_base*: pointer ## Base address of a memory region for input or output.
-    iov_len*: csize    ## The size of the memory pointed to by iov_base.
+    iov_len*: csize_t  ## The size of the memory pointed to by iov_base.
 
   Tmsghdr* {.importc: "struct msghdr", pure, final,
              header: "<sys/socket.h>".} = object  ## struct msghdr
diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim
index 4eb357d62..8d11c507d 100644
--- a/lib/posix/posix_linux_amd64.nim
+++ b/lib/posix/posix_linux_amd64.nim
@@ -309,7 +309,7 @@ type
     sa_mask*: Sigset ## Set of signals to be blocked during execution of
                       ## the signal handling function.
     sa_flags*: cint   ## Special flags.
-    sa_sigaction*: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}
+    sa_restorer: proc() {.noconv.}   ## not intended for application use.
 
   Stack* {.importc: "stack_t",
             header: "<signal.h>", final, pure.} = object ## stack_t
@@ -325,9 +325,9 @@ type
   SigInfo* {.importc: "siginfo_t",
               header: "<signal.h>", final, pure.} = object ## siginfo_t
     si_signo*: cint    ## Signal number.
-    si_code*: cint     ## Signal code.
     si_errno*: cint    ## If non-zero, an errno value associated with
                        ## this signal, as defined in <errno.h>.
+    si_code*: cint     ## Signal code.
     si_pid*: Pid       ## Sending process ID.
     si_uid*: Uid       ## Real user ID of sending process.
     si_addr*: pointer  ## Address of faulting instruction.
@@ -336,6 +336,12 @@ type
     si_value*: SigVal  ## Signal value.
     pad {.importc: "_pad".}: array[128 - 56, uint8]
 
+template sa_sigaction*(v: Sigaction): proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.} =
+  cast[proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}](v.sa_handler)
+proc `sa_sigaction=`*(v: var Sigaction, x: proc (x: cint, y: ptr SigInfo, z: pointer) {.noconv.}) =
+  v.sa_handler = cast[proc (x: cint) {.noconv.}](x)
+
+type
   Nl_item* {.importc: "nl_item", header: "<nl_types.h>".} = cint
   Nl_catd* {.importc: "nl_catd", header: "<nl_types.h>".} = pointer
 
diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim
index f3230f71d..fbe8d0666 100644
--- a/lib/posix/posix_linux_amd64_consts.nim
+++ b/lib/posix/posix_linux_amd64_consts.nim
@@ -453,6 +453,7 @@ const MAP_POPULATE* = cint(32768)
 
 # <sys/resource.h>
 const RLIMIT_NOFILE* = cint(7)
+const RLIMIT_STACK* = cint(3)
 
 # <sys/select.h>
 const FD_SETSIZE* = cint(1024)
@@ -464,6 +465,7 @@ const MSG_EOR* = cint(128)
 const MSG_OOB* = cint(1)
 const SCM_RIGHTS* = cint(1)
 const SO_ACCEPTCONN* = cint(30)
+const SO_BINDTODEVICE* = cint(25)
 const SO_BROADCAST* = cint(6)
 const SO_DEBUG* = cint(1)
 const SO_DONTROUTE* = cint(5)
diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim
index 2e68af330..a4b64ed62 100644
--- a/lib/posix/posix_macos_amd64.nim
+++ b/lib/posix/posix_macos_amd64.nim
@@ -108,15 +108,6 @@ type
     p_sign_posn*: char
     thousands_sep*: cstring
 
-  Mqd* {.importc: "mqd_t", header: "<mqueue.h>", final, pure.} = object
-  MqAttr* {.importc: "struct mq_attr",
-             header: "<mqueue.h>",
-             final, pure.} = object ## message queue attribute
-    mq_flags*: int   ## Message queue flags.
-    mq_maxmsg*: int  ## Maximum number of messages.
-    mq_msgsize*: int ## Maximum message size.
-    mq_curmsgs*: int ## Number of messages currently queued.
-
   Passwd* {.importc: "struct passwd", header: "<pwd.h>",
              final, pure.} = object ## struct passwd
     pw_name*: cstring   ## User's login name.
@@ -131,10 +122,14 @@ type
     ## used for block sizes
   Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int
   ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int
-  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = (
+    when defined(freebsd):
+      uint32
+    else:
+      int32)
   Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
   Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
-  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
   Id* {.importc: "id_t", header: "<sys/types.h>".} = int
   Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
   Key* {.importc: "key_t", header: "<sys/types.h>".} = int
@@ -144,7 +139,7 @@ type
     else:
       uint16
   )
-  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int16
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint16
   Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
   Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
   Pthread_attr* {.importc: "pthread_attr_t", header: "<sys/types.h>".} = int
@@ -176,7 +171,7 @@ type
   Trace_event_set* {.importc: "trace_event_set_t",
                       header: "<sys/types.h>".} = int
   Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
-  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
   Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
 
   Utsname* {.importc: "struct utsname",
@@ -375,6 +370,18 @@ when hasSpawnH:
     Tposix_spawn_file_actions* {.importc: "posix_spawn_file_actions_t",
                                  header: "<spawn.h>", final, pure.} = object
 
+
+when not defined(macos) and not defined(macosx): # freebsd
+  type
+    Mqd* {.importc: "mqd_t", header: "<mqueue.h>", final, pure.} = object
+    MqAttr* {.importc: "struct mq_attr",
+              header: "<mqueue.h>",
+              final, pure.} = object ## message queue attribute
+      mq_flags*: int   ## Message queue flags.
+      mq_maxmsg*: int  ## Maximum number of messages.
+      mq_msgsize*: int ## Maximum message size.
+      mq_curmsgs*: int ## Number of messages currently queued.
+
 when defined(linux):
   # from sys/un.h
   const Sockaddr_un_path_length* = 108
@@ -459,9 +466,9 @@ type
                    header: "<netinet/in.h>".} = object ## struct sockaddr_in6
     sin6_family*: TSa_Family ## AF_INET6.
     sin6_port*: InPort      ## Port number.
-    sin6_flowinfo*: int32    ## IPv6 traffic class and flow information.
+    sin6_flowinfo*: uint32    ## IPv6 traffic class and flow information.
     sin6_addr*: In6Addr     ## IPv6 address.
-    sin6_scope_id*: int32    ## Set of interfaces for a scope.
+    sin6_scope_id*: uint32    ## Set of interfaces for a scope.
 
   Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
                 header: "<netinet/in.h>".} = object ## struct ipv6_mreq
@@ -488,7 +495,7 @@ type
                              ## alternative network names, terminated by a
                              ## null pointer.
     n_addrtype*: cint        ## The address type of the network.
-    n_net*: int32            ## The network number, in host byte order.
+    n_net*: uint32            ## The network number, in host byte order.
 
   Protoent* {.importc: "struct protoent", pure, final,
               header: "<netdb.h>".} = object ## struct protoent
diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim
index 4cef80bb7..b66563695 100644
--- a/lib/posix/posix_nintendoswitch.nim
+++ b/lib/posix/posix_nintendoswitch.nim
@@ -286,7 +286,7 @@ type
             header: "<signal.h>", final, pure.} = object ## stack_t
     ss_sp*: pointer  ## Stack base or pointer.
     ss_flags*: cint  ## Flags.
-    ss_size*: csize  ## Stack size.
+    ss_size*: csize_t ## Stack size.
 
   SigInfo* {.importc: "siginfo_t",
               header: "<signal.h>", final, pure.} = object ## siginfo_t
@@ -321,7 +321,7 @@ type
     aio_lio_opcode*: cint     ## Operation to be performed.
     aio_reqprio*: cint        ## Request priority offset.
     aio_buf*: pointer         ## Location of buffer.
-    aio_nbytes*: csize        ## Length of transfer.
+    aio_nbytes*: csize_t      ## Length of transfer.
     aio_sigevent*: SigEvent   ## Signal number and value.
     next_prio: pointer
     abs_prio: cint
@@ -378,15 +378,15 @@ type
     msg_name*: pointer  ## Optional address.
     msg_namelen*: SockLen  ## Size of address.
     msg_iov*: ptr IOVec    ## Scatter/gather array.
-    msg_iovlen*: csize   ## Members in msg_iov.
+    msg_iovlen*: csize_t   ## Members in msg_iov.
     msg_control*: pointer  ## Ancillary data; see below.
-    msg_controllen*: csize ## Ancillary data buffer len.
+    msg_controllen*: csize_t ## Ancillary data buffer len.
     msg_flags*: cint ## Flags on received message.
 
 
   Tcmsghdr* {.importc: "struct cmsghdr", pure, final,
               header: "<sys/socket.h>".} = object ## struct cmsghdr
-    cmsg_len*: csize ## Data byte count, including the cmsghdr.
+    cmsg_len*: csize_t ## Data byte count, including the cmsghdr.
     cmsg_level*: cint   ## Originating protocol.
     cmsg_type*: cint    ## Protocol-specific type.
 
diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim
index 1ef4a4182..184cd89c0 100644
--- a/lib/posix/posix_openbsd_amd64.nim
+++ b/lib/posix/posix_openbsd_amd64.nim
@@ -131,15 +131,19 @@ type
     ## used for block sizes
   Clock* {.importc: "clock_t", header: "<sys/types.h>".} = int
   ClockId* {.importc: "clockid_t", header: "<sys/types.h>".} = int
-  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = int32
+  Dev* {.importc: "dev_t", header: "<sys/types.h>".} = (
+    when defined(freebsd):
+      uint32
+    else:
+      int32)
   Fsblkcnt* {.importc: "fsblkcnt_t", header: "<sys/types.h>".} = int
   Fsfilcnt* {.importc: "fsfilcnt_t", header: "<sys/types.h>".} = int
-  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = int32
+  Gid* {.importc: "gid_t", header: "<sys/types.h>".} = uint32
   Id* {.importc: "id_t", header: "<sys/types.h>".} = int
   Ino* {.importc: "ino_t", header: "<sys/types.h>".} = int
   Key* {.importc: "key_t", header: "<sys/types.h>".} = int
   Mode* {.importc: "mode_t", header: "<sys/types.h>".} = uint32
-  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = int16
+  Nlink* {.importc: "nlink_t", header: "<sys/types.h>".} = uint32
   Off* {.importc: "off_t", header: "<sys/types.h>".} = int64
   Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32
   Pthread_attr* {.importc: "pthread_attr_t", header: "<pthread.h>".} = int
@@ -171,7 +175,7 @@ type
   Trace_event_set* {.importc: "trace_event_set_t",
                       header: "<sys/types.h>".} = int
   Trace_id* {.importc: "trace_id_t", header: "<sys/types.h>".} = int
-  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = int32
+  Uid* {.importc: "uid_t", header: "<sys/types.h>".} = uint32
   Useconds* {.importc: "useconds_t", header: "<sys/types.h>".} = int
 
   Utsname* {.importc: "struct utsname",
@@ -446,9 +450,9 @@ type
                    header: "<netinet/in.h>".} = object ## struct sockaddr_in6
     sin6_family*: TSa_Family ## AF_INET6.
     sin6_port*: InPort      ## Port number.
-    sin6_flowinfo*: int32    ## IPv6 traffic class and flow information.
+    sin6_flowinfo*: uint32    ## IPv6 traffic class and flow information.
     sin6_addr*: In6Addr     ## IPv6 address.
-    sin6_scope_id*: int32    ## Set of interfaces for a scope.
+    sin6_scope_id*: uint32    ## Set of interfaces for a scope.
 
   Tipv6_mreq* {.importc: "struct ipv6_mreq", pure, final,
                 header: "<netinet/in.h>".} = object ## struct ipv6_mreq
@@ -475,7 +479,7 @@ type
                              ## alternative network names, terminated by a
                              ## null pointer.
     n_addrtype*: cint        ## The address type of the network.
-    n_net*: int32            ## The network number, in host byte order.
+    n_net*: uint32            ## The network number, in host byte order.
 
   Protoent* {.importc: "struct protoent", pure, final,
               header: "<netdb.h>".} = object ## struct protoent
diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim
index 941e13192..ea8731405 100644
--- a/lib/posix/posix_other.nim
+++ b/lib/posix/posix_other.nim
@@ -640,10 +640,13 @@ when defined(linux) or defined(nimdoc):
       ## or UDP packets. (Requires Linux kernel > 3.9)
   else:
     const SO_REUSEPORT* = cint(15)
+elif defined(nuttx):
+  # Not supported, use SO_REUSEADDR to avoid compilation errors.
+  var SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "<sys/socket.h>".}: cint
 else:
   var SO_REUSEPORT* {.importc, header: "<sys/socket.h>".}: cint
 
-when defined(linux) or defined(bsd):
+when defined(linux) or defined(bsd) or defined(nuttx):
   var SOCK_CLOEXEC* {.importc, header: "<sys/socket.h>".}: cint
 
 when defined(macosx):
@@ -672,14 +675,14 @@ when defined(haiku):
 
 when hasSpawnH:
   when defined(linux):
-    # better be safe than sorry; Linux has this flag, macosx doesn't, don't
-    # know about the other OSes
+    # better be safe than sorry; Linux has this flag, macosx and NuttX don't,
+    # don't know about the other OSes
 
-    # Non-GNU systems like TCC and musl-libc  don't define __USE_GNU, so we
+    # Non-GNU systems like TCC and musl-libc don't define __USE_GNU, so we
     # can't get the magic number from spawn.h
     const POSIX_SPAWN_USEVFORK* = cint(0x40)
   else:
-    # macosx lacks this, so we define the constant to be 0 to not affect
+    # macosx and NuttX lack this, so we define the constant to be 0 to not affect
     # OR'ing of flags:
     const POSIX_SPAWN_USEVFORK* = cint(0)
 
diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim
index 59ac6f9ed..d346b4150 100644
--- a/lib/posix/posix_other_consts.nim
+++ b/lib/posix/posix_other_consts.nim
@@ -99,7 +99,10 @@ var EXDEV* {.importc: "EXDEV", header: "<errno.h>".}: cint
 
 # <fcntl.h>
 var F_DUPFD* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint
-var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint
+when defined(nuttx):
+  var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD_CLOEXEC", header: "<fcntl.h>".}: cint
+else:
+  var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "<fcntl.h>".}: cint
 var F_GETFD* {.importc: "F_GETFD", header: "<fcntl.h>".}: cint
 var F_SETFD* {.importc: "F_SETFD", header: "<fcntl.h>".}: cint
 var F_GETFL* {.importc: "F_GETFL", header: "<fcntl.h>".}: cint
@@ -127,6 +130,11 @@ var O_RDONLY* {.importc: "O_RDONLY", header: "<fcntl.h>".}: cint
 var O_RDWR* {.importc: "O_RDWR", header: "<fcntl.h>".}: cint
 var O_WRONLY* {.importc: "O_WRONLY", header: "<fcntl.h>".}: cint
 var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "<fcntl.h>".}: cint
+when defined(nuttx):
+  var O_DIRECT* {.importc: "O_DIRECT", header: "<fcntl.h>".}: cint
+  var O_PATH* {.importc: "O_PATH", header: "<fcntl.h>".}: cint
+  var O_NOATIME* {.importc: "O_NOATIME", header: "<fcntl.h>".}: cint
+  var O_TMPFILE* {.importc: "O_TMPFILE", header: "<fcntl.h>".}: cint
 var POSIX_FADV_NORMAL* {.importc: "POSIX_FADV_NORMAL", header: "<fcntl.h>".}: cint
 var POSIX_FADV_SEQUENTIAL* {.importc: "POSIX_FADV_SEQUENTIAL", header: "<fcntl.h>".}: cint
 var POSIX_FADV_RANDOM* {.importc: "POSIX_FADV_RANDOM", header: "<fcntl.h>".}: cint
@@ -459,6 +467,7 @@ var POSIX_TYPED_MEM_MAP_ALLOCATABLE* {.importc: "POSIX_TYPED_MEM_MAP_ALLOCATABLE
 
 # <sys/resource.h>
 var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "<sys/resource.h>".}: cint
+var RLIMIT_STACK* {.importc: "RLIMIT_STACK", header: "<sys/resource.h>".}: cint
 
 # <sys/select.h>
 var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "<sys/select.h>".}: cint
@@ -473,6 +482,7 @@ var MSG_EOR* {.importc: "MSG_EOR", header: "<sys/socket.h>".}: cint
 var MSG_OOB* {.importc: "MSG_OOB", header: "<sys/socket.h>".}: cint
 var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "<sys/socket.h>".}: cint
 var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "<sys/socket.h>".}: cint
+var SO_BINDTODEVICE* {.importc: "SO_BINDTODEVICE", header: "<sys/socket.h>".}: cint
 var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "<sys/socket.h>".}: cint
 var SO_DEBUG* {.importc: "SO_DEBUG", header: "<sys/socket.h>".}: cint
 var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "<sys/socket.h>".}: cint
@@ -742,3 +752,6 @@ var SEEK_SET* {.importc: "SEEK_SET", header: "<unistd.h>".}: cint
 var SEEK_CUR* {.importc: "SEEK_CUR", header: "<unistd.h>".}: cint
 var SEEK_END* {.importc: "SEEK_END", header: "<unistd.h>".}: cint
 
+# <nuttx/config.h>
+when defined(nuttx):
+  var NEPOLL_MAX* {.importc: "CONFIG_FS_NEPOLL_DESCRIPTORS", header: "<nuttx/config.h>".}: cint
diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim
index c2d5aab56..0c668246f 100644
--- a/lib/posix/posix_utils.nim
+++ b/lib/posix/posix_utils.nim
@@ -11,14 +11,17 @@
 
 # Where possible, contribute OS-independent procs in `os <os.html>`_ instead.
 
-import posix, parsecfg, os
+import std/[posix, parsecfg, os]
 import std/private/since
 
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
 type Uname* = object
   sysname*, nodename*, release*, version*, machine*: string
 
 template charArrayToString(input: typed): string =
-  $cstring(addr input)
+  $cast[cstring](addr input)
 
 proc uname*(): Uname =
   ## Provides system information in a `Uname` struct with sysname, nodename,
@@ -31,7 +34,7 @@ proc uname*(): Uname =
 
   var u: Utsname
   if uname(u) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
   result.sysname = charArrayToString u.sysname
   result.nodename = charArrayToString u.nodename
@@ -40,44 +43,45 @@ proc uname*(): Uname =
   result.machine = charArrayToString u.machine
 
 proc fsync*(fd: int) =
- ## synchronize a file's buffer cache to the storage device
- if fsync(fd.cint) != 0:
-    raise newException(OSError, $strerror(errno))
+  ## synchronize a file's buffer cache to the storage device
+  if fsync(fd.cint) != 0:
+    raiseOSError(OSErrorCode(errno))
 
 proc stat*(path: string): Stat =
   ## Returns file status in a `Stat` structure
   if stat(path.cstring, result) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc memoryLock*(a1: pointer, a2: int) =
   ## Locks pages starting from a1 for a1 bytes and prevent them from being swapped.
   if mlock(a1, a2) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc memoryLockAll*(flags: int) =
   ## Locks all memory for the running process to prevent swapping.
   ##
-  ## example::
-  ##
+  ## example:
+  ##   ```nim
   ##   memoryLockAll(MCL_CURRENT or MCL_FUTURE)
+  ##   ```
   if mlockall(flags.cint) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc memoryUnlock*(a1: pointer, a2: int) =
   ## Unlock pages starting from a1 for a1 bytes and allow them to be swapped.
   if munlock(a1, a2) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc memoryUnlockAll*() =
   ## Unlocks all memory for the running process to allow swapping.
   if munlockall() != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc sendSignal*(pid: Pid, signal: int) =
   ## Sends a signal to a running process by calling `kill`.
   ## Raise exception in case of failure e.g. process not running.
   if kill(pid, signal.cint) != 0:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
 
 proc mkstemp*(prefix: string, suffix=""): (string, File) =
   ## Creates a unique temporary file from a prefix string. A six-character string
@@ -86,7 +90,7 @@ proc mkstemp*(prefix: string, suffix=""): (string, File) =
   ## Returns the filename and a file opened in r/w mode.
   var tmpl = cstring(prefix & "XXXXXX" & suffix)
   let fd =
-    if len(suffix)==0:
+    if len(suffix) == 0:
       when declared(mkostemp):
         mkostemp(tmpl, O_CLOEXEC)
       else:
@@ -99,14 +103,14 @@ proc mkstemp*(prefix: string, suffix=""): (string, File) =
   var f: File
   if open(f, fd, fmReadWrite):
     return ($tmpl, f)
-  raise newException(OSError, $strerror(errno))
+  raiseOSError(OSErrorCode(errno))
 
 proc mkdtemp*(prefix: string): string =
   ## Creates a unique temporary directory from a prefix string. Adds a six chars suffix.
   ## The directory is created with permissions 0700. Returns the directory name.
   var tmpl = cstring(prefix & "XXXXXX")
   if mkdtemp(tmpl) == nil:
-    raise newException(OSError, $strerror(errno))
+    raiseOSError(OSErrorCode(errno))
   return $tmpl
 
 proc osReleaseFile*(): Config {.since: (1, 5).} =
diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim
index f755c720d..7fb6bb81c 100644
--- a/lib/posix/termios.nim
+++ b/lib/posix/termios.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import posix
+import std/posix
 
 type
   Speed* = cuint
diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim
index fc0eceac3..b12ed7cdd 100644
--- a/lib/pure/algorithm.nim
+++ b/lib/pure/algorithm.nim
@@ -379,22 +379,22 @@ func sort*[T](a: var openArray[T],
   ## `cmp`, you may use `system.cmp` or instead call the overloaded
   ## version of `sort`, which uses `system.cmp`.
   ##
-  ## .. 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)
+  ##   ```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_experimental.html#do-notation>`_. Example:
-  ##
-  ## .. code-block:: nim
+  ## <manual.html#procedures-do-notation>`_. Example:
   ##
+  ##   ```nim
   ##   people.sort do (x, y: Person) -> int:
   ##     result = cmp(x.surname, y.surname)
   ##     if result == 0:
   ##       result = cmp(x.name, y.name)
+  ##   ```
   ##
   ## **See also:**
   ## * `sort proc<#sort,openArray[T]>`_
diff --git a/lib/pure/async.nim b/lib/pure/async.nim
index 482ab32c6..e4d8d41c3 100644
--- a/lib/pure/async.nim
+++ b/lib/pure/async.nim
@@ -1,6 +1,9 @@
+## Exports [asyncmacro](asyncmacro.html) and [asyncfutures](asyncfutures.html) for native backends,
+## and [asyncjs](asyncjs.html) on the JS backend. 
+
 when defined(js):
-  import asyncjs
+  import std/asyncjs
   export asyncjs
 else:
-  import asyncmacro, asyncfutures
+  import std/[asyncmacro, asyncfutures]
   export asyncmacro, asyncfutures
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 92ad9c5ff..126db7a7f 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -41,13 +41,13 @@
 ## requested amount of data is read **or** an exception occurs.
 ##
 ## Code to read some data from a socket may look something like this:
-##
-## .. code-block:: Nim
-##    var future = socket.recv(100)
-##    future.addCallback(
-##      proc () =
-##        echo(future.read)
-##    )
+##   ```Nim
+##   var future = socket.recv(100)
+##   future.addCallback(
+##     proc () =
+##       echo(future.read)
+##   )
+##   ```
 ##
 ## All asynchronous functions returning a `Future` will not block. They
 ## will not however return immediately. An asynchronous function will have
@@ -108,24 +108,24 @@
 ## You can handle exceptions in the same way as in ordinary Nim code;
 ## by using the try statement:
 ##
-##
-## .. code-block:: Nim
+##   ```Nim
 ##   try:
 ##     let data = await sock.recv(100)
 ##     echo("Received ", data)
 ##   except:
 ##     # Handle exception
-##
-##
+##   ```
 ##
 ## An alternative approach to handling exceptions is to use `yield` on a future
 ## then check the future's `failed` property. For example:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   var future = sock.recv(100)
 ##   yield future
 ##   if future.failed:
 ##     # Handle exception
+##   ```
+##
 ##
 ## Discarding futures
 ## ==================
@@ -226,11 +226,11 @@
 ## ``none`` can be used when a library supports both a synchronous and
 ## asynchronous API, to disable the latter.
 
-import os, tables, strutils, times, heapqueue, options, asyncstreams
-import options, math, std/monotimes
-import asyncfutures except callSoon
+import std/[os, tables, strutils, times, heapqueue, options, asyncstreams]
+import std/[math, monotimes]
+import std/asyncfutures except callSoon
 
-import nativesockets, net, deques
+import std/[nativesockets, net, deques]
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio]
@@ -281,6 +281,8 @@ proc adjustTimeout(
   result = max(nextTimer.get(), 0)
   result = min(pollTimeout, result)
 
+proc runOnce(timeout: int): bool {.gcsafe.}
+
 proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.}
   ## Schedule `cbproc` to be called as soon as possible.
   ## The callback is called when control returns to the event loop.
@@ -300,7 +302,7 @@ template implementSetInheritable() {.dirty.} =
       fd.FileHandle.setInheritable(inheritable)
 
 when defined(windows) or defined(nimdoc):
-  import winlean, sets, hashes
+  import std/[winlean, sets, hashes]
   type
     CompletionKey = ULONG_PTR
 
@@ -390,7 +392,7 @@ when defined(windows) or defined(nimdoc):
     let p = getGlobalDispatcher()
     p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0
 
-  proc runOnce(timeout = 500): bool =
+  proc runOnce(timeout: int): bool =
     let p = getGlobalDispatcher()
     if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0:
       raise newException(ValueError,
@@ -531,7 +533,7 @@ when defined(windows) or defined(nimdoc):
             if flags.isDisconnectionError(errcode):
               retFuture.complete("")
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if dataBuf.buf != nil:
           dealloc dataBuf.buf
           dataBuf.buf = nil
@@ -549,7 +551,7 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     elif ret == 0:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -601,7 +603,7 @@ when defined(windows) or defined(nimdoc):
             if flags.isDisconnectionError(errcode):
               retFuture.complete(0)
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if dataBuf.buf != nil:
           dataBuf.buf = nil
     )
@@ -617,7 +619,7 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     elif ret == 0:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -665,7 +667,7 @@ when defined(windows) or defined(nimdoc):
         if flags.isDisconnectionError(err):
           retFuture.complete()
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       retFuture.complete()
       # We don't deallocate `ol` here because even though this completed
@@ -700,7 +702,7 @@ when defined(windows) or defined(nimdoc):
           if errcode == OSErrorCode(-1):
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
 
     let ret = WSASendTo(socket.SocketHandle, addr dataBuf, 1, addr bytesSent,
@@ -710,7 +712,7 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       retFuture.complete()
       # We don't deallocate `ol` here because even though this completed
@@ -744,7 +746,7 @@ when defined(windows) or defined(nimdoc):
           else:
             # datagram sockets don't have disconnection,
             # so we can just raise an exception
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
 
     let res = WSARecvFrom(socket.SocketHandle, addr dataBuf, 1,
@@ -755,7 +757,7 @@ when defined(windows) or defined(nimdoc):
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       if bytesReceived != 0:
@@ -806,7 +808,7 @@ when defined(windows) or defined(nimdoc):
             else:
               retFuture.complete(newAcceptFut.read)
       else:
-        retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+        retFuture.fail(newOSError(errcode))
 
     template completeAccept() {.dirty.} =
       var listenSock = socket
@@ -1164,11 +1166,14 @@ when defined(windows) or defined(nimdoc):
 
   initAll()
 else:
-  import selectors
-  from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
+  import std/selectors
+  from std/posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
                     MSG_NOSIGNAL
   when declared(posix.accept4):
-    from posix import accept4, SOCK_CLOEXEC
+    from std/posix import accept4, SOCK_CLOEXEC
+  when defined(genode):
+    import genode/env # get the implicit Genode env
+    import genode/signals
 
   const
     InitCallbackListSize = 4         # initial size of callbacks sequence,
@@ -1187,6 +1192,8 @@ else:
 
     PDispatcher* = ref object of PDispatcherBase
       selector: Selector[AsyncData]
+      when defined(genode):
+        signalHandler: SignalHandler
 
   proc `==`*(x, y: AsyncFD): bool {.borrow.}
   proc `==`*(x, y: AsyncEvent): bool {.borrow.}
@@ -1202,9 +1209,22 @@ else:
     result.selector = newSelector[AsyncData]()
     result.timers.clear()
     result.callbacks = initDeque[proc () {.closure, gcsafe.}](InitDelayedCallbackListSize)
+    when defined(genode):
+      let entrypoint = ep(cast[GenodeEnv](runtimeEnv))
+      result.signalHandler = newSignalHandler(entrypoint):
+        discard runOnce(0)
 
   var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher
 
+  when defined(nuttx):
+    import std/exitprocs
+
+    proc cleanDispatcher() {.noconv.} =
+      gDisp = nil
+
+    proc addFinalyzer() =
+      addExitProc(cleanDispatcher)
+
   proc setGlobalDispatcher*(disp: owned PDispatcher) =
     if not gDisp.isNil:
       assert gDisp.callbacks.len == 0
@@ -1214,6 +1234,8 @@ else:
   proc getGlobalDispatcher*(): PDispatcher =
     if gDisp.isNil:
       setGlobalDispatcher(newDispatcher())
+      when defined(nuttx):
+        addFinalyzer()
     result = gDisp
 
   proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] =
@@ -1371,10 +1393,11 @@ else:
           ValueError, "Expecting async operations to stop when fd has closed."
         )
 
-
-  proc runOnce(timeout = 500): bool =
+  proc runOnce(timeout: int): bool =
     let p = getGlobalDispatcher()
     if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0:
+      when defined(genode):
+        if timeout == 0: return
       raise newException(ValueError,
         "No handles or timers registered in dispatcher.")
 
@@ -1445,7 +1468,7 @@ else:
           if flags.isDisconnectionError(lastError):
             retFuture.complete("")
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -1474,7 +1497,7 @@ else:
           if flags.isDisconnectionError(lastError):
             retFuture.complete(0)
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -1540,7 +1563,7 @@ else:
         let lastError = osLastError()
         if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
            lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -1566,7 +1589,7 @@ else:
         let lastError = osLastError()
         if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and
            lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false
       else:
@@ -1579,7 +1602,7 @@ else:
       owned(Future[tuple[address: string, client: AsyncFD]]) =
     var retFuture = newFuture[tuple[address: string,
         client: AsyncFD]]("acceptAddr")
-    proc cb(sock: AsyncFD): bool =
+    proc cb(sock: AsyncFD): bool {.gcsafe.} =
       result = true
       var sockAddress: Sockaddr_storage
       var addrLen = sizeof(sockAddress).SockLen
@@ -1607,7 +1630,7 @@ else:
           if flags.isDisconnectionError(lastError):
             return false
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+            retFuture.fail(newOSError(lastError))
       else:
         try:
           let address = getAddrString(cast[ptr SockAddr](addr sockAddress))
@@ -1740,7 +1763,7 @@ when defined(windows) or defined(nimdoc):
             socket.SocketHandle.setSockOptInt(SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, 1) # 15022
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
 
     let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
@@ -1758,7 +1781,7 @@ when defined(windows) or defined(nimdoc):
         # With ERROR_IO_PENDING `ol` will be deallocated in `poll`,
         # and the future will be completed/failed there, too.
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+        retFuture.fail(newOSError(lastError))
 else:
   proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) =
     let retFuture = newFuture[void]("doConnect")
@@ -1775,7 +1798,7 @@ else:
         # interrupted, keep waiting
         return false
       else:
-        retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+        retFuture.fail(newOSError(OSErrorCode(ret)))
         return true
 
     let ret = connect(socket.SocketHandle,
@@ -1789,7 +1812,7 @@ else:
       if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
         addWrite(socket, cb)
       else:
-        retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+        retFuture.fail(newOSError(lastError))
 
 template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
                            protocol: Protocol = IPPROTO_RAW) =
@@ -1971,7 +1994,8 @@ proc send*(socket: AsyncFD, data: string,
   return retFuture
 
 # -- Await Macro
-include asyncmacro
+import std/asyncmacro
+export asyncmacro
 
 proc readAll*(future: FutureStream[string]): owned(Future[string]) {.async.} =
   ## Returns a future that will complete when all the string data from the
@@ -2008,10 +2032,10 @@ proc activeDescriptors*(): int {.inline.} =
     result = getGlobalDispatcher().selector.count
 
 when defined(posix):
-  import posix
+  import std/posix
 
 when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
-       defined(solaris) or defined(zephyr) or defined(freertos):
+       defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(haiku):
   proc maxDescriptors*(): int {.raises: OSError.} =
     ## Returns the maximum number of active file descriptors for the current
     ## process. This involves a system call. For now `maxDescriptors` is
@@ -2025,3 +2049,17 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
       if getrlimit(RLIMIT_NOFILE, fdLim) < 0:
         raiseOSError(osLastError())
       result = int(fdLim.rlim_cur) - 1
+
+when defined(genode):
+  proc scheduleCallbacks*(): bool {.discardable.} =
+    ## *Genode only.*
+    ## Schedule callback processing and return immediately.
+    ## Returns `false` if there is nothing to schedule.
+    ## RPC servers should call this to dispatch `callSoon`
+    ## bodies after retiring an RPC to its client.
+    ## This is effectively a non-blocking `poll(…)` and is
+    ## equivalent to scheduling a momentary no-op timeout
+    ## but faster and with less overhead.
+    let dis = getGlobalDispatcher()
+    result = dis.callbacks.len > 0
+    if result: submit(dis.signalHandler.cap)
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 9cc9f5b48..0f6504342 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -9,30 +9,33 @@
 
 ## This module implements asynchronous file reading and writing.
 ##
-## .. code-block:: Nim
-##    import std/[asyncfile, asyncdispatch, os]
+##   ```Nim
+##   import std/[asyncfile, asyncdispatch, os]
 ##
-##    proc main() {.async.} =
-##      var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite)
-##      await file.write("test")
-##      file.setFilePos(0)
-##      let data = await file.readAll()
-##      doAssert data == "test"
-##      file.close()
+##   proc main() {.async.} =
+##     var file = openAsync(getTempDir() / "foobar.txt", fmReadWrite)
+##     await file.write("test")
+##     file.setFilePos(0)
+##     let data = await file.readAll()
+##     doAssert data == "test"
+##     file.close()
 ##
-##    waitFor main()
+##   waitFor main()
+##   ```
 
-import asyncdispatch, os
+import std/[asyncdispatch, os]
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio]
+  when defined(windows) or defined(nimdoc):
+    import std/widestrs
 
 # TODO: Fix duplication introduced by PR #4683.
 
 when defined(windows) or defined(nimdoc):
-  import winlean
+  import std/winlean
 else:
-  import posix
+  import std/posix
 
 type
   AsyncFile* = ref object
@@ -99,14 +102,9 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile =
     let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL
     let desiredAccess = getDesiredAccess(mode)
     let creationDisposition = getCreationDisposition(mode, filename)
-    when useWinUnicode:
-      let fd = createFileW(newWideCString(filename), desiredAccess,
-          FILE_SHARE_READ,
-          nil, creationDisposition, flags, 0)
-    else:
-      let fd = createFileA(filename, desiredAccess,
-          FILE_SHARE_READ,
-          nil, creationDisposition, flags, 0)
+    let fd = createFileW(newWideCString(filename), desiredAccess,
+        FILE_SHARE_READ,
+        nil, creationDisposition, flags, 0)
 
     if fd == INVALID_HANDLE_VALUE:
       raiseOSError(osLastError())
@@ -148,7 +146,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
             if errcode.int32 == ERROR_HANDLE_EOF:
               retFuture.complete(0)
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
     )
     ol.offset = DWORD(f.offset and 0xffffffff)
     ol.offsetHigh = DWORD(f.offset shr 32)
@@ -164,7 +162,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
           # This happens in Windows Server 2003
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       var bytesRead: DWORD
@@ -175,7 +173,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
         if err.int32 == ERROR_HANDLE_EOF:
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesRead > 0
         assert bytesRead <= size
@@ -188,7 +186,7 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -230,7 +228,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
             if errcode.int32 == ERROR_HANDLE_EOF:
               retFuture.complete("")
             else:
-              retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+              retFuture.fail(newOSError(errcode))
         if buffer != nil:
           dealloc buffer
           buffer = nil
@@ -253,7 +251,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
           # This happens in Windows Server 2003
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(err)))
+          retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       var bytesRead: DWORD
@@ -264,7 +262,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
         if err.int32 == ERROR_HANDLE_EOF:
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesRead > 0
         assert bytesRead <= size
@@ -281,7 +279,7 @@ proc read*(f: AsyncFile, size: int): Future[string] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       elif res == 0:
@@ -303,6 +301,8 @@ proc readLine*(f: AsyncFile): Future[string] {.async.} =
   result = ""
   while true:
     var c = await read(f, 1)
+    if c.len == 0:
+      break
     if c[0] == '\c':
       c = await read(f, 1)
       break
@@ -350,7 +350,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
             assert bytesCount == size.int32
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
     )
     # passing -1 here should work according to MSDN, but doesn't. For more
     # information see
@@ -367,14 +367,14 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
       let err = osLastError()
       if err.int32 != ERROR_IO_PENDING:
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       var bytesWritten: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
           cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
       if not overlappedRes.bool:
-        retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+        retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesWritten == size.int32
         retFuture.complete()
@@ -389,7 +389,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
@@ -423,7 +423,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
             assert bytesCount == data.len.int32
             retFuture.complete()
           else:
-            retFuture.fail(newException(OSError, osErrorMsg(errcode)))
+            retFuture.fail(newOSError(errcode))
         if buffer != nil:
           dealloc buffer
           buffer = nil
@@ -442,14 +442,14 @@ proc write*(f: AsyncFile, data: string): Future[void] =
           dealloc buffer
           buffer = nil
         GC_unref(ol)
-        retFuture.fail(newException(OSError, osErrorMsg(err)))
+        retFuture.fail(newOSError(err))
     else:
       # Request completed immediately.
       var bytesWritten: DWORD
       let overlappedRes = getOverlappedResult(f.fd.Handle,
           cast[POVERLAPPED](ol), bytesWritten, false.WINBOOL)
       if not overlappedRes.bool:
-        retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+        retFuture.fail(newOSError(osLastError()))
       else:
         assert bytesWritten == data.len.int32
         retFuture.complete()
@@ -470,7 +470,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
       if res < 0:
         let lastError = osLastError()
         if lastError.int32 != EAGAIN:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
         else:
           result = false # We still want this callback to be called.
       else:
diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim
deleted file mode 100644
index 0d2ee80c5..000000000
--- a/lib/pure/asyncftpclient.nim
+++ /dev/null
@@ -1,451 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Dominik Picheta
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements an asynchronous FTP client. It allows you to connect
-## to an FTP server and perform operations on it such as for example:
-##
-## * The upload of new files.
-## * The removal of existing files.
-## * Download of files.
-## * Changing of files' permissions.
-## * Navigation through the FTP server's directories.
-##
-## Connecting to an FTP server
-## ===========================
-##
-## In order to begin any sort of transfer of files you must first
-## connect to an FTP server. You can do so with the `connect` procedure.
-##
-## .. code-block:: Nim
-##    import std/[asyncdispatch, asyncftpclient]
-##    proc main() {.async.} =
-##      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-##      await ftp.connect()
-##      echo("Connected")
-##    waitFor(main())
-##
-## A new `main` async procedure must be declared to allow the use of the
-## `await` keyword. The connection will complete asynchronously and the
-## client will be connected after the `await ftp.connect()` call.
-##
-## Uploading a new file
-## ====================
-##
-## After a connection is made you can use the `store` procedure to upload
-## a new file to the FTP server. Make sure to check you are in the correct
-## working directory before you do so with the `pwd` procedure, you can also
-## instead specify an absolute path.
-##
-## .. code-block:: Nim
-##    import std/[asyncdispatch, asyncftpclient]
-##    proc main() {.async.} =
-##      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-##      await ftp.connect()
-##      let currentDir = await ftp.pwd()
-##      assert currentDir == "/home/user/"
-##      await ftp.store("file.txt", "file.txt")
-##      echo("File finished uploading")
-##    waitFor(main())
-##
-## Checking the progress of a file transfer
-## ========================================
-##
-## The progress of either a file upload or a file download can be checked
-## by specifying a `onProgressChanged` procedure to the `store` or
-## `retrFile` procedures.
-##
-## Procs that take an `onProgressChanged` callback will call this every
-## `progressInterval` milliseconds.
-##
-## .. code-block:: Nim
-##    import std/[asyncdispatch, asyncftpclient]
-##
-##    proc onProgressChanged(total, progress: BiggestInt,
-##                            speed: float) {.async.} =
-##      echo("Uploaded ", progress, " of ", total, " bytes")
-##      echo("Current speed: ", speed, " kb/s")
-##
-##    proc main() {.async.} =
-##      var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test", progressInterval = 500)
-##      await ftp.connect()
-##      await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged)
-##      echo("File finished uploading")
-##    waitFor(main())
-
-
-import asyncdispatch, asyncnet, nativesockets, strutils, parseutils, os, times
-from net import BufferSize
-
-when defined(nimPreviewSlimSystem):
-  import std/assertions
-
-type
-  AsyncFtpClient* = ref object
-    csock*: AsyncSocket
-    dsock*: AsyncSocket
-    user*, pass*: string
-    address*: string
-    port*: Port
-    progressInterval: int
-    jobInProgress*: bool
-    job*: FtpJob
-    dsockConnected*: bool
-
-  FtpJobType* = enum
-    JRetrText, JRetr, JStore
-
-  FtpJob = ref object
-    prc: proc (ftp: AsyncFtpClient, async: bool): bool {.nimcall, gcsafe.}
-    case typ*: FtpJobType
-    of JRetrText:
-      lines: string
-    of JRetr, JStore:
-      file: File
-      filename: string
-      total: BiggestInt         # In bytes.
-      progress: BiggestInt      # In bytes.
-      oneSecond: BiggestInt     # Bytes transferred in one second.
-      lastProgressReport: float # Time
-      toStore: string           # Data left to upload (Only used with async)
-
-  FtpEventType* = enum
-    EvTransferProgress, EvLines, EvRetr, EvStore
-
-  FtpEvent* = object             ## Event
-    filename*: string
-    case typ*: FtpEventType
-    of EvLines:
-      lines*: string             ## Lines that have been transferred.
-    of EvRetr, EvStore:          ## Retr/Store operation finished.
-      nil
-    of EvTransferProgress:
-      bytesTotal*: BiggestInt    ## Bytes total.
-      bytesFinished*: BiggestInt ## Bytes transferred.
-      speed*: BiggestInt         ## Speed in bytes/s
-      currentJob*: FtpJobType    ## The current job being performed.
-
-  ReplyError* = object of IOError
-
-  ProgressChangedProc* =
-    proc (total, progress: BiggestInt, speed: float):
-      Future[void] {.closure, gcsafe.}
-
-const multiLineLimit = 10000
-
-proc expectReply(ftp: AsyncFtpClient): Future[string] {.async.} =
-  var line = await ftp.csock.recvLine()
-  result = line
-  var count = 0
-  while line.len > 3 and line[3] == '-':
-    ## Multi-line reply.
-    line = await ftp.csock.recvLine()
-    result.add("\n" & line)
-    count.inc()
-    if count >= multiLineLimit:
-      raise newException(ReplyError, "Reached maximum multi-line reply count.")
-
-proc send*(ftp: AsyncFtpClient, m: string): Future[string] {.async.} =
-  ## Send a message to the server, and wait for a primary reply.
-  ## `\c\L` is added for you.
-  ##
-  ## You need to make sure that the message `m` doesn't contain any newline
-  ## characters. Failing to do so will raise `AssertionDefect`.
-  ##
-  ## **Note:** The server may return multiple lines of coded replies.
-  doAssert(not m.contains({'\c', '\L'}), "message shouldn't contain any newline characters")
-  await ftp.csock.send(m & "\c\L")
-  return await ftp.expectReply()
-
-proc assertReply(received: string, expected: varargs[string]) =
-  for i in items(expected):
-    if received.startsWith(i): return
-  raise newException(ReplyError,
-                     "Expected reply '$1' got: $2" %
-                      [expected.join("' or '"), received])
-
-proc pasv(ftp: AsyncFtpClient) {.async.} =
-  ## Negotiate a data connection.
-  ftp.dsock = newAsyncSocket()
-
-  var pasvMsg = (await ftp.send("PASV")).strip
-  assertReply(pasvMsg, "227")
-  var betweenParens = captureBetween(pasvMsg, '(', ')')
-  var nums = betweenParens.split(',')
-  var ip = nums[0 .. ^3]
-  var port = nums[^2 .. ^1]
-  var properPort = port[0].parseInt()*256+port[1].parseInt()
-  await ftp.dsock.connect(ip.join("."), Port(properPort))
-  ftp.dsockConnected = true
-
-proc normalizePathSep(path: string): string =
-  return replace(path, '\\', '/')
-
-proc connect*(ftp: AsyncFtpClient) {.async.} =
-  ## Connect to the FTP server specified by `ftp`.
-  await ftp.csock.connect(ftp.address, ftp.port)
-
-  var reply = await ftp.expectReply()
-  if reply.startsWith("120"):
-    # 120 Service ready in nnn minutes.
-    # We wait until we receive 220.
-    reply = await ftp.expectReply()
-
-  # Handle 220 messages from the server
-  assertReply(reply, "220")
-
-  if ftp.user != "":
-    assertReply(await(ftp.send("USER " & ftp.user)), "230", "331")
-
-  if ftp.pass != "":
-    assertReply(await(ftp.send("PASS " & ftp.pass)), "230")
-
-proc pwd*(ftp: AsyncFtpClient): Future[string] {.async.} =
-  ## Returns the current working directory.
-  let wd = await ftp.send("PWD")
-  assertReply wd, "257"
-  return wd.captureBetween('"') # "
-
-proc cd*(ftp: AsyncFtpClient, dir: string) {.async.} =
-  ## Changes the current directory on the remote FTP server to `dir`.
-  assertReply(await(ftp.send("CWD " & dir.normalizePathSep)), "250")
-
-proc cdup*(ftp: AsyncFtpClient) {.async.} =
-  ## Changes the current directory to the parent of the current directory.
-  assertReply(await(ftp.send("CDUP")), "200")
-
-proc getLines(ftp: AsyncFtpClient): Future[string] {.async.} =
-  ## Downloads text data in ASCII mode
-  result = ""
-  assert ftp.dsockConnected
-  while ftp.dsockConnected:
-    let r = await ftp.dsock.recvLine()
-    if r == "":
-      ftp.dsockConnected = false
-    else:
-      result.add(r & "\n")
-
-  assertReply(await(ftp.expectReply()), "226")
-
-proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} =
-  ## Returns a list of filenames in the given directory. If `dir` is "",
-  ## the current directory is used. If `async` is true, this
-  ## function will return immediately and it will be your job to
-  ## use asyncdispatch's `poll` to progress this operation.
-  await ftp.pasv()
-
-  assertReply(await(ftp.send("NLST " & dir.normalizePathSep)), ["125", "150"])
-
-  result = splitLines(await ftp.getLines())
-
-proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} =
-  ## Determines whether `file` exists.
-  var files = await ftp.listDirs()
-  for f in items(files):
-    if f.normalizePathSep == file.normalizePathSep: return true
-
-proc createDir*(ftp: AsyncFtpClient, dir: string, recursive = false){.async.} =
-  ## Creates a directory `dir`. If `recursive` is true, the topmost
-  ## subdirectory of `dir` will be created first, following the secondmost...
-  ## etc. this allows you to give a full path as the `dir` without worrying
-  ## about subdirectories not existing.
-  if not recursive:
-    assertReply(await(ftp.send("MKD " & dir.normalizePathSep)), "257")
-  else:
-    var reply = ""
-    var previousDirs = ""
-    for p in split(dir, {os.DirSep, os.AltSep}):
-      if p != "":
-        previousDirs.add(p)
-        reply = await ftp.send("MKD " & previousDirs)
-        previousDirs.add('/')
-    assertReply reply, "257"
-
-proc chmod*(ftp: AsyncFtpClient, path: string,
-            permissions: set[FilePermission]) {.async.} =
-  ## Changes permission of `path` to `permissions`.
-  var userOctal = 0
-  var groupOctal = 0
-  var otherOctal = 0
-  for i in items(permissions):
-    case i
-    of fpUserExec: userOctal.inc(1)
-    of fpUserWrite: userOctal.inc(2)
-    of fpUserRead: userOctal.inc(4)
-    of fpGroupExec: groupOctal.inc(1)
-    of fpGroupWrite: groupOctal.inc(2)
-    of fpGroupRead: groupOctal.inc(4)
-    of fpOthersExec: otherOctal.inc(1)
-    of fpOthersWrite: otherOctal.inc(2)
-    of fpOthersRead: otherOctal.inc(4)
-
-  var perm = $userOctal & $groupOctal & $otherOctal
-  assertReply(await(ftp.send("SITE CHMOD " & perm &
-                    " " & path.normalizePathSep)), "200")
-
-proc list*(ftp: AsyncFtpClient, dir = ""): Future[string] {.async.} =
-  ## Lists all files in `dir`. If `dir` is `""`, uses the current
-  ## working directory.
-  await ftp.pasv()
-
-  let reply = await ftp.send("LIST" & " " & dir.normalizePathSep)
-  assertReply(reply, ["125", "150"])
-
-  result = await ftp.getLines()
-
-proc retrText*(ftp: AsyncFtpClient, file: string): Future[string] {.async.} =
-  ## Retrieves `file`. File must be ASCII text.
-  await ftp.pasv()
-  let reply = await ftp.send("RETR " & file.normalizePathSep)
-  assertReply(reply, ["125", "150"])
-
-  result = await ftp.getLines()
-
-proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt,
-             onProgressChanged: ProgressChangedProc) {.async.} =
-  assert ftp.dsockConnected
-  var progress = 0
-  var progressInSecond = 0
-  var countdownFut = sleepAsync(ftp.progressInterval)
-  var dataFut = ftp.dsock.recv(BufferSize)
-  while ftp.dsockConnected:
-    await dataFut or countdownFut
-    if countdownFut.finished:
-      asyncCheck onProgressChanged(total, progress,
-          progressInSecond.float)
-      progressInSecond = 0
-      countdownFut = sleepAsync(ftp.progressInterval)
-
-    if dataFut.finished:
-      let data = dataFut.read
-      if data != "":
-        progress.inc(data.len)
-        progressInSecond.inc(data.len)
-        file.write(data)
-        dataFut = ftp.dsock.recv(BufferSize)
-      else:
-        ftp.dsockConnected = false
-
-  assertReply(await(ftp.expectReply()), "226")
-
-proc defaultOnProgressChanged*(total, progress: BiggestInt,
-    speed: float): Future[void] {.nimcall, gcsafe.} =
-  ## Default FTP `onProgressChanged` handler. Does nothing.
-  result = newFuture[void]()
-  #echo(total, " ", progress, " ", speed)
-  result.complete()
-
-proc retrFile*(ftp: AsyncFtpClient, file, dest: string,
-               onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
-  ## Downloads `file` and saves it to `dest`.
-  ## The `EvRetr` event is passed to the specified `handleEvent` function
-  ## when the download is finished. The event's `filename` field will be equal
-  ## to `file`.
-  var destFile = open(dest, mode = fmWrite)
-  await ftp.pasv()
-  var reply = await ftp.send("RETR " & file.normalizePathSep)
-  assertReply reply, ["125", "150"]
-  if {'(', ')'} notin reply:
-    raise newException(ReplyError, "Reply has no file size.")
-  var fileSize: BiggestInt
-  if reply.captureBetween('(', ')').parseBiggestInt(fileSize) == 0:
-    raise newException(ReplyError, "Reply has no file size.")
-
-  await getFile(ftp, destFile, fileSize, onProgressChanged)
-  destFile.close()
-
-proc doUpload(ftp: AsyncFtpClient, file: File,
-              onProgressChanged: ProgressChangedProc) {.async.} =
-  assert ftp.dsockConnected
-
-  let total = file.getFileSize()
-  var data = newString(4000)
-  var progress = 0
-  var progressInSecond = 0
-  var countdownFut = sleepAsync(ftp.progressInterval)
-  var sendFut: Future[void] = nil
-  while ftp.dsockConnected:
-    if sendFut == nil or sendFut.finished:
-      # TODO: Async file reading.
-      let len = file.readBuffer(addr(data[0]), 4000)
-      setLen(data, len)
-      if len == 0:
-        # File finished uploading.
-        ftp.dsock.close()
-        ftp.dsockConnected = false
-
-        assertReply(await(ftp.expectReply()), "226")
-      else:
-        progress.inc(len)
-        progressInSecond.inc(len)
-        sendFut = ftp.dsock.send(data)
-
-    if countdownFut.finished:
-      asyncCheck onProgressChanged(total, progress, progressInSecond.float)
-      progressInSecond = 0
-      countdownFut = sleepAsync(ftp.progressInterval)
-
-    await countdownFut or sendFut
-
-proc store*(ftp: AsyncFtpClient, file, dest: string,
-            onProgressChanged: ProgressChangedProc = defaultOnProgressChanged) {.async.} =
-  ## Uploads `file` to `dest` on the remote FTP server. Usage of this
-  ## function asynchronously is recommended to view the progress of
-  ## the download.
-  ## The `EvStore` event is passed to the specified `handleEvent` function
-  ## when the upload is finished, and the `filename` field will be
-  ## equal to `file`.
-  var destFile = open(file)
-  await ftp.pasv()
-
-  let reply = await ftp.send("STOR " & dest.normalizePathSep)
-  assertReply reply, ["125", "150"]
-
-  await doUpload(ftp, destFile, onProgressChanged)
-
-proc rename*(ftp: AsyncFtpClient, nameFrom: string, nameTo: string) {.async.} =
-  ## Rename a file or directory on the remote FTP Server from current name
-  ## `name_from` to new name `name_to`
-  assertReply(await ftp.send("RNFR " & nameFrom), "350")
-  assertReply(await ftp.send("RNTO " & nameTo), "250")
-
-proc removeFile*(ftp: AsyncFtpClient, filename: string) {.async.} =
-  ## Delete a file `filename` on the remote FTP server
-  assertReply(await ftp.send("DELE " & filename), "250")
-
-proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} =
-  ## Delete a directory `dir` on the remote FTP server
-  assertReply(await ftp.send("RMD " & dir), "250")
-
-proc newAsyncFtpClient*(address: string, port = Port(21),
-    user, pass = "", progressInterval: int = 1000): AsyncFtpClient =
-  ## Creates a new `AsyncFtpClient` object.
-  new result
-  result.user = user
-  result.pass = pass
-  result.address = address
-  result.port = port
-  result.progressInterval = progressInterval
-  result.dsockConnected = false
-  result.csock = newAsyncSocket()
-
-when not defined(testing) and isMainModule:
-  var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test")
-  proc main(ftp: AsyncFtpClient) {.async.} =
-    await ftp.connect()
-    echo await ftp.pwd()
-    echo await ftp.listDirs()
-    await ftp.store("payload.jpg", "payload.jpg")
-    await ftp.retrFile("payload.jpg", "payload2.jpg")
-    await ftp.rename("payload.jpg", "payload_renamed.jpg")
-    await ftp.store("payload.jpg", "payload_remove.jpg")
-    await ftp.removeFile("payload_remove.jpg")
-    await ftp.createDir("deleteme")
-    await ftp.removeDir("deleteme")
-    echo("Finished")
-
-  waitFor main(ftp)
diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim
index 035b6182d..29ebf8f89 100644
--- a/lib/pure/asyncfutures.nim
+++ b/lib/pure/asyncfutures.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import os, tables, strutils, times, heapqueue, options, deques, cstrutils
+import std/[os, tables, strutils, times, heapqueue, options, deques, cstrutils, typetraits]
 
 import system/stacktraces
 
@@ -29,7 +29,7 @@ type
     finished: bool
     error*: ref Exception              ## Stored exception
     errorStackTrace*: string
-    when not defined(release):
+    when not defined(release) or defined(futureLogging):
       stackTrace: seq[StackTraceEntry] ## For debugging purposes only.
       id: int
       fromProc: string
@@ -51,7 +51,7 @@ const
   NimAsyncContinueSuffix* = "NimAsyncContinue" ## For internal usage. Do not use.
 
 when isFutureLoggingEnabled:
-  import hashes
+  import std/hashes
   type
     FutureInfo* = object
       stackTrace*: seq[StackTraceEntry]
@@ -193,7 +193,7 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) =
         last = last.next
       last.next = newCallback
 
-proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) =
+proc completeImpl[T, U](future: Future[T], val: sink U, isVoid: static bool) =
   #assert(not future.finished, "Future already finished, cannot finish twice.")
   checkFinished(future)
   assert(future.error == nil)
@@ -203,7 +203,7 @@ proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) =
   future.callbacks.call()
   when isFutureLoggingEnabled: logFutureFinish(future)
 
-proc complete*[T](future: Future[T], val: T) =
+proc complete*[T](future: Future[T], val: sink T) =
   ## Completes `future` with value `val`.
   completeImpl(future, val, false)
 
@@ -219,7 +219,7 @@ proc complete*[T](future: FutureVar[T]) =
   fut.callbacks.call()
   when isFutureLoggingEnabled: logFutureFinish(Future[T](future))
 
-proc complete*[T](future: FutureVar[T], val: T) =
+proc complete*[T](future: FutureVar[T], val: sink T) =
   ## Completes a `FutureVar` with value `val`.
   ##
   ## Any previously stored value will be overwritten.
@@ -329,29 +329,21 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string =
     if leftLen > longestLeft:
       longestLeft = leftLen
 
-  var indent = 2
   # Format the entries.
   for entry in entries:
     let (filename, procname) = getFilenameProcname(entry)
 
-    if procname == "":
-      if entry.line == reraisedFromBegin:
-        result.add(spaces(indent) & "#[\n")
-        indent.inc(2)
-      elif entry.line == reraisedFromEnd:
-        indent.dec(2)
-        result.add(spaces(indent) & "]#\n")
-      continue
+    if procname == "" and entry.line == reraisedFromBegin:
+      break
 
     let left = "$#($#)" % [filename, $entry.line]
-    result.add((spaces(indent) & "$#$# $#\n") % [
+    result.add((spaces(2) & "$# $#\n") % [
       left,
-      spaces(longestLeft - left.len + 2),
       procname
     ])
     let hint = getHint(entry)
     if hint.len > 0:
-      result.add(spaces(indent+2) & "## " & hint & "\n")
+      result.add(spaces(4) & "## " & hint & "\n")
 
 proc injectStacktrace[T](future: Future[T]) =
   when not defined(release):
@@ -378,11 +370,7 @@ proc injectStacktrace[T](future: Future[T]) =
     #   newMsg.add "\n" & $entry
     future.error.msg = newMsg
 
-proc read*[T](future: Future[T] | FutureVar[T]): T =
-  ## Retrieves the value of `future`. Future must be finished otherwise
-  ## this function will fail with a `ValueError` exception.
-  ##
-  ## If the result of the future is an error then that error will be raised.
+template readImpl(future, T) =
   when future is Future[T]:
     let fut {.cursor.} = future
   else:
@@ -392,11 +380,21 @@ proc read*[T](future: Future[T] | FutureVar[T]): T =
       injectStacktrace(fut)
       raise fut.error
     when T isnot void:
-      result = fut.value
+      result = distinctBase(future).value
   else:
     # TODO: Make a custom exception type for this?
     raise newException(ValueError, "Future still in progress.")
 
+proc read*[T](future: Future[T] | FutureVar[T]): lent T =
+  ## Retrieves the value of `future`. Future must be finished otherwise
+  ## this function will fail with a `ValueError` exception.
+  ##
+  ## If the result of the future is an error then that error will be raised.
+  readImpl(future, T)
+
+proc read*(future: Future[void] | FutureVar[void]) =
+  readImpl(future, void)
+
 proc readError*[T](future: Future[T]): ref Exception =
   ## Retrieves the exception stored in `future`.
   ##
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 6694c4bc2..39e945d5e 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -39,9 +39,9 @@ runnableExamples("-r:off"):
 
   waitFor main()
 
-import asyncnet, asyncdispatch, parseutils, uri, strutils
-import httpcore
-from nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6
+import std/[asyncnet, asyncdispatch, parseutils, uri, strutils]
+import std/httpcore
+from std/nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6
 import std/private/since
 
 when defined(nimPreviewSlimSystem):
@@ -110,16 +110,16 @@ proc respond*(req: Request, code: HttpCode, content: string,
   ## This procedure will **not** close the client socket.
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
-  ##    import std/json
-  ##    proc handler(req: Request) {.async.} =
-  ##      if req.url.path == "/hello-world":
-  ##        let msg = %* {"message": "Hello World"}
-  ##        let headers = newHttpHeaders([("Content-Type","application/json")])
-  ##        await req.respond(Http200, $msg, headers)
-  ##      else:
-  ##        await req.respond(Http404, "Not Found")
+  ##   ```Nim
+  ##   import std/json
+  ##   proc handler(req: Request) {.async.} =
+  ##     if req.url.path == "/hello-world":
+  ##       let msg = %* {"message": "Hello World"}
+  ##       let headers = newHttpHeaders([("Content-Type","application/json")])
+  ##       await req.respond(Http200, $msg, headers)
+  ##     else:
+  ##       await req.respond(Http404, "Not Found")
+  ##   ```
   var msg = "HTTP/1.1 " & $code & "\c\L"
 
   if headers != nil:
@@ -158,7 +158,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] =
 proc sendStatus(client: AsyncSocket, status: string): Future[void] =
   client.send("HTTP/1.1 " & status & "\c\L\c\L")
 
-func hasChunkedEncoding(request: Request): bool = 
+func hasChunkedEncoding(request: Request): bool =
   ## Searches for a chunked transfer encoding
   const transferEncoding = "Transfer-Encoding"
 
@@ -187,7 +187,7 @@ proc processRequest(
   # \n
   request.headers.clear()
   request.body = ""
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     request.hostname = address
   else:
     request.hostname.shallowCopy(address)
@@ -300,7 +300,7 @@ proc processRequest(
     while true:
       lineFut.mget.setLen(0)
       lineFut.clean()
-      
+
       # The encoding format alternates between specifying a number of bytes to read
       # and the data to be read, of the previously specified size
       if sizeOrData mod 2 == 0:
@@ -387,8 +387,9 @@ proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET
   server.socket = newAsyncSocket(domain)
   if server.reuseAddr:
     server.socket.setSockOpt(OptReuseAddr, true)
-  if server.reusePort:
-    server.socket.setSockOpt(OptReusePort, true)
+  when not defined(nuttx):
+    if server.reusePort:
+      server.socket.setSockOpt(OptReusePort, true)
   server.socket.bindAddr(port, address)
   server.socket.listen()
 
diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim
index d85e3e621..d4e72c28a 100644
--- a/lib/pure/asyncmacro.nim
+++ b/lib/pure/asyncmacro.nim
@@ -7,9 +7,14 @@
 #    distribution, for details about the copyright.
 #
 
-## `asyncdispatch` module depends on the `asyncmacro` module to work properly.
+## Implements the `async` and `multisync` macros for `asyncdispatch`.
 
-import macros, strutils, asyncfutures
+import std/[macros, strutils, asyncfutures]
+
+type
+  Context = ref object
+    inTry: int
+    hasRet: bool
 
 # TODO: Ref https://github.com/nim-lang/Nim/issues/5617
 # TODO: Add more line infos
@@ -20,9 +25,8 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN
 template createCb(retFutureSym, iteratorNameSym,
                   strName, identName, futureVarCompletions: untyped) =
   bind finished
-
   var nameIterVar = iteratorNameSym
-  proc identName {.closure.} =
+  proc identName {.closure, stackTrace: off.} =
     try:
       if not nameIterVar.finished:
         var next = nameIterVar()
@@ -64,7 +68,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode
       )
     )
 
-proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
+proc processBody(ctx: Context; node, needsCompletionSym, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode =
   result = node
   case node.kind
   of nnkReturnStmt:
@@ -73,23 +77,53 @@ proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): Ni
     # As I've painfully found out, the order here really DOES matter.
     result.add createFutureVarCompletions(futureVarIdents, node)
 
+    ctx.hasRet = true
     if node[0].kind == nnkEmpty:
-      result.add newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result"))
+      if ctx.inTry == 0:
+        result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, newIdentNode("result"))
+      else:
+        result.add newAssignment(needsCompletionSym, newLit(true))
     else:
-      let x = node[0].processBody(retFutureSym, futureVarIdents)
+      let x = processBody(ctx, node[0], needsCompletionSym, retFutureSym, futureVarIdents)
       if x.kind == nnkYieldStmt: result.add x
+      elif ctx.inTry == 0:
+        result.add newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym, x)
       else:
-        result.add newCall(newIdentNode("complete"), retFutureSym, x)
+        result.add newAssignment(newIdentNode("result"), x)
+        result.add newAssignment(needsCompletionSym, newLit(true))
 
     result.add newNimNode(nnkReturnStmt, node).add(newNilLit())
     return # Don't process the children of this return stmt
   of RoutineNodes-{nnkTemplateDef}:
     # skip all the nested procedure definitions
     return
-  else: discard
-
-  for i in 0 ..< result.len:
-    result[i] = processBody(result[i], retFutureSym, futureVarIdents)
+  of nnkTryStmt:
+    if result[^1].kind == nnkFinally:
+      inc ctx.inTry
+      result[0] = processBody(ctx, result[0], needsCompletionSym, retFutureSym, futureVarIdents)
+      dec ctx.inTry
+      for i in 1 ..< result.len:
+        result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
+      if ctx.inTry == 0 and ctx.hasRet:
+        let finallyNode = copyNimNode(result[^1])
+        let stmtNode = newNimNode(nnkStmtList)
+        for child in result[^1]:
+          stmtNode.add child
+        stmtNode.add newIfStmt(
+          ( needsCompletionSym,
+            newCallWithLineInfo(node, newIdentNode("complete"), retFutureSym,
+            newIdentNode("result")
+            )
+          )
+        )
+        finallyNode.add stmtNode
+        result[^1] = finallyNode
+    else:
+      for i in 0 ..< result.len:
+        result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
+  else:
+    for i in 0 ..< result.len:
+      result[i] = processBody(ctx, result[i], needsCompletionSym, retFutureSym, futureVarIdents)
 
   # echo result.repr
 
@@ -151,6 +185,10 @@ proc asyncSingleProc(prc: NimNode): NimNode =
       result[0][0] = quote do: Future[void]
     return result
 
+  if prc.kind in RoutineNodes and prc.name.kind != nnkEmpty:
+    # Only non anonymous functions need/can have stack trace disabled
+    prc.addPragma(nnkExprColonExpr.newTree(ident"stackTrace", ident"off"))
+
   if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}:
     error("Cannot transform this node kind into an async proc." &
           " proc/method definition or lambda node expected.", prc)
@@ -209,14 +247,23 @@ proc asyncSingleProc(prc: NimNode): NimNode =
   # ->   {.pop.}
   # ->   <proc_body>
   # ->   complete(retFuture, result)
-  var iteratorNameSym = genSym(nskIterator, $prcName & "Iter")
-  var procBody = prc.body.processBody(retFutureSym, futureVarIdents)
+  var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)")
+  var needsCompletionSym = genSym(nskVar, "needsCompletion")
+  var ctx = Context()
+  var procBody = processBody(ctx, prc.body, needsCompletionSym, retFutureSym, futureVarIdents)
   # don't do anything with forward bodies (empty)
   if procBody.kind != nnkEmpty:
     # fix #13899, defer should not escape its original scope
-    procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
-    procBody.add(createFutureVarCompletions(futureVarIdents, nil))
+    let blockStmt = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody))
+    procBody = newStmtList()
     let resultIdent = ident"result"
+    procBody.add quote do:
+      # Check whether there is an implicit return
+      when typeof(`blockStmt`) is void:
+        `blockStmt`
+      else:
+        `resultIdent` = `blockStmt`
+    procBody.add(createFutureVarCompletions(futureVarIdents, nil))
     procBody.insert(0): quote do:
       {.push warning[resultshadowed]: off.}
       when `subRetType` isnot void:
@@ -224,6 +271,8 @@ proc asyncSingleProc(prc: NimNode): NimNode =
       else:
         var `resultIdent`: Future[void]
       {.pop.}
+
+      var `needsCompletionSym` = false
     procBody.add quote do:
       complete(`retFutureSym`, `resultIdent`)
 
@@ -242,9 +291,10 @@ proc asyncSingleProc(prc: NimNode): NimNode =
     # friendlier stack traces:
     var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix)
     var procCb = getAst createCb(retFutureSym, iteratorNameSym,
-                         newStrLitNode(prcName),
-                         cbName,
-                         createFutureVarCompletions(futureVarIdents, nil))
+                          newStrLitNode(prcName),
+                          cbName,
+                          createFutureVarCompletions(futureVarIdents, nil)
+                        )
     outerProcBody.add procCb
 
     # -> return retFuture
@@ -277,8 +327,8 @@ macro async*(prc: untyped): untyped =
 proc splitParamType(paramType: NimNode, async: bool): NimNode =
   result = paramType
   if paramType.kind == nnkInfix and paramType[0].strVal in ["|", "or"]:
-    let firstAsync = "async" in paramType[1].strVal.normalize
-    let secondAsync = "async" in paramType[2].strVal.normalize
+    let firstAsync = "async" in paramType[1].toStrLit().strVal.normalize
+    let secondAsync = "async" in paramType[2].toStrLit().strVal.normalize
 
     if firstAsync:
       result = paramType[if async: 1 else: 2]
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index b61eaa902..ee07e599e 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -65,8 +65,7 @@
 ##
 ## The following example demonstrates a simple chat server.
 ##
-## .. code-block:: Nim
-##
+##   ```Nim
 ##   import std/[asyncnet, asyncdispatch]
 ##
 ##   var clients {.threadvar.}: seq[AsyncSocket]
@@ -93,24 +92,25 @@
 ##
 ##   asyncCheck serve()
 ##   runForever()
-##
+##   ```
 
 import std/private/since
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, syncio]
 
-import asyncdispatch, nativesockets, net, os
+import std/[asyncdispatch, nativesockets, net, os]
 
 export SOBool
 
 # TODO: Remove duplication introduced by PR #4683.
 
 const defineSsl = defined(ssl) or defined(nimdoc)
-const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 
 when defineSsl:
-  import openssl
+  import std/openssl
 
 type
   # TODO: I would prefer to just do:
@@ -230,7 +230,7 @@ when defineSsl:
     let len = bioCtrlPending(socket.bioOut)
     if len > 0:
       var data = newString(len)
-      let read = bioRead(socket.bioOut, addr data[0], len)
+      let read = bioRead(socket.bioOut, cast[cstring](addr data[0]), len)
       assert read != 0
       if read < 0:
         raiseSSLError()
@@ -248,7 +248,7 @@ when defineSsl:
       var data = await recv(socket.fd.AsyncFD, BufferSize, flags)
       let length = len(data)
       if length > 0:
-        let ret = bioWrite(socket.bioIn, addr data[0], length.cint)
+        let ret = bioWrite(socket.bioIn, cast[cstring](addr data[0]), length.cint)
         if ret < 0:
           raiseSSLError()
       elif length == 0:
@@ -265,14 +265,17 @@ when defineSsl:
       ErrClearError()
       # Call the desired operation.
       opResult = op
-
+      let err =
+        if opResult < 0:
+          getSslError(socket, opResult.cint)
+        else:
+          SSL_ERROR_NONE
       # Send any remaining pending SSL data.
       await sendPendingSslData(socket, flags)
 
       # If the operation failed, try to see if SSL has some data to read
       # or write.
       if opResult < 0:
-        let err = getSslError(socket, opResult.cint)
         let fut = appeaseSsl(socket, flags, err.cint)
         yield fut
         if not fut.read():
@@ -460,7 +463,7 @@ proc send*(socket: AsyncSocket, data: string,
     when defineSsl:
       var copy = data
       sslLoop(socket, flags,
-        sslWrite(socket.sslHandle, addr copy[0], copy.len.cint))
+        sslWrite(socket.sslHandle, cast[cstring](addr copy[0]), copy.len.cint))
       await sendPendingSslData(socket, flags)
   else:
     await send(socket.fd.AsyncFD, data, flags)
@@ -679,12 +682,12 @@ when defined(posix) and not useNimNetLite:
         elif ret == EINTR:
           return false
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+          retFuture.fail(newOSError(OSErrorCode(ret)))
           return true
 
       var socketAddr = makeUnixAddr(path)
       let ret = socket.fd.connect(cast[ptr SockAddr](addr socketAddr),
-                        (sizeof(socketAddr.sun_family) + path.len).SockLen)
+                        (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen)
       if ret == 0:
         # Request to connect completed immediately.
         retFuture.complete()
@@ -693,7 +696,7 @@ when defined(posix) and not useNimNetLite:
         if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
           addWrite(AsyncFD(socket.fd), cb)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
 
   proc bindUnix*(socket: AsyncSocket, path: string) {.
     tags: [ReadIOEffect].} =
@@ -702,7 +705,7 @@ when defined(posix) and not useNimNetLite:
     when not defined(nimdoc):
       var socketAddr = makeUnixAddr(path)
       if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr),
-          (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
         raiseOSError(osLastError())
 
 elif defined(nimdoc):
diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim
index 3f7774ed8..c97b98d55 100644
--- a/lib/pure/asyncstreams.nim
+++ b/lib/pure/asyncstreams.nim
@@ -9,12 +9,12 @@
 
 ## Unstable API.
 
-import asyncfutures
+import std/asyncfutures
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-import deques
+import std/deques
 
 type
   FutureStream*[T] = ref object ## Special future that acts as
diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim
index bc01a9164..591d22cc0 100644
--- a/lib/pure/base64.nim
+++ b/lib/pure/base64.nim
@@ -31,7 +31,7 @@ runnableExamples:
 ##
 
 runnableExamples:
-  let encodedInts = encode([1,2,3])
+  let encodedInts = encode([1'u8,2,3])
   assert encodedInts == "AQID"
   let encodedChars = encode(['h','e','y'])
   assert encodedChars == "aGV5"
@@ -66,15 +66,11 @@ template cbBase(a, b): untyped = [
   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', a, b]
 
-let
+const
   cb64 = cbBase('+', '/')
   cb64safe = cbBase('-', '_')
 
 const
-  cb64VM = cbBase('+', '/')
-  cb64safeVM = cbBase('-', '_')
-
-const
   invalidChar = 255
 
 template encodeSize(size: int): int = (size * 4 div 3) + 6
@@ -84,10 +80,13 @@ template encodeInternal(s, alphabet: typed): untyped =
 
   result.setLen(encodeSize(s.len))
 
+  let
+    padding = s.len mod 3
+    inputEnds = s.len - padding
+
   var
     inputIndex = 0
     outputIndex = 0
-    inputEnds = s.len - s.len mod 3
     n: uint32
     b: uint32
 
@@ -113,7 +112,6 @@ template encodeInternal(s, alphabet: typed): untyped =
     outputChar(n shr 6)
     outputChar(n shr 0)
 
-  var padding = s.len mod 3
   if padding == 1:
     inputByte(b shl 16)
     outputChar(n shr 18)
@@ -132,21 +130,14 @@ template encodeInternal(s, alphabet: typed): untyped =
   result.setLen(outputIndex)
 
 template encodeImpl() {.dirty.} =
-  when nimvm:
-    block:
-      let lookupTableVM = if safe: cb64safeVM else: cb64VM
-      encodeInternal(s, lookupTableVM)
+  if safe:
+    encodeInternal(s, cb64safe)
   else:
-    block:
-      let lookupTable = if safe: unsafeAddr(cb64safe) else: unsafeAddr(cb64)
-      encodeInternal(s, lookupTable)
+    encodeInternal(s, cb64)
 
-proc encode*[T: SomeInteger|char](s: openArray[T], safe = false): string =
+proc encode*[T: byte|char](s: openArray[T], safe = false): string =
   ## Encodes `s` into base64 representation.
   ##
-  ## This procedure encodes an openarray (array or sequence) of either integers
-  ## or characters.
-  ##
   ## If `safe` is `true` then it will encode using the
   ## URL-Safe and Filesystem-safe standard alphabet characters,
   ## which substitutes `-` instead of `+` and `_` instead of `/`.
@@ -154,18 +145,24 @@ proc encode*[T: SomeInteger|char](s: openArray[T], safe = false): string =
   ## * https://tools.ietf.org/html/rfc4648#page-7
   ##
   ## **See also:**
-  ## * `encode proc<#encode,string>`_ for encoding a string
   ## * `decode proc<#decode,string>`_ for decoding a string
   runnableExamples:
+    assert encode("Hello World") == "SGVsbG8gV29ybGQ="
     assert encode(['n', 'i', 'm']) == "bmlt"
     assert encode(@['n', 'i', 'm']) == "bmlt"
-    assert encode([1, 2, 3, 4, 5]) == "AQIDBAU="
+    assert encode([1'u8, 2, 3, 4, 5]) == "AQIDBAU="
   encodeImpl()
 
-proc encode*(s: string, safe = false): string =
-  ## Encodes `s` into base64 representation.
+proc encode*[T: SomeInteger and not byte](s: openArray[T], safe = false): string
+  {.deprecated: "use `byte` or `char` instead".} =
+  encodeImpl()
+
+proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n",
+                 safe = false): string =
+  ## Encodes `s` into base64 representation as lines.
+  ## Used in email MIME format, use `lineLen` and `newline`.
   ##
-  ## This procedure encodes a string.
+  ## This procedure encodes a string according to MIME spec.
   ##
   ## If `safe` is `true` then it will encode using the
   ## URL-Safe and Filesystem-safe standard alphabet characters,
@@ -174,20 +171,7 @@ proc encode*(s: string, safe = false): string =
   ## * https://tools.ietf.org/html/rfc4648#page-7
   ##
   ## **See also:**
-  ## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray
-  ## * `decode proc<#decode,string>`_ for decoding a string
-  runnableExamples:
-    assert encode("Hello World") == "SGVsbG8gV29ybGQ="
-  encodeImpl()
-
-proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n"): string =
-  ## Encodes `s` into base64 representation as lines.
-  ## Used in email MIME format, use `lineLen` and `newline`.
-  ##
-  ## This procedure encodes a string according to MIME spec.
-  ##
-  ## **See also:**
-  ## * `encode proc<#encode,string>`_ for encoding a string
+  ## * `encode proc<#encode,openArray[T]>`_ for encoding an openArray
   ## * `decode proc<#decode,string>`_ for decoding a string
   runnableExamples:
     assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ="
@@ -199,7 +183,7 @@ proc encodeMime*(s: string, lineLen = 75.Positive, newLine = "\r\n"): string =
       inc idx
 
   if s.len == 0: return
-  let e = encode(s)
+  let e = encode(s, safe)
   if e.len <= lineLen or newLine.len == 0:
     return e
   result = newString(e.len + newLine.len * ((e.len div lineLen) - int(e.len mod lineLen == 0)))
@@ -232,7 +216,6 @@ proc decode*(s: string): string =
   ##
   ## **See also:**
   ## * `encode proc<#encode,openArray[T]>`_ for encoding an openarray
-  ## * `encode proc<#encode,string>`_ for encoding a string
   runnableExamples:
     assert decode("SGVsbG8gV29ybGQ=") == "Hello World"
     assert decode("  SGVsbG8gV29ybGQ=") == "Hello World"
@@ -261,7 +244,7 @@ proc decode*(s: string): string =
     inputLen = s.len
     inputEnds = 0
   # strip trailing characters
-  while s[inputLen - 1] in {'\n', '\r', ' ', '='}:
+  while inputLen > 0 and s[inputLen - 1] in {'\n', '\r', ' ', '='}:
     dec inputLen
   # hot loop: read 4 characters at at time
   inputEnds = inputLen - 4
diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim
index a518c25d2..0d3351ee5 100644
--- a/lib/pure/bitops.nim
+++ b/lib/pure/bitops.nim
@@ -25,7 +25,7 @@
 ## At this time only `fastLog2`, `firstSetBit`, `countLeadingZeroBits` and `countTrailingZeroBits`
 ## may return undefined and/or platform dependent values if given invalid input.
 
-import macros
+import std/macros
 import std/private/since
 from std/private/bitops_utils import forwardImpl, castToUnsigned
 
@@ -63,6 +63,12 @@ macro bitxor*[T: SomeInteger](x, y: T; z: varargs[T]): T =
 type BitsRange*[T] = range[0..sizeof(T)*8-1]
   ## A range with all bit positions for type `T`.
 
+template typeMasked[T: SomeInteger](x: T): T =
+  when defined(js):
+    T(x and ((0xffffffff_ffffffff'u shr (64 - sizeof(T) * 8))))
+  else:
+    x
+
 func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, 3).} =
   ## Returns an extracted (and shifted) slice of bits from `v`.
   runnableExamples:
@@ -73,7 +79,7 @@ func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1,
   let
     upmost = sizeof(T) * 8 - 1
     uv     = v.castToUnsigned
-  (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
+  ((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
 
 proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} =
   ## Mutates `v` into an extracted (and shifted) slice of bits from `v`.
@@ -85,7 +91,7 @@ proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1,
   let
     upmost = sizeof(T) * 8 - 1
     uv     = v.castToUnsigned
-  v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T
+  v = ((uv shl (upmost - slice.b)).typeMasked shr (upmost - slice.b + slice.a)).T
 
 func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
   ## Creates a bitmask based on a slice of bits.
@@ -96,7 +102,7 @@ func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} =
   let
     upmost = sizeof(T) * 8 - 1
     bitmask = bitnot(0.T).castToUnsigned
-  (bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T
+  ((bitmask shl (upmost - slice.b + slice.a)).typeMasked shr (upmost - slice.b)).T
 
 proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} =
   ## Returns `v`, with only the `1` bits from `mask` matching those of
@@ -457,7 +463,7 @@ elif useVCC_builtins:
       importc: "_BitScanForward64", header: "<intrin.h>".}
 
   template vcc_scan_impl(fnc: untyped; v: untyped): int =
-    var index: culong
+    var index {.inject.}: culong = 0
     discard fnc(index.addr, v)
     index.int
 
diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim
index c36e31b11..59e2078df 100644
--- a/lib/pure/browsers.nim
+++ b/lib/pure/browsers.nim
@@ -12,20 +12,22 @@
 ##
 ## Unstable API.
 
-import std/private/since
+import std/private/since # used by the deprecated `openDefaultBrowser()`
 
-import strutils
+import std/strutils
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 when defined(windows):
-  import winlean
-  from os import absolutePath
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+  from std/os import absolutePath
 else:
-  import os
+  import std/os
   when not defined(osx):
-    import osproc
+    import std/osproc
 
 const osOpenCmd* =
   when defined(macos) or defined(macosx) or defined(windows): "open" else: "xdg-open" ## \
@@ -38,15 +40,17 @@ proc prepare(s: string): string =
   else:
     result = "file://" & absolutePath(s)
 
-proc openDefaultBrowserImpl(url: string) =
+proc openDefaultBrowserRaw(url: string) =
+  ## note the url argument should be alreadly prepared, i.e. the url is passed "AS IS"
+
   when defined(windows):
     var o = newWideCString(osOpenCmd)
-    var u = newWideCString(prepare url)
+    var u = newWideCString(url)
     discard shellExecuteW(0'i32, o, u, nil, nil, SW_SHOWNORMAL)
   elif defined(macosx):
-    discard execShellCmd(osOpenCmd & " " & quoteShell(prepare url))
+    discard execShellCmd(osOpenCmd & " " & quoteShell(url))
   else:
-    var u = quoteShell(prepare url)
+    var u = quoteShell(url)
     if execShellCmd(osOpenCmd & " " & u) == 0: return
     for b in getEnv("BROWSER").split(PathSep):
       try:
@@ -67,26 +71,42 @@ proc openDefaultBrowser*(url: string) =
   ##
   ## This proc doesn't raise an exception on error, beware.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   block: openDefaultBrowser("https://nim-lang.org")
+  ##   ```
   doAssert url.len > 0, "URL must not be empty string"
-  openDefaultBrowserImpl(url)
+  openDefaultBrowserRaw(url)
 
-proc openDefaultBrowser*() {.since: (1, 1).} =
-  ## Opens the user's default browser without any `url` (blank page). This does not block.
-  ## Implements IETF RFC-6694 Section 3, "about:blank" must be reserved for a blank page.
+proc openDefaultBrowser*() {.since: (1, 1), deprecated: 
+  "not implemented, please open with a specific url instead".} =
+  ## Intends to open the user's default browser without any `url` (blank page).
+  ## This does not block.
+  ## Intends to implement IETF RFC-6694 Section 3,
+  ## ("about:blank" is reserved for a blank page).
   ##
-  ## Under Windows, `ShellExecute` is used. Under Mac OS X the `open`
-  ## command is used. Under Unix, it is checked if `xdg-open` exists and
-  ## used if it does. Otherwise the environment variable `BROWSER` is
-  ## used to determine the default browser to use.
+  ## Beware that this intended behavior is **not** implemented and 
+  ## considered not worthy to implement here.
+  ##
+  ## The following describes the behavior of current implementation:
+  ## 
+  ##  - Under Windows, this will only cause a pop-up dialog \
+  ## asking the assocated application with `about` \
+  ## (as Windows simply treats `about:` as a protocol like `http`).
+  ##  - Under Mac OS X the `open "about:blank"` command is used.
+  ##  - Under Unix, it is checked if `xdg-open` exists and used \
+  ## if it does and open the application assocated with `text/html` mime \
+  ## (not `x-scheme-handler/http`, so maybe html-viewer \
+  ## other than your default browser is opened). \
+  ## Otherwise the environment variable `BROWSER` is used \
+  ## to determine the default browser to use.
   ##
   ## This proc doesn't raise an exception on error, beware.
   ##
+  ##   ```nim
+  ##   block: openDefaultBrowser()
+  ##   ```
+  ##
   ## **See also:**
   ##
   ## * https://tools.ietf.org/html/rfc6694#section-3
-  ##
-  ## .. code-block:: nim
-  ##   block: openDefaultBrowser()
-  openDefaultBrowserImpl("http:about:blank")  # See IETF RFC-6694 Section 3.
+  openDefaultBrowserRaw("about:blank")  # See IETF RFC-6694 Section 3.
diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim
index 0ab8f4c95..034f224ac 100644
--- a/lib/pure/cgi.nim
+++ b/lib/pure/cgi.nim
@@ -9,27 +9,27 @@
 
 ## This module implements helper procs for CGI applications. Example:
 ##
-## .. code-block:: Nim
+##   ```Nim
+##   import std/[strtabs, cgi]
 ##
-##    import std/[strtabs, cgi]
-##
-##    # Fill the values when debugging:
-##    when debug:
-##      setTestData("name", "Klaus", "password", "123456")
-##    # read the data into `myData`
-##    var myData = readData()
-##    # check that the data's variable names are "name" or "password"
-##    validateData(myData, "name", "password")
-##    # start generating content:
-##    writeContentType()
-##    # generate content:
-##    write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
-##    write(stdout, "<html><head><title>Test</title></head><body>\n")
-##    writeLine(stdout, "your name: " & myData["name"])
-##    writeLine(stdout, "your password: " & myData["password"])
-##    writeLine(stdout, "</body></html>")
-
-import strutils, os, strtabs, cookies, uri
+##   # Fill the values when debugging:
+##   when debug:
+##     setTestData("name", "Klaus", "password", "123456")
+##   # read the data into `myData`
+##   var myData = readData()
+##   # check that the data's variable names are "name" or "password"
+##   validateData(myData, "name", "password")
+##   # start generating content:
+##   writeContentType()
+##   # generate content:
+##   write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
+##   write(stdout, "<html><head><title>Test</title></head><body>\n")
+##   writeLine(stdout, "your name: " & myData["name"])
+##   writeLine(stdout, "your password: " & myData["password"])
+##   writeLine(stdout, "</body></html>")
+##   ```
+
+import std/[strutils, os, strtabs, cookies, uri]
 export uri.encodeUrl, uri.decodeUrl
 
 when defined(nimPreviewSlimSystem):
@@ -252,9 +252,9 @@ proc setTestData*(keysvalues: varargs[string]) =
   ## Fills the appropriate environment variables to test your CGI application.
   ## This can only simulate the 'GET' request method. `keysvalues` should
   ## provide embedded (name, value)-pairs. Example:
-  ##
-  ## .. code-block:: Nim
-  ##    setTestData("name", "Hanz", "password", "12345")
+  ##   ```Nim
+  ##   setTestData("name", "Hanz", "password", "12345")
+  ##   ```
   putEnv("REQUEST_METHOD", "GET")
   var i = 0
   var query = ""
@@ -269,9 +269,9 @@ proc setTestData*(keysvalues: varargs[string]) =
 proc writeContentType*() =
   ## Calls this before starting to send your HTML data to `stdout`. This
   ## implements this part of the CGI protocol:
-  ##
-  ## .. code-block:: Nim
-  ##     write(stdout, "Content-type: text/html\n\n")
+  ##   ```Nim
+  ##   write(stdout, "Content-type: text/html\n\n")
+  ##   ```
   write(stdout, "Content-type: text/html\n\n")
 
 proc resetForStacktrace() =
diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim
index c4eb5331e..d2b0099f2 100644
--- a/lib/pure/collections/deques.nim
+++ b/lib/pure/collections/deques.nim
@@ -47,11 +47,10 @@ runnableExamples:
 ## See also
 ## ========
 ## * `lists module <lists.html>`_ for singly and doubly linked lists and rings
-## * `channels module <channels_builtin.html>`_ for inter-thread communication
 
 import std/private/since
 
-import math
+import std/[assertions, hashes, math]
 
 type
   Deque*[T] = object
@@ -60,20 +59,28 @@ type
     ## To initialize an empty deque,
     ## use the `initDeque proc <#initDeque,int>`_.
     data: seq[T]
-    head, tail, count, mask: int
+
+    # `head` and `tail` are masked only when accessing an element of `data`
+    # so that `tail - head == data.len` when the deque is full.
+    # They are uint so that incrementing/decrementing them doesn't cause
+    # over/underflow. You can get a number of items with `tail - head`
+    # even if `tail` or `head` is wraps around and `tail < head`, because
+    # `tail - head == (uint.high + 1 + tail) - head` when `tail < head`.
+    head, tail: uint
 
 const
   defaultInitialSize* = 4
 
 template initImpl(result: typed, initialSize: int) =
   let correctSize = nextPowerOfTwo(initialSize)
-  result.mask = correctSize - 1
   newSeq(result.data, correctSize)
 
 template checkIfInitialized(deq: typed) =
-  when compiles(defaultInitialSize):
-    if deq.mask == 0:
-      initImpl(deq, defaultInitialSize)
+  if deq.data.len == 0:
+    initImpl(deq, defaultInitialSize)
+
+func mask[T](deq: Deque[T]): uint {.inline.} =
+  uint(deq.data.len) - 1
 
 proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] =
   ## Creates a new empty deque.
@@ -87,22 +94,22 @@ proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] =
   ## * `toDeque proc <#toDeque,openArray[T]>`_
   result.initImpl(initialSize)
 
-proc len*[T](deq: Deque[T]): int {.inline.} =
+func len*[T](deq: Deque[T]): int {.inline.} =
   ## Returns the number of elements of `deq`.
-  result = deq.count
+  int(deq.tail - deq.head)
 
 template emptyCheck(deq) =
   # Bounds check for the regular deque access.
   when compileOption("boundChecks"):
-    if unlikely(deq.count < 1):
+    if unlikely(deq.len < 1):
       raise newException(IndexDefect, "Empty deque.")
 
 template xBoundsCheck(deq, i) =
   # Bounds check for the array like accesses.
   when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this.
-    if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter
+    if unlikely(i >= deq.len): # x < deq.low is taken care by the Natural parameter
       raise newException(IndexDefect,
-                         "Out of bounds: " & $i & " > " & $(deq.count - 1))
+                         "Out of bounds: " & $i & " > " & $(deq.len - 1))
     if unlikely(i < 0): # when used with BackwardsIndex
       raise newException(IndexDefect,
                          "Out of bounds: " & $i & " < 0")
@@ -116,7 +123,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} =
     doAssertRaises(IndexDefect, echo a[8])
 
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) and deq.mask]
 
 proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
   ## Accesses the `i`-th element of `deq` and returns a mutable
@@ -127,7 +134,7 @@ proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} =
     assert a[0] == 11
 
   xBoundsCheck(deq, i)
-  return deq.data[(deq.head + i) and deq.mask]
+  return deq.data[(deq.head + i.uint) and deq.mask]
 
 proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} =
   ## Sets the `i`-th element of `deq` to `val`.
@@ -139,7 +146,7 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} =
 
   checkIfInitialized(deq)
   xBoundsCheck(deq, i)
-  deq.data[(deq.head + i) and deq.mask] = val
+  deq.data[(deq.head + i.uint) and deq.mask] = val
 
 proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} =
   ## Accesses the backwards indexed `i`-th element.
@@ -192,10 +199,8 @@ iterator items*[T](deq: Deque[T]): lent T =
     let a = [10, 20, 30, 40, 50].toDeque
     assert toSeq(a.items) == @[10, 20, 30, 40, 50]
 
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator mitems*[T](deq: var Deque[T]): var T =
   ## Yields every element of `deq`, which can be modified.
@@ -209,10 +214,8 @@ iterator mitems*[T](deq: var Deque[T]): var T =
       x = 5 * x - 1
     assert $a == "[49, 99, 149, 199, 249]"
 
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield deq.data[i]
-    i = (i + 1) and deq.mask
+  for c in 0 ..< deq.len:
+    yield deq.data[(deq.head + c.uint) and deq.mask]
 
 iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
   ## Yields every `(position, value)`-pair of `deq`.
@@ -222,10 +225,8 @@ iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] =
     let a = [10, 20, 30].toDeque
     assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)]
 
-  var i = deq.head
-  for c in 0 ..< deq.count:
-    yield (c, deq.data[i])
-    i = (i + 1) and deq.mask
+  for c in 0 ..< deq.len:
+    yield (c, deq.data[(deq.head + c.uint) and deq.mask])
 
 proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
   ## Returns true if `item` is in `deq` or false if not found.
@@ -244,8 +245,9 @@ proc contains*[T](deq: Deque[T], item: T): bool {.inline.} =
 
 proc expandIfNeeded[T](deq: var Deque[T]) =
   checkIfInitialized(deq)
-  var cap = deq.mask + 1
-  if unlikely(deq.count >= cap):
+  let cap = deq.data.len
+  assert deq.len <= cap
+  if unlikely(deq.len == cap):
     var n = newSeq[T](cap * 2)
     var i = 0
     for x in mitems(deq):
@@ -253,8 +255,7 @@ proc expandIfNeeded[T](deq: var Deque[T]) =
       else: n[i] = move(x)
       inc i
     deq.data = move(n)
-    deq.mask = cap * 2 - 1
-    deq.tail = deq.count
+    deq.tail = cap.uint
     deq.head = 0
 
 proc addFirst*[T](deq: var Deque[T], item: sink T) =
@@ -269,9 +270,8 @@ proc addFirst*[T](deq: var Deque[T], item: sink T) =
     assert $a == "[50, 40, 30, 20, 10]"
 
   expandIfNeeded(deq)
-  inc deq.count
-  deq.head = (deq.head - 1) and deq.mask
-  deq.data[deq.head] = item
+  dec deq.head
+  deq.data[deq.head and deq.mask] = item
 
 proc addLast*[T](deq: var Deque[T], item: sink T) =
   ## Adds an `item` to the end of `deq`.
@@ -285,9 +285,8 @@ proc addLast*[T](deq: var Deque[T], item: sink T) =
     assert $a == "[10, 20, 30, 40, 50]"
 
   expandIfNeeded(deq)
-  inc deq.count
-  deq.data[deq.tail] = item
-  deq.tail = (deq.tail + 1) and deq.mask
+  deq.data[deq.tail and deq.mask] = item
+  inc deq.tail
 
 proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} =
   ## Creates a new deque that contains the elements of `x` (in the same order).
@@ -316,7 +315,7 @@ proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} =
     assert len(a) == 5
 
   emptyCheck(deq)
-  result = deq.data[deq.head]
+  result = deq.data[deq.head and deq.mask]
 
 proc peekLast*[T](deq: Deque[T]): lent T {.inline.} =
   ## Returns the last element of `deq`, but does not remove it from the deque.
@@ -346,7 +345,7 @@ proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
     assert $a == "[99, 20, 30, 40, 50]"
 
   emptyCheck(deq)
-  result = deq.data[deq.head]
+  result = deq.data[deq.head and deq.mask]
 
 proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} =
   ## Returns a mutable reference to the last element of `deq`,
@@ -379,9 +378,8 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} =
     assert $a == "[20, 30, 40, 50]"
 
   emptyCheck(deq)
-  dec deq.count
-  result = move deq.data[deq.head]
-  deq.head = (deq.head + 1) and deq.mask
+  result = move deq.data[deq.head and deq.mask]
+  inc deq.head
 
 proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
   ## Removes and returns the last element of the `deq`.
@@ -396,9 +394,8 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} =
     assert $a == "[10, 20, 30, 40]"
 
   emptyCheck(deq)
-  dec deq.count
-  deq.tail = (deq.tail - 1) and deq.mask
-  result = move deq.data[deq.tail]
+  dec deq.tail
+  result = move deq.data[deq.tail and deq.mask]
 
 proc clear*[T](deq: var Deque[T]) {.inline.} =
   ## Resets the deque so that it is empty.
@@ -412,7 +409,6 @@ proc clear*[T](deq: var Deque[T]) {.inline.} =
     assert len(a) == 0
 
   for el in mitems(deq): destroy(el)
-  deq.count = 0
   deq.tail = deq.head
 
 proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
@@ -432,19 +428,17 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) =
     a.shrink(fromFirst = 2, fromLast = 1)
     assert $a == "[30, 40]"
 
-  if fromFirst + fromLast > deq.count:
+  if fromFirst + fromLast > deq.len:
     clear(deq)
     return
 
   for i in 0 ..< fromFirst:
-    destroy(deq.data[deq.head])
-    deq.head = (deq.head + 1) and deq.mask
+    destroy(deq.data[deq.head and deq.mask])
+    inc deq.head
 
   for i in 0 ..< fromLast:
-    destroy(deq.data[deq.tail])
-    deq.tail = (deq.tail - 1) and deq.mask
-
-  dec deq.count, fromFirst + fromLast
+    destroy(deq.data[(deq.tail - 1) and deq.mask])
+    dec deq.tail
 
 proc `$`*[T](deq: Deque[T]): string =
   ## Turns a deque into its string representation.
@@ -457,3 +451,30 @@ proc `$`*[T](deq: Deque[T]): string =
     if result.len > 1: result.add(", ")
     result.addQuoted(x)
   result.add("]")
+
+func `==`*[T](deq1, deq2: Deque[T]): bool =
+  ## The `==` operator for Deque.
+  ## Returns `true` if both deques contains the same values in the same order.
+  runnableExamples:
+    var a, b = initDeque[int]()
+    a.addFirst(2)
+    a.addFirst(1)
+    b.addLast(1)
+    b.addLast(2)
+    doAssert a == b
+
+  if deq1.len != deq2.len:
+    return false
+
+  for i in 0 ..< deq1.len:
+    if deq1.data[(deq1.head + i.uint) and deq1.mask] != deq2.data[(deq2.head + i.uint) and deq2.mask]:
+      return false
+
+  true
+
+func hash*[T](deq: Deque[T]): Hash =
+  ## Hashing of Deque.
+  var h: Hash = 0
+  for x in deq:
+    h = h !& hash(x)
+  !$h
diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim
index deff8fa21..17785c8c7 100644
--- a/lib/pure/collections/hashcommon.nim
+++ b/lib/pure/collections/hashcommon.nim
@@ -13,6 +13,7 @@
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
+import std / outparams
 
 const
   growthFactor = 2
@@ -37,16 +38,6 @@ proc slotsNeeded(count: Natural): int {.inline.} =
   # Make sure to synchronize with `mustRehash` above
   result = nextPowerOfTwo(count * 3 div 2 + 4)
 
-proc rightSize*(count: Natural): int {.inline, deprecated: "Deprecated since 1.4.0".} =
-  ## It is not needed anymore because
-  ## picking the correct size is done internally.
-  ##
-  ## Returns the value of `initialSize` to support `count` items.
-  ##
-  ## If more items are expected to be added, simply add that
-  ## expected extra amount to the parameter before calling this.
-  result = count
-
 template rawGetKnownHCImpl() {.dirty.} =
   if t.dataLen == 0:
     return -1
@@ -67,7 +58,10 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} =
 template genHashImpl(key, hc: typed) =
   hc = hash(key)
   if hc == 0: # This almost never taken branch should be very predictable.
-    hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
+    when sizeof(int) < 4:
+      hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit.
+    else:
+      hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine.
 
 template genHash(key: typed): Hash =
   var res: Hash
@@ -78,5 +72,5 @@ template rawGetImpl() {.dirty.} =
   genHashImpl(key, hc)
   rawGetKnownHCImpl()
 
-proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
   rawGetImpl()
diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim
index 89e532951..96f9b4430 100644
--- a/lib/pure/collections/heapqueue.nim
+++ b/lib/pure/collections/heapqueue.nim
@@ -47,6 +47,9 @@ runnableExamples:
 
 import std/private/since
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 type HeapQueue*[T] = object
   ## A heap queue, commonly known as a priority queue.
   data: seq[T]
@@ -59,7 +62,7 @@ proc initHeapQueue*[T](): HeapQueue[T] =
   ##
   ## **See also:**
   ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_
-  discard
+  result = default(HeapQueue[T])
 
 proc len*[T](heap: HeapQueue[T]): int {.inline.} =
   ## Returns the number of elements of `heap`.
@@ -73,6 +76,13 @@ proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} =
   ## Accesses the i-th element of `heap`.
   heap.data[i]
 
+iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} =
+  ## Iterates over each item of `heap`.
+  let L = len(heap)
+  for i in 0 .. high(heap.data):
+    yield heap.data[i]
+    assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it")
+
 proc heapCmp[T](x, y: T): bool {.inline.} = x < y
 
 proc siftup[T](heap: var HeapQueue[T], startpos, p: int) =
@@ -178,6 +188,11 @@ proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} =
   for i in 0 ..< heap.len:
     if heap[i] == x: return i
 
+proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} =
+  ## Returns true if `x` is in `heap` or false if not found. This is a shortcut
+  ## for `find(heap, x) >= 0`.
+  result = find(heap, x) >= 0
+
 proc del*[T](heap: var HeapQueue[T], index: Natural) =
   ## Removes the element at `index` from `heap`, maintaining the heap invariant.
   runnableExamples:
diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim
index 829ec2ccb..6b88747ef 100644
--- a/lib/pure/collections/lists.nim
+++ b/lib/pure/collections/lists.nim
@@ -62,9 +62,6 @@ import std/private/since
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-when not defined(nimHasCursor):
-  {.pragma: cursor.}
-
 type
   DoublyLinkedNodeObj*[T] = object
     ## A node of a doubly linked list.
@@ -168,36 +165,14 @@ proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] =
   new(result)
   result.value = value
 
-func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
-  ## Creates a new `SinglyLinkedList` from the members of `elems`.
-  runnableExamples:
-    from std/sequtils import toSeq
-    let a = [1, 2, 3, 4, 5].toSinglyLinkedList
-    assert a.toSeq == [1, 2, 3, 4, 5]
-
-  result = initSinglyLinkedList[T]()
-  for elem in elems.items:
-    result.add(elem)
-
-func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
-  ## Creates a new `DoublyLinkedList` from the members of `elems`.
-  runnableExamples:
-    from std/sequtils import toSeq
-    let a = [1, 2, 3, 4, 5].toDoublyLinkedList
-    assert a.toSeq == [1, 2, 3, 4, 5]
-
-  result = initDoublyLinkedList[T]()
-  for elem in elems.items:
-    result.add(elem)
-
 template itemsListImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   while it != nil:
     yield it.value
     it = it.next
 
 template itemsRingImpl() {.dirty.} =
-  var it = L.head
+  var it {.cursor.} = L.head
   if it != nil:
     while true:
       yield it.value
@@ -289,7 +264,7 @@ iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] =
         x.value = 5 * x.value - 1
     assert $a == "[49, 99, 199, 249]"
 
-  var it = L.head
+  var it {.cursor.} = L.head
   while it != nil:
     let nxt = it.next
     yield it
@@ -314,7 +289,7 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] =
         x.value = 5 * x.value - 1
     assert $a == "[49, 99, 199, 249]"
 
-  var it = L.head
+  var it {.cursor.} = L.head
   if it != nil:
     while true:
       let nxt = it.next
@@ -409,9 +384,7 @@ proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
     assert s == [0, 1, 0, 1, 0, 1]
 
   b.addMoved(a)
-  when defined(js): # XXX: swap broken in js; bug #16771
-    (b, a) = (a, b)
-  else: swap a, b
+  swap a, b
 
 proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} =
   ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1).
@@ -736,7 +709,7 @@ proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.disc
     if L.tail.next == n:
       L.tail.next = L.head # restore cycle
   else:
-    var prev = L.head
+    var prev {.cursor.} = L.head
     while prev.next != n and prev.next != nil:
       prev = prev.next
     if prev.next == nil:
@@ -996,3 +969,47 @@ proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} =
   ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_
   ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_
   a.addMoved(b)
+
+func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `SinglyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toSinglyLinkedRing*[T](elems: openArray[T]): SinglyLinkedRing[T] =
+  ## Creates a new `SinglyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toSinglyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initSinglyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} =
+  ## Creates a new `DoublyLinkedList` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedList
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedList[T]()
+  for elem in elems.items:
+    result.add(elem)
+
+func toDoublyLinkedRing*[T](elems: openArray[T]): DoublyLinkedRing[T] =
+  ## Creates a new `DoublyLinkedRing` from the members of `elems`.
+  runnableExamples:
+    from std/sequtils import toSeq
+    let a = [1, 2, 3, 4, 5].toDoublyLinkedRing
+    assert a.toSeq == [1, 2, 3, 4, 5]
+
+  result = initDoublyLinkedRing[T]()
+  for elem in elems.items:
+    result.add(elem)
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index b86977539..3c0d8dc0e 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -82,7 +82,8 @@ runnableExamples:
 
 import std/private/since
 
-import macros
+import std/macros
+from std/typetraits import supportsCopyMem
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -140,6 +141,22 @@ func concat*[T](seqs: varargs[seq[T]]): seq[T] =
       result[i] = itm
       inc(i)
 
+func addUnique*[T](s: var seq[T], x: sink T) =
+  ## Adds `x` to the container `s` if it is not already present. 
+  ## Uses `==` to check if the item is already present.
+  runnableExamples:
+    var a = @[1, 2, 3]
+    a.addUnique(4)
+    a.addUnique(4)
+    assert a == @[1, 2, 3, 4]
+
+  for i in 0..high(s):
+    if s[i] == x: return
+  when declared(ensureMove):
+    s.add ensureMove(x)
+  else:
+    s.add x
+
 func count*[T](s: openArray[T], x: T): int =
   ## Returns the number of occurrences of the item `x` in the container `s`.
   ##
@@ -248,6 +265,15 @@ func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} =
   for i in 1..high(s):
     if s[i] > s[result]: result = i
 
+func minmax*[T](x: openArray[T]): (T, T) =
+  ## The minimum and maximum values of `x`. `T` needs to have a `<` operator.
+  var l = x[0]
+  var h = x[0]
+  for i in 1..high(x):
+    if x[i] < l: l = x[i]
+    if h < x[i]: h = x[i]
+  result = (l, h)
+
 
 template zipImpl(s1, s2, retType: untyped): untyped =
   proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType =
@@ -302,8 +328,7 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} =
       unzipped2 = @['a', 'b', 'c']
     assert zipped.unzip() == (unzipped1, unzipped2)
     assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2)
-  result[0] = newSeq[S](s.len)
-  result[1] = newSeq[T](s.len)
+  result = (newSeq[S](s.len), newSeq[T](s.len))
   for i in 0..<s.len:
     result[0][i] = s[i][0]
     result[1][i] = s[i][1]
@@ -852,7 +877,7 @@ template toSeq*(iter: untyped): untyped =
           inc i
         result
     else:
-      var result: seq[typeof(iter)]# = @[]
+      var result: seq[typeof(iter)] = @[]
       for x in iter:
         result.add(x)
       result
@@ -1078,9 +1103,13 @@ template newSeqWith*(len: int, init: untyped): untyped =
     import std/random
     var seqRand = newSeqWith(20, rand(1.0))
     assert seqRand[0] != seqRand[1]
-
-  var result = newSeq[typeof(init)](len)
-  for i in 0 ..< len:
+  type T = typeof(init)
+  let newLen = len
+  when supportsCopyMem(T) and declared(newSeqUninit):
+    var result = newSeqUninit[T](newLen)
+  else: # TODO: use `newSeqUnsafe` when that's available
+    var result = newSeq[T](newLen)
+  for i in 0 ..< newLen:
     result[i] = init
   move(result) # refs bug #7295
 
diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim
index 7ebd22760..360a075d6 100644
--- a/lib/pure/collections/setimpl.nim
+++ b/lib/pure/collections/setimpl.nim
@@ -62,6 +62,7 @@ template containsOrInclImpl() {.dirty.} =
   if index >= 0:
     result = true
   else:
+    result = false
     if mustRehash(s):
       enlarge(s)
       index = rawGetKnownHC(s, key, hc)
@@ -86,7 +87,9 @@ proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} =
       var j = i # The correctness of this depends on (h+1) in nextTry,
       var r = j # though may be adaptable to other simple sequences.
       s.data[i].hcode = 0 # mark current EMPTY
-      s.data[i].key = default(typeof(s.data[i].key))
+      {.push warning[UnsafeDefault]:off.}
+      reset(s.data[i].key)
+      {.pop.}
       doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)):
         i = (i + 1) and msk # increment mod table size
         if isEmpty(s.data[i].hcode): # end of collision cluster; So all done
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 114e4582a..af13135aa 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -25,7 +25,9 @@
 ##   `difference <#difference,HashSet[A],HashSet[A]>`_, and
 ##   `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_
 ##
-## .. code-block::
+## **Examples:**
+##
+##   ```Nim
 ##   echo toHashSet([9, 5, 1])     # {9, 1, 5}
 ##   echo toOrderedSet([9, 5, 1])  # {9, 5, 1}
 ##
@@ -37,7 +39,7 @@
 ##   echo s1 - s2    # {1, 9}
 ##   echo s1 * s2    # {5}
 ##   echo s1 -+- s2  # {9, 1, 3, 7}
-##
+##   ```
 ##
 ## Note: The data types declared here have *value semantics*: This means
 ## that `=` performs a copy of the set.
@@ -48,7 +50,10 @@
 
 
 import
-  hashes, math
+  std/[hashes, math]
+
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 {.pragma: myShallow.}
 # For "integer-like A" that are too big for intsets/bit-vectors to be practical,
@@ -125,7 +130,7 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] =
     var a = initHashSet[int]()
     a.incl(3)
     assert len(a) == 1
-
+  result = default(HashSet[A])
   result.init(initialSize)
 
 proc `[]`*[A](s: var HashSet[A], key: A): var A =
@@ -246,7 +251,7 @@ iterator items*[A](s: HashSet[A]): A =
   ## If you need a sequence with the elements you can use `sequtils.toSeq
   ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   type
   ##     pair = tuple[a, b: int]
   ##   var
@@ -259,6 +264,7 @@ iterator items*[A](s: HashSet[A]): A =
   ##   assert a.len == 2
   ##   echo b
   ##   # --> {(a: 1, b: 3), (a: 0, b: 4)}
+  ##   ```
   let length = s.len
   for h in 0 .. high(s.data):
     if isFilled(s.data[h].hcode):
@@ -344,7 +350,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool =
 proc pop*[A](s: var HashSet[A]): A =
   ## Removes and returns an arbitrary element from the set `s`.
   ##
-  ## Raises KeyError if the set `s` is empty.
+  ## Raises `KeyError` if the set `s` is empty.
   ##
   ## See also:
   ## * `clear proc <#clear,HashSet[A]>`_
@@ -376,7 +382,9 @@ proc clear*[A](s: var HashSet[A]) =
   s.counter = 0
   for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
-    s.data[i].key = default(typeof(s.data[i].key))
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
 
 proc union*[A](s1, s2: HashSet[A]): HashSet[A] =
@@ -422,7 +430,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
     assert c == toHashSet(["b"])
 
   result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2))
-  
+
   # iterate over the elements of the smaller set
   if s1.data.len < s2.data.len:
     for item in s1:
@@ -430,7 +438,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] =
   else:
     for item in s2:
       if item in s1: incl(result, item)
-  
+
 
 proc difference*[A](s1, s2: HashSet[A]): HashSet[A] =
   ## Returns the difference of the sets `s1` and `s2`.
@@ -556,7 +564,7 @@ proc `==`*[A](s, t: HashSet[A]): bool =
 
   s.counter == t.counter and s <= t
 
-proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] =
+proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] {.effectsOf: op.} =
   ## Returns a new set after applying `op` proc on each of the elements of
   ##`data` set.
   ##
@@ -583,12 +591,12 @@ proc `$`*[A](s: HashSet[A]): string =
   ## any moment and values are not escaped.
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   echo toHashSet([2, 4, 5])
   ##   # --> {2, 4, 5}
   ##   echo toHashSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
+  ##   ```
   dollarImpl()
 
 
@@ -810,7 +818,9 @@ proc clear*[A](s: var OrderedSet[A]) =
   for i in 0 ..< s.data.len:
     s.data[i].hcode = 0
     s.data[i].next = 0
-    s.data[i].key = default(typeof(s.data[i].key))
+    {.push warning[UnsafeDefault]:off.}
+    reset(s.data[i].key)
+    {.pop.}
 
 proc len*[A](s: OrderedSet[A]): int {.inline.} =
   ## Returns the number of elements in `s`.
@@ -871,12 +881,12 @@ proc `$`*[A](s: OrderedSet[A]): string =
   ## any moment and values are not escaped.
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   echo toOrderedSet([2, 4, 5])
   ##   # --> {2, 4, 5}
   ##   echo toOrderedSet(["no", "esc'aping", "is \" provided"])
   ##   # --> {no, esc'aping, is " provided}
+  ##   ```
   dollarImpl()
 
 
@@ -887,7 +897,7 @@ iterator items*[A](s: OrderedSet[A]): A =
   ## If you need a sequence with the elements you can use `sequtils.toSeq
   ## template <sequtils.html#toSeq.t,untyped>`_.
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var a = initOrderedSet[int]()
   ##   for value in [9, 2, 1, 5, 1, 8, 4, 2]:
   ##     a.incl(value)
@@ -899,6 +909,7 @@ iterator items*[A](s: OrderedSet[A]): A =
   ##   # --> Got 5
   ##   # --> Got 8
   ##   # --> Got 4
+  ##   ```
   let length = s.len
   forAllOrderedPairs:
     yield s.data[h].key
diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim
index e61883220..ec8f1cd86 100644
--- a/lib/pure/collections/sharedlist.nim
+++ b/lib/pure/collections/sharedlist.nim
@@ -16,7 +16,7 @@
 {.push stackTrace: off.}
 
 import
-  locks
+  std/locks
 
 const
   ElemsPerNode = 100
diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim
index 816ab49ab..b474ecd31 100644
--- a/lib/pure/collections/sharedtables.nim
+++ b/lib/pure/collections/sharedtables.nim
@@ -17,7 +17,7 @@
 {.deprecated.}
 
 import
-  hashes, math, locks
+  std/[hashes, math, locks]
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
@@ -191,8 +191,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
   ##
   ## Example usage:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   # If value exists, decrement it.
   ##   # If it becomes zero or less, delete the key
   ##   t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool):
@@ -200,6 +199,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A,
   ##       dec v
   ##       if v <= 0:
   ##         pairExists = false
+  ##   ```
   withLock t:
     var hc: Hash
     var index = rawGet(t, key, hc)
diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim
index 27c339177..3542741fa 100644
--- a/lib/pure/collections/tableimpl.nim
+++ b/lib/pure/collections/tableimpl.nim
@@ -11,6 +11,9 @@
 
 include hashcommon
 
+const
+  defaultInitialSize* = 32
+
 template rawGetDeepImpl() {.dirty.} =   # Search algo for unconditional add
   genHashImpl(key, hc)
   var h: Hash = hc and maxHash(t)
@@ -23,7 +26,7 @@ template rawInsertImpl() {.dirty.} =
   data[h].val = val
   data[h].hcode = hc
 
-proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} =
+proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} =
   rawGetDeepImpl()
 
 proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
@@ -31,9 +34,8 @@ proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B],
   rawInsertImpl()
 
 template checkIfInitialized() =
-  when compiles(defaultInitialSize):
-    if t.dataLen == 0:
-      initImpl(t, defaultInitialSize)
+  if t.dataLen == 0:
+    initImpl(t, defaultInitialSize)
 
 template addImpl(enlarge) {.dirty.} =
   checkIfInitialized()
@@ -43,7 +45,7 @@ template addImpl(enlarge) {.dirty.} =
   rawInsert(t, t.data, key, val, hc, j)
   inc(t.counter)
 
-template maybeRehashPutImpl(enlarge) {.dirty.} =
+template maybeRehashPutImpl(enlarge, val) {.dirty.} =
   checkIfInitialized()
   if mustRehash(t):
     enlarge(t)
@@ -54,28 +56,41 @@ template maybeRehashPutImpl(enlarge) {.dirty.} =
 
 template putImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index >= 0: t.data[index].val = val
-  else: maybeRehashPutImpl(enlarge)
+  else: maybeRehashPutImpl(enlarge, val)
 
 template mgetOrPutImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     # not present: insert (flipping index)
-    maybeRehashPutImpl(enlarge)
+    when declared(val):
+      maybeRehashPutImpl(enlarge, val)
+    else:
+      maybeRehashPutImpl(enlarge, default(B))
   # either way return modifiable val
   result = t.data[index].val
 
+# template mgetOrPutDefaultImpl(enlarge) {.dirty.} =
+#   checkIfInitialized()
+#   var hc: Hash = default(Hash)
+#   var index = rawGet(t, key, hc)
+#   if index < 0:
+#     # not present: insert (flipping index)
+#     maybeRehashPutImpl(enlarge, default(B))
+#   # either way return modifiable val
+#   result = t.data[index].val
+
 template hasKeyOrPutImpl(enlarge) {.dirty.} =
   checkIfInitialized()
-  var hc: Hash
+  var hc: Hash = default(Hash)
   var index = rawGet(t, key, hc)
   if index < 0:
     result = false
-    maybeRehashPutImpl(enlarge)
+    maybeRehashPutImpl(enlarge, val)
   else: result = true
 
 # delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to
@@ -117,8 +132,10 @@ template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) =
         var j = i         # The correctness of this depends on (h+1) in nextTry
         var r = j         # though may be adaptable to other simple sequences.
         makeEmpty(i)                     # mark current EMPTY
-        t.data[i].key = default(typeof(t.data[i].key))
-        t.data[i].val = default(typeof(t.data[i].val))
+        {.push warning[UnsafeDefault]:off.}
+        reset(t.data[i].key)
+        reset(t.data[i].val)
+        {.pop.}
         while true:
           i = (i + 1) and msk            # increment mod table size
           if cellEmpty(i):               # end of collision cluster; So all done
@@ -149,8 +166,10 @@ template clearImpl() {.dirty.} =
   for i in 0 ..< t.dataLen:
     when compiles(t.data[i].hcode): # CountTable records don't contain a hcode
       t.data[i].hcode = 0
-    t.data[i].key = default(typeof(t.data[i].key))
-    t.data[i].val = default(typeof(t.data[i].val))
+    {.push warning[UnsafeDefault]:off.}
+    reset(t.data[i].key)
+    reset(t.data[i].val)
+    {.pop.}
   t.counter = 0
 
 template ctAnd(a, b): bool =
@@ -208,3 +227,5 @@ template equalsImpl(s, t: typed) =
       if not t.hasKey(key): return false
       if t.getOrDefault(key) != val: return false
     return true
+  else:
+    return false
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 5c62ed56a..d414caeed 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -136,14 +136,11 @@ runnableExamples:
 ## a more complex object as a key you will be greeted by a strange compiler
 ## error:
 ##
-## .. code::
-##
-##   Error: type mismatch: got (Person)
-##   but expected one of:
-##   hashes.hash(x: openArray[A]): Hash
-##   hashes.hash(x: int): Hash
-##   hashes.hash(x: float): Hash
-##   …
+##     Error: type mismatch: got (Person)
+##     but expected one of:
+##     hashes.hash(x: openArray[A]): Hash
+##     hashes.hash(x: int): Hash
+##     hashes.hash(x: float): Hash
 ##
 ## What is happening here is that the types used for table keys require to have
 ## a `hash()` proc which will convert them to a `Hash <hashes.html#Hash>`_
@@ -197,7 +194,11 @@ runnableExamples:
 
 
 import std/private/since
-import hashes, math, algorithm
+import std/[hashes, math, algorithm]
+
+
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 type
   KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B]
@@ -216,8 +217,6 @@ type
     ## For creating a new empty TableRef, use `newTable proc
     ## <#newTable>`_.
 
-const
-  defaultInitialSize* = 32
 
 # ------------------------------ helpers ---------------------------------
 
@@ -279,6 +278,7 @@ proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] =
     let
       a = initTable[int, string]()
       b = initTable[char, seq[int]]()
+  result = default(Table[A, B])
   initImpl(result, initialSize)
 
 proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) =
@@ -313,7 +313,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] =
   result = initTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
-proc `[]`*[A, B](t: Table[A, B], key: A): B =
+proc `[]`*[A, B](t: Table[A, B], key: A): lent B =
   ## Retrieves the value at `t[key]`.
   ##
   ## If `key` is not in `t`, the `KeyError` exception is raised.
@@ -416,7 +416,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
-
+  result = default(B)
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
@@ -434,7 +434,7 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
-
+  result = default(B)
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
@@ -443,7 +443,7 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
   ##
   ##
   ## Note that while the value returned is of type `var B`,
-  ## it is easy to accidentally create an copy of the value at `t[key]`.
+  ## it is easy to accidentally create a copy of the value at `t[key]`.
   ## Remember that seqs and strings are value types, and therefore
   ## cannot be copied into a separate variable for modification.
   ## See the example below.
@@ -475,6 +475,18 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B =
 
   mgetOrPutImpl(enlarge)
 
+proc mgetOrPut*[A, B](t: var Table[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
+
+  mgetOrPutImpl(enlarge)
+
 proc len*[A, B](t: Table[A, B]): int =
   ## Returns the number of keys in `t`.
   runnableExamples:
@@ -676,7 +688,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -690,6 +702,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) =
   ##   # value: [2, 4, 6, 8]
   ##   # key: o
   ##   # value: [1, 5, 7, 9]
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if isFilled(t.data[h].hcode):
@@ -824,7 +837,8 @@ proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] =
       b = newTable[char, seq[int]]()
 
   new(result)
-  result[] = initTable[A, B](initialSize)
+  {.noSideEffect.}:
+    result[] = initTable[A, B](initialSize)
 
 proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
   ## Creates a new ref hash table that contains the given `pairs`.
@@ -840,14 +854,16 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] =
     assert b == {'a': 5, 'b': 9}.newTable
 
   new(result)
-  result[] = toTable[A, B](pairs)
+  {.noSideEffect.}:
+    result[] = toTable[A, B](pairs)
 
 proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] =
   ## Index the collection with the proc provided.
   # TODO: As soon as supported, change collection: A to collection: A[B]
   result = newTable[C, B]()
-  for item in collection:
-    result[index(item)] = item
+  {.noSideEffect.}:
+    for item in collection:
+      result[index(item)] = item
 
 proc `[]`*[A, B](t: TableRef[A, B], key: A): var B =
   ## Retrieves the value at `t[key]`.
@@ -1010,6 +1026,18 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B =
     doAssert t[25] == @[25, 35]
   t[].mgetOrPut(key, val)
 
+proc mgetOrPut*[A, B](t: TableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.newTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.newTable
+
+  t[].mgetOrPut(key)
+
 proc len*[A, B](t: TableRef[A, B]): int =
   ## Returns the number of keys in `t`.
   runnableExamples:
@@ -1122,7 +1150,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -1136,6 +1164,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) =
   ##   # value: [2, 4, 6, 8]
   ##   # key: o
   ##   # value: [1, 5, 7, 9]
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if isFilled(t.data[h].hcode):
@@ -1318,6 +1347,7 @@ proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A,
     let
       a = initOrderedTable[int, string]()
       b = initOrderedTable[char, seq[int]]()
+  result = default(OrderedTable[A, B])
   initImpl(result, initialSize)
 
 proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) =
@@ -1353,7 +1383,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] =
   result = initOrderedTable[A, B](pairs.len)
   for key, val in items(pairs): result[key] = val
 
-proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B =
+proc `[]`*[A, B](t: OrderedTable[A, B], key: A): lent B =
   ## Retrieves the value at `t[key]`.
   ##
   ## If `key` is not in `t`, the  `KeyError` exception is raised.
@@ -1409,7 +1439,7 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool =
     doAssert a.hasKey('a') == true
     doAssert a.hasKey('z') == false
 
-  var hc: Hash
+  var hc: Hash = default(Hash)
   result = rawGet(t, key, hc) >= 0
 
 proc contains*[A, B](t: OrderedTable[A, B], key: A): bool =
@@ -1458,7 +1488,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a') == 5
     doAssert a.getOrDefault('z') == 0
-
+  result = default(B)
   getOrDefaultImpl(t, key)
 
 proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
@@ -1476,7 +1506,7 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B =
     let a = {'a': 5, 'b': 9}.toOrderedTable
     doAssert a.getOrDefault('a', 99) == 5
     doAssert a.getOrDefault('z', 99) == 99
-
+  result = default(B)
   getOrDefaultImpl(t, key, default)
 
 proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
@@ -1499,6 +1529,18 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B =
 
   mgetOrPutImpl(enlarge)
 
+proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.toOrderedTable
+
+  mgetOrPutImpl(enlarge)
+
 proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} =
   ## Returns the number of keys in `t`.
   runnableExamples:
@@ -1597,7 +1639,7 @@ proc clear*[A, B](t: var OrderedTable[A, B]) =
   t.last = -1
 
 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int,
-    order = SortOrder.Ascending) =
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
   ## Sorts `t` according to the function `cmp`.
   ##
   ## This modifies the internal list
@@ -1698,7 +1740,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -1712,6 +1754,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) =
   ##   # value: [1, 5, 7, 9]
   ##   # key: e
   ##   # value: [2, 4, 6, 8]
+  ##   ```
 
   let L = len(t)
   forAllOrderedPairs:
@@ -1821,7 +1864,8 @@ proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A
       a = newOrderedTable[int, string]()
       b = newOrderedTable[char, seq[int]]()
   new(result)
-  result[] = initOrderedTable[A, B](initialSize)
+  {.noSideEffect.}:
+    result[] = initOrderedTable[A, B](initialSize)
 
 proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
   ## Creates a new ordered ref hash table that contains the given `pairs`.
@@ -1838,7 +1882,8 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] =
     assert b == {'a': 5, 'b': 9}.newOrderedTable
 
   result = newOrderedTable[A, B](pairs.len)
-  for key, val in items(pairs): result[key] = val
+  {.noSideEffect.}:
+    for key, val in items(pairs): result[key] = val
 
 
 proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B =
@@ -1985,6 +2030,18 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B =
 
   result = t[].mgetOrPut(key, val)
 
+proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A): var B =
+  ## Retrieves the value at `t[key]` or puts the
+  ## default initialization value for type `B` (e.g. 0 for any
+  ## integer type).
+  runnableExamples:
+    var a = {'a': 5}.toOrderedTable
+    doAssert a.mgetOrPut('a') == 5
+    a.mgetOrPut('z').inc
+    doAssert a == {'a': 5, 'z': 1}.toOrderedTable
+
+  t[].mgetOrPut(key)
+
 proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} =
   ## Returns the number of keys in `t`.
   runnableExamples:
@@ -2054,7 +2111,7 @@ proc clear*[A, B](t: OrderedTableRef[A, B]) =
   clear(t[])
 
 proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int,
-    order = SortOrder.Ascending) =
+    order = SortOrder.Ascending) {.effectsOf: cmp.} =
   ## Sorts `t` according to the function `cmp`.
   ##
   ## This modifies the internal list
@@ -2106,7 +2163,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = {
   ##     'o': [1, 5, 7, 9],
   ##     'e': [2, 4, 6, 8]
@@ -2120,6 +2177,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) =
   ##   # value: [1, 5, 7, 9]
   ##   # key: e
   ##   # value: [2, 4, 6, 8]
+  ##   ```
 
   let L = len(t)
   forAllOrderedPairs:
@@ -2280,6 +2338,7 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] =
   ## * `toCountTable proc<#toCountTable,openArray[A]>`_
   ## * `newCountTable proc<#newCountTable>`_ for creating a
   ##   `CountTableRef`
+  result = default(CountTable[A])
   initImpl(result, initialSize)
 
 proc toCountTable*[A](keys: openArray[A]): CountTable[A] =
@@ -2389,7 +2448,7 @@ proc contains*[A](t: CountTable[A], key: A): bool =
   return hasKey[A](t, key)
 
 proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int =
-  ## Retrieves the value at `t[key]` if`key` is in `t`. Otherwise, the
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
   ## integer value of `default` is returned.
   ##
   ## See also:
@@ -2519,7 +2578,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = toCountTable("abracadabra")
   ##
   ##   for k, v in pairs(a):
@@ -2536,6 +2595,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) =
   ##   # value: 1
   ##   # key: r
   ##   # value: 2
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if t.data[h].val != 0:
@@ -2637,13 +2697,15 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] =
   ## * `initCountTable proc<#initCountTable>`_ for creating a
   ##   `CountTable`
   new(result)
-  result[] = initCountTable[A](initialSize)
+  {.noSideEffect.}:
+    result[] = initCountTable[A](initialSize)
 
 proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] =
   ## Creates a new ref count table with every member of a container `keys`
   ## having a count of how many times it occurs in that container.
   result = newCountTable[A](keys.len)
-  for key in items(keys): result.inc(key)
+  {.noSideEffect.}:
+    for key in items(keys): result.inc(key)
 
 proc `[]`*[A](t: CountTableRef[A], key: A): int =
   ## Retrieves the value at `t[key]` if `key` is in `t`.
@@ -2667,7 +2729,8 @@ proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) =
   ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a
   ##   value of a key
   assert val > 0
-  t[][key] = val
+  {.noSideEffect.}:
+    t[][key] = val
 
 proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
   ## Increments `t[key]` by `val` (default: 1).
@@ -2676,7 +2739,8 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) =
     a.inc('a')
     a.inc('b', 10)
     doAssert a == newCountTable("aaabbbbbbbbbbb")
-  t[].inc(key, val)
+  {.noSideEffect.}:
+    t[].inc(key, val)
 
 proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] =
   ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n)
@@ -2709,7 +2773,7 @@ proc contains*[A](t: CountTableRef[A], key: A): bool =
   return hasKey[A](t, key)
 
 proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int =
-  ## Retrieves the value at `t[key]` if`key` is in `t`. Otherwise, the
+  ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the
   ## integer value of `default` is returned.
   ##
   ## See also:
@@ -2795,7 +2859,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   let a = newCountTable("abracadabra")
   ##
   ##   for k, v in pairs(a):
@@ -2812,6 +2876,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) =
   ##   # value: 1
   ##   # key: r
   ##   # value: 2
+  ##   ```
   let L = len(t)
   for h in 0 .. high(t.data):
     if t.data[h].val != 0:
@@ -2890,3 +2955,18 @@ iterator mvalues*[A](t: CountTableRef[A]): var int =
     if t.data[h].val != 0:
       yield t.data[h].val
       assert(len(t) == L, "the length of the table changed while iterating over it")
+
+proc hash*[K,V](s: Table[K,V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result
+
+proc hash*[K,V](s: OrderedTable[K,V]): Hash =
+  for p in pairs(s):
+    result = result !& hash(p)
+  result = !$result
+
+proc hash*[V](s: CountTable[V]): Hash =
+  for p in pairs(s):
+    result = result xor hash(p)
+  result = !$result
diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim
index 8cda2fab8..d3e6dc063 100644
--- a/lib/pure/colors.nim
+++ b/lib/pure/colors.nim
@@ -9,22 +9,23 @@
 ## This module implements color handling for Nim,
 ## namely color mixing and parsing the CSS color names.
 
-import strutils
-from algorithm import binarySearch
+import std/strutils
+from std/algorithm import binarySearch
 
 type
   Color* = distinct int ## A color stored as RGB, e.g. `0xff00cc`.
 
-proc `==` *(a, b: Color): bool {.borrow.}
+proc `==`*(a, b: Color): bool {.borrow.}
   ## Compares two colors.
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var
   ##     a = Color(0xff_00_ff)
   ##     b = colFuchsia
   ##     c = Color(0x00_ff_cc)
   ##   assert a == b
-  ##   assert not a == c
+  ##   assert not (a == c)
+  ##   ```
 
 template extract(a: Color, r, g, b: untyped) =
   var r = a.int shr 16 and 0xff
@@ -200,8 +201,8 @@ const
   colGoldenRod* = Color(0xDAA520)
   colGray* = Color(0x808080)
   colGreen* = Color(0x008000)
-  colGrey* = Color(0x808080)
   colGreenYellow* = Color(0xADFF2F)
+  colGrey* = Color(0x808080)
   colHoneyDew* = Color(0xF0FFF0)
   colHotPink* = Color(0xFF69B4)
   colIndianRed* = Color(0xCD5C5C)
@@ -350,8 +351,8 @@ const
     "goldenrod": colGoldenRod,
     "gray": colGray,
     "green": colGreen,
-    "grey": colGrey,
     "greenyellow": colGreenYellow,
+    "grey": colGrey,
     "honeydew": colHoneyDew,
     "hotpink": colHotPink,
     "indianred": colIndianRed,
diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim
index 1e8349ef6..b48811eae 100644
--- a/lib/pure/complex.nim
+++ b/lib/pure/complex.nim
@@ -15,9 +15,6 @@
 runnableExamples:
   from std/math import almostEqual, sqrt
 
-  func almostEqual(a, b: Complex): bool =
-    almostEqual(a.re, b.re) and almostEqual(a.im, b.im)
-
   let
     z1 = complex(1.0, 2.0)
     z2 = complex(3.0, -4.0)
@@ -36,7 +33,7 @@ runnableExamples:
 {.push checks: off, line_dir: off, stack_trace: off, debugger: off.}
 # the user does not want to trace a part of the standard library!
 
-import math
+import std/[math, strformat]
 
 type
   Complex*[T: SomeFloat] = object
@@ -81,7 +78,7 @@ func abs2*[T](z: Complex[T]): T =
   ## that is the squared distance from (0, 0) to `z`.
   ## This is more efficient than `abs(z) ^ 2`.
   result = z.re * z.re + z.im * z.im
-  
+
 func sgn*[T](z: Complex[T]): Complex[T] =
   ## Returns the phase of `z` as a unit complex number,
   ## or 0 if `z` is 0.
@@ -249,10 +246,31 @@ func pow*[T](x, y: Complex[T]): Complex[T] =
     else:
       result.re = 0.0
       result.im = 0.0
-  elif y.re == 1.0 and y.im == 0.0:
-    result = x
-  elif y.re == -1.0 and y.im == 0.0:
-    result = T(1.0) / x
+  elif y.im == 0.0:
+    if y.re == 1.0:
+      result = x
+    elif y.re == -1.0:
+      result = T(1.0) / x
+    elif y.re == 2.0:
+      result = x * x
+    elif y.re == 0.5:
+      result = sqrt(x)
+    elif x.im == 0.0:
+      # Revert to real pow when both base and exponent are real
+      result.re = pow(x.re, y.re)
+      result.im = 0.0
+    else:
+      # Special case when the exponent is real
+      let
+        rho = abs(x)
+        theta = arctan2(x.im, x.re)
+        s = pow(rho, y.re)
+        r = y.re * theta
+      result.re = s * cos(r)
+      result.im = s * sin(r)
+  elif x.im == 0.0 and x.re == E:
+   # Special case Euler's formula
+   result = exp(y)
   else:
     let
       rho = abs(x)
@@ -391,6 +409,24 @@ func rect*[T](r, phi: T): Complex[T] =
   ## * `polar func<#polar,Complex[T]>`_ for the inverse operation
   complex(r * cos(phi), r * sin(phi))
 
+func almostEqual*[T: SomeFloat](x, y: Complex[T]; unitsInLastPlace: Natural = 4): bool =
+  ## Checks if two complex values are almost equal, using the
+  ## [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon).
+  ##
+  ## Two complex values are considered almost equal if their real and imaginary
+  ## components are almost equal.
+  ##
+  ## `unitsInLastPlace` is the max number of
+  ## [units in the last place](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
+  ## difference tolerated when comparing two numbers. The larger the value, the
+  ## more error is allowed. A `0` value means that two numbers must be exactly the
+  ## same to be considered equal.
+  ##
+  ## The machine epsilon has to be scaled to the magnitude of the values used
+  ## and multiplied by the desired precision in ULPs unless the difference is
+  ## subnormal.
+  almostEqual(x.re, y.re, unitsInLastPlace = unitsInLastPlace) and
+  almostEqual(x.im, y.im, unitsInLastPlace = unitsInLastPlace)
 
 func `$`*(z: Complex): string =
   ## Returns `z`'s string representation as `"(re, im)"`.
@@ -399,4 +435,39 @@ func `$`*(z: Complex): string =
 
   result = "(" & $z.re & ", " & $z.im & ")"
 
+proc formatValueAsTuple(result: var string; value: Complex; specifier: string) =
+  ## Format implementation for `Complex` representing the value as a (real, imaginary) tuple.
+  result.add "("
+  formatValue(result, value.re, specifier)
+  result.add ", "
+  formatValue(result, value.im, specifier)
+  result.add ")"
+
+proc formatValueAsComplexNumber(result: var string; value: Complex; specifier: string) =
+  ## Format implementation for `Complex` representing the value as a (RE+IMj) number
+  ## By default, the real and imaginary parts are formatted using the general ('g') format
+  let specifier = if specifier.contains({'e', 'E', 'f', 'F', 'g', 'G'}):
+      specifier.replace("j")
+    else:
+      specifier.replace('j', 'g')
+  result.add "("
+  formatValue(result, value.re, specifier)
+  if value.im >= 0 and not specifier.contains({'+', '-'}):
+    result.add "+"
+  formatValue(result, value.im, specifier)
+  result.add "j)"
+
+proc formatValue*(result: var string; value: Complex; specifier: string) =
+  ## Standard format implementation for `Complex`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  ## For complex numbers, we add a specific 'j' specifier, which formats
+  ## the value as (A+Bj) like in mathematics.
+  if specifier.len == 0:
+    result.add $value
+  elif 'j' in specifier:
+    formatValueAsComplexNumber(result, value, specifier)
+  else:
+    formatValueAsTuple(result, value, specifier)
+
 {.pop.}
diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim
index 315f44f33..818f1b37a 100644
--- a/lib/pure/concurrency/atomics.nim
+++ b/lib/pure/concurrency/atomics.nim
@@ -10,6 +10,9 @@
 ## Types and operations for atomic operations and lockless algorithms.
 ##
 ## Unstable API.
+## 
+## By default, C++ uses C11 atomic primitives. To use C++ `std::atomic`,
+## `-d:nimUseCppAtomics` can be defined.
 
 runnableExamples:
   # Atomic
@@ -50,8 +53,7 @@ runnableExamples:
   flag.clear(moRelaxed)
   assert not flag.testAndSet
 
-
-when defined(cpp) or defined(nimdoc):
+when (defined(cpp) and defined(nimUseCppAtomics)) or defined(nimdoc):
   # For the C++ backend, types and operations map directly to C++11 atomics.
 
   {.push, header: "<atomic>".}
@@ -211,8 +213,8 @@ else:
 
     # MSVC intrinsics
     proc interlockedExchange(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8".}
-    proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange".}
-    proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange16".}
+    proc interlockedExchange(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16".}
+    proc interlockedExchange(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange".}
     proc interlockedExchange(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64".}
 
     proc interlockedCompareExchange(location: pointer; desired, expected: int8): int8 {.importc: "_InterlockedCompareExchange8".}
@@ -274,10 +276,17 @@ else:
       cast[T](interlockedXor(addr(location.value), cast[nonAtomicType(T)](value)))
 
   else:
-    {.push, header: "<stdatomic.h>".}
+    when defined(cpp):
+      {.push, header: "<atomic>".}
+      template maybeWrapStd(x: string): string =
+        "std::" & x
+    else:
+      {.push, header: "<stdatomic.h>".}
+      template maybeWrapStd(x: string): string =
+        x
 
     type
-      MemoryOrder* {.importc: "memory_order".} = enum
+      MemoryOrder* {.importc: "memory_order".maybeWrapStd.} = enum
         moRelaxed
         moConsume
         moAcquire
@@ -285,16 +294,25 @@ else:
         moAcquireRelease
         moSequentiallyConsistent
 
-    type
-      # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
+    when defined(cpp):
+      type
+        # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
+
+        AtomicInt8 {.importc: "std::atomic<NI8>".} = int8
+        AtomicInt16 {.importc: "std::atomic<NI16>".} = int16
+        AtomicInt32 {.importc: "std::atomic<NI32>".} = int32
+        AtomicInt64 {.importc: "std::atomic<NI64>".} = int64
+    else:
+      type
+        # Atomic*[T] {.importcpp: "_Atomic('0)".} = object
 
-      AtomicInt8 {.importc: "_Atomic NI8".} = int8
-      AtomicInt16 {.importc: "_Atomic NI16".} = int16
-      AtomicInt32 {.importc: "_Atomic NI32".} = int32
-      AtomicInt64 {.importc: "_Atomic NI64".} = int64
+        AtomicInt8 {.importc: "_Atomic NI8".} = int8
+        AtomicInt16 {.importc: "_Atomic NI16".} = int16
+        AtomicInt32 {.importc: "_Atomic NI32".} = int32
+        AtomicInt64 {.importc: "_Atomic NI64".} = int64
 
     type
-      AtomicFlag* {.importc: "atomic_flag", size: 1.} = object
+      AtomicFlag* {.importc: "atomic_flag".maybeWrapStd, size: 1.} = object
 
       Atomic*[T] = object
         when T is Trivial:
@@ -308,27 +326,27 @@ else:
           guard: AtomicFlag
 
     #proc init*[T](location: var Atomic[T]; value: T): T {.importcpp: "atomic_init(@)".}
-    proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc.}
-    proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc.}
-    proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
-    proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
-    proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc.}
+    proc atomic_load_explicit[T, A](location: ptr A; order: MemoryOrder): T {.importc: "atomic_load_explicit".maybeWrapStd.}
+    proc atomic_store_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_store_explicit".maybeWrapStd.}
+    proc atomic_exchange_explicit[T, A](location: ptr A; desired: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_exchange_explicit".maybeWrapStd.}
+    proc atomic_compare_exchange_strong_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_strong_explicit".maybeWrapStd.}
+    proc atomic_compare_exchange_weak_explicit[T, A](location: ptr A; expected: ptr T; desired: T; success, failure: MemoryOrder): bool {.importc: "atomic_compare_exchange_weak_explicit".maybeWrapStd.}
 
     # Numerical operations
-    proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
-    proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
-    proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
-    proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
-    proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc.}
+    proc atomic_fetch_add_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_add_explicit".maybeWrapStd.}
+    proc atomic_fetch_sub_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_sub_explicit".maybeWrapStd.}
+    proc atomic_fetch_and_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_and_explicit".maybeWrapStd.}
+    proc atomic_fetch_or_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_or_explicit".maybeWrapStd.}
+    proc atomic_fetch_xor_explicit[T, A](location: ptr A; value: T; order: MemoryOrder = moSequentiallyConsistent): T {.importc: "atomic_fetch_xor_explicit".maybeWrapStd.}
 
     # Flag operations
     # var ATOMIC_FLAG_INIT {.importc, nodecl.}: AtomicFlag
     # proc init*(location: var AtomicFlag) {.inline.} = location = ATOMIC_FLAG_INIT
-    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".}
-    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".}
+    proc testAndSet*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent): bool {.importc: "atomic_flag_test_and_set_explicit".maybeWrapStd.}
+    proc clear*(location: var AtomicFlag; order: MemoryOrder = moSequentiallyConsistent) {.importc: "atomic_flag_clear_explicit".maybeWrapStd.}
 
-    proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".}
-    proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".}
+    proc fence*(order: MemoryOrder) {.importc: "atomic_thread_fence".maybeWrapStd.}
+    proc signalFence*(order: MemoryOrder) {.importc: "atomic_signal_fence".maybeWrapStd.}
 
     {.pop.}
 
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 16d32002d..9bc3fd579 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -15,75 +15,96 @@ runnableExamples:
 
 include "system/inclrtl"
 
-when defined(posix) and not (defined(macosx) or defined(bsd)):
-  import posix
+when defined(js):
+  import std/jsffi
+  proc countProcessorsImpl(): int =
+    when defined(nodejs):
+      let jsOs = require("os")
+      let jsObj = jsOs.cpus().length
+    else:
+      # `navigator.hardwareConcurrency`
+      # works on browser as well as deno.
+      let navigator{.importcpp.}: JsObject
+      let jsObj = navigator.hardwareConcurrency
+    result = jsObj.to int
+else:
+  when defined(posix) and not (defined(macosx) or defined(bsd)):
+    import std/posix
 
-when defined(windows):
-  import std/private/win_getsysteminfo
+  when defined(windows):
+    import std/private/win_getsysteminfo
+
+  when defined(freebsd) or defined(macosx):
+    {.emit: "#include <sys/types.h>".}
+
+  when defined(openbsd) or defined(netbsd):
+    {.emit: "#include <sys/param.h>".}
 
-when defined(freebsd) or defined(macosx):
-  {.emit: "#include <sys/types.h>".}
+  when defined(macosx) or defined(bsd):
+    # we HAVE to emit param.h before sysctl.h so we cannot use .header here
+    # either. The amount of archaic bullshit in Poonix based OSes is just insane.
+    {.emit: "#include <sys/sysctl.h>".}
+    {.push nodecl.}
+    when defined(macosx):
+      proc sysctlbyname(name: cstring,
+        oldp: pointer, oldlenp: var csize_t,
+        newp: pointer, newlen: csize_t): cint {.importc.}
+    let
+      CTL_HW{.importc.}: cint
+      HW_NCPU{.importc.}: cint
+    proc sysctl[I: static[int]](name: var array[I, cint], namelen: cuint,
+      oldp: pointer, oldlenp: var csize_t,
+      newp: pointer, newlen: csize_t): cint {.importc.}
+    {.pop.}
 
-when defined(openbsd) or defined(netbsd):
-  {.emit: "#include <sys/param.h>".}
+  when defined(genode):
+    import genode/env
 
-when defined(macosx) or defined(bsd):
-  # we HAVE to emit param.h before sysctl.h so we cannot use .header here
-  # either. The amount of archaic bullshit in Poonix based OSes is just insane.
-  {.emit: "#include <sys/sysctl.h>".}
-  const
-    CTL_HW = 6
-    HW_AVAILCPU = 25
-    HW_NCPU = 3
-  proc sysctl(x: ptr array[0..3, cint], y: cint, z: pointer,
-              a: var csize_t, b: pointer, c: csize_t): cint {.
-              importc: "sysctl", nodecl.}
+    proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
+      importcpp: "@->cpu().affinity_space().total()".}
 
-when defined(genode):
-  include genode/env
+  when defined(haiku):
+    type
+      SystemInfo {.importc: "system_info", header: "<OS.h>".} = object
+        cpuCount {.importc: "cpu_count".}: uint32
 
-  proc affinitySpaceTotal(env: GenodeEnvPtr): cuint {.
-    importcpp: "@->cpu().affinity_space().total()".}
+    proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info",
+                                                      header: "<OS.h>".}
+
+  proc countProcessorsImpl(): int {.inline.} =
+    when defined(windows):
+      var
+        si: SystemInfo
+      getSystemInfo(addr si)
+      result = int(si.dwNumberOfProcessors)
+    elif defined(macosx) or defined(bsd):
+      let dest = addr result
+      var len = sizeof(result).csize_t
+      when defined(macosx):
+        # alias of "hw.activecpu"
+        if sysctlbyname("hw.logicalcpu", dest, len, nil, 0) == 0:
+          return
+      var mib = [CTL_HW, HW_NCPU]
+      if sysctl(mib, 2, dest, len, nil, 0) == 0:
+        return
+    elif defined(hpux):
+      result = mpctl(MPC_GETNUMSPUS, nil, nil)
+    elif defined(irix):
+      var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
+      result = sysconf(SC_NPROC_ONLN)
+    elif defined(genode):
+      result = runtimeEnv.affinitySpaceTotal().int
+    elif defined(haiku):
+      var sysinfo: SystemInfo
+      if getSystemInfo(addr sysinfo) == 0:
+        result = sysinfo.cpuCount.int
+    else:
+      result = sysconf(SC_NPROCESSORS_ONLN)
+    if result < 0: result = 0
 
-when defined(haiku):
-  type
-    SystemInfo {.importc: "system_info", header: "<OS.h>".} = object
-      cpuCount {.importc: "cpu_count".}: uint32
 
-  proc getSystemInfo(info: ptr SystemInfo): int32 {.importc: "get_system_info",
-                                                    header: "<OS.h>".}
 
 proc countProcessors*(): int {.rtl, extern: "ncpi$1".} =
   ## Returns the number of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
-  when defined(windows):
-    var
-      si: SystemInfo
-    getSystemInfo(addr si)
-    result = int(si.dwNumberOfProcessors)
-  elif defined(macosx) or defined(bsd):
-    var
-      mib: array[0..3, cint]
-      numCPU: int
-    mib[0] = CTL_HW
-    mib[1] = HW_AVAILCPU
-    var len = sizeof(numCPU).csize_t
-    discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
-    if numCPU < 1:
-      mib[1] = HW_NCPU
-      discard sysctl(addr(mib), 2, addr(numCPU), len, nil, 0)
-    result = numCPU
-  elif defined(hpux):
-    result = mpctl(MPC_GETNUMSPUS, nil, nil)
-  elif defined(irix):
-    var SC_NPROC_ONLN {.importc: "_SC_NPROC_ONLN", header: "<unistd.h>".}: cint
-    result = sysconf(SC_NPROC_ONLN)
-  elif defined(genode):
-    result = runtimeEnv.affinitySpaceTotal().int
-  elif defined(haiku):
-    var sysinfo: SystemInfo
-    if getSystemInfo(addr sysinfo) == 0:
-      result = sysinfo.cpuCount.int
-  else:
-    result = sysconf(SC_NPROCESSORS_ONLN)
-  if result <= 0: result = 0
+  countProcessorsImpl()
diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim
index 137dd67ad..bfbf16721 100644
--- a/lib/pure/concurrency/cpuload.nim
+++ b/lib/pure/concurrency/cpuload.nim
@@ -13,11 +13,11 @@
 ## Unstable API.
 
 when defined(windows):
-  import winlean, os, strutils, math
+  import std/[winlean, os, strutils, math]
 
   proc `-`(a, b: FILETIME): int64 = a.rdFileTime - b.rdFileTime
 elif defined(linux):
-  from cpuinfo import countProcessors
+  from std/cpuinfo import countProcessors
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -87,7 +87,7 @@ proc advice*(s: var ThreadPoolState): ThreadPoolAdvice =
   inc s.calls
 
 when not defined(testing) and isMainModule and not defined(nimdoc):
-  import random
+  import std/random
 
   proc busyLoop() =
     while true:
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 5d9e7452b..06ed2fe54 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -7,24 +7,25 @@
 #    distribution, for details about the copyright.
 #
 
+{.deprecated: "use the nimble packages `malebolgia`, `taskpools` or `weave` instead".}
+
 ## Implements Nim's `parallel & spawn statements <manual_experimental.html#parallel-amp-spawn>`_.
 ##
 ## Unstable API.
 ##
 ## See also
 ## ========
-## * `threads module <threads.html>`_ for basic thread support
-## * `channels module <channels_builtin.html>`_ for message passing support
+## * `threads module <typedthreads.html>`_ for basic thread support
 ## * `locks module <locks.html>`_ for locks and condition variables
 ## * `asyncdispatch module <asyncdispatch.html>`_ for asynchronous IO
 
 when not compileOption("threads"):
   {.error: "Threadpool requires --threads:on option.".}
 
-import cpuinfo, cpuload, locks, os
+import std/[cpuinfo, cpuload, locks, os]
 
 when defined(nimPreviewSlimSystem):
-  import std/assertions
+  import std/[assertions, typedthreads, sysatomics]
 
 {.push stackTrace:off.}
 
@@ -103,7 +104,7 @@ type
     idx: int
 
   FlowVarBase* = ref FlowVarBaseObj ## Untyped base class for `FlowVar[T] <#FlowVar>`_.
-  FlowVarBaseObj = object of RootObj
+  FlowVarBaseObj {.acyclic.} = object of RootObj
     ready, usesSemaphore, awaited: bool
     cv: Semaphore  # for 'blockUntilAny' support
     ai: ptr AwaitInfo
@@ -112,7 +113,7 @@ type
                    # be RootRef here otherwise the wrong GC keeps track of it!
     owner: pointer # ptr Worker
 
-  FlowVarObj[T] = object of FlowVarBaseObj
+  FlowVarObj[T] {.acyclic.} = object of FlowVarBaseObj
     blob: T
 
   FlowVar*[T] {.compilerproc.} = ref FlowVarObj[T] ## A data flow variable.
diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim
index 22704e434..f628aaf6b 100644
--- a/lib/pure/cookies.nim
+++ b/lib/pure/cookies.nim
@@ -9,7 +9,7 @@
 
 ## This module implements helper procs for parsing Cookies.
 
-import strtabs, times, options
+import std/[strtabs, times, options]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim
index aaf442a83..24836e316 100644
--- a/lib/pure/coro.nim
+++ b/lib/pure/coro.nim
@@ -29,12 +29,14 @@ when not nimCoroutines and not defined(nimdoc):
   else:
     {.error: "Coroutines require -d:nimCoroutines".}
 
-import os
-import lists
+import std/[os, lists]
 include system/timers
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 const defaultStackSize = 512 * 1024
-const useOrcArc = defined(gcArc) or defined(gcOrc)
+const useOrcArc = defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc)
 
 when useOrcArc:
   proc nimGC_setStackBottom*(theStackBottom: pointer) = discard
@@ -65,7 +67,7 @@ else:
   const coroBackend = CORO_BACKEND_UCONTEXT
 
 when coroBackend == CORO_BACKEND_FIBERS:
-  import windows/winlean
+  import std/winlean
   type
     Context = pointer
 
@@ -221,7 +223,7 @@ proc switchTo(current, to: CoroutinePtr) =
         elif to.state == CORO_CREATED:
           # Coroutine is started.
           coroExecWithStack(runCurrentTask, to.stack.bottom)
-          #doAssert false
+          #raiseAssert "unreachable"
     else:
       {.error: "Invalid coroutine backend set.".}
   # Execution was just resumed. Restore frame information and set active stack.
@@ -263,7 +265,7 @@ proc runCurrentTask() =
     current.state = CORO_FINISHED
   nimGC_setStackBottom(ctx.ncbottom)
   suspend(0) # Exit coroutine without returning from coroExecWithStack()
-  doAssert false
+  raiseAssert "unreachable"
 
 proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discardable.} =
   ## Schedule coroutine for execution. It does not run immediately.
@@ -278,8 +280,8 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar
       (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil)
   else:
     coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize))
-    coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine))
-    coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize)
+    coro.stack.top = cast[pointer](cast[int](coro) + sizeof(Coroutine))
+    coro.stack.bottom = cast[pointer](cast[int](coro.stack.top) + stacksize)
     when coroBackend == CORO_BACKEND_UCONTEXT:
       discard getcontext(coro.execContext)
       coro.execContext.uc_stack.ss_sp = coro.stack.top
diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim
index 0eae00fba..c907e54d8 100644
--- a/lib/pure/cstrutils.nim
+++ b/lib/pure/cstrutils.nim
@@ -75,7 +75,7 @@ func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} =
   ## for that. Returns:
   ## * 0 if `a == b`
   ## * < 0 if `a < b`
-  ## * > 0 if `a > b`
+  ## * \> 0 if `a > b`
   runnableExamples:
     assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0
 
@@ -101,7 +101,7 @@ func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} =
   ## Compares two strings in a case insensitive manner. Returns:
   ## * 0 if `a == b`
   ## * < 0 if `a < b`
-  ## * > 0 if `a > b`
+  ## * \> 0 if `a > b`
   runnableExamples:
     assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0
     assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0
diff --git a/lib/pure/db_common.nim b/lib/pure/db_common.nim
deleted file mode 100644
index 852c8e1c3..000000000
--- a/lib/pure/db_common.nim
+++ /dev/null
@@ -1,100 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Common datatypes and definitions for all `db_*.nim` (
-## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
-## and `db_sqlite <db_sqlite.html>`_) modules.
-
-type
-  DbError* = object of IOError ## exception that is raised if a database error occurs
-
-  SqlQuery* = distinct string ## an SQL query string
-
-
-  DbEffect* = object of IOEffect ## effect that denotes a database operation
-  ReadDbEffect* = object of DbEffect ## effect that denotes a read operation
-  WriteDbEffect* = object of DbEffect ## effect that denotes a write operation
-
-  DbTypeKind* = enum ## a superset of datatypes that might be supported.
-    dbUnknown,       ## unknown datatype
-    dbSerial,        ## datatype used for primary auto-increment keys
-    dbNull,          ## datatype used for the NULL value
-    dbBit,           ## bit datatype
-    dbBool,          ## boolean datatype
-    dbBlob,          ## blob datatype
-    dbFixedChar,     ## string of fixed length
-    dbVarchar,       ## string datatype
-    dbJson,          ## JSON datatype
-    dbXml,           ## XML datatype
-    dbInt,           ## some integer type
-    dbUInt,          ## some unsigned integer type
-    dbDecimal,       ## decimal numbers (fixed-point number)
-    dbFloat,         ## some floating point type
-    dbDate,          ## a year-month-day description
-    dbTime,          ## HH:MM:SS information
-    dbDatetime,      ## year-month-day and HH:MM:SS information,
-                     ## plus optional time or timezone information
-    dbTimestamp,     ## Timestamp values are stored as the number of seconds
-                     ## since the epoch ('1970-01-01 00:00:00' UTC).
-    dbTimeInterval,  ## an interval [a,b] of times
-    dbEnum,          ## some enum
-    dbSet,           ## set of enum values
-    dbArray,         ## an array of values
-    dbComposite,     ## composite type (record, struct, etc)
-    dbUrl,           ## a URL
-    dbUuid,          ## a UUID
-    dbInet,          ## an IP address
-    dbMacAddress,    ## a MAC address
-    dbGeometry,      ## some geometric type
-    dbPoint,         ## Point on a plane   (x,y)
-    dbLine,          ## Infinite line ((x1,y1),(x2,y2))
-    dbLseg,          ## Finite line segment   ((x1,y1),(x2,y2))
-    dbBox,           ## Rectangular box   ((x1,y1),(x2,y2))
-    dbPath,          ## Closed or open path (similar to polygon) ((x1,y1),...)
-    dbPolygon,       ## Polygon (similar to closed path)   ((x1,y1),...)
-    dbCircle,        ## Circle   <(x,y),r> (center point and radius)
-    dbUser1,         ## user definable datatype 1 (for unknown extensions)
-    dbUser2,         ## user definable datatype 2 (for unknown extensions)
-    dbUser3,         ## user definable datatype 3 (for unknown extensions)
-    dbUser4,         ## user definable datatype 4 (for unknown extensions)
-    dbUser5          ## user definable datatype 5 (for unknown extensions)
-
-  DbType* = object              ## describes a database type
-    kind*: DbTypeKind           ## the kind of the described type
-    notNull*: bool              ## does the type contain NULL?
-    name*: string               ## the name of the type
-    size*: Natural              ## the size of the datatype; 0 if of variable size
-    maxReprLen*: Natural        ## maximal length required for the representation
-    precision*, scale*: Natural ## precision and scale of the number
-    min*, max*: BiggestInt      ## the minimum and maximum of allowed values
-    validValues*: seq[string]   ## valid values of an enum or a set
-
-  DbColumn* = object   ## information about a database column
-    name*: string      ## name of the column
-    tableName*: string ## name of the table the column belongs to (optional)
-    typ*: DbType       ## type of the column
-    primaryKey*: bool  ## is this a primary key?
-    foreignKey*: bool  ## is this a foreign key?
-  DbColumns* = seq[DbColumn]
-
-template sql*(query: string): SqlQuery =
-  ## constructs a SqlQuery from the string `query`. This is supposed to be
-  ## used as a raw-string-literal modifier:
-  ## `sql"update user set counter = counter + 1"`
-  ##
-  ## If assertions are turned off, it does nothing. If assertions are turned
-  ## on, later versions will check the string for valid syntax.
-  SqlQuery(query)
-
-proc dbError*(msg: string) {.noreturn, noinline.} =
-  ## raises an DbError exception with message `msg`.
-  var e: ref DbError
-  new(e)
-  e.msg = msg
-  raise e
diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim
index 797698d61..9e71d4ce0 100644
--- a/lib/pure/distros.nim
+++ b/lib/pure/distros.nim
@@ -9,21 +9,20 @@
 
 ## This module implements the basics for Linux distribution ("distro")
 ## detection and the OS's native package manager. Its primary purpose is to
-## produce output for Nimble packages, like::
+## produce output for Nimble packages, like:
 ##
-##   To complete the installation, run:
+##     To complete the installation, run:
 ##
-##   sudo apt-get install libblas-dev
-##   sudo apt-get install libvoodoo
+##     sudo apt-get install libblas-dev
+##     sudo apt-get install libvoodoo
 ##
 ## The above output could be the result of a code snippet like:
 ##
-## .. code-block:: nim
-##
+##   ```nim
 ##   if detectOs(Ubuntu):
 ##     foreignDep "lbiblas-dev"
 ##     foreignDep "libvoodoo"
-##
+##   ```
 ##
 ## See `packaging <packaging.html>`_ for hints on distributing Nim using OS packages.
 
@@ -31,7 +30,7 @@ from std/strutils import contains, toLowerAscii
 
 when not defined(nimscript):
   from std/osproc import execProcess
-  from std/os import existsEnv
+  from std/envvars import existsEnv
 
 type
   Distribution* {.pure.} = enum ## the list of known distributions
@@ -128,6 +127,7 @@ type
 
     BSD
     FreeBSD
+    NetBSD
     OpenBSD
     DragonFlyBSD
 
@@ -169,7 +169,7 @@ proc detectOsImpl(d: Distribution): bool =
   else:
     when defined(bsd):
       case d
-      of Distribution.FreeBSD, Distribution.OpenBSD:
+      of Distribution.FreeBSD, Distribution.NetBSD, Distribution.OpenBSD:
         result = $d in uname()
       else:
         result = false
@@ -252,7 +252,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) =
       result = ("nix-env -i " & p, false)
     elif detectOs(Solaris) or detectOs(FreeBSD):
       result = ("pkg install " & p, true)
-    elif detectOs(OpenBSD):
+    elif detectOs(NetBSD) or detectOs(OpenBSD):
       result = ("pkg_add " & p, true)
     elif detectOs(PCLinuxOS):
       result = ("rpm -ivh " & p, true)
diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim
index 48fd91b8f..a162fe37f 100644
--- a/lib/pure/dynlib.nim
+++ b/lib/pure/dynlib.nim
@@ -105,7 +105,7 @@ when defined(posix) and not defined(nintendoswitch):
   # as an emulation layer on top of native functions.
   # =========================================================================
   #
-  import posix
+  import std/posix
 
   proc loadLib(path: string, globalSymbols = false): LibHandle =
     let flags =
@@ -148,7 +148,7 @@ elif defined(genode):
   #
 
   template raiseErr(prc: string) =
-    raise newException(OSError, prc & " not implemented, compile with POSIX suport")
+    raise newException(OSError, prc & " not implemented, compile with POSIX support")
 
   proc dlclose(lib: LibHandle) =
     raiseErr(OSError, "dlclose")
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index 26b6e1f22..bbadca655 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -39,7 +39,7 @@ runnableExamples:
     assert fromGB2312.convert(second) == "有白头如新,倾盖如故"
 
 
-import os
+import std/os
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -59,7 +59,7 @@ type
                                         ## for encoding errors.
 
 when defined(windows):
-  import parseutils, strutils
+  import std/[parseutils, strutils]
   proc eqEncodingNames(a, b: string): bool =
     var i = 0
     var j = 0
@@ -339,10 +339,10 @@ proc getCurrentEncoding*(uiApp = false): string =
 
 proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter =
   ## Opens a converter that can convert from `srcEncoding` to `destEncoding`.
-  ## Raises `IOError` if it cannot fulfill the request.
+  ## Raises `EncodingError` if it cannot fulfill the request.
   when not defined(windows):
     result = iconvOpen(destEncoding, srcEncoding)
-    if result == nil:
+    if result == cast[EncodingConverter](-1):
       raise newException(EncodingError,
         "cannot create encoding converter from " &
         srcEncoding & " to " & destEncoding)
diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim
index a0dc97c1d..4c1d45ae5 100644
--- a/lib/pure/endians.nim
+++ b/lib/pure/endians.nim
@@ -10,7 +10,7 @@
 ## This module contains helpers that deal with different byte orders
 ## (`endian`:idx:).
 ##
-## Endianess is the order of bytes of a value in memory. Big-endian means that
+## Endianness is the order of bytes of a value in memory. Big-endian means that
 ## the most significant byte is stored at the smallest memory address,
 ## while little endian means that the least-significant byte is stored
 ## at the smallest address. See also https://en.wikipedia.org/wiki/Endianness.
diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim
index ddb6a1a0c..1d96fd6be 100644
--- a/lib/pure/fenv.nim
+++ b/lib/pure/fenv.nim
@@ -12,7 +12,7 @@
 ## The types, vars and procs are bindings for the C standard library
 ## [<fenv.h>](https://en.cppreference.com/w/c/numeric/fenv) header.
 
-when defined(posix) and not defined(genode):
+when defined(posix) and not defined(genode) and not defined(macosx):
   {.passl: "-lm".}
 
 var
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index e88210757..1038d55a1 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -62,7 +62,7 @@ runnableExamples:
 ## ========
 ## * `md5 module <md5.html>`_ for the MD5 checksum algorithm
 ## * `base64 module <base64.html>`_ for a Base64 encoder and decoder
-## * `std/sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
+## * `sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
 ## * `tables module <tables.html>`_ for hash tables
 
 import std/private/since
@@ -190,7 +190,7 @@ proc hashData*(data: pointer, size: int): Hash =
   var h: Hash = 0
   when defined(js):
     var p: cstring
-    asm """`p` = `Data`"""
+    {.emit: """`p` = `Data`;""".}
   else:
     var p = cast[cstring](data)
   var i = 0
@@ -217,7 +217,7 @@ else:
 when defined(js):
   var objectID = 0
   proc getObjectId(x: pointer): int =
-    asm """
+    {.emit: """
       if (typeof `x` == "object") {
         if ("_NimID" in `x`)
           `result` = `x`["_NimID"];
@@ -226,7 +226,7 @@ when defined(js):
           `x`["_NimID"] = `result`;
         }
       }
-    """
+    """.}
 
 proc hash*(x: pointer): Hash {.inline.} =
   ## Efficient `hash` overload.
@@ -247,7 +247,7 @@ proc hash*[T](x: ptr[T]): Hash {.inline.} =
 when defined(nimPreviewHashRef) or defined(nimdoc):
   proc hash*[T](x: ref[T]): Hash {.inline.} =
     ## Efficient `hash` overload.
-    ## 
+    ##
     ## .. important:: Use `-d:nimPreviewHashRef` to
     ##    enable hashing `ref`s. It is expected that this behavior
     ##    becomes the new default in upcoming versions.
@@ -319,16 +319,24 @@ proc murmurHash(x: openArray[byte]): Hash =
     h1: uint32
     i = 0
 
+
+  template impl =
+    var j = stepSize
+    while j > 0:
+      dec j
+      k1 = (k1 shl 8) or (ord(x[i+j])).uint32
+
   # body
   while i < n * stepSize:
     var k1: uint32
-    when defined(js) or defined(sparc) or defined(sparc64):
-      var j = stepSize
-      while j > 0:
-        dec j
-        k1 = (k1 shl 8) or (ord(x[i+j])).uint32
+
+    when nimvm:
+      impl()
     else:
-      k1 = cast[ptr uint32](unsafeAddr x[i])[]
+      when declared(copyMem):
+        copyMem(addr k1, addr x[i], 4)
+      else:
+        impl()
     inc i, stepSize
 
     k1 = imul(k1, c1)
@@ -360,16 +368,168 @@ proc murmurHash(x: openArray[byte]): Hash =
   return cast[Hash](h1)
 
 proc hashVmImpl(x: cstring, sPos, ePos: int): Hash =
-  doAssert false, "implementation override in compiler/vmops.nim"
+  raiseAssert "implementation override in compiler/vmops.nim"
 
 proc hashVmImpl(x: string, sPos, ePos: int): Hash =
-  doAssert false, "implementation override in compiler/vmops.nim"
+  raiseAssert "implementation override in compiler/vmops.nim"
 
 proc hashVmImplChar(x: openArray[char], sPos, ePos: int): Hash =
-  doAssert false, "implementation override in compiler/vmops.nim"
+  raiseAssert "implementation override in compiler/vmops.nim"
 
 proc hashVmImplByte(x: openArray[byte], sPos, ePos: int): Hash =
-  doAssert false, "implementation override in compiler/vmops.nim"
+  raiseAssert "implementation override in compiler/vmops.nim"
+
+const k0 = 0xc3a5c85c97cb3127u64 # Primes on (2^63, 2^64) for various uses
+const k1 = 0xb492b66fbe98f273u64
+const k2 = 0x9ae16a3b2f90404fu64
+
+proc load4e(s: openArray[byte], o=0): uint32 {.inline.} =
+  uint32(s[o + 3]) shl 24 or uint32(s[o + 2]) shl 16 or
+  uint32(s[o + 1]) shl  8 or uint32(s[o + 0])
+
+proc load8e(s: openArray[byte], o=0): uint64 {.inline.} =
+  uint64(s[o + 7]) shl 56 or uint64(s[o + 6]) shl 48 or
+  uint64(s[o + 5]) shl 40 or uint64(s[o + 4]) shl 32 or
+  uint64(s[o + 3]) shl 24 or uint64(s[o + 2]) shl 16 or
+  uint64(s[o + 1]) shl  8 or uint64(s[o + 0])
+
+proc load4(s: openArray[byte], o=0): uint32 {.inline.} =
+  when nimvm: result = load4e(s, o)
+  else:
+    when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
+    else: result = load4e(s, o)
+
+proc load8(s: openArray[byte], o=0): uint64 {.inline.} =
+  when nimvm: result = load8e(s, o)
+  else:
+    when declared copyMem: copyMem result.addr, s[o].addr, result.sizeof
+    else: result = load8e(s, o)
+
+proc lenU(s: openArray[byte]): uint64 {.inline.} = s.len.uint64
+
+proc shiftMix(v: uint64): uint64 {.inline.} = v xor (v shr 47)
+
+proc rotR(v: uint64; bits: cint): uint64 {.inline.} =
+  (v shr bits) or (v shl (64 - bits))
+
+proc len16(u: uint64; v: uint64; mul: uint64): uint64 {.inline.} =
+  var a = (u xor v)*mul
+  a = a xor (a shr 47)
+  var b = (v xor a)*mul
+  b = b xor (b shr 47)
+  b*mul
+
+proc len0_16(s: openArray[byte]): uint64 {.inline.} =
+  if s.len >= 8:
+    let mul = k2 + 2*s.lenU
+    let a   = load8(s) + k2
+    let b   = load8(s, s.len - 8)
+    let c   = rotR(b, 37)*mul + a
+    let d   = (rotR(a, 25) + b)*mul
+    len16 c, d, mul
+  elif s.len >= 4:
+    let mul = k2 + 2*s.lenU
+    let a   = load4(s).uint64
+    len16 s.lenU + (a shl 3), load4(s, s.len - 4), mul
+  elif s.len > 0:
+    let a = uint32(s[0])
+    let b = uint32(s[s.len shr 1])
+    let c = uint32(s[s.len - 1])
+    let y = a      + (b shl 8)
+    let z = s.lenU + (c shl 2)
+    shiftMix(y*k2 xor z*k0)*k2
+  else: k2      # s.len == 0
+
+proc len17_32(s: openArray[byte]): uint64 {.inline.} =
+  let mul = k2 + 2*s.lenU
+  let a = load8(s)*k1
+  let b = load8(s, 8)
+  let c = load8(s, s.len - 8)*mul
+  let d = load8(s, s.len - 16)*k2
+  len16 rotR(a + b, 43) + rotR(c, 30) + d, a + rotR(b + k2, 18) + c, mul
+
+proc len33_64(s: openArray[byte]): uint64 {.inline.} =
+  let mul = k2 + 2*s.lenU
+  let a = load8(s)*k2
+  let b = load8(s, 8)
+  let c = load8(s, s.len - 8)*mul
+  let d = load8(s, s.len - 16)*k2
+  let y = rotR(a + b, 43) + rotR(c, 30) + d
+  let z = len16(y, a + rotR(b + k2, 18) + c, mul)
+  let e = load8(s, 16)*mul
+  let f = load8(s, 24)
+  let g = (y + load8(s, s.len - 32))*mul
+  let h = (z + load8(s, s.len - 24))*mul
+  len16 rotR(e + f, 43) + rotR(g, 30) + h, e + rotR(f + a, 18) + g, mul
+
+type Pair = tuple[first, second: uint64]
+
+proc weakLen32withSeeds2(w, x, y, z, a, b: uint64): Pair {.inline.} =
+  var a = a + w
+  var b = rotR(b + a + z, 21)
+  let c = a
+  a += x
+  a += y
+  b += rotR(a, 44)
+  result[0] = a + z
+  result[1] = b + c
+
+proc weakLen32withSeeds(s: openArray[byte]; o: int; a,b: uint64): Pair {.inline.} =
+  weakLen32withSeeds2 load8(s, o     ), load8(s, o + 8),
+                      load8(s, o + 16), load8(s, o + 24), a, b
+
+proc hashFarm(s: openArray[byte]): uint64 {.inline.} =
+  if s.len <= 16: return len0_16(s)
+  if s.len <= 32: return len17_32(s)
+  if s.len <= 64: return len33_64(s)
+  const seed = 81u64 # not const to use input `h`
+  var
+    o = 0         # s[] ptr arith -> variable origin variable `o`
+    x = seed
+    y = seed*k1 + 113
+    z = shiftMix(y*k2 + 113)*k2
+    v, w: Pair
+  x = x*k2 + load8(s)
+  let eos    = ((s.len - 1) div 64)*64
+  let last64 = eos + ((s.len - 1) and 63) - 63
+  while true:
+    x = rotR(x + y + v[0] + load8(s, o+8), 37)*k1
+    y = rotR(y + v[1] + load8(s, o+48), 42)*k1
+    x = x xor w[1]
+    y += v[0] + load8(s, o+40)
+    z = rotR(z + w[0], 33)*k1
+    v = weakLen32withSeeds(s, o+0 , v[1]*k1, x + w[0])
+    w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
+    swap z, x
+    inc o, 64
+    if o == eos: break
+  let mul = k1 + ((z and 0xff) shl 1)
+  o = last64
+  w[0] += (s.lenU - 1) and 63
+  v[0] += w[0]
+  w[0] += v[0]
+  x = rotR(x + y + v[0] + load8(s, o+8), 37)*mul
+  y = rotR(y + v[1] + load8(s, o+48), 42)*mul
+  x = x xor w[1]*9
+  y += v[0]*9 + load8(s, o+40)
+  z = rotR(z + w[0], 33)*mul
+  v = weakLen32withSeeds(s, o+0 , v[1]*mul, x + w[0])
+  w = weakLen32withSeeds(s, o+32, z + w[1], y + load8(s, o+16))
+  swap z, x
+  len16 len16(v[0],w[0],mul) + shiftMix(y)*k0 + z, len16(v[1],w[1],mul) + x, mul
+
+template jsNoInt64: untyped =
+  when defined js:
+    when compiles(compileOption("jsbigint64")):
+      when not compileOption("jsbigint64"): true
+      else: false
+    else: false
+  else: false
+const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false)
+
+template maybeFailJS_Number =
+  when jsNoInt64() and not defined(nimStringHash2):
+    {.error: "Must use `-d:nimStringHash2` when using `--jsbigint64:off`".}
 
 proc hash*(x: string): Hash =
   ## Efficient hashing of strings.
@@ -379,16 +539,13 @@ proc hash*(x: string): Hash =
   ## * `hashIgnoreCase <#hashIgnoreCase,string>`_
   runnableExamples:
     doAssert hash("abracadabra") != hash("AbracadabrA")
-
-  when not defined(nimToOpenArrayCString):
-    result = 0
-    for c in x:
-      result = result !& ord(c)
-    result = !$result
+  maybeFailJS_Number()
+  when not sHash2:
+    result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
   else:
-    when nimvm:
-      result = hashVmImpl(x, 0, high(x))
-    else:
+    #when nimvm:
+    #  result = hashVmImpl(x, 0, high(x))
+    when true:
       result = murmurHash(toOpenArrayByte(x, 0, high(x)))
 
 proc hash*(x: cstring): Hash =
@@ -398,22 +555,22 @@ proc hash*(x: cstring): Hash =
     doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA")
     doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA")
 
-  when not defined(nimToOpenArrayCString):
-    result = 0
-    var i = 0
-    while x[i] != '\0':
-      result = result !& ord(x[i])
-      inc i
-    result = !$result
-  else:
-    when nimvm:
-      hashVmImpl(x, 0, high(x))
+  maybeFailJS_Number()
+  when not sHash2:
+    when defined js:
+      let xx = $x
+      result = cast[Hash](hashFarm(toOpenArrayByte(xx, 0, xx.high)))
     else:
-      when not defined(js) and defined(nimToOpenArrayCString):
-        murmurHash(toOpenArrayByte(x, 0, x.high))
+      result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
+  else:
+    #when nimvm:
+    #  result = hashVmImpl(x, 0, high(x))
+    when true:
+      when not defined(js):
+        result = murmurHash(toOpenArrayByte(x, 0, x.high))
       else:
         let xx = $x
-        murmurHash(toOpenArrayByte(xx, 0, high(xx)))
+        result = murmurHash(toOpenArrayByte(xx, 0, high(xx)))
 
 proc hash*(sBuf: string, sPos, ePos: int): Hash =
   ## Efficient hashing of a string buffer, from starting
@@ -424,11 +581,9 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash =
     var a = "abracadabra"
     doAssert hash(a, 0, 3) == hash(a, 7, 10)
 
-  when not defined(nimToOpenArrayCString):
-    result = 0
-    for i in sPos..ePos:
-      result = result !& ord(sBuf[i])
-    result = !$result
+  maybeFailJS_Number()
+  when not sHash2:
+    result = cast[Hash](hashFarm(toOpenArrayByte(sBuf, sPos, ePos)))
   else:
     murmurHash(toOpenArrayByte(sBuf, sPos, ePos))
 
@@ -521,7 +676,7 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash =
     h = h !& ord(c)
   result = !$h
 
-proc hash*[T: tuple | object | proc](x: T): Hash =
+proc hash*[T: tuple | object | proc | iterator {.closure.}](x: T): Hash =
   ## Efficient `hash` overload.
   runnableExamples:
     # for `tuple|object`, `hash` must be defined for each component of `x`.
@@ -554,8 +709,9 @@ proc hash*[T: tuple | object | proc](x: T): Hash =
   when T is "closure":
     result = hash((rawProc(x), rawEnv(x)))
   elif T is (proc):
-    result = hash(pointer(x))
+    result = hash(cast[pointer](x))
   else:
+    result = 0
     for f in fields(x):
       result = result !& hash(f)
     result = !$result
@@ -564,13 +720,20 @@ proc hash*[A](x: openArray[A]): Hash =
   ## Efficient hashing of arrays and sequences.
   ## There must be a `hash` proc defined for the element type `A`.
   when A is byte:
-    result = murmurHash(x)
+    when not sHash2:
+      result = cast[Hash](hashFarm(x))
+    else:
+      result = murmurHash(x)
   elif A is char:
-    when nimvm:
-      result = hashVmImplChar(x, 0, x.high)
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArrayByte(x, 0, x.high)))
     else:
-      result = murmurHash(toOpenArrayByte(x, 0, x.high))
+      #when nimvm:
+      #  result = hashVmImplChar(x, 0, x.high)
+      when true:
+        result = murmurHash(toOpenArrayByte(x, 0, x.high))
   else:
+    result = 0
     for a in x:
       result = result !& hash(a)
     result = !$result
@@ -584,17 +747,24 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
   runnableExamples:
     let a = [1, 2, 5, 1, 2, 6]
     doAssert hash(a, 0, 1) == hash(a, 3, 4)
-
   when A is byte:
-    when nimvm:
-      result = hashVmImplByte(aBuf, sPos, ePos)
+    maybeFailJS_Number()
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArray(aBuf, sPos, ePos)))
     else:
-      result = murmurHash(toOpenArray(aBuf, sPos, ePos))
+      #when nimvm:
+      #  result = hashVmImplByte(aBuf, sPos, ePos)
+      when true:
+        result = murmurHash(toOpenArray(aBuf, sPos, ePos))
   elif A is char:
-    when nimvm:
-      result = hashVmImplChar(aBuf, sPos, ePos)
+    maybeFailJS_Number()
+    when not sHash2:
+      result = cast[Hash](hashFarm(toOpenArrayByte(aBuf, sPos, ePos)))
     else:
-      result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos))
+      #when nimvm:
+      #  result = hashVmImplChar(aBuf, sPos, ePos)
+      when true:
+        result = murmurHash(toOpenArrayByte(aBuf, sPos, ePos))
   else:
     for i in sPos .. ePos:
       result = result !& hash(aBuf[i])
@@ -603,6 +773,7 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash =
 proc hash*[A](x: set[A]): Hash =
   ## Efficient hashing of sets.
   ## There must be a `hash` proc defined for the element type `A`.
+  result = 0
   for it in items(x):
     result = result !& hash(it)
   result = !$result
diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim
index 89eb24bb9..fafa72463 100644
--- a/lib/pure/htmlgen.nim
+++ b/lib/pure/htmlgen.nim
@@ -8,7 +8,7 @@
 #
 
 ## Do yourself a favor and import the module
-## as `from htmlgen import nil` and then fully qualify the macros.
+## as `from std/htmlgen import nil` and then fully qualify the macros.
 ##
 ## *Note*: The Karax project (`nimble install karax`) has a better
 ## way to achieve the same, see https://github.com/pragmagic/karax/blob/master/tests/nativehtmlgen.nim
@@ -30,17 +30,18 @@
 ## Examples
 ## ========
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   var nim = "Nim"
 ##   echo h1(a(href="https://nim-lang.org", nim))
+##   ```
 ##
-## Writes the string::
+## Writes the string:
 ##
-##   <h1><a href="https://nim-lang.org">Nim</a></h1>
+##     <h1><a href="https://nim-lang.org">Nim</a></h1>
 ##
 
 import
-  macros, strutils
+  std/[macros, strutils]
 
 const
   coreAttr* = " accesskey class contenteditable dir hidden id lang " &
diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim
index 6b1300f11..62919546f 100644
--- a/lib/pure/htmlparser.nim
+++ b/lib/pure/htmlparser.nim
@@ -12,10 +12,9 @@
 ##
 ## It can be used to parse a wild HTML document and output it as valid XHTML
 ## document (well, if you are lucky):
-##
-## .. code-block:: Nim
-##
+##   ```Nim
 ##   echo loadHtml("mydirty.html")
+##   ```
 ##
 ## Every tag in the resulting tree is in lower case.
 ##
@@ -29,9 +28,7 @@
 ## and write back the modified version. In this case we look for hyperlinks
 ## ending with the extension `.rst` and convert them to `.html`.
 ##
-## .. code-block:: Nim
-##     :test:
-##
+##   ```Nim test
 ##   import std/htmlparser
 ##   import std/xmltree  # To use '$' for XmlNode
 ##   import std/strtabs  # To access XmlAttributes
@@ -48,8 +45,11 @@
 ##           a.attrs["href"] = dir / filename & ".html"
 ##
 ##     writeFile("output.html", $html)
+##   ```
+
+{.deprecated: "use `nimble install htmlparser` and import `pkg/htmlparser` instead".}
 
-import strutils, streams, parsexml, xmltree, unicode, strtabs
+import std/[strutils, streams, parsexml, xmltree, unicode, strtabs]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -394,10 +394,10 @@ proc entityToRune*(entity: string): Rune =
     case entity[1]
     of '0'..'9':
       try: runeValue = parseInt(entity[1..^1])
-      except: discard
+      except ValueError: discard
     of 'x', 'X': # not case sensitive here
       try: runeValue = parseHexInt(entity[2..^1])
-      except: discard
+      except ValueError: discard
     else: discard # other entities are not defined with prefix `#`
     if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values
     return Rune(runeValue)
@@ -2064,7 +2064,7 @@ proc loadHtml*(path: string): XmlNode =
   result = loadHtml(path, errors)
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
 
   var errors: seq[string] = @[]
   var x = loadHtml(paramStr(1), errors)
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 5c7f538b5..08ea99627 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -10,34 +10,38 @@
 ## This module implements a simple HTTP client that can be used to retrieve
 ## webpages and other data.
 ##
+## .. warning:: Validate untrusted inputs: URI parsers and getters are not detecting malicious URIs.
+##
 ## Retrieving a website
 ## ====================
 ##
 ## This example uses HTTP GET to retrieve
 ## `http://google.com`:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/httpclient
 ##   var client = newHttpClient()
 ##   try:
 ##     echo client.getContent("http://google.com")
 ##   finally:
 ##     client.close()
+##   ```
 ##
 ## The same action can also be performed asynchronously, simply use the
 ## `AsyncHttpClient`:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/[asyncdispatch, httpclient]
 ##
 ##   proc asyncProc(): Future[string] {.async.} =
 ##     var client = newAsyncHttpClient()
 ##     try:
-##       return await client.getContent("http://example.com")
+##       return await client.getContent("http://google.com")
 ##     finally:
 ##       client.close()
 ##
 ##   echo waitFor asyncProc()
+##   ```
 ##
 ## The functionality implemented by `HttpClient` and `AsyncHttpClient`
 ## is the same, so you can use whichever one suits you best in the examples
@@ -46,6 +50,10 @@
 ## **Note:** You need to run asynchronous examples in an async proc
 ## otherwise you will get an `Undeclared identifier: 'await'` error.
 ##
+## **Note:** An asynchronous client instance can only deal with one
+## request at a time. To send multiple requests in parallel, use
+## multiple client instances.
+##
 ## Using HTTP POST
 ## ===============
 ##
@@ -53,7 +61,7 @@
 ## uses `multipart/form-data` as the `Content-Type` to send the HTML to be
 ## validated to the server.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   var client = newHttpClient()
 ##   var data = newMultipartData()
 ##   data["output"] = "soap12"
@@ -63,13 +71,14 @@
 ##     echo client.postContent("http://validator.w3.org/check", multipart=data)
 ##   finally:
 ##     client.close()
+##   ```
 ##
 ## To stream files from disk when performing the request, use `addFiles`.
 ##
 ## **Note:** This will allocate a new `Mimetypes` database every time you call
 ## it, you can pass your own via the `mimeDb` parameter to avoid this.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   let mimes = newMimetypes()
 ##   var client = newHttpClient()
 ##   var data = newMultipartData()
@@ -78,12 +87,13 @@
 ##     echo client.postContent("http://validator.w3.org/check", multipart=data)
 ##   finally:
 ##     client.close()
+##   ```
 ##
 ## You can also make post requests with custom headers.
 ## This example sets `Content-Type` to `application/json`
 ## and uses a json object for the body
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/[httpclient, json]
 ##
 ##   let client = newHttpClient()
@@ -96,6 +106,7 @@
 ##     echo response.status
 ##   finally:
 ##     client.close()
+##   ```
 ##
 ## Progress reporting
 ## ==================
@@ -104,27 +115,29 @@
 ## This callback will be executed every second with information about the
 ## progress of the HTTP request.
 ##
-## .. code-block:: Nim
-##    import std/[asyncdispatch, httpclient]
+##   ```Nim
+##   import std/[asyncdispatch, httpclient]
 ##
-##    proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
-##      echo("Downloaded ", progress, " of ", total)
-##      echo("Current rate: ", speed div 1000, "kb/s")
+##   proc onProgressChanged(total, progress, speed: BiggestInt) {.async.} =
+##     echo("Downloaded ", progress, " of ", total)
+##     echo("Current rate: ", speed div 1000, "kb/s")
 ##
-##    proc asyncProc() {.async.} =
-##      var client = newAsyncHttpClient()
-##      client.onProgressChanged = onProgressChanged
-##      try:
-##        discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
-##      finally:
-##        client.close()
+##   proc asyncProc() {.async.} =
+##     var client = newAsyncHttpClient()
+##     client.onProgressChanged = onProgressChanged
+##     try:
+##       discard await client.getContent("http://speedtest-ams2.digitalocean.com/100mb.test")
+##     finally:
+##       client.close()
 ##
-##    waitFor asyncProc()
+##   waitFor asyncProc()
+##   ```
 ##
 ## If you would like to remove the callback simply set it to `nil`.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   client.onProgressChanged = nil
+##   ```
 ##
 ## .. warning:: The `total` reported by httpclient may be 0 in some cases.
 ##
@@ -146,9 +159,10 @@
 ##
 ## Example of setting SSL verification parameters in a new client:
 ##
-## .. code-block:: Nim
-##    import httpclient
-##    var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
+##   ```Nim
+##   import httpclient
+##   var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer))
+##   ```
 ##
 ## There are three options for verify mode:
 ##
@@ -177,10 +191,11 @@
 ##
 ## Here is how to set a timeout when creating an `HttpClient` instance:
 ##
-## .. code-block:: Nim
-##    import std/httpclient
+##   ```Nim
+##   import std/httpclient
 ##
-##    let client = newHttpClient(timeout = 42)
+##   let client = newHttpClient(timeout = 42)
+##   ```
 ##
 ## Proxy
 ## =====
@@ -191,28 +206,39 @@
 ##
 ## Some examples on how to configure a Proxy for `HttpClient`:
 ##
-## .. code-block:: Nim
-##    import std/httpclient
+##   ```Nim
+##   import std/httpclient
+##
+##   let myProxy = newProxy("http://myproxy.network")
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
+##
+## Use proxies with basic authentication:
 ##
-##    let myProxy = newProxy("http://myproxy.network")
-##    let client = newHttpClient(proxy = myProxy)
+##   ```Nim
+##   import std/httpclient
+##
+##   let myProxy = newProxy("http://myproxy.network", auth="user:password")
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
 ##
 ## Get Proxy URL from environment variables:
 ##
-## .. code-block:: Nim
-##    import std/httpclient
+##   ```Nim
+##   import std/httpclient
 ##
-##    var url = ""
-##    try:
-##      if existsEnv("http_proxy"):
-##        url = getEnv("http_proxy")
-##      elif existsEnv("https_proxy"):
-##        url = getEnv("https_proxy")
-##    except ValueError:
-##      echo "Unable to parse proxy from environment variables."
+##   var url = ""
+##   try:
+##     if existsEnv("http_proxy"):
+##       url = getEnv("http_proxy")
+##     elif existsEnv("https_proxy"):
+##       url = getEnv("https_proxy")
+##   except ValueError:
+##     echo "Unable to parse proxy from environment variables."
 ##
-##    let myProxy = newProxy(url = url)
-##    let client = newHttpClient(proxy = myProxy)
+##   let myProxy = newProxy(url = url)
+##   let client = newHttpClient(proxy = myProxy)
+##   ```
 ##
 ## Redirects
 ## =========
@@ -223,10 +249,11 @@
 ##
 ## Here you can see an example about how to set the `maxRedirects` of `HttpClient`:
 ##
-## .. code-block:: Nim
-##    import std/httpclient
+##   ```Nim
+##   import std/httpclient
 ##
-##    let client = newHttpClient(maxRedirects = 0)
+##   let client = newHttpClient(maxRedirects = 0)
+##   ```
 ##
 
 import std/private/since
@@ -278,7 +305,7 @@ proc contentLength*(response: Response | AsyncResponse): int =
   ##
   ## A `ValueError` exception will be raised if the value is not an integer.
   ## If the Content-Length header is not set in the response, ContentLength is set to the value -1.
-  var contentLengthHeader = response.headers.getOrDefault("Content-Length", @["-1"])
+  var contentLengthHeader = response.headers.getOrDefault("Content-Length", HttpHeaderValues(@["-1"]))
   result = contentLengthHeader.parseInt()
 
 proc lastModified*(response: Response | AsyncResponse): DateTime =
@@ -332,7 +359,7 @@ type
                                         ## and `postContent` proc,
                                         ## when the server returns an error
 
-const defUserAgent* = "Nim httpclient/" & NimVersion
+const defUserAgent* = "Nim-httpclient/" & NimVersion
 
 proc httpError(msg: string) =
   var e: ref ProtocolError
@@ -415,8 +442,9 @@ proc add*(p: MultipartData, xs: MultipartEntries): MultipartData
   ## Add a list of multipart entries to the multipart data `p`. All values are
   ## added without a filename and without a content type.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data.add({"action": "login", "format": "json"})
+  ##   ```
   for name, content in xs.items:
     p.add(name, content)
   result = p
@@ -425,8 +453,9 @@ proc newMultipartData*(xs: MultipartEntries): MultipartData =
   ## Create a new multipart data object and fill it with the entries `xs`
   ## directly.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   var data = newMultipartData({"action": "login", "format": "json"})
+  ##   ```
   result = MultipartData()
   for entry in xs:
     result.add(entry.name, entry.content)
@@ -441,8 +470,9 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]],
   ## Raises an `IOError` if the file cannot be opened or reading fails. To
   ## manually specify file content, filename and MIME type, use `[]=` instead.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data.addFiles({"uploaded_file": "public/test.html"})
+  ##   ```
   for name, file in xs.items:
     var contentType: string
     let (_, fName, ext) = splitFile(file)
@@ -456,8 +486,9 @@ proc `[]=`*(p: MultipartData, name, content: string) {.inline.} =
   ## Add a multipart entry to the multipart data `p`. The value is added
   ## without a filename and without a content type.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data["username"] = "NimUser"
+  ##   ```
   p.add(name, content)
 
 proc `[]=`*(p: MultipartData, name: string,
@@ -465,9 +496,10 @@ proc `[]=`*(p: MultipartData, name: string,
   ## Add a file to the multipart data `p`, specifying filename, contentType
   ## and content manually.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   data["uploaded_file"] = ("test.html", "text/html",
   ##     "<html><head></head><body><p>test</p></body></html>")
+  ##   ```
   p.add(name, file.content, file.name, file.contentType, useStream = false)
 
 proc getBoundary(p: MultipartData): string =
@@ -674,15 +706,15 @@ proc close*(client: HttpClient | AsyncHttpClient) =
     client.connected = false
 
 proc getSocket*(client: HttpClient): Socket {.inline.} =
-  ## Get network socket, useful if you want to find out more details about the connection
+  ## Get network socket, useful if you want to find out more details about the connection.
   ##
-  ## this example shows info about local and remote endpoints
+  ## This example shows info about local and remote endpoints:
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   if client.connected:
   ##     echo client.getSocket.getLocalAddr
   ##     echo client.getSocket.getPeerAddr
-  ##
+  ##   ```
   return client.socket
 
 proc getSocket*(client: AsyncHttpClient): AsyncSocket {.inline.} =
@@ -824,6 +856,7 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
   var parsedStatus = false
   var linei = 0
   var fullyRead = false
+  var lastHeaderName = ""
   var line = ""
   result.headers = newHttpHeaders()
   while true:
@@ -858,16 +891,29 @@ proc parseResponse(client: HttpClient | AsyncHttpClient,
       parsedStatus = true
     else:
       # Parse headers
-      var name = ""
-      var le = parseUntil(line, name, ':', linei)
-      if le <= 0: httpError("invalid headers")
-      inc(linei, le)
-      if line[linei] != ':': httpError("invalid headers")
-      inc(linei) # Skip :
-
-      result.headers.add(name, line[linei .. ^1].strip())
-      if result.headers.len > headerLimit:
-        httpError("too many headers")
+      # There's at least one char because empty lines are handled above (with client.close)
+      if line[0] in {' ', '\t'}:
+        # Check if it's a multiline header value, if so, append to the header we're currently parsing
+        # This works because a line with a header must start with the header name without any leading space
+        # See https://datatracker.ietf.org/doc/html/rfc7230, section 3.2 and 3.2.4
+        # Multiline headers are deprecated in the spec, but it's better to parse them than crash
+        if lastHeaderName == "":
+          # Some extra unparsable lines in the HTTP output - we ignore them
+          discard
+        else:
+          result.headers.table[result.headers.toCaseInsensitive(lastHeaderName)][^1].add "\n" & line
+      else:
+        var name = ""
+        var le = parseUntil(line, name, ':', linei)
+        if le <= 0: httpError("Invalid headers - received empty header name")
+        if line.len == le: httpError("Invalid headers - no colon after header name")
+        inc(linei, le) # Skip the parsed header name
+        inc(linei) # Skip :
+        # If we want to be HTTP spec compliant later, error on linei == line.len (for empty header value)
+        lastHeaderName = name # Remember the header name for the possible multi-line header
+        result.headers.add(name, line[linei .. ^1].strip())
+        if result.headers.len > headerLimit:
+          httpError("too many headers")
 
   if not fullyRead:
     httpError("Connection was closed before full request has been made")
@@ -1186,7 +1232,7 @@ proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync
   ## A `HttpRequestError` will be raised if the server responds with a
   ## client error (status code 4xx) or a server error (status code 5xx).
   if resp.code.is4xx or resp.code.is5xx:
-    raise newException(HttpRequestError, resp.status)
+    raise newException(HttpRequestError, resp.status.move)
   else:
     return await resp.bodyStream.readAll()
 
diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim
index 7e995ac46..5ccab379c 100644
--- a/lib/pure/httpcore.nim
+++ b/lib/pure/httpcore.nim
@@ -12,7 +12,7 @@
 ##
 ## Unstable API.
 import std/private/since
-import tables, strutils, parseutils
+import std/[tables, strutils, parseutils]
 
 type
   HttpHeaders* = ref object
@@ -126,23 +126,20 @@ func toTitleCase(s: string): string =
     result[i] = if upper: toUpperAscii(s[i]) else: toLowerAscii(s[i])
     upper = s[i] == '-'
 
-func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} =
+func toCaseInsensitive*(headers: HttpHeaders, s: string): string {.inline.} =
+  ## For internal usage only. Do not use.
   return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s)
 
 func newHttpHeaders*(titleCase=false): HttpHeaders =
   ## Returns a new `HttpHeaders` object. if `titleCase` is set to true,
   ## headers are passed to the server in title case (e.g. "Content-Length")
-  new result
-  result.table = newTable[string, seq[string]]()
-  result.isTitleCase = titleCase
+  result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase)
 
 func newHttpHeaders*(keyValuePairs:
     openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders =
   ## Returns a new `HttpHeaders` object from an array. if `titleCase` is set to true,
   ## headers are passed to the server in title case (e.g. "Content-Length")
-  new result
-  result.table = newTable[string, seq[string]]()
-  result.isTitleCase = titleCase
+  result = HttpHeaders(table: newTable[string, seq[string]](), isTitleCase: titleCase)
 
   for pair in keyValuePairs:
     let key = result.toCaseInsensitive(pair.key)
@@ -167,7 +164,8 @@ func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues =
   ## To access multiple values of a key, use the overloaded `[]` below or
   ## to get all of them access the `table` field directly.
   {.cast(noSideEffect).}:
-    return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues
+    let tmp = headers.table[headers.toCaseInsensitive(key)]
+    return HttpHeaderValues(tmp)
 
 converter toString*(values: HttpHeaderValues): string =
   return seq[string](values)[0]
@@ -237,10 +235,9 @@ func parseList(line: string, list: var seq[string], start: int): int =
   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)
+    list.add(move current)  # implicit current.setLen(0)
     if start+i < line.len and line[start + i] == ',':
       i.inc # Skip ,
-    current.setLen(0)
 
 func parseHeader*(line: string): tuple[key: string, value: seq[string]] =
   ## Parses a single raw header HTTP line into key value pairs.
@@ -352,20 +349,20 @@ func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} =
   runnableExamples:
     doAssert is1xx(HttpCode(103))
 
-  code.int in {100 .. 199}
+  code.int in 100 .. 199
 
 func is2xx*(code: HttpCode): bool {.inline.} =
   ## Determines whether `code` is a 2xx HTTP status code.
-  code.int in {200 .. 299}
+  code.int in 200 .. 299
 
 func is3xx*(code: HttpCode): bool {.inline.} =
   ## Determines whether `code` is a 3xx HTTP status code.
-  code.int in {300 .. 399}
+  code.int in 300 .. 399
 
 func is4xx*(code: HttpCode): bool {.inline.} =
   ## Determines whether `code` is a 4xx HTTP status code.
-  code.int in {400 .. 499}
+  code.int in 400 .. 499
 
 func is5xx*(code: HttpCode): bool {.inline.} =
   ## Determines whether `code` is a 5xx HTTP status code.
-  code.int in {500 .. 599}
+  code.int in 500 .. 599
diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim
deleted file mode 100644
index b7200a8e2..000000000
--- a/lib/pure/includes/osenv.nim
+++ /dev/null
@@ -1,207 +0,0 @@
-# Include file that implements 'getEnv' and friends. Do not import it!
-
-when not declared(os):
-  {.error: "This is an include file for os.nim!".}
-
-when not defined(nimscript):
-  when defined(nodejs):
-    proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
-      var ret = default.cstring
-      let key2 = key.cstring
-      {.emit: "const value = process.env[`key2`];".}
-      {.emit: "if (value !== undefined) { `ret` = value };".}
-      result = $ret
-
-    proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
-      var key2 = key.cstring
-      var ret: bool
-      {.emit: "`ret` = `key2` in process.env;".}
-      result = ret
-
-    proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
-      var key2 = key.cstring
-      var val2 = val.cstring
-      {.emit: "process.env[`key2`] = `val2`;".}
-
-    proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
-      var key2 = key.cstring
-      {.emit: "delete process.env[`key2`];".}
-
-    iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
-      var num: int
-      var keys: RootObj
-      {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".}
-      for i in 0..<num:
-        var key, value: cstring
-        {.emit: "`key` = `keys`[`i`]; `value` = process.env[`key`];".}
-        yield ($key, $value)
-
-  # commented because it must keep working with js+VM
-  # elif defined(js):
-  #   {.error: "requires -d:nodejs".}
-
-  else:
-
-    when defined(windows):
-      proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".}
-      from std/private/win_setenv import setEnvImpl
-      proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv",
-          header: "<stdlib.h>".}
-      proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString)
-    else:
-      proc c_getenv(env: cstring): cstring {.
-        importc: "getenv", header: "<stdlib.h>".}
-      proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "<stdlib.h>".}
-      proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "<stdlib.h>".}
-      proc getEnvImpl(env: cstring): cstring = c_getenv(env)
-
-    proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} =
-      ## Returns the value of the `environment variable`:idx: named `key`.
-      ##
-      ## If the variable does not exist, `""` is returned. To distinguish
-      ## whether a variable exists or it's value is just `""`, call
-      ## `existsEnv(key) proc`_.
-      ##
-      ## See also:
-      ## * `existsEnv proc`_
-      ## * `putEnv proc`_
-      ## * `delEnv proc`_
-      ## * `envPairs iterator`_
-      runnableExamples:
-        assert getEnv("unknownEnv") == ""
-        assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
-
-      let env = getEnvImpl(key)
-      if env == nil: return default
-      result = $env
-
-    proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
-      ## Checks whether the environment variable named `key` exists.
-      ## Returns true if it exists, false otherwise.
-      ##
-      ## See also:
-      ## * `getEnv proc`_
-      ## * `putEnv proc`_
-      ## * `delEnv proc`_
-      ## * `envPairs iterator`_
-      runnableExamples:
-        assert not existsEnv("unknownEnv")
-
-      return getEnvImpl(key) != nil
-
-    proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
-      ## Sets the value of the `environment variable`:idx: named `key` to `val`.
-      ## If an error occurs, `OSError` is raised.
-      ##
-      ## See also:
-      ## * `getEnv proc`_
-      ## * `existsEnv proc`_
-      ## * `delEnv proc`_
-      ## * `envPairs iterator`_
-      when defined(windows):
-        if key.len == 0 or '=' in key:
-          raise newException(OSError, "invalid key, got: " & $(key, val))
-        if setEnvImpl(key, val, 1'i32) != 0'i32:
-          raiseOSError(osLastError(), $(key, val))
-      else:
-        if c_setenv(key, val, 1'i32) != 0'i32:
-          raiseOSError(osLastError(), $(key, val))
-
-    proc delEnv*(key: string) {.tags: [WriteEnvEffect].} =
-      ## Deletes the `environment variable`:idx: named `key`.
-      ## If an error occurs, `OSError` is raised.
-      ##
-      ## See also:ven
-      ## * `getEnv proc`_
-      ## * `existsEnv proc`_
-      ## * `putEnv proc`_
-      ## * `envPairs iterator`_
-      template bail = raiseOSError(osLastError(), key)
-      when defined(windows):
-        #[ 
-        # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160
-        > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string
-        note that nil is not legal
-        ]#
-        if key.len == 0 or '=' in key:
-          raise newException(OSError, "invalid key, got: " & key)
-        let envToDel = key & "="
-        if c_putenv(cstring envToDel) != 0'i32: bail
-      else:
-        if c_unsetenv(key) != 0'i32: bail
-
-    when defined(windows):
-      when useWinUnicode:
-        when defined(cpp):
-          proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)",
-              header: "<string.h>".}
-        else:
-          proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr",
-              header: "<string.h>".}
-      else:
-        proc strEnd(cstr: cstring, c = 0'i32): cstring {.importc: "strchr",
-            header: "<string.h>".}
-    elif defined(macosx) and not defined(ios) and not defined(emscripten):
-      # From the manual:
-      # Shared libraries and bundles don't have direct access to environ,
-      # which is only available to the loader ld(1) when a complete program
-      # is being linked.
-      # The environment routines can still be used, but if direct access to
-      # environ is needed, the _NSGetEnviron() routine, defined in
-      # <crt_externs.h>, can be used to retrieve the address of environ
-      # at runtime.
-      proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron",
-          header: "<crt_externs.h>".}
-    elif defined(haiku):
-      var gEnv {.importc: "environ", header: "<stdlib.h>".}: cstringArray
-    else:
-      var gEnv {.importc: "environ".}: cstringArray
-
-    iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
-      when defined(windows):
-        block:
-          template impl(get_fun, typ, size, zero, free_fun) =
-            let env = get_fun()
-            var e = env
-            if e == nil: break
-            while true:
-              let eend = strEnd(e)
-              let kv = $e
-              let p = find(kv, '=')
-              yield (substr(kv, 0, p-1), substr(kv, p+1))
-              e = cast[typ](cast[ByteAddress](eend)+size)
-              if typeof(zero)(eend[1]) == zero: break
-            discard free_fun(env)
-          when useWinUnicode:
-            impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW)
-          else:
-            impl(getEnvironmentStringsA, cstring, 1, '\0', freeEnvironmentStringsA)
-      else:
-        var i = 0
-        when defined(macosx) and not defined(ios) and not defined(emscripten):
-          var gEnv = NSGetEnviron()[]
-        while gEnv[i] != nil:
-          let kv = $gEnv[i]
-          inc(i)
-          let p = find(kv, '=')
-          yield (substr(kv, 0, p-1), substr(kv, p+1))
-
-proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops
-
-iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
-  ## Iterate over all `environments variables`:idx:.
-  ##
-  ## In the first component of the tuple is the name of the current variable stored,
-  ## in the second its value.
-  ##
-  ## Works in native backends, nodejs and vm, like the following APIs:
-  ## * `getEnv proc`_
-  ## * `existsEnv proc`_
-  ## * `putEnv proc`_
-  ## * `delEnv proc`_
-  when nimvm:
-    for ai in envPairsImplSeq(): yield ai
-  else:
-    when defined(nimscript): discard
-    else:
-      for ai in envPairsImpl(): yield ai
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
deleted file mode 100644
index a6eba84ba..000000000
--- a/lib/pure/includes/oserr.nim
+++ /dev/null
@@ -1,119 +0,0 @@
-# Include file that implements 'osErrorMsg' and friends. Do not import it!
-
-when not declared(os):
-  {.error: "This is an include file for os.nim!".}
-
-when not defined(nimscript):
-  var errno {.importc, header: "<errno.h>".}: cint
-
-  proc c_strerror(errnum: cint): cstring {.
-    importc: "strerror", header: "<string.h>".}
-
-  when defined(windows):
-    import winlean
-
-proc `==`*(err1, err2: OSErrorCode): bool {.borrow.}
-proc `$`*(err: OSErrorCode): string {.borrow.}
-
-proc osErrorMsg*(errorCode: OSErrorCode): string =
-  ## Converts an OS error code into a human readable string.
-  ##
-  ## The error code can be retrieved using the `osLastError proc`_.
-  ##
-  ## If conversion fails, or `errorCode` is `0` then `""` will be
-  ## returned.
-  ##
-  ## On Windows, the `-d:useWinAnsi` compilation flag can be used to
-  ## make this procedure use the non-unicode Win API calls to retrieve the
-  ## message.
-  ##
-  ## See also:
-  ## * `raiseOSError proc`_
-  ## * `osLastError proc`_
-  runnableExamples:
-    when defined(linux):
-      assert osErrorMsg(OSErrorCode(0)) == ""
-      assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted"
-      assert osErrorMsg(OSErrorCode(2)) == "No such file or directory"
-
-  result = ""
-  when defined(nimscript):
-    discard
-  elif defined(windows):
-    if errorCode != OSErrorCode(0'i32):
-      when useWinUnicode:
-        var msgbuf: WideCString
-        if formatMessageW(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(cast[pointer](msgbuf))
-      else:
-        var msgbuf: cstring
-        if formatMessageA(0x00000100 or 0x00001000 or 0x00000200,
-                        nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32:
-          result = $msgbuf
-          if msgbuf != nil: localFree(msgbuf)
-  else:
-    if errorCode != OSErrorCode(0'i32):
-      result = $c_strerror(errorCode.int32)
-
-proc newOSError*(
-  errorCode: OSErrorCode, additionalInfo = ""
-): owned(ref OSError) {.noinline.} =
-  ## Creates a new `OSError exception <system.html#OSError>`_.
-  ##
-  ## The `errorCode` will determine the
-  ## message, `osErrorMsg proc`_ will be used
-  ## to get this message.
-  ##
-  ## The error code can be retrieved using the `osLastError proc`_.
-  ##
-  ## If the error code is `0` or an error message could not be retrieved,
-  ## the message `unknown OS error` will be used.
-  ##
-  ## See also:
-  ## * `osErrorMsg proc`_
-  ## * `osLastError proc`_
-  var e: owned(ref OSError); new(e)
-  e.errorCode = errorCode.int32
-  e.msg = osErrorMsg(errorCode)
-  if additionalInfo.len > 0:
-    if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n'
-    e.msg.add "Additional info: "
-    e.msg.add additionalInfo
-      # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs.
-  if e.msg == "":
-    e.msg = "unknown OS error"
-  return e
-
-proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} =
-  ## Raises an `OSError exception <system.html#OSError>`_.
-  ##
-  ## Read the description of the `newOSError proc`_ to learn
-  ## how the exception object is created.
-  raise newOSError(errorCode, additionalInfo)
-
-{.push stackTrace:off.}
-proc osLastError*(): OSErrorCode {.sideEffect.} =
-  ## Retrieves the last operating system error code.
-  ##
-  ## This procedure is useful in the event when an OS call fails. In that case
-  ## this procedure will return the error code describing the reason why the
-  ## OS call failed. The `OSErrorMsg` procedure can then be used to convert
-  ## this code into a string.
-  ##
-  ## .. warning:: The behaviour of this procedure varies between Windows and POSIX systems.
-  ##   On Windows some OS calls can reset the error code to `0` causing this
-  ##   procedure to return `0`. It is therefore advised to call this procedure
-  ##   immediately after an OS call fails. On POSIX systems this is not a problem.
-  ##
-  ## See also:
-  ## * `osErrorMsg proc`_
-  ## * `raiseOSError proc`_
-  when defined(nimscript):
-    discard
-  elif defined(windows):
-    result = cast[OSErrorCode](getLastError())
-  else:
-    result = OSErrorCode(errno)
-{.pop.}
diff --git a/lib/pure/includes/unicode_ranges.nim b/lib/pure/includes/unicode_ranges.nim
index 74c8c6399..04ccfb747 100644
--- a/lib/pure/includes/unicode_ranges.nim
+++ b/lib/pure/includes/unicode_ranges.nim
@@ -2,1988 +2,1979 @@
 
 const
   toLowerRanges = [
-    0x00041, 0x0005A, 532,
-    0x000C0, 0x000D6, 532,
-    0x000D8, 0x000DE, 532,
-    0x00189, 0x0018A, 705,
-    0x001B1, 0x001B2, 717,
-    0x00388, 0x0038A, 537,
-    0x0038E, 0x0038F, 563,
-    0x00391, 0x003A1, 532,
-    0x003A3, 0x003AB, 532,
-    0x003FD, 0x003FF, 370,
-    0x00400, 0x0040F, 580,
-    0x00410, 0x0042F, 532,
-    0x00531, 0x00556, 548,
-    0x010A0, 0x010C5, 7764,
-    0x013A0, 0x013EF, 39364,
-    0x013F0, 0x013F5, 508,
-    0x01C90, 0x01CBA, -2508,
-    0x01CBD, 0x01CBF, -2508,
-    0x01F08, 0x01F0F, 492,
-    0x01F18, 0x01F1D, 492,
-    0x01F28, 0x01F2F, 492,
-    0x01F38, 0x01F3F, 492,
-    0x01F48, 0x01F4D, 492,
-    0x01F68, 0x01F6F, 492,
-    0x01F88, 0x01F8F, 492,
-    0x01F98, 0x01F9F, 492,
-    0x01FA8, 0x01FAF, 492,
-    0x01FB8, 0x01FB9, 492,
-    0x01FBA, 0x01FBB, 426,
-    0x01FC8, 0x01FCB, 414,
-    0x01FD8, 0x01FD9, 492,
-    0x01FDA, 0x01FDB, 400,
-    0x01FE8, 0x01FE9, 492,
-    0x01FEA, 0x01FEB, 388,
-    0x01FF8, 0x01FF9, 372,
-    0x01FFA, 0x01FFB, 374,
-    0x02C00, 0x02C2E, 548,
-    0x02C7E, 0x02C7F, -10315,
-    0x0FF21, 0x0FF3A, 532,
-    0x10400, 0x10427, 540,
-    0x104B0, 0x104D3, 540,
-    0x10C80, 0x10CB2, 564,
-    0x118A0, 0x118BF, 532,
-    0x16E40, 0x16E5F, 532,
-    0x1E900, 0x1E921, 534,
+    0x00041'i32, 0x0005A'i32, 532,
+    0x000C0'i32, 0x000D6'i32, 532,
+    0x000D8'i32, 0x000DE'i32, 532,
+    0x00189'i32, 0x0018A'i32, 705,
+    0x001B1'i32, 0x001B2'i32, 717,
+    0x00388'i32, 0x0038A'i32, 537,
+    0x0038E'i32, 0x0038F'i32, 563,
+    0x00391'i32, 0x003A1'i32, 532,
+    0x003A3'i32, 0x003AB'i32, 532,
+    0x003FD'i32, 0x003FF'i32, 370,
+    0x00400'i32, 0x0040F'i32, 580,
+    0x00410'i32, 0x0042F'i32, 532,
+    0x00531'i32, 0x00556'i32, 548,
+    0x010A0'i32, 0x010C5'i32, 7764,
+    0x013A0'i32, 0x013EF'i32, 39364,
+    0x013F0'i32, 0x013F5'i32, 508,
+    0x01C90'i32, 0x01CBA'i32, -2508,
+    0x01CBD'i32, 0x01CBF'i32, -2508,
+    0x01F08'i32, 0x01F0F'i32, 492,
+    0x01F18'i32, 0x01F1D'i32, 492,
+    0x01F28'i32, 0x01F2F'i32, 492,
+    0x01F38'i32, 0x01F3F'i32, 492,
+    0x01F48'i32, 0x01F4D'i32, 492,
+    0x01F68'i32, 0x01F6F'i32, 492,
+    0x01F88'i32, 0x01F8F'i32, 492,
+    0x01F98'i32, 0x01F9F'i32, 492,
+    0x01FA8'i32, 0x01FAF'i32, 492,
+    0x01FB8'i32, 0x01FB9'i32, 492,
+    0x01FBA'i32, 0x01FBB'i32, 426,
+    0x01FC8'i32, 0x01FCB'i32, 414,
+    0x01FD8'i32, 0x01FD9'i32, 492,
+    0x01FDA'i32, 0x01FDB'i32, 400,
+    0x01FE8'i32, 0x01FE9'i32, 492,
+    0x01FEA'i32, 0x01FEB'i32, 388,
+    0x01FF8'i32, 0x01FF9'i32, 372,
+    0x01FFA'i32, 0x01FFB'i32, 374,
+    0x02C00'i32, 0x02C2E'i32, 548,
+    0x02C7E'i32, 0x02C7F'i32, -10315,
+    0x0FF21'i32, 0x0FF3A'i32, 532,
+    0x10400'i32, 0x10427'i32, 540,
+    0x104B0'i32, 0x104D3'i32, 540,
+    0x10C80'i32, 0x10CB2'i32, 564,
+    0x118A0'i32, 0x118BF'i32, 532,
+    0x16E40'i32, 0x16E5F'i32, 532,
+    0x1E900'i32, 0x1E921'i32, 534,
   ]
 
   toLowerSinglets = [
-    0x00100, 501,
-    0x00102, 501,
-    0x00104, 501,
-    0x00106, 501,
-    0x00108, 501,
-    0x0010A, 501,
-    0x0010C, 501,
-    0x0010E, 501,
-    0x00110, 501,
-    0x00112, 501,
-    0x00114, 501,
-    0x00116, 501,
-    0x00118, 501,
-    0x0011A, 501,
-    0x0011C, 501,
-    0x0011E, 501,
-    0x00120, 501,
-    0x00122, 501,
-    0x00124, 501,
-    0x00126, 501,
-    0x00128, 501,
-    0x0012A, 501,
-    0x0012C, 501,
-    0x0012E, 501,
-    0x00130, 301,
-    0x00132, 501,
-    0x00134, 501,
-    0x00136, 501,
-    0x00139, 501,
-    0x0013B, 501,
-    0x0013D, 501,
-    0x0013F, 501,
-    0x00141, 501,
-    0x00143, 501,
-    0x00145, 501,
-    0x00147, 501,
-    0x0014A, 501,
-    0x0014C, 501,
-    0x0014E, 501,
-    0x00150, 501,
-    0x00152, 501,
-    0x00154, 501,
-    0x00156, 501,
-    0x00158, 501,
-    0x0015A, 501,
-    0x0015C, 501,
-    0x0015E, 501,
-    0x00160, 501,
-    0x00162, 501,
-    0x00164, 501,
-    0x00166, 501,
-    0x00168, 501,
-    0x0016A, 501,
-    0x0016C, 501,
-    0x0016E, 501,
-    0x00170, 501,
-    0x00172, 501,
-    0x00174, 501,
-    0x00176, 501,
-    0x00178, 379,
-    0x00179, 501,
-    0x0017B, 501,
-    0x0017D, 501,
-    0x00181, 710,
-    0x00182, 501,
-    0x00184, 501,
-    0x00186, 706,
-    0x00187, 501,
-    0x0018B, 501,
-    0x0018E, 579,
-    0x0018F, 702,
-    0x00190, 703,
-    0x00191, 501,
-    0x00193, 705,
-    0x00194, 707,
-    0x00196, 711,
-    0x00197, 709,
-    0x00198, 501,
-    0x0019C, 711,
-    0x0019D, 713,
-    0x0019F, 714,
-    0x001A0, 501,
-    0x001A2, 501,
-    0x001A4, 501,
-    0x001A6, 718,
-    0x001A7, 501,
-    0x001A9, 718,
-    0x001AC, 501,
-    0x001AE, 718,
-    0x001AF, 501,
-    0x001B3, 501,
-    0x001B5, 501,
-    0x001B7, 719,
-    0x001B8, 501,
-    0x001BC, 501,
-    0x001C4, 502,
-    0x001C5, 501,
-    0x001C7, 502,
-    0x001C8, 501,
-    0x001CA, 502,
-    0x001CB, 501,
-    0x001CD, 501,
-    0x001CF, 501,
-    0x001D1, 501,
-    0x001D3, 501,
-    0x001D5, 501,
-    0x001D7, 501,
-    0x001D9, 501,
-    0x001DB, 501,
-    0x001DE, 501,
-    0x001E0, 501,
-    0x001E2, 501,
-    0x001E4, 501,
-    0x001E6, 501,
-    0x001E8, 501,
-    0x001EA, 501,
-    0x001EC, 501,
-    0x001EE, 501,
-    0x001F1, 502,
-    0x001F2, 501,
-    0x001F4, 501,
-    0x001F6, 403,
-    0x001F7, 444,
-    0x001F8, 501,
-    0x001FA, 501,
-    0x001FC, 501,
-    0x001FE, 501,
-    0x00200, 501,
-    0x00202, 501,
-    0x00204, 501,
-    0x00206, 501,
-    0x00208, 501,
-    0x0020A, 501,
-    0x0020C, 501,
-    0x0020E, 501,
-    0x00210, 501,
-    0x00212, 501,
-    0x00214, 501,
-    0x00216, 501,
-    0x00218, 501,
-    0x0021A, 501,
-    0x0021C, 501,
-    0x0021E, 501,
-    0x00220, 370,
-    0x00222, 501,
-    0x00224, 501,
-    0x00226, 501,
-    0x00228, 501,
-    0x0022A, 501,
-    0x0022C, 501,
-    0x0022E, 501,
-    0x00230, 501,
-    0x00232, 501,
-    0x0023A, 11295,
-    0x0023B, 501,
-    0x0023D, 337,
-    0x0023E, 11292,
-    0x00241, 501,
-    0x00243, 305,
-    0x00244, 569,
-    0x00245, 571,
-    0x00246, 501,
-    0x00248, 501,
-    0x0024A, 501,
-    0x0024C, 501,
-    0x0024E, 501,
-    0x00370, 501,
-    0x00372, 501,
-    0x00376, 501,
-    0x0037F, 616,
-    0x00386, 538,
-    0x0038C, 564,
-    0x003CF, 508,
-    0x003D8, 501,
-    0x003DA, 501,
-    0x003DC, 501,
-    0x003DE, 501,
-    0x003E0, 501,
-    0x003E2, 501,
-    0x003E4, 501,
-    0x003E6, 501,
-    0x003E8, 501,
-    0x003EA, 501,
-    0x003EC, 501,
-    0x003EE, 501,
-    0x003F4, 440,
-    0x003F7, 501,
-    0x003F9, 493,
-    0x003FA, 501,
-    0x00460, 501,
-    0x00462, 501,
-    0x00464, 501,
-    0x00466, 501,
-    0x00468, 501,
-    0x0046A, 501,
-    0x0046C, 501,
-    0x0046E, 501,
-    0x00470, 501,
-    0x00472, 501,
-    0x00474, 501,
-    0x00476, 501,
-    0x00478, 501,
-    0x0047A, 501,
-    0x0047C, 501,
-    0x0047E, 501,
-    0x00480, 501,
-    0x0048A, 501,
-    0x0048C, 501,
-    0x0048E, 501,
-    0x00490, 501,
-    0x00492, 501,
-    0x00494, 501,
-    0x00496, 501,
-    0x00498, 501,
-    0x0049A, 501,
-    0x0049C, 501,
-    0x0049E, 501,
-    0x004A0, 501,
-    0x004A2, 501,
-    0x004A4, 501,
-    0x004A6, 501,
-    0x004A8, 501,
-    0x004AA, 501,
-    0x004AC, 501,
-    0x004AE, 501,
-    0x004B0, 501,
-    0x004B2, 501,
-    0x004B4, 501,
-    0x004B6, 501,
-    0x004B8, 501,
-    0x004BA, 501,
-    0x004BC, 501,
-    0x004BE, 501,
-    0x004C0, 515,
-    0x004C1, 501,
-    0x004C3, 501,
-    0x004C5, 501,
-    0x004C7, 501,
-    0x004C9, 501,
-    0x004CB, 501,
-    0x004CD, 501,
-    0x004D0, 501,
-    0x004D2, 501,
-    0x004D4, 501,
-    0x004D6, 501,
-    0x004D8, 501,
-    0x004DA, 501,
-    0x004DC, 501,
-    0x004DE, 501,
-    0x004E0, 501,
-    0x004E2, 501,
-    0x004E4, 501,
-    0x004E6, 501,
-    0x004E8, 501,
-    0x004EA, 501,
-    0x004EC, 501,
-    0x004EE, 501,
-    0x004F0, 501,
-    0x004F2, 501,
-    0x004F4, 501,
-    0x004F6, 501,
-    0x004F8, 501,
-    0x004FA, 501,
-    0x004FC, 501,
-    0x004FE, 501,
-    0x00500, 501,
-    0x00502, 501,
-    0x00504, 501,
-    0x00506, 501,
-    0x00508, 501,
-    0x0050A, 501,
-    0x0050C, 501,
-    0x0050E, 501,
-    0x00510, 501,
-    0x00512, 501,
-    0x00514, 501,
-    0x00516, 501,
-    0x00518, 501,
-    0x0051A, 501,
-    0x0051C, 501,
-    0x0051E, 501,
-    0x00520, 501,
-    0x00522, 501,
-    0x00524, 501,
-    0x00526, 501,
-    0x00528, 501,
-    0x0052A, 501,
-    0x0052C, 501,
-    0x0052E, 501,
-    0x010C7, 7764,
-    0x010CD, 7764,
-    0x01E00, 501,
-    0x01E02, 501,
-    0x01E04, 501,
-    0x01E06, 501,
-    0x01E08, 501,
-    0x01E0A, 501,
-    0x01E0C, 501,
-    0x01E0E, 501,
-    0x01E10, 501,
-    0x01E12, 501,
-    0x01E14, 501,
-    0x01E16, 501,
-    0x01E18, 501,
-    0x01E1A, 501,
-    0x01E1C, 501,
-    0x01E1E, 501,
-    0x01E20, 501,
-    0x01E22, 501,
-    0x01E24, 501,
-    0x01E26, 501,
-    0x01E28, 501,
-    0x01E2A, 501,
-    0x01E2C, 501,
-    0x01E2E, 501,
-    0x01E30, 501,
-    0x01E32, 501,
-    0x01E34, 501,
-    0x01E36, 501,
-    0x01E38, 501,
-    0x01E3A, 501,
-    0x01E3C, 501,
-    0x01E3E, 501,
-    0x01E40, 501,
-    0x01E42, 501,
-    0x01E44, 501,
-    0x01E46, 501,
-    0x01E48, 501,
-    0x01E4A, 501,
-    0x01E4C, 501,
-    0x01E4E, 501,
-    0x01E50, 501,
-    0x01E52, 501,
-    0x01E54, 501,
-    0x01E56, 501,
-    0x01E58, 501,
-    0x01E5A, 501,
-    0x01E5C, 501,
-    0x01E5E, 501,
-    0x01E60, 501,
-    0x01E62, 501,
-    0x01E64, 501,
-    0x01E66, 501,
-    0x01E68, 501,
-    0x01E6A, 501,
-    0x01E6C, 501,
-    0x01E6E, 501,
-    0x01E70, 501,
-    0x01E72, 501,
-    0x01E74, 501,
-    0x01E76, 501,
-    0x01E78, 501,
-    0x01E7A, 501,
-    0x01E7C, 501,
-    0x01E7E, 501,
-    0x01E80, 501,
-    0x01E82, 501,
-    0x01E84, 501,
-    0x01E86, 501,
-    0x01E88, 501,
-    0x01E8A, 501,
-    0x01E8C, 501,
-    0x01E8E, 501,
-    0x01E90, 501,
-    0x01E92, 501,
-    0x01E94, 501,
-    0x01E9E, -7115,
-    0x01EA0, 501,
-    0x01EA2, 501,
-    0x01EA4, 501,
-    0x01EA6, 501,
-    0x01EA8, 501,
-    0x01EAA, 501,
-    0x01EAC, 501,
-    0x01EAE, 501,
-    0x01EB0, 501,
-    0x01EB2, 501,
-    0x01EB4, 501,
-    0x01EB6, 501,
-    0x01EB8, 501,
-    0x01EBA, 501,
-    0x01EBC, 501,
-    0x01EBE, 501,
-    0x01EC0, 501,
-    0x01EC2, 501,
-    0x01EC4, 501,
-    0x01EC6, 501,
-    0x01EC8, 501,
-    0x01ECA, 501,
-    0x01ECC, 501,
-    0x01ECE, 501,
-    0x01ED0, 501,
-    0x01ED2, 501,
-    0x01ED4, 501,
-    0x01ED6, 501,
-    0x01ED8, 501,
-    0x01EDA, 501,
-    0x01EDC, 501,
-    0x01EDE, 501,
-    0x01EE0, 501,
-    0x01EE2, 501,
-    0x01EE4, 501,
-    0x01EE6, 501,
-    0x01EE8, 501,
-    0x01EEA, 501,
-    0x01EEC, 501,
-    0x01EEE, 501,
-    0x01EF0, 501,
-    0x01EF2, 501,
-    0x01EF4, 501,
-    0x01EF6, 501,
-    0x01EF8, 501,
-    0x01EFA, 501,
-    0x01EFC, 501,
-    0x01EFE, 501,
-    0x01F59, 492,
-    0x01F5B, 492,
-    0x01F5D, 492,
-    0x01F5F, 492,
-    0x01FBC, 491,
-    0x01FCC, 491,
-    0x01FEC, 493,
-    0x01FFC, 491,
-    0x02126, -7017,
-    0x0212A, -7883,
-    0x0212B, -7762,
-    0x02132, 528,
-    0x02183, 501,
-    0x02C60, 501,
-    0x02C62, -10243,
-    0x02C63, -3314,
-    0x02C64, -10227,
-    0x02C67, 501,
-    0x02C69, 501,
-    0x02C6B, 501,
-    0x02C6D, -10280,
-    0x02C6E, -10249,
-    0x02C6F, -10283,
-    0x02C70, -10282,
-    0x02C72, 501,
-    0x02C75, 501,
-    0x02C80, 501,
-    0x02C82, 501,
-    0x02C84, 501,
-    0x02C86, 501,
-    0x02C88, 501,
-    0x02C8A, 501,
-    0x02C8C, 501,
-    0x02C8E, 501,
-    0x02C90, 501,
-    0x02C92, 501,
-    0x02C94, 501,
-    0x02C96, 501,
-    0x02C98, 501,
-    0x02C9A, 501,
-    0x02C9C, 501,
-    0x02C9E, 501,
-    0x02CA0, 501,
-    0x02CA2, 501,
-    0x02CA4, 501,
-    0x02CA6, 501,
-    0x02CA8, 501,
-    0x02CAA, 501,
-    0x02CAC, 501,
-    0x02CAE, 501,
-    0x02CB0, 501,
-    0x02CB2, 501,
-    0x02CB4, 501,
-    0x02CB6, 501,
-    0x02CB8, 501,
-    0x02CBA, 501,
-    0x02CBC, 501,
-    0x02CBE, 501,
-    0x02CC0, 501,
-    0x02CC2, 501,
-    0x02CC4, 501,
-    0x02CC6, 501,
-    0x02CC8, 501,
-    0x02CCA, 501,
-    0x02CCC, 501,
-    0x02CCE, 501,
-    0x02CD0, 501,
-    0x02CD2, 501,
-    0x02CD4, 501,
-    0x02CD6, 501,
-    0x02CD8, 501,
-    0x02CDA, 501,
-    0x02CDC, 501,
-    0x02CDE, 501,
-    0x02CE0, 501,
-    0x02CE2, 501,
-    0x02CEB, 501,
-    0x02CED, 501,
-    0x02CF2, 501,
-    0x0A640, 501,
-    0x0A642, 501,
-    0x0A644, 501,
-    0x0A646, 501,
-    0x0A648, 501,
-    0x0A64A, 501,
-    0x0A64C, 501,
-    0x0A64E, 501,
-    0x0A650, 501,
-    0x0A652, 501,
-    0x0A654, 501,
-    0x0A656, 501,
-    0x0A658, 501,
-    0x0A65A, 501,
-    0x0A65C, 501,
-    0x0A65E, 501,
-    0x0A660, 501,
-    0x0A662, 501,
-    0x0A664, 501,
-    0x0A666, 501,
-    0x0A668, 501,
-    0x0A66A, 501,
-    0x0A66C, 501,
-    0x0A680, 501,
-    0x0A682, 501,
-    0x0A684, 501,
-    0x0A686, 501,
-    0x0A688, 501,
-    0x0A68A, 501,
-    0x0A68C, 501,
-    0x0A68E, 501,
-    0x0A690, 501,
-    0x0A692, 501,
-    0x0A694, 501,
-    0x0A696, 501,
-    0x0A698, 501,
-    0x0A69A, 501,
-    0x0A722, 501,
-    0x0A724, 501,
-    0x0A726, 501,
-    0x0A728, 501,
-    0x0A72A, 501,
-    0x0A72C, 501,
-    0x0A72E, 501,
-    0x0A732, 501,
-    0x0A734, 501,
-    0x0A736, 501,
-    0x0A738, 501,
-    0x0A73A, 501,
-    0x0A73C, 501,
-    0x0A73E, 501,
-    0x0A740, 501,
-    0x0A742, 501,
-    0x0A744, 501,
-    0x0A746, 501,
-    0x0A748, 501,
-    0x0A74A, 501,
-    0x0A74C, 501,
-    0x0A74E, 501,
-    0x0A750, 501,
-    0x0A752, 501,
-    0x0A754, 501,
-    0x0A756, 501,
-    0x0A758, 501,
-    0x0A75A, 501,
-    0x0A75C, 501,
-    0x0A75E, 501,
-    0x0A760, 501,
-    0x0A762, 501,
-    0x0A764, 501,
-    0x0A766, 501,
-    0x0A768, 501,
-    0x0A76A, 501,
-    0x0A76C, 501,
-    0x0A76E, 501,
-    0x0A779, 501,
-    0x0A77B, 501,
-    0x0A77D, -34832,
-    0x0A77E, 501,
-    0x0A780, 501,
-    0x0A782, 501,
-    0x0A784, 501,
-    0x0A786, 501,
-    0x0A78B, 501,
-    0x0A78D, -41780,
-    0x0A790, 501,
-    0x0A792, 501,
-    0x0A796, 501,
-    0x0A798, 501,
-    0x0A79A, 501,
-    0x0A79C, 501,
-    0x0A79E, 501,
-    0x0A7A0, 501,
-    0x0A7A2, 501,
-    0x0A7A4, 501,
-    0x0A7A6, 501,
-    0x0A7A8, 501,
-    0x0A7AA, -41808,
-    0x0A7AB, -41819,
-    0x0A7AC, -41815,
-    0x0A7AD, -41805,
-    0x0A7AE, -41808,
-    0x0A7B0, -41758,
-    0x0A7B1, -41782,
-    0x0A7B2, -41761,
-    0x0A7B3, 1428,
-    0x0A7B4, 501,
-    0x0A7B6, 501,
-    0x0A7B8, 501,
-    0x0A7BA, 501,
-    0x0A7BC, 501,
-    0x0A7BE, 501,
-    0x0A7C2, 501,
-    0x0A7C4, 452,
-    0x0A7C5, -41807,
-    0x0A7C6, -34884,
+    0x00100'i32, 501,
+    0x00102'i32, 501,
+    0x00104'i32, 501,
+    0x00106'i32, 501,
+    0x00108'i32, 501,
+    0x0010A'i32, 501,
+    0x0010C'i32, 501,
+    0x0010E'i32, 501,
+    0x00110'i32, 501,
+    0x00112'i32, 501,
+    0x00114'i32, 501,
+    0x00116'i32, 501,
+    0x00118'i32, 501,
+    0x0011A'i32, 501,
+    0x0011C'i32, 501,
+    0x0011E'i32, 501,
+    0x00120'i32, 501,
+    0x00122'i32, 501,
+    0x00124'i32, 501,
+    0x00126'i32, 501,
+    0x00128'i32, 501,
+    0x0012A'i32, 501,
+    0x0012C'i32, 501,
+    0x0012E'i32, 501,
+    0x00130'i32, 301,
+    0x00132'i32, 501,
+    0x00134'i32, 501,
+    0x00136'i32, 501,
+    0x00139'i32, 501,
+    0x0013B'i32, 501,
+    0x0013D'i32, 501,
+    0x0013F'i32, 501,
+    0x00141'i32, 501,
+    0x00143'i32, 501,
+    0x00145'i32, 501,
+    0x00147'i32, 501,
+    0x0014A'i32, 501,
+    0x0014C'i32, 501,
+    0x0014E'i32, 501,
+    0x00150'i32, 501,
+    0x00152'i32, 501,
+    0x00154'i32, 501,
+    0x00156'i32, 501,
+    0x00158'i32, 501,
+    0x0015A'i32, 501,
+    0x0015C'i32, 501,
+    0x0015E'i32, 501,
+    0x00160'i32, 501,
+    0x00162'i32, 501,
+    0x00164'i32, 501,
+    0x00166'i32, 501,
+    0x00168'i32, 501,
+    0x0016A'i32, 501,
+    0x0016C'i32, 501,
+    0x0016E'i32, 501,
+    0x00170'i32, 501,
+    0x00172'i32, 501,
+    0x00174'i32, 501,
+    0x00176'i32, 501,
+    0x00178'i32, 379,
+    0x00179'i32, 501,
+    0x0017B'i32, 501,
+    0x0017D'i32, 501,
+    0x00181'i32, 710,
+    0x00182'i32, 501,
+    0x00184'i32, 501,
+    0x00186'i32, 706,
+    0x00187'i32, 501,
+    0x0018B'i32, 501,
+    0x0018E'i32, 579,
+    0x0018F'i32, 702,
+    0x00190'i32, 703,
+    0x00191'i32, 501,
+    0x00193'i32, 705,
+    0x00194'i32, 707,
+    0x00196'i32, 711,
+    0x00197'i32, 709,
+    0x00198'i32, 501,
+    0x0019C'i32, 711,
+    0x0019D'i32, 713,
+    0x0019F'i32, 714,
+    0x001A0'i32, 501,
+    0x001A2'i32, 501,
+    0x001A4'i32, 501,
+    0x001A6'i32, 718,
+    0x001A7'i32, 501,
+    0x001A9'i32, 718,
+    0x001AC'i32, 501,
+    0x001AE'i32, 718,
+    0x001AF'i32, 501,
+    0x001B3'i32, 501,
+    0x001B5'i32, 501,
+    0x001B7'i32, 719,
+    0x001B8'i32, 501,
+    0x001BC'i32, 501,
+    0x001C4'i32, 502,
+    0x001C5'i32, 501,
+    0x001C7'i32, 502,
+    0x001C8'i32, 501,
+    0x001CA'i32, 502,
+    0x001CB'i32, 501,
+    0x001CD'i32, 501,
+    0x001CF'i32, 501,
+    0x001D1'i32, 501,
+    0x001D3'i32, 501,
+    0x001D5'i32, 501,
+    0x001D7'i32, 501,
+    0x001D9'i32, 501,
+    0x001DB'i32, 501,
+    0x001DE'i32, 501,
+    0x001E0'i32, 501,
+    0x001E2'i32, 501,
+    0x001E4'i32, 501,
+    0x001E6'i32, 501,
+    0x001E8'i32, 501,
+    0x001EA'i32, 501,
+    0x001EC'i32, 501,
+    0x001EE'i32, 501,
+    0x001F1'i32, 502,
+    0x001F2'i32, 501,
+    0x001F4'i32, 501,
+    0x001F6'i32, 403,
+    0x001F7'i32, 444,
+    0x001F8'i32, 501,
+    0x001FA'i32, 501,
+    0x001FC'i32, 501,
+    0x001FE'i32, 501,
+    0x00200'i32, 501,
+    0x00202'i32, 501,
+    0x00204'i32, 501,
+    0x00206'i32, 501,
+    0x00208'i32, 501,
+    0x0020A'i32, 501,
+    0x0020C'i32, 501,
+    0x0020E'i32, 501,
+    0x00210'i32, 501,
+    0x00212'i32, 501,
+    0x00214'i32, 501,
+    0x00216'i32, 501,
+    0x00218'i32, 501,
+    0x0021A'i32, 501,
+    0x0021C'i32, 501,
+    0x0021E'i32, 501,
+    0x00220'i32, 370,
+    0x00222'i32, 501,
+    0x00224'i32, 501,
+    0x00226'i32, 501,
+    0x00228'i32, 501,
+    0x0022A'i32, 501,
+    0x0022C'i32, 501,
+    0x0022E'i32, 501,
+    0x00230'i32, 501,
+    0x00232'i32, 501,
+    0x0023A'i32, 11295,
+    0x0023B'i32, 501,
+    0x0023D'i32, 337,
+    0x0023E'i32, 11292,
+    0x00241'i32, 501,
+    0x00243'i32, 305,
+    0x00244'i32, 569,
+    0x00245'i32, 571,
+    0x00246'i32, 501,
+    0x00248'i32, 501,
+    0x0024A'i32, 501,
+    0x0024C'i32, 501,
+    0x0024E'i32, 501,
+    0x00370'i32, 501,
+    0x00372'i32, 501,
+    0x00376'i32, 501,
+    0x0037F'i32, 616,
+    0x00386'i32, 538,
+    0x0038C'i32, 564,
+    0x003CF'i32, 508,
+    0x003D8'i32, 501,
+    0x003DA'i32, 501,
+    0x003DC'i32, 501,
+    0x003DE'i32, 501,
+    0x003E0'i32, 501,
+    0x003E2'i32, 501,
+    0x003E4'i32, 501,
+    0x003E6'i32, 501,
+    0x003E8'i32, 501,
+    0x003EA'i32, 501,
+    0x003EC'i32, 501,
+    0x003EE'i32, 501,
+    0x003F4'i32, 440,
+    0x003F7'i32, 501,
+    0x003F9'i32, 493,
+    0x003FA'i32, 501,
+    0x00460'i32, 501,
+    0x00462'i32, 501,
+    0x00464'i32, 501,
+    0x00466'i32, 501,
+    0x00468'i32, 501,
+    0x0046A'i32, 501,
+    0x0046C'i32, 501,
+    0x0046E'i32, 501,
+    0x00470'i32, 501,
+    0x00472'i32, 501,
+    0x00474'i32, 501,
+    0x00476'i32, 501,
+    0x00478'i32, 501,
+    0x0047A'i32, 501,
+    0x0047C'i32, 501,
+    0x0047E'i32, 501,
+    0x00480'i32, 501,
+    0x0048A'i32, 501,
+    0x0048C'i32, 501,
+    0x0048E'i32, 501,
+    0x00490'i32, 501,
+    0x00492'i32, 501,
+    0x00494'i32, 501,
+    0x00496'i32, 501,
+    0x00498'i32, 501,
+    0x0049A'i32, 501,
+    0x0049C'i32, 501,
+    0x0049E'i32, 501,
+    0x004A0'i32, 501,
+    0x004A2'i32, 501,
+    0x004A4'i32, 501,
+    0x004A6'i32, 501,
+    0x004A8'i32, 501,
+    0x004AA'i32, 501,
+    0x004AC'i32, 501,
+    0x004AE'i32, 501,
+    0x004B0'i32, 501,
+    0x004B2'i32, 501,
+    0x004B4'i32, 501,
+    0x004B6'i32, 501,
+    0x004B8'i32, 501,
+    0x004BA'i32, 501,
+    0x004BC'i32, 501,
+    0x004BE'i32, 501,
+    0x004C0'i32, 515,
+    0x004C1'i32, 501,
+    0x004C3'i32, 501,
+    0x004C5'i32, 501,
+    0x004C7'i32, 501,
+    0x004C9'i32, 501,
+    0x004CB'i32, 501,
+    0x004CD'i32, 501,
+    0x004D0'i32, 501,
+    0x004D2'i32, 501,
+    0x004D4'i32, 501,
+    0x004D6'i32, 501,
+    0x004D8'i32, 501,
+    0x004DA'i32, 501,
+    0x004DC'i32, 501,
+    0x004DE'i32, 501,
+    0x004E0'i32, 501,
+    0x004E2'i32, 501,
+    0x004E4'i32, 501,
+    0x004E6'i32, 501,
+    0x004E8'i32, 501,
+    0x004EA'i32, 501,
+    0x004EC'i32, 501,
+    0x004EE'i32, 501,
+    0x004F0'i32, 501,
+    0x004F2'i32, 501,
+    0x004F4'i32, 501,
+    0x004F6'i32, 501,
+    0x004F8'i32, 501,
+    0x004FA'i32, 501,
+    0x004FC'i32, 501,
+    0x004FE'i32, 501,
+    0x00500'i32, 501,
+    0x00502'i32, 501,
+    0x00504'i32, 501,
+    0x00506'i32, 501,
+    0x00508'i32, 501,
+    0x0050A'i32, 501,
+    0x0050C'i32, 501,
+    0x0050E'i32, 501,
+    0x00510'i32, 501,
+    0x00512'i32, 501,
+    0x00514'i32, 501,
+    0x00516'i32, 501,
+    0x00518'i32, 501,
+    0x0051A'i32, 501,
+    0x0051C'i32, 501,
+    0x0051E'i32, 501,
+    0x00520'i32, 501,
+    0x00522'i32, 501,
+    0x00524'i32, 501,
+    0x00526'i32, 501,
+    0x00528'i32, 501,
+    0x0052A'i32, 501,
+    0x0052C'i32, 501,
+    0x0052E'i32, 501,
+    0x010C7'i32, 7764,
+    0x010CD'i32, 7764,
+    0x01E00'i32, 501,
+    0x01E02'i32, 501,
+    0x01E04'i32, 501,
+    0x01E06'i32, 501,
+    0x01E08'i32, 501,
+    0x01E0A'i32, 501,
+    0x01E0C'i32, 501,
+    0x01E0E'i32, 501,
+    0x01E10'i32, 501,
+    0x01E12'i32, 501,
+    0x01E14'i32, 501,
+    0x01E16'i32, 501,
+    0x01E18'i32, 501,
+    0x01E1A'i32, 501,
+    0x01E1C'i32, 501,
+    0x01E1E'i32, 501,
+    0x01E20'i32, 501,
+    0x01E22'i32, 501,
+    0x01E24'i32, 501,
+    0x01E26'i32, 501,
+    0x01E28'i32, 501,
+    0x01E2A'i32, 501,
+    0x01E2C'i32, 501,
+    0x01E2E'i32, 501,
+    0x01E30'i32, 501,
+    0x01E32'i32, 501,
+    0x01E34'i32, 501,
+    0x01E36'i32, 501,
+    0x01E38'i32, 501,
+    0x01E3A'i32, 501,
+    0x01E3C'i32, 501,
+    0x01E3E'i32, 501,
+    0x01E40'i32, 501,
+    0x01E42'i32, 501,
+    0x01E44'i32, 501,
+    0x01E46'i32, 501,
+    0x01E48'i32, 501,
+    0x01E4A'i32, 501,
+    0x01E4C'i32, 501,
+    0x01E4E'i32, 501,
+    0x01E50'i32, 501,
+    0x01E52'i32, 501,
+    0x01E54'i32, 501,
+    0x01E56'i32, 501,
+    0x01E58'i32, 501,
+    0x01E5A'i32, 501,
+    0x01E5C'i32, 501,
+    0x01E5E'i32, 501,
+    0x01E60'i32, 501,
+    0x01E62'i32, 501,
+    0x01E64'i32, 501,
+    0x01E66'i32, 501,
+    0x01E68'i32, 501,
+    0x01E6A'i32, 501,
+    0x01E6C'i32, 501,
+    0x01E6E'i32, 501,
+    0x01E70'i32, 501,
+    0x01E72'i32, 501,
+    0x01E74'i32, 501,
+    0x01E76'i32, 501,
+    0x01E78'i32, 501,
+    0x01E7A'i32, 501,
+    0x01E7C'i32, 501,
+    0x01E7E'i32, 501,
+    0x01E80'i32, 501,
+    0x01E82'i32, 501,
+    0x01E84'i32, 501,
+    0x01E86'i32, 501,
+    0x01E88'i32, 501,
+    0x01E8A'i32, 501,
+    0x01E8C'i32, 501,
+    0x01E8E'i32, 501,
+    0x01E90'i32, 501,
+    0x01E92'i32, 501,
+    0x01E94'i32, 501,
+    0x01E9E'i32, -7115,
+    0x01EA0'i32, 501,
+    0x01EA2'i32, 501,
+    0x01EA4'i32, 501,
+    0x01EA6'i32, 501,
+    0x01EA8'i32, 501,
+    0x01EAA'i32, 501,
+    0x01EAC'i32, 501,
+    0x01EAE'i32, 501,
+    0x01EB0'i32, 501,
+    0x01EB2'i32, 501,
+    0x01EB4'i32, 501,
+    0x01EB6'i32, 501,
+    0x01EB8'i32, 501,
+    0x01EBA'i32, 501,
+    0x01EBC'i32, 501,
+    0x01EBE'i32, 501,
+    0x01EC0'i32, 501,
+    0x01EC2'i32, 501,
+    0x01EC4'i32, 501,
+    0x01EC6'i32, 501,
+    0x01EC8'i32, 501,
+    0x01ECA'i32, 501,
+    0x01ECC'i32, 501,
+    0x01ECE'i32, 501,
+    0x01ED0'i32, 501,
+    0x01ED2'i32, 501,
+    0x01ED4'i32, 501,
+    0x01ED6'i32, 501,
+    0x01ED8'i32, 501,
+    0x01EDA'i32, 501,
+    0x01EDC'i32, 501,
+    0x01EDE'i32, 501,
+    0x01EE0'i32, 501,
+    0x01EE2'i32, 501,
+    0x01EE4'i32, 501,
+    0x01EE6'i32, 501,
+    0x01EE8'i32, 501,
+    0x01EEA'i32, 501,
+    0x01EEC'i32, 501,
+    0x01EEE'i32, 501,
+    0x01EF0'i32, 501,
+    0x01EF2'i32, 501,
+    0x01EF4'i32, 501,
+    0x01EF6'i32, 501,
+    0x01EF8'i32, 501,
+    0x01EFA'i32, 501,
+    0x01EFC'i32, 501,
+    0x01EFE'i32, 501,
+    0x01F59'i32, 492,
+    0x01F5B'i32, 492,
+    0x01F5D'i32, 492,
+    0x01F5F'i32, 492,
+    0x01FBC'i32, 491,
+    0x01FCC'i32, 491,
+    0x01FEC'i32, 493,
+    0x01FFC'i32, 491,
+    0x02126'i32, -7017,
+    0x0212A'i32, -7883,
+    0x0212B'i32, -7762,
+    0x02132'i32, 528,
+    0x02183'i32, 501,
+    0x02C60'i32, 501,
+    0x02C62'i32, -10243,
+    0x02C63'i32, -3314,
+    0x02C64'i32, -10227,
+    0x02C67'i32, 501,
+    0x02C69'i32, 501,
+    0x02C6B'i32, 501,
+    0x02C6D'i32, -10280,
+    0x02C6E'i32, -10249,
+    0x02C6F'i32, -10283,
+    0x02C70'i32, -10282,
+    0x02C72'i32, 501,
+    0x02C75'i32, 501,
+    0x02C80'i32, 501,
+    0x02C82'i32, 501,
+    0x02C84'i32, 501,
+    0x02C86'i32, 501,
+    0x02C88'i32, 501,
+    0x02C8A'i32, 501,
+    0x02C8C'i32, 501,
+    0x02C8E'i32, 501,
+    0x02C90'i32, 501,
+    0x02C92'i32, 501,
+    0x02C94'i32, 501,
+    0x02C96'i32, 501,
+    0x02C98'i32, 501,
+    0x02C9A'i32, 501,
+    0x02C9C'i32, 501,
+    0x02C9E'i32, 501,
+    0x02CA0'i32, 501,
+    0x02CA2'i32, 501,
+    0x02CA4'i32, 501,
+    0x02CA6'i32, 501,
+    0x02CA8'i32, 501,
+    0x02CAA'i32, 501,
+    0x02CAC'i32, 501,
+    0x02CAE'i32, 501,
+    0x02CB0'i32, 501,
+    0x02CB2'i32, 501,
+    0x02CB4'i32, 501,
+    0x02CB6'i32, 501,
+    0x02CB8'i32, 501,
+    0x02CBA'i32, 501,
+    0x02CBC'i32, 501,
+    0x02CBE'i32, 501,
+    0x02CC0'i32, 501,
+    0x02CC2'i32, 501,
+    0x02CC4'i32, 501,
+    0x02CC6'i32, 501,
+    0x02CC8'i32, 501,
+    0x02CCA'i32, 501,
+    0x02CCC'i32, 501,
+    0x02CCE'i32, 501,
+    0x02CD0'i32, 501,
+    0x02CD2'i32, 501,
+    0x02CD4'i32, 501,
+    0x02CD6'i32, 501,
+    0x02CD8'i32, 501,
+    0x02CDA'i32, 501,
+    0x02CDC'i32, 501,
+    0x02CDE'i32, 501,
+    0x02CE0'i32, 501,
+    0x02CE2'i32, 501,
+    0x02CEB'i32, 501,
+    0x02CED'i32, 501,
+    0x02CF2'i32, 501,
+    0x0A640'i32, 501,
+    0x0A642'i32, 501,
+    0x0A644'i32, 501,
+    0x0A646'i32, 501,
+    0x0A648'i32, 501,
+    0x0A64A'i32, 501,
+    0x0A64C'i32, 501,
+    0x0A64E'i32, 501,
+    0x0A650'i32, 501,
+    0x0A652'i32, 501,
+    0x0A654'i32, 501,
+    0x0A656'i32, 501,
+    0x0A658'i32, 501,
+    0x0A65A'i32, 501,
+    0x0A65C'i32, 501,
+    0x0A65E'i32, 501,
+    0x0A660'i32, 501,
+    0x0A662'i32, 501,
+    0x0A664'i32, 501,
+    0x0A666'i32, 501,
+    0x0A668'i32, 501,
+    0x0A66A'i32, 501,
+    0x0A66C'i32, 501,
+    0x0A680'i32, 501,
+    0x0A682'i32, 501,
+    0x0A684'i32, 501,
+    0x0A686'i32, 501,
+    0x0A688'i32, 501,
+    0x0A68A'i32, 501,
+    0x0A68C'i32, 501,
+    0x0A68E'i32, 501,
+    0x0A690'i32, 501,
+    0x0A692'i32, 501,
+    0x0A694'i32, 501,
+    0x0A696'i32, 501,
+    0x0A698'i32, 501,
+    0x0A69A'i32, 501,
+    0x0A722'i32, 501,
+    0x0A724'i32, 501,
+    0x0A726'i32, 501,
+    0x0A728'i32, 501,
+    0x0A72A'i32, 501,
+    0x0A72C'i32, 501,
+    0x0A72E'i32, 501,
+    0x0A732'i32, 501,
+    0x0A734'i32, 501,
+    0x0A736'i32, 501,
+    0x0A738'i32, 501,
+    0x0A73A'i32, 501,
+    0x0A73C'i32, 501,
+    0x0A73E'i32, 501,
+    0x0A740'i32, 501,
+    0x0A742'i32, 501,
+    0x0A744'i32, 501,
+    0x0A746'i32, 501,
+    0x0A748'i32, 501,
+    0x0A74A'i32, 501,
+    0x0A74C'i32, 501,
+    0x0A74E'i32, 501,
+    0x0A750'i32, 501,
+    0x0A752'i32, 501,
+    0x0A754'i32, 501,
+    0x0A756'i32, 501,
+    0x0A758'i32, 501,
+    0x0A75A'i32, 501,
+    0x0A75C'i32, 501,
+    0x0A75E'i32, 501,
+    0x0A760'i32, 501,
+    0x0A762'i32, 501,
+    0x0A764'i32, 501,
+    0x0A766'i32, 501,
+    0x0A768'i32, 501,
+    0x0A76A'i32, 501,
+    0x0A76C'i32, 501,
+    0x0A76E'i32, 501,
+    0x0A779'i32, 501,
+    0x0A77B'i32, 501,
+    0x0A77D'i32, -34832,
+    0x0A77E'i32, 501,
+    0x0A780'i32, 501,
+    0x0A782'i32, 501,
+    0x0A784'i32, 501,
+    0x0A786'i32, 501,
+    0x0A78B'i32, 501,
+    0x0A78D'i32, -41780,
+    0x0A790'i32, 501,
+    0x0A792'i32, 501,
+    0x0A796'i32, 501,
+    0x0A798'i32, 501,
+    0x0A79A'i32, 501,
+    0x0A79C'i32, 501,
+    0x0A79E'i32, 501,
+    0x0A7A0'i32, 501,
+    0x0A7A2'i32, 501,
+    0x0A7A4'i32, 501,
+    0x0A7A6'i32, 501,
+    0x0A7A8'i32, 501,
+    0x0A7AA'i32, -41808,
+    0x0A7AB'i32, -41819,
+    0x0A7AC'i32, -41815,
+    0x0A7AD'i32, -41805,
+    0x0A7AE'i32, -41808,
+    0x0A7B0'i32, -41758,
+    0x0A7B1'i32, -41782,
+    0x0A7B2'i32, -41761,
+    0x0A7B3'i32, 1428,
+    0x0A7B4'i32, 501,
+    0x0A7B6'i32, 501,
+    0x0A7B8'i32, 501,
+    0x0A7BA'i32, 501,
+    0x0A7BC'i32, 501,
+    0x0A7BE'i32, 501,
+    0x0A7C2'i32, 501,
+    0x0A7C4'i32, 452,
+    0x0A7C5'i32, -41807,
+    0x0A7C6'i32, -34884,
   ]
 
   toUpperRanges = [
-    0x00061, 0x0007A, 468,
-    0x000E0, 0x000F6, 468,
-    0x000F8, 0x000FE, 468,
-    0x0023F, 0x00240, 11315,
-    0x00256, 0x00257, 295,
-    0x0028A, 0x0028B, 283,
-    0x0037B, 0x0037D, 630,
-    0x003AD, 0x003AF, 463,
-    0x003B1, 0x003C1, 468,
-    0x003C3, 0x003CB, 468,
-    0x003CD, 0x003CE, 437,
-    0x00430, 0x0044F, 468,
-    0x00450, 0x0045F, 420,
-    0x00561, 0x00586, 452,
-    0x010D0, 0x010FA, 3508,
-    0x010FD, 0x010FF, 3508,
-    0x013F8, 0x013FD, 492,
-    0x01C83, 0x01C84, -5742,
-    0x01F00, 0x01F07, 508,
-    0x01F10, 0x01F15, 508,
-    0x01F20, 0x01F27, 508,
-    0x01F30, 0x01F37, 508,
-    0x01F40, 0x01F45, 508,
-    0x01F60, 0x01F67, 508,
-    0x01F70, 0x01F71, 574,
-    0x01F72, 0x01F75, 586,
-    0x01F76, 0x01F77, 600,
-    0x01F78, 0x01F79, 628,
-    0x01F7A, 0x01F7B, 612,
-    0x01F7C, 0x01F7D, 626,
-    0x01F80, 0x01F87, 508,
-    0x01F90, 0x01F97, 508,
-    0x01FA0, 0x01FA7, 508,
-    0x01FB0, 0x01FB1, 508,
-    0x01FD0, 0x01FD1, 508,
-    0x01FE0, 0x01FE1, 508,
-    0x02C30, 0x02C5E, 452,
-    0x02D00, 0x02D25, -6764,
-    0x0AB70, 0x0ABBF, -38364,
-    0x0FF41, 0x0FF5A, 468,
-    0x10428, 0x1044F, 460,
-    0x104D8, 0x104FB, 460,
-    0x10CC0, 0x10CF2, 436,
-    0x118C0, 0x118DF, 468,
-    0x16E60, 0x16E7F, 468,
-    0x1E922, 0x1E943, 466,
+    0x00061'i32, 0x0007A'i32, 468,
+    0x000E0'i32, 0x000F6'i32, 468,
+    0x000F8'i32, 0x000FE'i32, 468,
+    0x0023F'i32, 0x00240'i32, 11315,
+    0x00256'i32, 0x00257'i32, 295,
+    0x0028A'i32, 0x0028B'i32, 283,
+    0x0037B'i32, 0x0037D'i32, 630,
+    0x003AD'i32, 0x003AF'i32, 463,
+    0x003B1'i32, 0x003C1'i32, 468,
+    0x003C3'i32, 0x003CB'i32, 468,
+    0x003CD'i32, 0x003CE'i32, 437,
+    0x00430'i32, 0x0044F'i32, 468,
+    0x00450'i32, 0x0045F'i32, 420,
+    0x00561'i32, 0x00586'i32, 452,
+    0x010D0'i32, 0x010FA'i32, 3508,
+    0x010FD'i32, 0x010FF'i32, 3508,
+    0x013F8'i32, 0x013FD'i32, 492,
+    0x01C83'i32, 0x01C84'i32, -5742,
+    0x01F00'i32, 0x01F07'i32, 508,
+    0x01F10'i32, 0x01F15'i32, 508,
+    0x01F20'i32, 0x01F27'i32, 508,
+    0x01F30'i32, 0x01F37'i32, 508,
+    0x01F40'i32, 0x01F45'i32, 508,
+    0x01F60'i32, 0x01F67'i32, 508,
+    0x01F70'i32, 0x01F71'i32, 574,
+    0x01F72'i32, 0x01F75'i32, 586,
+    0x01F76'i32, 0x01F77'i32, 600,
+    0x01F78'i32, 0x01F79'i32, 628,
+    0x01F7A'i32, 0x01F7B'i32, 612,
+    0x01F7C'i32, 0x01F7D'i32, 626,
+    0x01F80'i32, 0x01F87'i32, 508,
+    0x01F90'i32, 0x01F97'i32, 508,
+    0x01FA0'i32, 0x01FA7'i32, 508,
+    0x01FB0'i32, 0x01FB1'i32, 508,
+    0x01FD0'i32, 0x01FD1'i32, 508,
+    0x01FE0'i32, 0x01FE1'i32, 508,
+    0x02C30'i32, 0x02C5E'i32, 452,
+    0x02D00'i32, 0x02D25'i32, -6764,
+    0x0AB70'i32, 0x0ABBF'i32, -38364,
+    0x0FF41'i32, 0x0FF5A'i32, 468,
+    0x10428'i32, 0x1044F'i32, 460,
+    0x104D8'i32, 0x104FB'i32, 460,
+    0x10CC0'i32, 0x10CF2'i32, 436,
+    0x118C0'i32, 0x118DF'i32, 468,
+    0x16E60'i32, 0x16E7F'i32, 468,
+    0x1E922'i32, 0x1E943'i32, 466,
   ]
 
   toUpperSinglets = [
-    0x000B5, 1243,
-    0x000FF, 621,
-    0x00101, 499,
-    0x00103, 499,
-    0x00105, 499,
-    0x00107, 499,
-    0x00109, 499,
-    0x0010B, 499,
-    0x0010D, 499,
-    0x0010F, 499,
-    0x00111, 499,
-    0x00113, 499,
-    0x00115, 499,
-    0x00117, 499,
-    0x00119, 499,
-    0x0011B, 499,
-    0x0011D, 499,
-    0x0011F, 499,
-    0x00121, 499,
-    0x00123, 499,
-    0x00125, 499,
-    0x00127, 499,
-    0x00129, 499,
-    0x0012B, 499,
-    0x0012D, 499,
-    0x0012F, 499,
-    0x00131, 268,
-    0x00133, 499,
-    0x00135, 499,
-    0x00137, 499,
-    0x0013A, 499,
-    0x0013C, 499,
-    0x0013E, 499,
-    0x00140, 499,
-    0x00142, 499,
-    0x00144, 499,
-    0x00146, 499,
-    0x00148, 499,
-    0x0014B, 499,
-    0x0014D, 499,
-    0x0014F, 499,
-    0x00151, 499,
-    0x00153, 499,
-    0x00155, 499,
-    0x00157, 499,
-    0x00159, 499,
-    0x0015B, 499,
-    0x0015D, 499,
-    0x0015F, 499,
-    0x00161, 499,
-    0x00163, 499,
-    0x00165, 499,
-    0x00167, 499,
-    0x00169, 499,
-    0x0016B, 499,
-    0x0016D, 499,
-    0x0016F, 499,
-    0x00171, 499,
-    0x00173, 499,
-    0x00175, 499,
-    0x00177, 499,
-    0x0017A, 499,
-    0x0017C, 499,
-    0x0017E, 499,
-    0x0017F, 200,
-    0x00180, 695,
-    0x00183, 499,
-    0x00185, 499,
-    0x00188, 499,
-    0x0018C, 499,
-    0x00192, 499,
-    0x00195, 597,
-    0x00199, 499,
-    0x0019A, 663,
-    0x0019E, 630,
-    0x001A1, 499,
-    0x001A3, 499,
-    0x001A5, 499,
-    0x001A8, 499,
-    0x001AD, 499,
-    0x001B0, 499,
-    0x001B4, 499,
-    0x001B6, 499,
-    0x001B9, 499,
-    0x001BD, 499,
-    0x001BF, 556,
-    0x001C5, 499,
-    0x001C6, 498,
-    0x001C8, 499,
-    0x001C9, 498,
-    0x001CB, 499,
-    0x001CC, 498,
-    0x001CE, 499,
-    0x001D0, 499,
-    0x001D2, 499,
-    0x001D4, 499,
-    0x001D6, 499,
-    0x001D8, 499,
-    0x001DA, 499,
-    0x001DC, 499,
-    0x001DD, 421,
-    0x001DF, 499,
-    0x001E1, 499,
-    0x001E3, 499,
-    0x001E5, 499,
-    0x001E7, 499,
-    0x001E9, 499,
-    0x001EB, 499,
-    0x001ED, 499,
-    0x001EF, 499,
-    0x001F2, 499,
-    0x001F3, 498,
-    0x001F5, 499,
-    0x001F9, 499,
-    0x001FB, 499,
-    0x001FD, 499,
-    0x001FF, 499,
-    0x00201, 499,
-    0x00203, 499,
-    0x00205, 499,
-    0x00207, 499,
-    0x00209, 499,
-    0x0020B, 499,
-    0x0020D, 499,
-    0x0020F, 499,
-    0x00211, 499,
-    0x00213, 499,
-    0x00215, 499,
-    0x00217, 499,
-    0x00219, 499,
-    0x0021B, 499,
-    0x0021D, 499,
-    0x0021F, 499,
-    0x00223, 499,
-    0x00225, 499,
-    0x00227, 499,
-    0x00229, 499,
-    0x0022B, 499,
-    0x0022D, 499,
-    0x0022F, 499,
-    0x00231, 499,
-    0x00233, 499,
-    0x0023C, 499,
-    0x00242, 499,
-    0x00247, 499,
-    0x00249, 499,
-    0x0024B, 499,
-    0x0024D, 499,
-    0x0024F, 499,
-    0x00250, 11283,
-    0x00251, 11280,
-    0x00252, 11282,
-    0x00253, 290,
-    0x00254, 294,
-    0x00259, 298,
-    0x0025B, 297,
-    0x0025C, 42819,
-    0x00260, 295,
-    0x00261, 42815,
-    0x00263, 293,
-    0x00265, 42780,
-    0x00266, 42808,
-    0x00268, 291,
-    0x00269, 289,
-    0x0026A, 42808,
-    0x0026B, 11243,
-    0x0026C, 42805,
-    0x0026F, 289,
-    0x00271, 11249,
-    0x00272, 287,
-    0x00275, 286,
-    0x0027D, 11227,
-    0x00280, 282,
-    0x00282, 42807,
-    0x00283, 282,
-    0x00287, 42782,
-    0x00288, 282,
-    0x00289, 431,
-    0x0028C, 429,
-    0x00292, 281,
-    0x0029D, 42761,
-    0x0029E, 42758,
-    0x00371, 499,
-    0x00373, 499,
-    0x00377, 499,
-    0x003AC, 462,
-    0x003C2, 469,
-    0x003CC, 436,
-    0x003D0, 438,
-    0x003D1, 443,
-    0x003D5, 453,
-    0x003D6, 446,
-    0x003D7, 492,
-    0x003D9, 499,
-    0x003DB, 499,
-    0x003DD, 499,
-    0x003DF, 499,
-    0x003E1, 499,
-    0x003E3, 499,
-    0x003E5, 499,
-    0x003E7, 499,
-    0x003E9, 499,
-    0x003EB, 499,
-    0x003ED, 499,
-    0x003EF, 499,
-    0x003F0, 414,
-    0x003F1, 420,
-    0x003F2, 507,
-    0x003F3, 384,
-    0x003F5, 404,
-    0x003F8, 499,
-    0x003FB, 499,
-    0x00461, 499,
-    0x00463, 499,
-    0x00465, 499,
-    0x00467, 499,
-    0x00469, 499,
-    0x0046B, 499,
-    0x0046D, 499,
-    0x0046F, 499,
-    0x00471, 499,
-    0x00473, 499,
-    0x00475, 499,
-    0x00477, 499,
-    0x00479, 499,
-    0x0047B, 499,
-    0x0047D, 499,
-    0x0047F, 499,
-    0x00481, 499,
-    0x0048B, 499,
-    0x0048D, 499,
-    0x0048F, 499,
-    0x00491, 499,
-    0x00493, 499,
-    0x00495, 499,
-    0x00497, 499,
-    0x00499, 499,
-    0x0049B, 499,
-    0x0049D, 499,
-    0x0049F, 499,
-    0x004A1, 499,
-    0x004A3, 499,
-    0x004A5, 499,
-    0x004A7, 499,
-    0x004A9, 499,
-    0x004AB, 499,
-    0x004AD, 499,
-    0x004AF, 499,
-    0x004B1, 499,
-    0x004B3, 499,
-    0x004B5, 499,
-    0x004B7, 499,
-    0x004B9, 499,
-    0x004BB, 499,
-    0x004BD, 499,
-    0x004BF, 499,
-    0x004C2, 499,
-    0x004C4, 499,
-    0x004C6, 499,
-    0x004C8, 499,
-    0x004CA, 499,
-    0x004CC, 499,
-    0x004CE, 499,
-    0x004CF, 485,
-    0x004D1, 499,
-    0x004D3, 499,
-    0x004D5, 499,
-    0x004D7, 499,
-    0x004D9, 499,
-    0x004DB, 499,
-    0x004DD, 499,
-    0x004DF, 499,
-    0x004E1, 499,
-    0x004E3, 499,
-    0x004E5, 499,
-    0x004E7, 499,
-    0x004E9, 499,
-    0x004EB, 499,
-    0x004ED, 499,
-    0x004EF, 499,
-    0x004F1, 499,
-    0x004F3, 499,
-    0x004F5, 499,
-    0x004F7, 499,
-    0x004F9, 499,
-    0x004FB, 499,
-    0x004FD, 499,
-    0x004FF, 499,
-    0x00501, 499,
-    0x00503, 499,
-    0x00505, 499,
-    0x00507, 499,
-    0x00509, 499,
-    0x0050B, 499,
-    0x0050D, 499,
-    0x0050F, 499,
-    0x00511, 499,
-    0x00513, 499,
-    0x00515, 499,
-    0x00517, 499,
-    0x00519, 499,
-    0x0051B, 499,
-    0x0051D, 499,
-    0x0051F, 499,
-    0x00521, 499,
-    0x00523, 499,
-    0x00525, 499,
-    0x00527, 499,
-    0x00529, 499,
-    0x0052B, 499,
-    0x0052D, 499,
-    0x0052F, 499,
-    0x01C80, -5754,
-    0x01C81, -5753,
-    0x01C82, -5744,
-    0x01C85, -5743,
-    0x01C86, -5736,
-    0x01C87, -5681,
-    0x01C88, 35766,
-    0x01D79, 35832,
-    0x01D7D, 4314,
-    0x01D8E, 35884,
-    0x01E01, 499,
-    0x01E03, 499,
-    0x01E05, 499,
-    0x01E07, 499,
-    0x01E09, 499,
-    0x01E0B, 499,
-    0x01E0D, 499,
-    0x01E0F, 499,
-    0x01E11, 499,
-    0x01E13, 499,
-    0x01E15, 499,
-    0x01E17, 499,
-    0x01E19, 499,
-    0x01E1B, 499,
-    0x01E1D, 499,
-    0x01E1F, 499,
-    0x01E21, 499,
-    0x01E23, 499,
-    0x01E25, 499,
-    0x01E27, 499,
-    0x01E29, 499,
-    0x01E2B, 499,
-    0x01E2D, 499,
-    0x01E2F, 499,
-    0x01E31, 499,
-    0x01E33, 499,
-    0x01E35, 499,
-    0x01E37, 499,
-    0x01E39, 499,
-    0x01E3B, 499,
-    0x01E3D, 499,
-    0x01E3F, 499,
-    0x01E41, 499,
-    0x01E43, 499,
-    0x01E45, 499,
-    0x01E47, 499,
-    0x01E49, 499,
-    0x01E4B, 499,
-    0x01E4D, 499,
-    0x01E4F, 499,
-    0x01E51, 499,
-    0x01E53, 499,
-    0x01E55, 499,
-    0x01E57, 499,
-    0x01E59, 499,
-    0x01E5B, 499,
-    0x01E5D, 499,
-    0x01E5F, 499,
-    0x01E61, 499,
-    0x01E63, 499,
-    0x01E65, 499,
-    0x01E67, 499,
-    0x01E69, 499,
-    0x01E6B, 499,
-    0x01E6D, 499,
-    0x01E6F, 499,
-    0x01E71, 499,
-    0x01E73, 499,
-    0x01E75, 499,
-    0x01E77, 499,
-    0x01E79, 499,
-    0x01E7B, 499,
-    0x01E7D, 499,
-    0x01E7F, 499,
-    0x01E81, 499,
-    0x01E83, 499,
-    0x01E85, 499,
-    0x01E87, 499,
-    0x01E89, 499,
-    0x01E8B, 499,
-    0x01E8D, 499,
-    0x01E8F, 499,
-    0x01E91, 499,
-    0x01E93, 499,
-    0x01E95, 499,
-    0x01E9B, 441,
-    0x01EA1, 499,
-    0x01EA3, 499,
-    0x01EA5, 499,
-    0x01EA7, 499,
-    0x01EA9, 499,
-    0x01EAB, 499,
-    0x01EAD, 499,
-    0x01EAF, 499,
-    0x01EB1, 499,
-    0x01EB3, 499,
-    0x01EB5, 499,
-    0x01EB7, 499,
-    0x01EB9, 499,
-    0x01EBB, 499,
-    0x01EBD, 499,
-    0x01EBF, 499,
-    0x01EC1, 499,
-    0x01EC3, 499,
-    0x01EC5, 499,
-    0x01EC7, 499,
-    0x01EC9, 499,
-    0x01ECB, 499,
-    0x01ECD, 499,
-    0x01ECF, 499,
-    0x01ED1, 499,
-    0x01ED3, 499,
-    0x01ED5, 499,
-    0x01ED7, 499,
-    0x01ED9, 499,
-    0x01EDB, 499,
-    0x01EDD, 499,
-    0x01EDF, 499,
-    0x01EE1, 499,
-    0x01EE3, 499,
-    0x01EE5, 499,
-    0x01EE7, 499,
-    0x01EE9, 499,
-    0x01EEB, 499,
-    0x01EED, 499,
-    0x01EEF, 499,
-    0x01EF1, 499,
-    0x01EF3, 499,
-    0x01EF5, 499,
-    0x01EF7, 499,
-    0x01EF9, 499,
-    0x01EFB, 499,
-    0x01EFD, 499,
-    0x01EFF, 499,
-    0x01F51, 508,
-    0x01F53, 508,
-    0x01F55, 508,
-    0x01F57, 508,
-    0x01FB3, 509,
-    0x01FBE, -6705,
-    0x01FC3, 509,
-    0x01FE5, 507,
-    0x01FF3, 509,
-    0x0214E, 472,
-    0x02184, 499,
-    0x02C61, 499,
-    0x02C65, -10295,
-    0x02C66, -10292,
-    0x02C68, 499,
-    0x02C6A, 499,
-    0x02C6C, 499,
-    0x02C73, 499,
-    0x02C76, 499,
-    0x02C81, 499,
-    0x02C83, 499,
-    0x02C85, 499,
-    0x02C87, 499,
-    0x02C89, 499,
-    0x02C8B, 499,
-    0x02C8D, 499,
-    0x02C8F, 499,
-    0x02C91, 499,
-    0x02C93, 499,
-    0x02C95, 499,
-    0x02C97, 499,
-    0x02C99, 499,
-    0x02C9B, 499,
-    0x02C9D, 499,
-    0x02C9F, 499,
-    0x02CA1, 499,
-    0x02CA3, 499,
-    0x02CA5, 499,
-    0x02CA7, 499,
-    0x02CA9, 499,
-    0x02CAB, 499,
-    0x02CAD, 499,
-    0x02CAF, 499,
-    0x02CB1, 499,
-    0x02CB3, 499,
-    0x02CB5, 499,
-    0x02CB7, 499,
-    0x02CB9, 499,
-    0x02CBB, 499,
-    0x02CBD, 499,
-    0x02CBF, 499,
-    0x02CC1, 499,
-    0x02CC3, 499,
-    0x02CC5, 499,
-    0x02CC7, 499,
-    0x02CC9, 499,
-    0x02CCB, 499,
-    0x02CCD, 499,
-    0x02CCF, 499,
-    0x02CD1, 499,
-    0x02CD3, 499,
-    0x02CD5, 499,
-    0x02CD7, 499,
-    0x02CD9, 499,
-    0x02CDB, 499,
-    0x02CDD, 499,
-    0x02CDF, 499,
-    0x02CE1, 499,
-    0x02CE3, 499,
-    0x02CEC, 499,
-    0x02CEE, 499,
-    0x02CF3, 499,
-    0x02D27, -6764,
-    0x02D2D, -6764,
-    0x0A641, 499,
-    0x0A643, 499,
-    0x0A645, 499,
-    0x0A647, 499,
-    0x0A649, 499,
-    0x0A64B, 499,
-    0x0A64D, 499,
-    0x0A64F, 499,
-    0x0A651, 499,
-    0x0A653, 499,
-    0x0A655, 499,
-    0x0A657, 499,
-    0x0A659, 499,
-    0x0A65B, 499,
-    0x0A65D, 499,
-    0x0A65F, 499,
-    0x0A661, 499,
-    0x0A663, 499,
-    0x0A665, 499,
-    0x0A667, 499,
-    0x0A669, 499,
-    0x0A66B, 499,
-    0x0A66D, 499,
-    0x0A681, 499,
-    0x0A683, 499,
-    0x0A685, 499,
-    0x0A687, 499,
-    0x0A689, 499,
-    0x0A68B, 499,
-    0x0A68D, 499,
-    0x0A68F, 499,
-    0x0A691, 499,
-    0x0A693, 499,
-    0x0A695, 499,
-    0x0A697, 499,
-    0x0A699, 499,
-    0x0A69B, 499,
-    0x0A723, 499,
-    0x0A725, 499,
-    0x0A727, 499,
-    0x0A729, 499,
-    0x0A72B, 499,
-    0x0A72D, 499,
-    0x0A72F, 499,
-    0x0A733, 499,
-    0x0A735, 499,
-    0x0A737, 499,
-    0x0A739, 499,
-    0x0A73B, 499,
-    0x0A73D, 499,
-    0x0A73F, 499,
-    0x0A741, 499,
-    0x0A743, 499,
-    0x0A745, 499,
-    0x0A747, 499,
-    0x0A749, 499,
-    0x0A74B, 499,
-    0x0A74D, 499,
-    0x0A74F, 499,
-    0x0A751, 499,
-    0x0A753, 499,
-    0x0A755, 499,
-    0x0A757, 499,
-    0x0A759, 499,
-    0x0A75B, 499,
-    0x0A75D, 499,
-    0x0A75F, 499,
-    0x0A761, 499,
-    0x0A763, 499,
-    0x0A765, 499,
-    0x0A767, 499,
-    0x0A769, 499,
-    0x0A76B, 499,
-    0x0A76D, 499,
-    0x0A76F, 499,
-    0x0A77A, 499,
-    0x0A77C, 499,
-    0x0A77F, 499,
-    0x0A781, 499,
-    0x0A783, 499,
-    0x0A785, 499,
-    0x0A787, 499,
-    0x0A78C, 499,
-    0x0A791, 499,
-    0x0A793, 499,
-    0x0A794, 548,
-    0x0A797, 499,
-    0x0A799, 499,
-    0x0A79B, 499,
-    0x0A79D, 499,
-    0x0A79F, 499,
-    0x0A7A1, 499,
-    0x0A7A3, 499,
-    0x0A7A5, 499,
-    0x0A7A7, 499,
-    0x0A7A9, 499,
-    0x0A7B5, 499,
-    0x0A7B7, 499,
-    0x0A7B9, 499,
-    0x0A7BB, 499,
-    0x0A7BD, 499,
-    0x0A7BF, 499,
-    0x0A7C3, 499,
-    0x0AB53, -428,
+    0x000B5'i32, 1243,
+    0x000FF'i32, 621,
+    0x00101'i32, 499,
+    0x00103'i32, 499,
+    0x00105'i32, 499,
+    0x00107'i32, 499,
+    0x00109'i32, 499,
+    0x0010B'i32, 499,
+    0x0010D'i32, 499,
+    0x0010F'i32, 499,
+    0x00111'i32, 499,
+    0x00113'i32, 499,
+    0x00115'i32, 499,
+    0x00117'i32, 499,
+    0x00119'i32, 499,
+    0x0011B'i32, 499,
+    0x0011D'i32, 499,
+    0x0011F'i32, 499,
+    0x00121'i32, 499,
+    0x00123'i32, 499,
+    0x00125'i32, 499,
+    0x00127'i32, 499,
+    0x00129'i32, 499,
+    0x0012B'i32, 499,
+    0x0012D'i32, 499,
+    0x0012F'i32, 499,
+    0x00131'i32, 268,
+    0x00133'i32, 499,
+    0x00135'i32, 499,
+    0x00137'i32, 499,
+    0x0013A'i32, 499,
+    0x0013C'i32, 499,
+    0x0013E'i32, 499,
+    0x00140'i32, 499,
+    0x00142'i32, 499,
+    0x00144'i32, 499,
+    0x00146'i32, 499,
+    0x00148'i32, 499,
+    0x0014B'i32, 499,
+    0x0014D'i32, 499,
+    0x0014F'i32, 499,
+    0x00151'i32, 499,
+    0x00153'i32, 499,
+    0x00155'i32, 499,
+    0x00157'i32, 499,
+    0x00159'i32, 499,
+    0x0015B'i32, 499,
+    0x0015D'i32, 499,
+    0x0015F'i32, 499,
+    0x00161'i32, 499,
+    0x00163'i32, 499,
+    0x00165'i32, 499,
+    0x00167'i32, 499,
+    0x00169'i32, 499,
+    0x0016B'i32, 499,
+    0x0016D'i32, 499,
+    0x0016F'i32, 499,
+    0x00171'i32, 499,
+    0x00173'i32, 499,
+    0x00175'i32, 499,
+    0x00177'i32, 499,
+    0x0017A'i32, 499,
+    0x0017C'i32, 499,
+    0x0017E'i32, 499,
+    0x0017F'i32, 200,
+    0x00180'i32, 695,
+    0x00183'i32, 499,
+    0x00185'i32, 499,
+    0x00188'i32, 499,
+    0x0018C'i32, 499,
+    0x00192'i32, 499,
+    0x00195'i32, 597,
+    0x00199'i32, 499,
+    0x0019A'i32, 663,
+    0x0019E'i32, 630,
+    0x001A1'i32, 499,
+    0x001A3'i32, 499,
+    0x001A5'i32, 499,
+    0x001A8'i32, 499,
+    0x001AD'i32, 499,
+    0x001B0'i32, 499,
+    0x001B4'i32, 499,
+    0x001B6'i32, 499,
+    0x001B9'i32, 499,
+    0x001BD'i32, 499,
+    0x001BF'i32, 556,
+    0x001C5'i32, 499,
+    0x001C6'i32, 498,
+    0x001C8'i32, 499,
+    0x001C9'i32, 498,
+    0x001CB'i32, 499,
+    0x001CC'i32, 498,
+    0x001CE'i32, 499,
+    0x001D0'i32, 499,
+    0x001D2'i32, 499,
+    0x001D4'i32, 499,
+    0x001D6'i32, 499,
+    0x001D8'i32, 499,
+    0x001DA'i32, 499,
+    0x001DC'i32, 499,
+    0x001DD'i32, 421,
+    0x001DF'i32, 499,
+    0x001E1'i32, 499,
+    0x001E3'i32, 499,
+    0x001E5'i32, 499,
+    0x001E7'i32, 499,
+    0x001E9'i32, 499,
+    0x001EB'i32, 499,
+    0x001ED'i32, 499,
+    0x001EF'i32, 499,
+    0x001F2'i32, 499,
+    0x001F3'i32, 498,
+    0x001F5'i32, 499,
+    0x001F9'i32, 499,
+    0x001FB'i32, 499,
+    0x001FD'i32, 499,
+    0x001FF'i32, 499,
+    0x00201'i32, 499,
+    0x00203'i32, 499,
+    0x00205'i32, 499,
+    0x00207'i32, 499,
+    0x00209'i32, 499,
+    0x0020B'i32, 499,
+    0x0020D'i32, 499,
+    0x0020F'i32, 499,
+    0x00211'i32, 499,
+    0x00213'i32, 499,
+    0x00215'i32, 499,
+    0x00217'i32, 499,
+    0x00219'i32, 499,
+    0x0021B'i32, 499,
+    0x0021D'i32, 499,
+    0x0021F'i32, 499,
+    0x00223'i32, 499,
+    0x00225'i32, 499,
+    0x00227'i32, 499,
+    0x00229'i32, 499,
+    0x0022B'i32, 499,
+    0x0022D'i32, 499,
+    0x0022F'i32, 499,
+    0x00231'i32, 499,
+    0x00233'i32, 499,
+    0x0023C'i32, 499,
+    0x00242'i32, 499,
+    0x00247'i32, 499,
+    0x00249'i32, 499,
+    0x0024B'i32, 499,
+    0x0024D'i32, 499,
+    0x0024F'i32, 499,
+    0x00250'i32, 11283,
+    0x00251'i32, 11280,
+    0x00252'i32, 11282,
+    0x00253'i32, 290,
+    0x00254'i32, 294,
+    0x00259'i32, 298,
+    0x0025B'i32, 297,
+    0x0025C'i32, 42819,
+    0x00260'i32, 295,
+    0x00261'i32, 42815,
+    0x00263'i32, 293,
+    0x00265'i32, 42780,
+    0x00266'i32, 42808,
+    0x00268'i32, 291,
+    0x00269'i32, 289,
+    0x0026A'i32, 42808,
+    0x0026B'i32, 11243,
+    0x0026C'i32, 42805,
+    0x0026F'i32, 289,
+    0x00271'i32, 11249,
+    0x00272'i32, 287,
+    0x00275'i32, 286,
+    0x0027D'i32, 11227,
+    0x00280'i32, 282,
+    0x00282'i32, 42807,
+    0x00283'i32, 282,
+    0x00287'i32, 42782,
+    0x00288'i32, 282,
+    0x00289'i32, 431,
+    0x0028C'i32, 429,
+    0x00292'i32, 281,
+    0x0029D'i32, 42761,
+    0x0029E'i32, 42758,
+    0x00371'i32, 499,
+    0x00373'i32, 499,
+    0x00377'i32, 499,
+    0x003AC'i32, 462,
+    0x003C2'i32, 469,
+    0x003CC'i32, 436,
+    0x003D0'i32, 438,
+    0x003D1'i32, 443,
+    0x003D5'i32, 453,
+    0x003D6'i32, 446,
+    0x003D7'i32, 492,
+    0x003D9'i32, 499,
+    0x003DB'i32, 499,
+    0x003DD'i32, 499,
+    0x003DF'i32, 499,
+    0x003E1'i32, 499,
+    0x003E3'i32, 499,
+    0x003E5'i32, 499,
+    0x003E7'i32, 499,
+    0x003E9'i32, 499,
+    0x003EB'i32, 499,
+    0x003ED'i32, 499,
+    0x003EF'i32, 499,
+    0x003F0'i32, 414,
+    0x003F1'i32, 420,
+    0x003F2'i32, 507,
+    0x003F3'i32, 384,
+    0x003F5'i32, 404,
+    0x003F8'i32, 499,
+    0x003FB'i32, 499,
+    0x00461'i32, 499,
+    0x00463'i32, 499,
+    0x00465'i32, 499,
+    0x00467'i32, 499,
+    0x00469'i32, 499,
+    0x0046B'i32, 499,
+    0x0046D'i32, 499,
+    0x0046F'i32, 499,
+    0x00471'i32, 499,
+    0x00473'i32, 499,
+    0x00475'i32, 499,
+    0x00477'i32, 499,
+    0x00479'i32, 499,
+    0x0047B'i32, 499,
+    0x0047D'i32, 499,
+    0x0047F'i32, 499,
+    0x00481'i32, 499,
+    0x0048B'i32, 499,
+    0x0048D'i32, 499,
+    0x0048F'i32, 499,
+    0x00491'i32, 499,
+    0x00493'i32, 499,
+    0x00495'i32, 499,
+    0x00497'i32, 499,
+    0x00499'i32, 499,
+    0x0049B'i32, 499,
+    0x0049D'i32, 499,
+    0x0049F'i32, 499,
+    0x004A1'i32, 499,
+    0x004A3'i32, 499,
+    0x004A5'i32, 499,
+    0x004A7'i32, 499,
+    0x004A9'i32, 499,
+    0x004AB'i32, 499,
+    0x004AD'i32, 499,
+    0x004AF'i32, 499,
+    0x004B1'i32, 499,
+    0x004B3'i32, 499,
+    0x004B5'i32, 499,
+    0x004B7'i32, 499,
+    0x004B9'i32, 499,
+    0x004BB'i32, 499,
+    0x004BD'i32, 499,
+    0x004BF'i32, 499,
+    0x004C2'i32, 499,
+    0x004C4'i32, 499,
+    0x004C6'i32, 499,
+    0x004C8'i32, 499,
+    0x004CA'i32, 499,
+    0x004CC'i32, 499,
+    0x004CE'i32, 499,
+    0x004CF'i32, 485,
+    0x004D1'i32, 499,
+    0x004D3'i32, 499,
+    0x004D5'i32, 499,
+    0x004D7'i32, 499,
+    0x004D9'i32, 499,
+    0x004DB'i32, 499,
+    0x004DD'i32, 499,
+    0x004DF'i32, 499,
+    0x004E1'i32, 499,
+    0x004E3'i32, 499,
+    0x004E5'i32, 499,
+    0x004E7'i32, 499,
+    0x004E9'i32, 499,
+    0x004EB'i32, 499,
+    0x004ED'i32, 499,
+    0x004EF'i32, 499,
+    0x004F1'i32, 499,
+    0x004F3'i32, 499,
+    0x004F5'i32, 499,
+    0x004F7'i32, 499,
+    0x004F9'i32, 499,
+    0x004FB'i32, 499,
+    0x004FD'i32, 499,
+    0x004FF'i32, 499,
+    0x00501'i32, 499,
+    0x00503'i32, 499,
+    0x00505'i32, 499,
+    0x00507'i32, 499,
+    0x00509'i32, 499,
+    0x0050B'i32, 499,
+    0x0050D'i32, 499,
+    0x0050F'i32, 499,
+    0x00511'i32, 499,
+    0x00513'i32, 499,
+    0x00515'i32, 499,
+    0x00517'i32, 499,
+    0x00519'i32, 499,
+    0x0051B'i32, 499,
+    0x0051D'i32, 499,
+    0x0051F'i32, 499,
+    0x00521'i32, 499,
+    0x00523'i32, 499,
+    0x00525'i32, 499,
+    0x00527'i32, 499,
+    0x00529'i32, 499,
+    0x0052B'i32, 499,
+    0x0052D'i32, 499,
+    0x0052F'i32, 499,
+    0x01C80'i32, -5754,
+    0x01C81'i32, -5753,
+    0x01C82'i32, -5744,
+    0x01C85'i32, -5743,
+    0x01C86'i32, -5736,
+    0x01C87'i32, -5681,
+    0x01C88'i32, 35766,
+    0x01D79'i32, 35832,
+    0x01D7D'i32, 4314,
+    0x01D8E'i32, 35884,
+    0x01E01'i32, 499,
+    0x01E03'i32, 499,
+    0x01E05'i32, 499,
+    0x01E07'i32, 499,
+    0x01E09'i32, 499,
+    0x01E0B'i32, 499,
+    0x01E0D'i32, 499,
+    0x01E0F'i32, 499,
+    0x01E11'i32, 499,
+    0x01E13'i32, 499,
+    0x01E15'i32, 499,
+    0x01E17'i32, 499,
+    0x01E19'i32, 499,
+    0x01E1B'i32, 499,
+    0x01E1D'i32, 499,
+    0x01E1F'i32, 499,
+    0x01E21'i32, 499,
+    0x01E23'i32, 499,
+    0x01E25'i32, 499,
+    0x01E27'i32, 499,
+    0x01E29'i32, 499,
+    0x01E2B'i32, 499,
+    0x01E2D'i32, 499,
+    0x01E2F'i32, 499,
+    0x01E31'i32, 499,
+    0x01E33'i32, 499,
+    0x01E35'i32, 499,
+    0x01E37'i32, 499,
+    0x01E39'i32, 499,
+    0x01E3B'i32, 499,
+    0x01E3D'i32, 499,
+    0x01E3F'i32, 499,
+    0x01E41'i32, 499,
+    0x01E43'i32, 499,
+    0x01E45'i32, 499,
+    0x01E47'i32, 499,
+    0x01E49'i32, 499,
+    0x01E4B'i32, 499,
+    0x01E4D'i32, 499,
+    0x01E4F'i32, 499,
+    0x01E51'i32, 499,
+    0x01E53'i32, 499,
+    0x01E55'i32, 499,
+    0x01E57'i32, 499,
+    0x01E59'i32, 499,
+    0x01E5B'i32, 499,
+    0x01E5D'i32, 499,
+    0x01E5F'i32, 499,
+    0x01E61'i32, 499,
+    0x01E63'i32, 499,
+    0x01E65'i32, 499,
+    0x01E67'i32, 499,
+    0x01E69'i32, 499,
+    0x01E6B'i32, 499,
+    0x01E6D'i32, 499,
+    0x01E6F'i32, 499,
+    0x01E71'i32, 499,
+    0x01E73'i32, 499,
+    0x01E75'i32, 499,
+    0x01E77'i32, 499,
+    0x01E79'i32, 499,
+    0x01E7B'i32, 499,
+    0x01E7D'i32, 499,
+    0x01E7F'i32, 499,
+    0x01E81'i32, 499,
+    0x01E83'i32, 499,
+    0x01E85'i32, 499,
+    0x01E87'i32, 499,
+    0x01E89'i32, 499,
+    0x01E8B'i32, 499,
+    0x01E8D'i32, 499,
+    0x01E8F'i32, 499,
+    0x01E91'i32, 499,
+    0x01E93'i32, 499,
+    0x01E95'i32, 499,
+    0x01E9B'i32, 441,
+    0x01EA1'i32, 499,
+    0x01EA3'i32, 499,
+    0x01EA5'i32, 499,
+    0x01EA7'i32, 499,
+    0x01EA9'i32, 499,
+    0x01EAB'i32, 499,
+    0x01EAD'i32, 499,
+    0x01EAF'i32, 499,
+    0x01EB1'i32, 499,
+    0x01EB3'i32, 499,
+    0x01EB5'i32, 499,
+    0x01EB7'i32, 499,
+    0x01EB9'i32, 499,
+    0x01EBB'i32, 499,
+    0x01EBD'i32, 499,
+    0x01EBF'i32, 499,
+    0x01EC1'i32, 499,
+    0x01EC3'i32, 499,
+    0x01EC5'i32, 499,
+    0x01EC7'i32, 499,
+    0x01EC9'i32, 499,
+    0x01ECB'i32, 499,
+    0x01ECD'i32, 499,
+    0x01ECF'i32, 499,
+    0x01ED1'i32, 499,
+    0x01ED3'i32, 499,
+    0x01ED5'i32, 499,
+    0x01ED7'i32, 499,
+    0x01ED9'i32, 499,
+    0x01EDB'i32, 499,
+    0x01EDD'i32, 499,
+    0x01EDF'i32, 499,
+    0x01EE1'i32, 499,
+    0x01EE3'i32, 499,
+    0x01EE5'i32, 499,
+    0x01EE7'i32, 499,
+    0x01EE9'i32, 499,
+    0x01EEB'i32, 499,
+    0x01EED'i32, 499,
+    0x01EEF'i32, 499,
+    0x01EF1'i32, 499,
+    0x01EF3'i32, 499,
+    0x01EF5'i32, 499,
+    0x01EF7'i32, 499,
+    0x01EF9'i32, 499,
+    0x01EFB'i32, 499,
+    0x01EFD'i32, 499,
+    0x01EFF'i32, 499,
+    0x01F51'i32, 508,
+    0x01F53'i32, 508,
+    0x01F55'i32, 508,
+    0x01F57'i32, 508,
+    0x01FB3'i32, 509,
+    0x01FBE'i32, -6705,
+    0x01FC3'i32, 509,
+    0x01FE5'i32, 507,
+    0x01FF3'i32, 509,
+    0x0214E'i32, 472,
+    0x02184'i32, 499,
+    0x02C61'i32, 499,
+    0x02C65'i32, -10295,
+    0x02C66'i32, -10292,
+    0x02C68'i32, 499,
+    0x02C6A'i32, 499,
+    0x02C6C'i32, 499,
+    0x02C73'i32, 499,
+    0x02C76'i32, 499,
+    0x02C81'i32, 499,
+    0x02C83'i32, 499,
+    0x02C85'i32, 499,
+    0x02C87'i32, 499,
+    0x02C89'i32, 499,
+    0x02C8B'i32, 499,
+    0x02C8D'i32, 499,
+    0x02C8F'i32, 499,
+    0x02C91'i32, 499,
+    0x02C93'i32, 499,
+    0x02C95'i32, 499,
+    0x02C97'i32, 499,
+    0x02C99'i32, 499,
+    0x02C9B'i32, 499,
+    0x02C9D'i32, 499,
+    0x02C9F'i32, 499,
+    0x02CA1'i32, 499,
+    0x02CA3'i32, 499,
+    0x02CA5'i32, 499,
+    0x02CA7'i32, 499,
+    0x02CA9'i32, 499,
+    0x02CAB'i32, 499,
+    0x02CAD'i32, 499,
+    0x02CAF'i32, 499,
+    0x02CB1'i32, 499,
+    0x02CB3'i32, 499,
+    0x02CB5'i32, 499,
+    0x02CB7'i32, 499,
+    0x02CB9'i32, 499,
+    0x02CBB'i32, 499,
+    0x02CBD'i32, 499,
+    0x02CBF'i32, 499,
+    0x02CC1'i32, 499,
+    0x02CC3'i32, 499,
+    0x02CC5'i32, 499,
+    0x02CC7'i32, 499,
+    0x02CC9'i32, 499,
+    0x02CCB'i32, 499,
+    0x02CCD'i32, 499,
+    0x02CCF'i32, 499,
+    0x02CD1'i32, 499,
+    0x02CD3'i32, 499,
+    0x02CD5'i32, 499,
+    0x02CD7'i32, 499,
+    0x02CD9'i32, 499,
+    0x02CDB'i32, 499,
+    0x02CDD'i32, 499,
+    0x02CDF'i32, 499,
+    0x02CE1'i32, 499,
+    0x02CE3'i32, 499,
+    0x02CEC'i32, 499,
+    0x02CEE'i32, 499,
+    0x02CF3'i32, 499,
+    0x02D27'i32, -6764,
+    0x02D2D'i32, -6764,
+    0x0A641'i32, 499,
+    0x0A643'i32, 499,
+    0x0A645'i32, 499,
+    0x0A647'i32, 499,
+    0x0A649'i32, 499,
+    0x0A64B'i32, 499,
+    0x0A64D'i32, 499,
+    0x0A64F'i32, 499,
+    0x0A651'i32, 499,
+    0x0A653'i32, 499,
+    0x0A655'i32, 499,
+    0x0A657'i32, 499,
+    0x0A659'i32, 499,
+    0x0A65B'i32, 499,
+    0x0A65D'i32, 499,
+    0x0A65F'i32, 499,
+    0x0A661'i32, 499,
+    0x0A663'i32, 499,
+    0x0A665'i32, 499,
+    0x0A667'i32, 499,
+    0x0A669'i32, 499,
+    0x0A66B'i32, 499,
+    0x0A66D'i32, 499,
+    0x0A681'i32, 499,
+    0x0A683'i32, 499,
+    0x0A685'i32, 499,
+    0x0A687'i32, 499,
+    0x0A689'i32, 499,
+    0x0A68B'i32, 499,
+    0x0A68D'i32, 499,
+    0x0A68F'i32, 499,
+    0x0A691'i32, 499,
+    0x0A693'i32, 499,
+    0x0A695'i32, 499,
+    0x0A697'i32, 499,
+    0x0A699'i32, 499,
+    0x0A69B'i32, 499,
+    0x0A723'i32, 499,
+    0x0A725'i32, 499,
+    0x0A727'i32, 499,
+    0x0A729'i32, 499,
+    0x0A72B'i32, 499,
+    0x0A72D'i32, 499,
+    0x0A72F'i32, 499,
+    0x0A733'i32, 499,
+    0x0A735'i32, 499,
+    0x0A737'i32, 499,
+    0x0A739'i32, 499,
+    0x0A73B'i32, 499,
+    0x0A73D'i32, 499,
+    0x0A73F'i32, 499,
+    0x0A741'i32, 499,
+    0x0A743'i32, 499,
+    0x0A745'i32, 499,
+    0x0A747'i32, 499,
+    0x0A749'i32, 499,
+    0x0A74B'i32, 499,
+    0x0A74D'i32, 499,
+    0x0A74F'i32, 499,
+    0x0A751'i32, 499,
+    0x0A753'i32, 499,
+    0x0A755'i32, 499,
+    0x0A757'i32, 499,
+    0x0A759'i32, 499,
+    0x0A75B'i32, 499,
+    0x0A75D'i32, 499,
+    0x0A75F'i32, 499,
+    0x0A761'i32, 499,
+    0x0A763'i32, 499,
+    0x0A765'i32, 499,
+    0x0A767'i32, 499,
+    0x0A769'i32, 499,
+    0x0A76B'i32, 499,
+    0x0A76D'i32, 499,
+    0x0A76F'i32, 499,
+    0x0A77A'i32, 499,
+    0x0A77C'i32, 499,
+    0x0A77F'i32, 499,
+    0x0A781'i32, 499,
+    0x0A783'i32, 499,
+    0x0A785'i32, 499,
+    0x0A787'i32, 499,
+    0x0A78C'i32, 499,
+    0x0A791'i32, 499,
+    0x0A793'i32, 499,
+    0x0A794'i32, 548,
+    0x0A797'i32, 499,
+    0x0A799'i32, 499,
+    0x0A79B'i32, 499,
+    0x0A79D'i32, 499,
+    0x0A79F'i32, 499,
+    0x0A7A1'i32, 499,
+    0x0A7A3'i32, 499,
+    0x0A7A5'i32, 499,
+    0x0A7A7'i32, 499,
+    0x0A7A9'i32, 499,
+    0x0A7B5'i32, 499,
+    0x0A7B7'i32, 499,
+    0x0A7B9'i32, 499,
+    0x0A7BB'i32, 499,
+    0x0A7BD'i32, 499,
+    0x0A7BF'i32, 499,
+    0x0A7C3'i32, 499,
+    0x0AB53'i32, -428,
   ]
 
   toTitleSinglets = [
-    0x001C4, 501,
-    0x001C6, 499,
-    0x001C7, 501,
-    0x001C9, 499,
-    0x001CA, 501,
-    0x001CC, 499,
-    0x001F1, 501,
-    0x001F3, 499,
+    0x001C4'i32, 501,
+    0x001C6'i32, 499,
+    0x001C7'i32, 501,
+    0x001C9'i32, 499,
+    0x001CA'i32, 501,
+    0x001CC'i32, 499,
+    0x001F1'i32, 501,
+    0x001F3'i32, 499,
   ]
 
   alphaRanges = [
-    0x00041, 0x0005A,
-    0x00061, 0x0007A,
-    0x000C0, 0x000D6,
-    0x000D8, 0x000F6,
-    0x000F8, 0x002C1,
-    0x002C6, 0x002D1,
-    0x002E0, 0x002E4,
-    0x00370, 0x00374,
-    0x00376, 0x00377,
-    0x0037A, 0x0037D,
-    0x00388, 0x0038A,
-    0x0038E, 0x003A1,
-    0x003A3, 0x003F5,
-    0x003F7, 0x00481,
-    0x0048A, 0x0052F,
-    0x00531, 0x00556,
-    0x00560, 0x00588,
-    0x005D0, 0x005EA,
-    0x005EF, 0x005F2,
-    0x00620, 0x0064A,
-    0x0066E, 0x0066F,
-    0x00671, 0x006D3,
-    0x006E5, 0x006E6,
-    0x006EE, 0x006EF,
-    0x006FA, 0x006FC,
-    0x00712, 0x0072F,
-    0x0074D, 0x007A5,
-    0x007CA, 0x007EA,
-    0x007F4, 0x007F5,
-    0x00800, 0x00815,
-    0x00840, 0x00858,
-    0x00860, 0x0086A,
-    0x008A0, 0x008B4,
-    0x008B6, 0x008BD,
-    0x00904, 0x00939,
-    0x00958, 0x00961,
-    0x00971, 0x00980,
-    0x00985, 0x0098C,
-    0x0098F, 0x00990,
-    0x00993, 0x009A8,
-    0x009AA, 0x009B0,
-    0x009B6, 0x009B9,
-    0x009DC, 0x009DD,
-    0x009DF, 0x009E1,
-    0x009F0, 0x009F1,
-    0x00A05, 0x00A0A,
-    0x00A0F, 0x00A10,
-    0x00A13, 0x00A28,
-    0x00A2A, 0x00A30,
-    0x00A32, 0x00A33,
-    0x00A35, 0x00A36,
-    0x00A38, 0x00A39,
-    0x00A59, 0x00A5C,
-    0x00A72, 0x00A74,
-    0x00A85, 0x00A8D,
-    0x00A8F, 0x00A91,
-    0x00A93, 0x00AA8,
-    0x00AAA, 0x00AB0,
-    0x00AB2, 0x00AB3,
-    0x00AB5, 0x00AB9,
-    0x00AE0, 0x00AE1,
-    0x00B05, 0x00B0C,
-    0x00B0F, 0x00B10,
-    0x00B13, 0x00B28,
-    0x00B2A, 0x00B30,
-    0x00B32, 0x00B33,
-    0x00B35, 0x00B39,
-    0x00B5C, 0x00B5D,
-    0x00B5F, 0x00B61,
-    0x00B85, 0x00B8A,
-    0x00B8E, 0x00B90,
-    0x00B92, 0x00B95,
-    0x00B99, 0x00B9A,
-    0x00B9E, 0x00B9F,
-    0x00BA3, 0x00BA4,
-    0x00BA8, 0x00BAA,
-    0x00BAE, 0x00BB9,
-    0x00C05, 0x00C0C,
-    0x00C0E, 0x00C10,
-    0x00C12, 0x00C28,
-    0x00C2A, 0x00C39,
-    0x00C58, 0x00C5A,
-    0x00C60, 0x00C61,
-    0x00C85, 0x00C8C,
-    0x00C8E, 0x00C90,
-    0x00C92, 0x00CA8,
-    0x00CAA, 0x00CB3,
-    0x00CB5, 0x00CB9,
-    0x00CE0, 0x00CE1,
-    0x00CF1, 0x00CF2,
-    0x00D05, 0x00D0C,
-    0x00D0E, 0x00D10,
-    0x00D12, 0x00D3A,
-    0x00D54, 0x00D56,
-    0x00D5F, 0x00D61,
-    0x00D7A, 0x00D7F,
-    0x00D85, 0x00D96,
-    0x00D9A, 0x00DB1,
-    0x00DB3, 0x00DBB,
-    0x00DC0, 0x00DC6,
-    0x00E01, 0x00E30,
-    0x00E32, 0x00E33,
-    0x00E40, 0x00E46,
-    0x00E81, 0x00E82,
-    0x00E86, 0x00E8A,
-    0x00E8C, 0x00EA3,
-    0x00EA7, 0x00EB0,
-    0x00EB2, 0x00EB3,
-    0x00EC0, 0x00EC4,
-    0x00EDC, 0x00EDF,
-    0x00F40, 0x00F47,
-    0x00F49, 0x00F6C,
-    0x00F88, 0x00F8C,
-    0x01000, 0x0102A,
-    0x01050, 0x01055,
-    0x0105A, 0x0105D,
-    0x01065, 0x01066,
-    0x0106E, 0x01070,
-    0x01075, 0x01081,
-    0x010A0, 0x010C5,
-    0x010D0, 0x010FA,
-    0x010FC, 0x01248,
-    0x0124A, 0x0124D,
-    0x01250, 0x01256,
-    0x0125A, 0x0125D,
-    0x01260, 0x01288,
-    0x0128A, 0x0128D,
-    0x01290, 0x012B0,
-    0x012B2, 0x012B5,
-    0x012B8, 0x012BE,
-    0x012C2, 0x012C5,
-    0x012C8, 0x012D6,
-    0x012D8, 0x01310,
-    0x01312, 0x01315,
-    0x01318, 0x0135A,
-    0x01380, 0x0138F,
-    0x013A0, 0x013F5,
-    0x013F8, 0x013FD,
-    0x01401, 0x0166C,
-    0x0166F, 0x0167F,
-    0x01681, 0x0169A,
-    0x016A0, 0x016EA,
-    0x016F1, 0x016F8,
-    0x01700, 0x0170C,
-    0x0170E, 0x01711,
-    0x01720, 0x01731,
-    0x01740, 0x01751,
-    0x01760, 0x0176C,
-    0x0176E, 0x01770,
-    0x01780, 0x017B3,
-    0x01820, 0x01878,
-    0x01880, 0x01884,
-    0x01887, 0x018A8,
-    0x018B0, 0x018F5,
-    0x01900, 0x0191E,
-    0x01950, 0x0196D,
-    0x01970, 0x01974,
-    0x01980, 0x019AB,
-    0x019B0, 0x019C9,
-    0x01A00, 0x01A16,
-    0x01A20, 0x01A54,
-    0x01B05, 0x01B33,
-    0x01B45, 0x01B4B,
-    0x01B83, 0x01BA0,
-    0x01BAE, 0x01BAF,
-    0x01BBA, 0x01BE5,
-    0x01C00, 0x01C23,
-    0x01C4D, 0x01C4F,
-    0x01C5A, 0x01C7D,
-    0x01C80, 0x01C88,
-    0x01C90, 0x01CBA,
-    0x01CBD, 0x01CBF,
-    0x01CE9, 0x01CEC,
-    0x01CEE, 0x01CF3,
-    0x01CF5, 0x01CF6,
-    0x01D00, 0x01DBF,
-    0x01E00, 0x01F15,
-    0x01F18, 0x01F1D,
-    0x01F20, 0x01F45,
-    0x01F48, 0x01F4D,
-    0x01F50, 0x01F57,
-    0x01F5F, 0x01F7D,
-    0x01F80, 0x01FB4,
-    0x01FB6, 0x01FBC,
-    0x01FC2, 0x01FC4,
-    0x01FC6, 0x01FCC,
-    0x01FD0, 0x01FD3,
-    0x01FD6, 0x01FDB,
-    0x01FE0, 0x01FEC,
-    0x01FF2, 0x01FF4,
-    0x01FF6, 0x01FFC,
-    0x02090, 0x0209C,
-    0x0210A, 0x02113,
-    0x02119, 0x0211D,
-    0x0212A, 0x0212D,
-    0x0212F, 0x02139,
-    0x0213C, 0x0213F,
-    0x02145, 0x02149,
-    0x02183, 0x02184,
-    0x02C00, 0x02C2E,
-    0x02C30, 0x02C5E,
-    0x02C60, 0x02CE4,
-    0x02CEB, 0x02CEE,
-    0x02CF2, 0x02CF3,
-    0x02D00, 0x02D25,
-    0x02D30, 0x02D67,
-    0x02D80, 0x02D96,
-    0x02DA0, 0x02DA6,
-    0x02DA8, 0x02DAE,
-    0x02DB0, 0x02DB6,
-    0x02DB8, 0x02DBE,
-    0x02DC0, 0x02DC6,
-    0x02DC8, 0x02DCE,
-    0x02DD0, 0x02DD6,
-    0x02DD8, 0x02DDE,
-    0x03005, 0x03006,
-    0x03031, 0x03035,
-    0x0303B, 0x0303C,
-    0x03041, 0x03096,
-    0x0309D, 0x0309F,
-    0x030A1, 0x030FA,
-    0x030FC, 0x030FF,
-    0x03105, 0x0312F,
-    0x03131, 0x0318E,
-    0x031A0, 0x031BA,
-    0x031F0, 0x031FF,
-    0x0A000, 0x0A48C,
-    0x0A4D0, 0x0A4FD,
-    0x0A500, 0x0A60C,
-    0x0A610, 0x0A61F,
-    0x0A62A, 0x0A62B,
-    0x0A640, 0x0A66E,
-    0x0A67F, 0x0A69D,
-    0x0A6A0, 0x0A6E5,
-    0x0A717, 0x0A71F,
-    0x0A722, 0x0A788,
-    0x0A78B, 0x0A7BF,
-    0x0A7C2, 0x0A7C6,
-    0x0A7F7, 0x0A801,
-    0x0A803, 0x0A805,
-    0x0A807, 0x0A80A,
-    0x0A80C, 0x0A822,
-    0x0A840, 0x0A873,
-    0x0A882, 0x0A8B3,
-    0x0A8F2, 0x0A8F7,
-    0x0A8FD, 0x0A8FE,
-    0x0A90A, 0x0A925,
-    0x0A930, 0x0A946,
-    0x0A960, 0x0A97C,
-    0x0A984, 0x0A9B2,
-    0x0A9E0, 0x0A9E4,
-    0x0A9E6, 0x0A9EF,
-    0x0A9FA, 0x0A9FE,
-    0x0AA00, 0x0AA28,
-    0x0AA40, 0x0AA42,
-    0x0AA44, 0x0AA4B,
-    0x0AA60, 0x0AA76,
-    0x0AA7E, 0x0AAAF,
-    0x0AAB5, 0x0AAB6,
-    0x0AAB9, 0x0AABD,
-    0x0AADB, 0x0AADD,
-    0x0AAE0, 0x0AAEA,
-    0x0AAF2, 0x0AAF4,
-    0x0AB01, 0x0AB06,
-    0x0AB09, 0x0AB0E,
-    0x0AB11, 0x0AB16,
-    0x0AB20, 0x0AB26,
-    0x0AB28, 0x0AB2E,
-    0x0AB30, 0x0AB5A,
-    0x0AB5C, 0x0AB67,
-    0x0AB70, 0x0ABE2,
-    0x0D7B0, 0x0D7C6,
-    0x0D7CB, 0x0D7FB,
-    0x0F900, 0x0FA6D,
-    0x0FA70, 0x0FAD9,
-    0x0FB00, 0x0FB06,
-    0x0FB13, 0x0FB17,
-    0x0FB1F, 0x0FB28,
-    0x0FB2A, 0x0FB36,
-    0x0FB38, 0x0FB3C,
-    0x0FB40, 0x0FB41,
-    0x0FB43, 0x0FB44,
-    0x0FB46, 0x0FBB1,
-    0x0FBD3, 0x0FD3D,
-    0x0FD50, 0x0FD8F,
-    0x0FD92, 0x0FDC7,
-    0x0FDF0, 0x0FDFB,
-    0x0FE70, 0x0FE74,
-    0x0FE76, 0x0FEFC,
-    0x0FF21, 0x0FF3A,
-    0x0FF41, 0x0FF5A,
-    0x0FF66, 0x0FFBE,
-    0x0FFC2, 0x0FFC7,
-    0x0FFCA, 0x0FFCF,
-    0x0FFD2, 0x0FFD7,
-    0x0FFDA, 0x0FFDC,
-    0x10000, 0x1000B,
-    0x1000D, 0x10026,
-    0x10028, 0x1003A,
-    0x1003C, 0x1003D,
-    0x1003F, 0x1004D,
-    0x10050, 0x1005D,
-    0x10080, 0x100FA,
-    0x10280, 0x1029C,
-    0x102A0, 0x102D0,
-    0x10300, 0x1031F,
-    0x1032D, 0x10340,
-    0x10342, 0x10349,
-    0x10350, 0x10375,
-    0x10380, 0x1039D,
-    0x103A0, 0x103C3,
-    0x103C8, 0x103CF,
-    0x10400, 0x1049D,
-    0x104B0, 0x104D3,
-    0x104D8, 0x104FB,
-    0x10500, 0x10527,
-    0x10530, 0x10563,
-    0x10600, 0x10736,
-    0x10740, 0x10755,
-    0x10760, 0x10767,
-    0x10800, 0x10805,
-    0x1080A, 0x10835,
-    0x10837, 0x10838,
-    0x1083F, 0x10855,
-    0x10860, 0x10876,
-    0x10880, 0x1089E,
-    0x108E0, 0x108F2,
-    0x108F4, 0x108F5,
-    0x10900, 0x10915,
-    0x10920, 0x10939,
-    0x10980, 0x109B7,
-    0x109BE, 0x109BF,
-    0x10A10, 0x10A13,
-    0x10A15, 0x10A17,
-    0x10A19, 0x10A35,
-    0x10A60, 0x10A7C,
-    0x10A80, 0x10A9C,
-    0x10AC0, 0x10AC7,
-    0x10AC9, 0x10AE4,
-    0x10B00, 0x10B35,
-    0x10B40, 0x10B55,
-    0x10B60, 0x10B72,
-    0x10B80, 0x10B91,
-    0x10C00, 0x10C48,
-    0x10C80, 0x10CB2,
-    0x10CC0, 0x10CF2,
-    0x10D00, 0x10D23,
-    0x10F00, 0x10F1C,
-    0x10F30, 0x10F45,
-    0x10FE0, 0x10FF6,
-    0x11003, 0x11037,
-    0x11083, 0x110AF,
-    0x110D0, 0x110E8,
-    0x11103, 0x11126,
-    0x11150, 0x11172,
-    0x11183, 0x111B2,
-    0x111C1, 0x111C4,
-    0x11200, 0x11211,
-    0x11213, 0x1122B,
-    0x11280, 0x11286,
-    0x1128A, 0x1128D,
-    0x1128F, 0x1129D,
-    0x1129F, 0x112A8,
-    0x112B0, 0x112DE,
-    0x11305, 0x1130C,
-    0x1130F, 0x11310,
-    0x11313, 0x11328,
-    0x1132A, 0x11330,
-    0x11332, 0x11333,
-    0x11335, 0x11339,
-    0x1135D, 0x11361,
-    0x11400, 0x11434,
-    0x11447, 0x1144A,
-    0x11480, 0x114AF,
-    0x114C4, 0x114C5,
-    0x11580, 0x115AE,
-    0x115D8, 0x115DB,
-    0x11600, 0x1162F,
-    0x11680, 0x116AA,
-    0x11700, 0x1171A,
-    0x11800, 0x1182B,
-    0x118A0, 0x118DF,
-    0x119A0, 0x119A7,
-    0x119AA, 0x119D0,
-    0x11A0B, 0x11A32,
-    0x11A5C, 0x11A89,
-    0x11AC0, 0x11AF8,
-    0x11C00, 0x11C08,
-    0x11C0A, 0x11C2E,
-    0x11C72, 0x11C8F,
-    0x11D00, 0x11D06,
-    0x11D08, 0x11D09,
-    0x11D0B, 0x11D30,
-    0x11D60, 0x11D65,
-    0x11D67, 0x11D68,
-    0x11D6A, 0x11D89,
-    0x11EE0, 0x11EF2,
-    0x12000, 0x12399,
-    0x12480, 0x12543,
-    0x13000, 0x1342E,
-    0x14400, 0x14646,
-    0x16800, 0x16A38,
-    0x16A40, 0x16A5E,
-    0x16AD0, 0x16AED,
-    0x16B00, 0x16B2F,
-    0x16B40, 0x16B43,
-    0x16B63, 0x16B77,
-    0x16B7D, 0x16B8F,
-    0x16E40, 0x16E7F,
-    0x16F00, 0x16F4A,
-    0x16F93, 0x16F9F,
-    0x16FE0, 0x16FE1,
-    0x18800, 0x18AF2,
-    0x1B000, 0x1B11E,
-    0x1B150, 0x1B152,
-    0x1B164, 0x1B167,
-    0x1B170, 0x1B2FB,
-    0x1BC00, 0x1BC6A,
-    0x1BC70, 0x1BC7C,
-    0x1BC80, 0x1BC88,
-    0x1BC90, 0x1BC99,
-    0x1D400, 0x1D454,
-    0x1D456, 0x1D49C,
-    0x1D49E, 0x1D49F,
-    0x1D4A5, 0x1D4A6,
-    0x1D4A9, 0x1D4AC,
-    0x1D4AE, 0x1D4B9,
-    0x1D4BD, 0x1D4C3,
-    0x1D4C5, 0x1D505,
-    0x1D507, 0x1D50A,
-    0x1D50D, 0x1D514,
-    0x1D516, 0x1D51C,
-    0x1D51E, 0x1D539,
-    0x1D53B, 0x1D53E,
-    0x1D540, 0x1D544,
-    0x1D54A, 0x1D550,
-    0x1D552, 0x1D6A5,
-    0x1D6A8, 0x1D6C0,
-    0x1D6C2, 0x1D6DA,
-    0x1D6DC, 0x1D6FA,
-    0x1D6FC, 0x1D714,
-    0x1D716, 0x1D734,
-    0x1D736, 0x1D74E,
-    0x1D750, 0x1D76E,
-    0x1D770, 0x1D788,
-    0x1D78A, 0x1D7A8,
-    0x1D7AA, 0x1D7C2,
-    0x1D7C4, 0x1D7CB,
-    0x1E100, 0x1E12C,
-    0x1E137, 0x1E13D,
-    0x1E2C0, 0x1E2EB,
-    0x1E800, 0x1E8C4,
-    0x1E900, 0x1E943,
-    0x1EE00, 0x1EE03,
-    0x1EE05, 0x1EE1F,
-    0x1EE21, 0x1EE22,
-    0x1EE29, 0x1EE32,
-    0x1EE34, 0x1EE37,
-    0x1EE4D, 0x1EE4F,
-    0x1EE51, 0x1EE52,
-    0x1EE61, 0x1EE62,
-    0x1EE67, 0x1EE6A,
-    0x1EE6C, 0x1EE72,
-    0x1EE74, 0x1EE77,
-    0x1EE79, 0x1EE7C,
-    0x1EE80, 0x1EE89,
-    0x1EE8B, 0x1EE9B,
-    0x1EEA1, 0x1EEA3,
-    0x1EEA5, 0x1EEA9,
-    0x1EEAB, 0x1EEBB,
-    0x2F800, 0x2FA1D,
+    0x00041'i32, 0x0005A'i32,
+    0x00061'i32, 0x0007A'i32,
+    0x000C0'i32, 0x000D6'i32,
+    0x000D8'i32, 0x000F6'i32,
+    0x000F8'i32, 0x002C1'i32,
+    0x002C6'i32, 0x002D1'i32,
+    0x002E0'i32, 0x002E4'i32,
+    0x00370'i32, 0x00374'i32,
+    0x00376'i32, 0x00377'i32,
+    0x0037A'i32, 0x0037D'i32,
+    0x00388'i32, 0x0038A'i32,
+    0x0038E'i32, 0x003A1'i32,
+    0x003A3'i32, 0x003F5'i32,
+    0x003F7'i32, 0x00481'i32,
+    0x0048A'i32, 0x0052F'i32,
+    0x00531'i32, 0x00556'i32,
+    0x00560'i32, 0x00588'i32,
+    0x005D0'i32, 0x005EA'i32,
+    0x005EF'i32, 0x005F2'i32,
+    0x00620'i32, 0x0064A'i32,
+    0x0066E'i32, 0x0066F'i32,
+    0x00671'i32, 0x006D3'i32,
+    0x006E5'i32, 0x006E6'i32,
+    0x006EE'i32, 0x006EF'i32,
+    0x006FA'i32, 0x006FC'i32,
+    0x00712'i32, 0x0072F'i32,
+    0x0074D'i32, 0x007A5'i32,
+    0x007CA'i32, 0x007EA'i32,
+    0x007F4'i32, 0x007F5'i32,
+    0x00800'i32, 0x00815'i32,
+    0x00840'i32, 0x00858'i32,
+    0x00860'i32, 0x0086A'i32,
+    0x008A0'i32, 0x008B4'i32,
+    0x008B6'i32, 0x008BD'i32,
+    0x00904'i32, 0x00939'i32,
+    0x00958'i32, 0x00961'i32,
+    0x00971'i32, 0x00980'i32,
+    0x00985'i32, 0x0098C'i32,
+    0x0098F'i32, 0x00990'i32,
+    0x00993'i32, 0x009A8'i32,
+    0x009AA'i32, 0x009B0'i32,
+    0x009B6'i32, 0x009B9'i32,
+    0x009DC'i32, 0x009DD'i32,
+    0x009DF'i32, 0x009E1'i32,
+    0x009F0'i32, 0x009F1'i32,
+    0x00A05'i32, 0x00A0A'i32,
+    0x00A0F'i32, 0x00A10'i32,
+    0x00A13'i32, 0x00A28'i32,
+    0x00A2A'i32, 0x00A30'i32,
+    0x00A32'i32, 0x00A33'i32,
+    0x00A35'i32, 0x00A36'i32,
+    0x00A38'i32, 0x00A39'i32,
+    0x00A59'i32, 0x00A5C'i32,
+    0x00A72'i32, 0x00A74'i32,
+    0x00A85'i32, 0x00A8D'i32,
+    0x00A8F'i32, 0x00A91'i32,
+    0x00A93'i32, 0x00AA8'i32,
+    0x00AAA'i32, 0x00AB0'i32,
+    0x00AB2'i32, 0x00AB3'i32,
+    0x00AB5'i32, 0x00AB9'i32,
+    0x00AE0'i32, 0x00AE1'i32,
+    0x00B05'i32, 0x00B0C'i32,
+    0x00B0F'i32, 0x00B10'i32,
+    0x00B13'i32, 0x00B28'i32,
+    0x00B2A'i32, 0x00B30'i32,
+    0x00B32'i32, 0x00B33'i32,
+    0x00B35'i32, 0x00B39'i32,
+    0x00B5C'i32, 0x00B5D'i32,
+    0x00B5F'i32, 0x00B61'i32,
+    0x00B85'i32, 0x00B8A'i32,
+    0x00B8E'i32, 0x00B90'i32,
+    0x00B92'i32, 0x00B95'i32,
+    0x00B99'i32, 0x00B9A'i32,
+    0x00B9E'i32, 0x00B9F'i32,
+    0x00BA3'i32, 0x00BA4'i32,
+    0x00BA8'i32, 0x00BAA'i32,
+    0x00BAE'i32, 0x00BB9'i32,
+    0x00C05'i32, 0x00C0C'i32,
+    0x00C0E'i32, 0x00C10'i32,
+    0x00C12'i32, 0x00C28'i32,
+    0x00C2A'i32, 0x00C39'i32,
+    0x00C58'i32, 0x00C5A'i32,
+    0x00C60'i32, 0x00C61'i32,
+    0x00C85'i32, 0x00C8C'i32,
+    0x00C8E'i32, 0x00C90'i32,
+    0x00C92'i32, 0x00CA8'i32,
+    0x00CAA'i32, 0x00CB3'i32,
+    0x00CB5'i32, 0x00CB9'i32,
+    0x00CE0'i32, 0x00CE1'i32,
+    0x00CF1'i32, 0x00CF2'i32,
+    0x00D05'i32, 0x00D0C'i32,
+    0x00D0E'i32, 0x00D10'i32,
+    0x00D12'i32, 0x00D3A'i32,
+    0x00D54'i32, 0x00D56'i32,
+    0x00D5F'i32, 0x00D61'i32,
+    0x00D7A'i32, 0x00D7F'i32,
+    0x00D85'i32, 0x00D96'i32,
+    0x00D9A'i32, 0x00DB1'i32,
+    0x00DB3'i32, 0x00DBB'i32,
+    0x00DC0'i32, 0x00DC6'i32,
+    0x00E01'i32, 0x00E30'i32,
+    0x00E32'i32, 0x00E33'i32,
+    0x00E40'i32, 0x00E46'i32,
+    0x00E81'i32, 0x00E82'i32,
+    0x00E86'i32, 0x00E8A'i32,
+    0x00E8C'i32, 0x00EA3'i32,
+    0x00EA7'i32, 0x00EB0'i32,
+    0x00EB2'i32, 0x00EB3'i32,
+    0x00EC0'i32, 0x00EC4'i32,
+    0x00EDC'i32, 0x00EDF'i32,
+    0x00F40'i32, 0x00F47'i32,
+    0x00F49'i32, 0x00F6C'i32,
+    0x00F88'i32, 0x00F8C'i32,
+    0x01000'i32, 0x0102A'i32,
+    0x01050'i32, 0x01055'i32,
+    0x0105A'i32, 0x0105D'i32,
+    0x01065'i32, 0x01066'i32,
+    0x0106E'i32, 0x01070'i32,
+    0x01075'i32, 0x01081'i32,
+    0x010A0'i32, 0x010C5'i32,
+    0x010D0'i32, 0x010FA'i32,
+    0x010FC'i32, 0x01248'i32,
+    0x0124A'i32, 0x0124D'i32,
+    0x01250'i32, 0x01256'i32,
+    0x0125A'i32, 0x0125D'i32,
+    0x01260'i32, 0x01288'i32,
+    0x0128A'i32, 0x0128D'i32,
+    0x01290'i32, 0x012B0'i32,
+    0x012B2'i32, 0x012B5'i32,
+    0x012B8'i32, 0x012BE'i32,
+    0x012C2'i32, 0x012C5'i32,
+    0x012C8'i32, 0x012D6'i32,
+    0x012D8'i32, 0x01310'i32,
+    0x01312'i32, 0x01315'i32,
+    0x01318'i32, 0x0135A'i32,
+    0x01380'i32, 0x0138F'i32,
+    0x013A0'i32, 0x013F5'i32,
+    0x013F8'i32, 0x013FD'i32,
+    0x01401'i32, 0x0166C'i32,
+    0x0166F'i32, 0x0167F'i32,
+    0x01681'i32, 0x0169A'i32,
+    0x016A0'i32, 0x016EA'i32,
+    0x016F1'i32, 0x016F8'i32,
+    0x01700'i32, 0x0170C'i32,
+    0x0170E'i32, 0x01711'i32,
+    0x01720'i32, 0x01731'i32,
+    0x01740'i32, 0x01751'i32,
+    0x01760'i32, 0x0176C'i32,
+    0x0176E'i32, 0x01770'i32,
+    0x01780'i32, 0x017B3'i32,
+    0x01820'i32, 0x01878'i32,
+    0x01880'i32, 0x01884'i32,
+    0x01887'i32, 0x018A8'i32,
+    0x018B0'i32, 0x018F5'i32,
+    0x01900'i32, 0x0191E'i32,
+    0x01950'i32, 0x0196D'i32,
+    0x01970'i32, 0x01974'i32,
+    0x01980'i32, 0x019AB'i32,
+    0x019B0'i32, 0x019C9'i32,
+    0x01A00'i32, 0x01A16'i32,
+    0x01A20'i32, 0x01A54'i32,
+    0x01B05'i32, 0x01B33'i32,
+    0x01B45'i32, 0x01B4B'i32,
+    0x01B83'i32, 0x01BA0'i32,
+    0x01BAE'i32, 0x01BAF'i32,
+    0x01BBA'i32, 0x01BE5'i32,
+    0x01C00'i32, 0x01C23'i32,
+    0x01C4D'i32, 0x01C4F'i32,
+    0x01C5A'i32, 0x01C7D'i32,
+    0x01C80'i32, 0x01C88'i32,
+    0x01C90'i32, 0x01CBA'i32,
+    0x01CBD'i32, 0x01CBF'i32,
+    0x01CE9'i32, 0x01CEC'i32,
+    0x01CEE'i32, 0x01CF3'i32,
+    0x01CF5'i32, 0x01CF6'i32,
+    0x01D00'i32, 0x01DBF'i32,
+    0x01E00'i32, 0x01F15'i32,
+    0x01F18'i32, 0x01F1D'i32,
+    0x01F20'i32, 0x01F45'i32,
+    0x01F48'i32, 0x01F4D'i32,
+    0x01F50'i32, 0x01F57'i32,
+    0x01F5F'i32, 0x01F7D'i32,
+    0x01F80'i32, 0x01FB4'i32,
+    0x01FB6'i32, 0x01FBC'i32,
+    0x01FC2'i32, 0x01FC4'i32,
+    0x01FC6'i32, 0x01FCC'i32,
+    0x01FD0'i32, 0x01FD3'i32,
+    0x01FD6'i32, 0x01FDB'i32,
+    0x01FE0'i32, 0x01FEC'i32,
+    0x01FF2'i32, 0x01FF4'i32,
+    0x01FF6'i32, 0x01FFC'i32,
+    0x02090'i32, 0x0209C'i32,
+    0x0210A'i32, 0x02113'i32,
+    0x02119'i32, 0x0211D'i32,
+    0x0212A'i32, 0x0212D'i32,
+    0x0212F'i32, 0x02139'i32,
+    0x0213C'i32, 0x0213F'i32,
+    0x02145'i32, 0x02149'i32,
+    0x02183'i32, 0x02184'i32,
+    0x02C00'i32, 0x02C2E'i32,
+    0x02C30'i32, 0x02C5E'i32,
+    0x02C60'i32, 0x02CE4'i32,
+    0x02CEB'i32, 0x02CEE'i32,
+    0x02CF2'i32, 0x02CF3'i32,
+    0x02D00'i32, 0x02D25'i32,
+    0x02D30'i32, 0x02D67'i32,
+    0x02D80'i32, 0x02D96'i32,
+    0x02DA0'i32, 0x02DA6'i32,
+    0x02DA8'i32, 0x02DAE'i32,
+    0x02DB0'i32, 0x02DB6'i32,
+    0x02DB8'i32, 0x02DBE'i32,
+    0x02DC0'i32, 0x02DC6'i32,
+    0x02DC8'i32, 0x02DCE'i32,
+    0x02DD0'i32, 0x02DD6'i32,
+    0x02DD8'i32, 0x02DDE'i32,
+    0x03005'i32, 0x03006'i32,
+    0x03031'i32, 0x03035'i32,
+    0x0303B'i32, 0x0303C'i32,
+    0x03041'i32, 0x03096'i32,
+    0x0309D'i32, 0x0309F'i32,
+    0x030A1'i32, 0x030FA'i32,
+    0x030FC'i32, 0x030FF'i32,
+    0x03105'i32, 0x0312F'i32,
+    0x03131'i32, 0x0318E'i32,
+    0x031A0'i32, 0x031BA'i32,
+    0x031F0'i32, 0x031FF'i32,
+    0x03400'i32, 0x04DB5'i32,
+    0x04E00'i32, 0x09FEF'i32,
+    0x0A000'i32, 0x0A48C'i32,
+    0x0A4D0'i32, 0x0A4FD'i32,
+    0x0A500'i32, 0x0A60C'i32,
+    0x0A610'i32, 0x0A61F'i32,
+    0x0A62A'i32, 0x0A62B'i32,
+    0x0A640'i32, 0x0A66E'i32,
+    0x0A67F'i32, 0x0A69D'i32,
+    0x0A6A0'i32, 0x0A6E5'i32,
+    0x0A717'i32, 0x0A71F'i32,
+    0x0A722'i32, 0x0A788'i32,
+    0x0A78B'i32, 0x0A7BF'i32,
+    0x0A7C2'i32, 0x0A7C6'i32,
+    0x0A7F7'i32, 0x0A801'i32,
+    0x0A803'i32, 0x0A805'i32,
+    0x0A807'i32, 0x0A80A'i32,
+    0x0A80C'i32, 0x0A822'i32,
+    0x0A840'i32, 0x0A873'i32,
+    0x0A882'i32, 0x0A8B3'i32,
+    0x0A8F2'i32, 0x0A8F7'i32,
+    0x0A8FD'i32, 0x0A8FE'i32,
+    0x0A90A'i32, 0x0A925'i32,
+    0x0A930'i32, 0x0A946'i32,
+    0x0A960'i32, 0x0A97C'i32,
+    0x0A984'i32, 0x0A9B2'i32,
+    0x0A9E0'i32, 0x0A9E4'i32,
+    0x0A9E6'i32, 0x0A9EF'i32,
+    0x0A9FA'i32, 0x0A9FE'i32,
+    0x0AA00'i32, 0x0AA28'i32,
+    0x0AA40'i32, 0x0AA42'i32,
+    0x0AA44'i32, 0x0AA4B'i32,
+    0x0AA60'i32, 0x0AA76'i32,
+    0x0AA7E'i32, 0x0AAAF'i32,
+    0x0AAB5'i32, 0x0AAB6'i32,
+    0x0AAB9'i32, 0x0AABD'i32,
+    0x0AADB'i32, 0x0AADD'i32,
+    0x0AAE0'i32, 0x0AAEA'i32,
+    0x0AAF2'i32, 0x0AAF4'i32,
+    0x0AB01'i32, 0x0AB06'i32,
+    0x0AB09'i32, 0x0AB0E'i32,
+    0x0AB11'i32, 0x0AB16'i32,
+    0x0AB20'i32, 0x0AB26'i32,
+    0x0AB28'i32, 0x0AB2E'i32,
+    0x0AB30'i32, 0x0AB5A'i32,
+    0x0AB5C'i32, 0x0AB67'i32,
+    0x0AB70'i32, 0x0ABE2'i32,
+    0x0AC00'i32, 0x0D7A3'i32,
+    0x0D7B0'i32, 0x0D7C6'i32,
+    0x0D7CB'i32, 0x0D7FB'i32,
+    0x0F900'i32, 0x0FA6D'i32,
+    0x0FA70'i32, 0x0FAD9'i32,
+    0x0FB00'i32, 0x0FB06'i32,
+    0x0FB13'i32, 0x0FB17'i32,
+    0x0FB1F'i32, 0x0FB28'i32,
+    0x0FB2A'i32, 0x0FB36'i32,
+    0x0FB38'i32, 0x0FB3C'i32,
+    0x0FB40'i32, 0x0FB41'i32,
+    0x0FB43'i32, 0x0FB44'i32,
+    0x0FB46'i32, 0x0FBB1'i32,
+    0x0FBD3'i32, 0x0FD3D'i32,
+    0x0FD50'i32, 0x0FD8F'i32,
+    0x0FD92'i32, 0x0FDC7'i32,
+    0x0FDF0'i32, 0x0FDFB'i32,
+    0x0FE70'i32, 0x0FE74'i32,
+    0x0FE76'i32, 0x0FEFC'i32,
+    0x0FF21'i32, 0x0FF3A'i32,
+    0x0FF41'i32, 0x0FF5A'i32,
+    0x0FF66'i32, 0x0FFBE'i32,
+    0x0FFC2'i32, 0x0FFC7'i32,
+    0x0FFCA'i32, 0x0FFCF'i32,
+    0x0FFD2'i32, 0x0FFD7'i32,
+    0x0FFDA'i32, 0x0FFDC'i32,
+    0x10000'i32, 0x1000B'i32,
+    0x1000D'i32, 0x10026'i32,
+    0x10028'i32, 0x1003A'i32,
+    0x1003C'i32, 0x1003D'i32,
+    0x1003F'i32, 0x1004D'i32,
+    0x10050'i32, 0x1005D'i32,
+    0x10080'i32, 0x100FA'i32,
+    0x10280'i32, 0x1029C'i32,
+    0x102A0'i32, 0x102D0'i32,
+    0x10300'i32, 0x1031F'i32,
+    0x1032D'i32, 0x10340'i32,
+    0x10342'i32, 0x10349'i32,
+    0x10350'i32, 0x10375'i32,
+    0x10380'i32, 0x1039D'i32,
+    0x103A0'i32, 0x103C3'i32,
+    0x103C8'i32, 0x103CF'i32,
+    0x10400'i32, 0x1049D'i32,
+    0x104B0'i32, 0x104D3'i32,
+    0x104D8'i32, 0x104FB'i32,
+    0x10500'i32, 0x10527'i32,
+    0x10530'i32, 0x10563'i32,
+    0x10600'i32, 0x10736'i32,
+    0x10740'i32, 0x10755'i32,
+    0x10760'i32, 0x10767'i32,
+    0x10800'i32, 0x10805'i32,
+    0x1080A'i32, 0x10835'i32,
+    0x10837'i32, 0x10838'i32,
+    0x1083F'i32, 0x10855'i32,
+    0x10860'i32, 0x10876'i32,
+    0x10880'i32, 0x1089E'i32,
+    0x108E0'i32, 0x108F2'i32,
+    0x108F4'i32, 0x108F5'i32,
+    0x10900'i32, 0x10915'i32,
+    0x10920'i32, 0x10939'i32,
+    0x10980'i32, 0x109B7'i32,
+    0x109BE'i32, 0x109BF'i32,
+    0x10A10'i32, 0x10A13'i32,
+    0x10A15'i32, 0x10A17'i32,
+    0x10A19'i32, 0x10A35'i32,
+    0x10A60'i32, 0x10A7C'i32,
+    0x10A80'i32, 0x10A9C'i32,
+    0x10AC0'i32, 0x10AC7'i32,
+    0x10AC9'i32, 0x10AE4'i32,
+    0x10B00'i32, 0x10B35'i32,
+    0x10B40'i32, 0x10B55'i32,
+    0x10B60'i32, 0x10B72'i32,
+    0x10B80'i32, 0x10B91'i32,
+    0x10C00'i32, 0x10C48'i32,
+    0x10C80'i32, 0x10CB2'i32,
+    0x10CC0'i32, 0x10CF2'i32,
+    0x10D00'i32, 0x10D23'i32,
+    0x10F00'i32, 0x10F1C'i32,
+    0x10F30'i32, 0x10F45'i32,
+    0x10FE0'i32, 0x10FF6'i32,
+    0x11003'i32, 0x11037'i32,
+    0x11083'i32, 0x110AF'i32,
+    0x110D0'i32, 0x110E8'i32,
+    0x11103'i32, 0x11126'i32,
+    0x11150'i32, 0x11172'i32,
+    0x11183'i32, 0x111B2'i32,
+    0x111C1'i32, 0x111C4'i32,
+    0x11200'i32, 0x11211'i32,
+    0x11213'i32, 0x1122B'i32,
+    0x11280'i32, 0x11286'i32,
+    0x1128A'i32, 0x1128D'i32,
+    0x1128F'i32, 0x1129D'i32,
+    0x1129F'i32, 0x112A8'i32,
+    0x112B0'i32, 0x112DE'i32,
+    0x11305'i32, 0x1130C'i32,
+    0x1130F'i32, 0x11310'i32,
+    0x11313'i32, 0x11328'i32,
+    0x1132A'i32, 0x11330'i32,
+    0x11332'i32, 0x11333'i32,
+    0x11335'i32, 0x11339'i32,
+    0x1135D'i32, 0x11361'i32,
+    0x11400'i32, 0x11434'i32,
+    0x11447'i32, 0x1144A'i32,
+    0x11480'i32, 0x114AF'i32,
+    0x114C4'i32, 0x114C5'i32,
+    0x11580'i32, 0x115AE'i32,
+    0x115D8'i32, 0x115DB'i32,
+    0x11600'i32, 0x1162F'i32,
+    0x11680'i32, 0x116AA'i32,
+    0x11700'i32, 0x1171A'i32,
+    0x11800'i32, 0x1182B'i32,
+    0x118A0'i32, 0x118DF'i32,
+    0x119A0'i32, 0x119A7'i32,
+    0x119AA'i32, 0x119D0'i32,
+    0x11A0B'i32, 0x11A32'i32,
+    0x11A5C'i32, 0x11A89'i32,
+    0x11AC0'i32, 0x11AF8'i32,
+    0x11C00'i32, 0x11C08'i32,
+    0x11C0A'i32, 0x11C2E'i32,
+    0x11C72'i32, 0x11C8F'i32,
+    0x11D00'i32, 0x11D06'i32,
+    0x11D08'i32, 0x11D09'i32,
+    0x11D0B'i32, 0x11D30'i32,
+    0x11D60'i32, 0x11D65'i32,
+    0x11D67'i32, 0x11D68'i32,
+    0x11D6A'i32, 0x11D89'i32,
+    0x11EE0'i32, 0x11EF2'i32,
+    0x12000'i32, 0x12399'i32,
+    0x12480'i32, 0x12543'i32,
+    0x13000'i32, 0x1342E'i32,
+    0x14400'i32, 0x14646'i32,
+    0x16800'i32, 0x16A38'i32,
+    0x16A40'i32, 0x16A5E'i32,
+    0x16AD0'i32, 0x16AED'i32,
+    0x16B00'i32, 0x16B2F'i32,
+    0x16B40'i32, 0x16B43'i32,
+    0x16B63'i32, 0x16B77'i32,
+    0x16B7D'i32, 0x16B8F'i32,
+    0x16E40'i32, 0x16E7F'i32,
+    0x16F00'i32, 0x16F4A'i32,
+    0x16F93'i32, 0x16F9F'i32,
+    0x16FE0'i32, 0x16FE1'i32,
+    0x17000'i32, 0x187F7'i32,
+    0x18800'i32, 0x18AF2'i32,
+    0x1B000'i32, 0x1B11E'i32,
+    0x1B150'i32, 0x1B152'i32,
+    0x1B164'i32, 0x1B167'i32,
+    0x1B170'i32, 0x1B2FB'i32,
+    0x1BC00'i32, 0x1BC6A'i32,
+    0x1BC70'i32, 0x1BC7C'i32,
+    0x1BC80'i32, 0x1BC88'i32,
+    0x1BC90'i32, 0x1BC99'i32,
+    0x1D400'i32, 0x1D454'i32,
+    0x1D456'i32, 0x1D49C'i32,
+    0x1D49E'i32, 0x1D49F'i32,
+    0x1D4A5'i32, 0x1D4A6'i32,
+    0x1D4A9'i32, 0x1D4AC'i32,
+    0x1D4AE'i32, 0x1D4B9'i32,
+    0x1D4BD'i32, 0x1D4C3'i32,
+    0x1D4C5'i32, 0x1D505'i32,
+    0x1D507'i32, 0x1D50A'i32,
+    0x1D50D'i32, 0x1D514'i32,
+    0x1D516'i32, 0x1D51C'i32,
+    0x1D51E'i32, 0x1D539'i32,
+    0x1D53B'i32, 0x1D53E'i32,
+    0x1D540'i32, 0x1D544'i32,
+    0x1D54A'i32, 0x1D550'i32,
+    0x1D552'i32, 0x1D6A5'i32,
+    0x1D6A8'i32, 0x1D6C0'i32,
+    0x1D6C2'i32, 0x1D6DA'i32,
+    0x1D6DC'i32, 0x1D6FA'i32,
+    0x1D6FC'i32, 0x1D714'i32,
+    0x1D716'i32, 0x1D734'i32,
+    0x1D736'i32, 0x1D74E'i32,
+    0x1D750'i32, 0x1D76E'i32,
+    0x1D770'i32, 0x1D788'i32,
+    0x1D78A'i32, 0x1D7A8'i32,
+    0x1D7AA'i32, 0x1D7C2'i32,
+    0x1D7C4'i32, 0x1D7CB'i32,
+    0x1E100'i32, 0x1E12C'i32,
+    0x1E137'i32, 0x1E13D'i32,
+    0x1E2C0'i32, 0x1E2EB'i32,
+    0x1E800'i32, 0x1E8C4'i32,
+    0x1E900'i32, 0x1E943'i32,
+    0x1EE00'i32, 0x1EE03'i32,
+    0x1EE05'i32, 0x1EE1F'i32,
+    0x1EE21'i32, 0x1EE22'i32,
+    0x1EE29'i32, 0x1EE32'i32,
+    0x1EE34'i32, 0x1EE37'i32,
+    0x1EE4D'i32, 0x1EE4F'i32,
+    0x1EE51'i32, 0x1EE52'i32,
+    0x1EE61'i32, 0x1EE62'i32,
+    0x1EE67'i32, 0x1EE6A'i32,
+    0x1EE6C'i32, 0x1EE72'i32,
+    0x1EE74'i32, 0x1EE77'i32,
+    0x1EE79'i32, 0x1EE7C'i32,
+    0x1EE80'i32, 0x1EE89'i32,
+    0x1EE8B'i32, 0x1EE9B'i32,
+    0x1EEA1'i32, 0x1EEA3'i32,
+    0x1EEA5'i32, 0x1EEA9'i32,
+    0x1EEAB'i32, 0x1EEBB'i32,
+    0x20000'i32, 0x2A6D6'i32,
+    0x2A700'i32, 0x2B734'i32,
+    0x2B740'i32, 0x2B81D'i32,
+    0x2B820'i32, 0x2CEA1'i32,
+    0x2CEB0'i32, 0x2EBE0'i32,
+    0x2F800'i32, 0x2FA1D'i32,
   ]
 
   alphaSinglets = [
-    0x000AA,
-    0x000B5,
-    0x000BA,
-    0x002EC,
-    0x002EE,
-    0x0037F,
-    0x00386,
-    0x0038C,
-    0x00559,
-    0x006D5,
-    0x006FF,
-    0x00710,
-    0x007B1,
-    0x007FA,
-    0x0081A,
-    0x00824,
-    0x00828,
-    0x0093D,
-    0x00950,
-    0x009B2,
-    0x009BD,
-    0x009CE,
-    0x009FC,
-    0x00A5E,
-    0x00ABD,
-    0x00AD0,
-    0x00AF9,
-    0x00B3D,
-    0x00B71,
-    0x00B83,
-    0x00B9C,
-    0x00BD0,
-    0x00C3D,
-    0x00C80,
-    0x00CBD,
-    0x00CDE,
-    0x00D3D,
-    0x00D4E,
-    0x00DBD,
-    0x00E84,
-    0x00EA5,
-    0x00EBD,
-    0x00EC6,
-    0x00F00,
-    0x0103F,
-    0x01061,
-    0x0108E,
-    0x010C7,
-    0x010CD,
-    0x01258,
-    0x012C0,
-    0x017D7,
-    0x017DC,
-    0x018AA,
-    0x01AA7,
-    0x01CFA,
-    0x01F59,
-    0x01F5B,
-    0x01F5D,
-    0x01FBE,
-    0x02071,
-    0x0207F,
-    0x02102,
-    0x02107,
-    0x02115,
-    0x02124,
-    0x02126,
-    0x02128,
-    0x0214E,
-    0x02D27,
-    0x02D2D,
-    0x02D6F,
-    0x02E2F,
-    0x03400,
-    0x04DB5,
-    0x04E00,
-    0x09FEF,
-    0x0A8FB,
-    0x0A9CF,
-    0x0AA7A,
-    0x0AAB1,
-    0x0AAC0,
-    0x0AAC2,
-    0x0AC00,
-    0x0D7A3,
-    0x0FB1D,
-    0x0FB3E,
-    0x10808,
-    0x1083C,
-    0x10A00,
-    0x10F27,
-    0x11144,
-    0x11176,
-    0x111DA,
-    0x111DC,
-    0x11288,
-    0x1133D,
-    0x11350,
-    0x1145F,
-    0x114C7,
-    0x11644,
-    0x116B8,
-    0x118FF,
-    0x119E1,
-    0x119E3,
-    0x11A00,
-    0x11A3A,
-    0x11A50,
-    0x11A9D,
-    0x11C40,
-    0x11D46,
-    0x11D98,
-    0x16F50,
-    0x16FE3,
-    0x17000,
-    0x187F7,
-    0x1D4A2,
-    0x1D4BB,
-    0x1D546,
-    0x1E14E,
-    0x1E94B,
-    0x1EE24,
-    0x1EE27,
-    0x1EE39,
-    0x1EE3B,
-    0x1EE42,
-    0x1EE47,
-    0x1EE49,
-    0x1EE4B,
-    0x1EE54,
-    0x1EE57,
-    0x1EE59,
-    0x1EE5B,
-    0x1EE5D,
-    0x1EE5F,
-    0x1EE64,
-    0x1EE7E,
-    0x20000,
-    0x2A6D6,
-    0x2A700,
-    0x2B734,
-    0x2B740,
-    0x2B81D,
-    0x2B820,
-    0x2CEA1,
-    0x2CEB0,
-    0x2EBE0,
+    0x000AA'i32,
+    0x000B5'i32,
+    0x000BA'i32,
+    0x002EC'i32,
+    0x002EE'i32,
+    0x0037F'i32,
+    0x00386'i32,
+    0x0038C'i32,
+    0x00559'i32,
+    0x006D5'i32,
+    0x006FF'i32,
+    0x00710'i32,
+    0x007B1'i32,
+    0x007FA'i32,
+    0x0081A'i32,
+    0x00824'i32,
+    0x00828'i32,
+    0x0093D'i32,
+    0x00950'i32,
+    0x009B2'i32,
+    0x009BD'i32,
+    0x009CE'i32,
+    0x009FC'i32,
+    0x00A5E'i32,
+    0x00ABD'i32,
+    0x00AD0'i32,
+    0x00AF9'i32,
+    0x00B3D'i32,
+    0x00B71'i32,
+    0x00B83'i32,
+    0x00B9C'i32,
+    0x00BD0'i32,
+    0x00C3D'i32,
+    0x00C80'i32,
+    0x00CBD'i32,
+    0x00CDE'i32,
+    0x00D3D'i32,
+    0x00D4E'i32,
+    0x00DBD'i32,
+    0x00E84'i32,
+    0x00EA5'i32,
+    0x00EBD'i32,
+    0x00EC6'i32,
+    0x00F00'i32,
+    0x0103F'i32,
+    0x01061'i32,
+    0x0108E'i32,
+    0x010C7'i32,
+    0x010CD'i32,
+    0x01258'i32,
+    0x012C0'i32,
+    0x017D7'i32,
+    0x017DC'i32,
+    0x018AA'i32,
+    0x01AA7'i32,
+    0x01CFA'i32,
+    0x01F59'i32,
+    0x01F5B'i32,
+    0x01F5D'i32,
+    0x01FBE'i32,
+    0x02071'i32,
+    0x0207F'i32,
+    0x02102'i32,
+    0x02107'i32,
+    0x02115'i32,
+    0x02124'i32,
+    0x02126'i32,
+    0x02128'i32,
+    0x0214E'i32,
+    0x02D27'i32,
+    0x02D2D'i32,
+    0x02D6F'i32,
+    0x02E2F'i32,
+    0x0A8FB'i32,
+    0x0A9CF'i32,
+    0x0AA7A'i32,
+    0x0AAB1'i32,
+    0x0AAC0'i32,
+    0x0AAC2'i32,
+    0x0FB1D'i32,
+    0x0FB3E'i32,
+    0x10808'i32,
+    0x1083C'i32,
+    0x10A00'i32,
+    0x10F27'i32,
+    0x11144'i32,
+    0x11176'i32,
+    0x111DA'i32,
+    0x111DC'i32,
+    0x11288'i32,
+    0x1133D'i32,
+    0x11350'i32,
+    0x1145F'i32,
+    0x114C7'i32,
+    0x11644'i32,
+    0x116B8'i32,
+    0x118FF'i32,
+    0x119E1'i32,
+    0x119E3'i32,
+    0x11A00'i32,
+    0x11A3A'i32,
+    0x11A50'i32,
+    0x11A9D'i32,
+    0x11C40'i32,
+    0x11D46'i32,
+    0x11D98'i32,
+    0x16F50'i32,
+    0x16FE3'i32,
+    0x1D4A2'i32,
+    0x1D4BB'i32,
+    0x1D546'i32,
+    0x1E14E'i32,
+    0x1E94B'i32,
+    0x1EE24'i32,
+    0x1EE27'i32,
+    0x1EE39'i32,
+    0x1EE3B'i32,
+    0x1EE42'i32,
+    0x1EE47'i32,
+    0x1EE49'i32,
+    0x1EE4B'i32,
+    0x1EE54'i32,
+    0x1EE57'i32,
+    0x1EE59'i32,
+    0x1EE5B'i32,
+    0x1EE5D'i32,
+    0x1EE5F'i32,
+    0x1EE64'i32,
+    0x1EE7E'i32,
   ]
 
   spaceRanges = [
-    0x00009, 0x0000D,
-    0x00020, 0x00020,
-    0x00085, 0x00085,
-    0x000A0, 0x000A0,
-    0x01680, 0x01680,
-    0x02000, 0x0200A,
-    0x02028, 0x02029,
-    0x0202F, 0x0202F,
-    0x0205F, 0x0205F,
-    0x03000, 0x03000,
+    0x00009'i32, 0x0000D'i32,
+    0x00020'i32, 0x00020'i32,
+    0x00085'i32, 0x00085'i32,
+    0x000A0'i32, 0x000A0'i32,
+    0x01680'i32, 0x01680'i32,
+    0x02000'i32, 0x0200A'i32,
+    0x02028'i32, 0x02029'i32,
+    0x0202F'i32, 0x0202F'i32,
+    0x0205F'i32, 0x0205F'i32,
+    0x03000'i32, 0x03000'i32,
   ]
 
   unicodeSpaces = [
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 8526eb8a3..10658b78e 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -9,7 +9,7 @@
 
 # This module implements Linux epoll().
 
-import posix, times, epoll
+import std/[posix, times, epoll]
 
 # Maximum number of events that can be returned
 const MAX_EPOLL_EVENTS = 64
@@ -72,11 +72,16 @@ type
   SelectEvent* = ptr SelectEventImpl
 
 proc newSelector*[T](): Selector[T] =
+  proc initialNumFD(): int {.inline.} =
+    when defined(nuttx):
+      result = NEPOLL_MAX
+    else:
+      result = 1024
   # Retrieve the maximum fd count (for current OS) via getrlimit()
   var maxFD = maxDescriptors()
   doAssert(maxFD > 0)
   # Start with a reasonable size, checkFd() will grow this on demand
-  const numFD = 1024
+  let numFD = initialNumFD()
 
   var epollFD = epoll_create1(O_CLOEXEC)
   if epollFD < 0:
@@ -133,7 +138,7 @@ template checkFd(s, f) =
     var numFD = s.numFD
     while numFD <= f: numFD *= 2
     when hasThreadSupport:
-      s.fds = reallocSharedArray(s.fds, numFD)
+      s.fds = reallocSharedArray(s.fds, s.numFD, numFD)
     else:
       s.fds.setLen(numFD)
     for i in s.numFD ..< numFD:
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 68be969c7..513578eda 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -9,7 +9,7 @@
 
 #  This module implements BSD kqueue().
 
-import posix, times, kqueue, nativesockets
+import std/[posix, times, kqueue, nativesockets]
 
 const
   # Maximum number of events that can be returned.
@@ -194,7 +194,9 @@ when hasThreadSupport:
         if s.changesLength > 0:
           if kevent(s.kqFD, addr(s.changes[0]), cint(s.changesLength),
                     nil, 0, nil) == -1:
-            raiseIOSelectorsError(osLastError())
+            let res = osLastError()
+            if cint(res) != ENOENT: # ignore pipes whose read end is closed
+              raiseIOSelectorsError(res)
           s.changesLength = 0
 else:
   template modifyKQueue[T](s: Selector[T], nident: uint, nfilter: cshort,
@@ -211,7 +213,9 @@ else:
       if length > 0:
         if kevent(s.kqFD, addr(s.changes[0]), length,
                   nil, 0, nil) == -1:
-          raiseIOSelectorsError(osLastError())
+          let res = osLastError()
+          if cint(res) != ENOENT: # ignore pipes whose read end is closed
+            raiseIOSelectorsError(res)
         s.changes.setLen(0)
 
 proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim
index 12812ac80..7c5347156 100644
--- a/lib/pure/ioselects/ioselectors_poll.nim
+++ b/lib/pure/ioselects/ioselectors_poll.nim
@@ -9,7 +9,7 @@
 
 # This module implements Posix poll().
 
-import posix, times
+import std/[posix, times]
 
 # Maximum number of events that can be returned
 const MAX_POLL_EVENTS = 64
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index 34c88d85e..6c516395b 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -9,10 +9,10 @@
 
 # This module implements Posix and Windows select().
 
-import times, nativesockets
+import std/[times, nativesockets]
 
 when defined(windows):
-  import winlean
+  import std/winlean
   when defined(gcc):
     {.passl: "-lws2_32".}
   elif defined(vcc):
@@ -313,8 +313,8 @@ proc selectInto*[T](s: Selector[T], timeout: int,
   verifySelectParams(timeout)
 
   if timeout != -1:
-    when defined(genode) or defined(freertos) or defined(zephyr):
-      tv.tv_sec = Time(timeout div 1_000)
+    when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx):
+      tv.tv_sec = posix.Time(timeout div 1_000)
     else:
       tv.tv_sec = timeout.int32 div 1_000
     tv.tv_usec = (timeout.int32 %% 1_000) * 1_000
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index d0b1a4051..53fa7553a 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -41,13 +41,14 @@
 ## For a `JsonNode` who's kind is `JObject`, you can access its fields using
 ## the `[]` operator. The following example shows how to do this:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/json
 ##
 ##   let jsonNode = parseJson("""{"key": 3.14}""")
 ##
 ##   doAssert jsonNode.kind == JObject
 ##   doAssert jsonNode["key"].kind == JFloat
+##   ```
 ##
 ## Reading values
 ## --------------
@@ -62,12 +63,13 @@
 ##
 ## To retrieve the value of `"key"` you can do the following:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/json
 ##
 ##   let jsonNode = parseJson("""{"key": 3.14}""")
 ##
 ##   doAssert jsonNode["key"].getFloat() == 3.14
+##   ```
 ##
 ## **Important:** The `[]` operator will raise an exception when the
 ## specified field does not exist.
@@ -79,7 +81,7 @@
 ## when the field is not found. The `get`-family of procedures will return a
 ## type's default value when called on `nil`.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/json
 ##
 ##   let jsonNode = parseJson("{}")
@@ -88,6 +90,7 @@
 ##   doAssert jsonNode{"nope"}.getFloat() == 0
 ##   doAssert jsonNode{"nope"}.getStr() == ""
 ##   doAssert jsonNode{"nope"}.getBool() == false
+##   ```
 ##
 ## Using default values
 ## --------------------
@@ -95,7 +98,7 @@
 ## The `get`-family helpers also accept an additional parameter which allow
 ## you to fallback to a default value should the key's values be `null`:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/json
 ##
 ##   let jsonNode = parseJson("""{"key": 3.14, "key2": null}""")
@@ -103,6 +106,7 @@
 ##   doAssert jsonNode["key"].getFloat(6.28) == 3.14
 ##   doAssert jsonNode["key2"].getFloat(3.14) == 3.14
 ##   doAssert jsonNode{"nope"}.getFloat(3.14) == 3.14 # note the {}
+##   ```
 ##
 ## Unmarshalling
 ## -------------
@@ -113,7 +117,7 @@
 ## Note: Use `Option <options.html#Option>`_ for keys sometimes missing in json
 ## responses, and backticks around keys with a reserved keyword as name.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   import std/json
 ##   import std/options
 ##
@@ -127,6 +131,7 @@
 ##   let user = to(userJson, User)
 ##   if user.`type`.isSome():
 ##     assert user.`type`.get() != "robot"
+##   ```
 ##
 ## Creating JSON
 ## =============
@@ -134,7 +139,7 @@
 ## This module can also be used to comfortably create JSON using the `%*`
 ## operator:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   import std/json
 ##
 ##   var hisName = "John"
@@ -148,6 +153,7 @@
 ##   var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]}
 ##   j2["details"] = %* {"age":35, "pi":3.1415}
 ##   echo j2
+##   ```
 ##
 ## See also: std/jsonutils for hookable json serialization/deserialization
 ## of arbitrary types.
@@ -159,9 +165,9 @@ runnableExamples:
     a1, a2, a0, a3, a4: int
   doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
 
-import hashes, tables, strutils, lexbase, streams, macros, parsejson
+import std/[hashes, tables, strutils, lexbase, streams, macros, parsejson]
 
-import options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563
+import std/options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563
 import std/private/since
 
 when defined(nimPreviewSlimSystem):
@@ -438,7 +444,7 @@ macro `%*`*(x: untyped): untyped =
   ## `%` for every element.
   result = toJsonImpl(x)
 
-proc `==`*(a, b: JsonNode): bool {.noSideEffect.} =
+proc `==`*(a, b: JsonNode): bool {.noSideEffect, raises: [].} =
   ## Check two nodes for equality
   if a.isNil:
     if b.isNil: return true
@@ -458,18 +464,20 @@ proc `==`*(a, b: JsonNode): bool {.noSideEffect.} =
     of JNull:
       result = true
     of JArray:
-      result = a.elems == b.elems
+      {.cast(raises: []).}: # bug #19303
+        result = a.elems == b.elems
     of JObject:
       # we cannot use OrderedTable's equality here as
       # the order does not matter for equality here.
       if a.fields.len != b.fields.len: return false
       for key, val in a.fields:
         if not b.fields.hasKey(key): return false
-        when defined(nimHasEffectsOf):
-          {.noSideEffect.}:
+        {.cast(raises: []).}:
+          when defined(nimHasEffectsOf):
+            {.noSideEffect.}:
+              if b.fields[key] != val: return false
+          else:
             if b.fields[key] != val: return false
-        else:
-          if b.fields[key] != val: return false
       result = true
 
 proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.}
@@ -541,7 +549,7 @@ proc `[]`*[U, V](a: JsonNode, x: HSlice[U, V]): JsonNode =
   ##
   ## Returns the inclusive range `[a[x.a], a[x.b]]`:
   runnableExamples:
-    import json
+    import std/json
     let arr = %[0,1,2,3,4,5]
     doAssert arr[2..4] == %[2,3,4]
     doAssert arr[2..^2] == %[2,3,4]
@@ -626,7 +634,7 @@ proc delete*(obj: JsonNode, key: string) =
   obj.fields.del(key)
 
 proc copy*(p: JsonNode): JsonNode =
-  ## Performs a deep copy of `a`.
+  ## Performs a deep copy of `p`.
   case p.kind
   of JString:
     result = newJString(p.str)
@@ -855,7 +863,7 @@ proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool, depth = 0): Json
   case p.tok
   of tkString:
     # we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
-    when defined(gcArc) or defined(gcOrc):
+    when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
       result = JsonNode(kind: JString, str: move p.a)
     else:
       result = JsonNode(kind: JString)
@@ -957,7 +965,7 @@ proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats
     p.close()
 
 when defined(js):
-  from math import `mod`
+  from std/math import `mod`
   from std/jsffi import JsObject, `[]`, to
   from std/private/jsutils import getProtoName, isInteger, isSafeInteger
 
@@ -983,9 +991,9 @@ when defined(js):
     else: assert false
 
   proc len(x: JsObject): int =
-    asm """
+    {.emit: """
       `result` = `x`.length;
-    """
+    """.}
 
   proc convertObject(x: JsObject): JsonNode =
     var isRawNumber = false
@@ -996,14 +1004,15 @@ when defined(js):
         result.add(x[i].convertObject())
     of JObject:
       result = newJObject()
-      asm """for (var property in `x`) {
+      {.emit: """for (var property in `x`) {
         if (`x`.hasOwnProperty(property)) {
-      """
+      """.}
+
       var nimProperty: cstring
       var nimValue: JsObject
-      asm "`nimProperty` = property; `nimValue` = `x`[property];"
+      {.emit: "`nimProperty` = property; `nimValue` = `x`[property];".}
       result[$nimProperty] = nimValue.convertObject()
-      asm "}}"
+      {.emit: "}}".}
     of JInt:
       result = newJInt(x.to(int))
     of JFloat:
@@ -1059,313 +1068,306 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind],
     ]
     raise newException(JsonKindError, msg)
 
-when defined(nimFixedForwardGeneric):
-
-  macro isRefSkipDistinct*(arg: typed): untyped =
-    ## internal only, do not use
-    var impl = getTypeImpl(arg)
-    if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"):
-      impl = getTypeImpl(impl[1])
-    while impl.kind == nnkDistinctTy:
-      impl = getTypeImpl(impl[0])
-    result = newLit(impl.kind == nnkRefTy)
-
-  # The following forward declarations don't work in older versions of Nim
-
-  # forward declare all initFromJson
-
-  proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string)
-  proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string)
-  proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string)
-
-  # initFromJson definitions
-
-  proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
-    # since strings don't have a nil state anymore, this mapping of
-    # JNull to the default string is questionable. `none(string)` and
-    # `some("")` have the same potentional json value `JNull`.
-    if jsonNode.kind == JNull:
-      dst = ""
-    else:
-      dst = jsonNode.str
-
-  proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JBool}, jsonPath)
-    dst = jsonNode.bval
-
-  proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) =
-    if jsonNode == nil:
-      raise newException(KeyError, "key not found: " & jsonPath)
-    dst = jsonNode.copy
-
-  proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) =
-    when T is uint|uint64 or (not defined(js) and int.sizeof == 4):
-      verifyJsonKind(jsonNode, {JInt, JString}, jsonPath)
-      case jsonNode.kind
-      of JString:
-        let x = parseBiggestUInt(jsonNode.str)
-        dst = cast[T](x)
-      else:
-        dst = T(jsonNode.num)
-    else:
-      verifyJsonKind(jsonNode, {JInt}, jsonPath)
-      dst = cast[T](jsonNode.num)
-
-  proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
-    if jsonNode.kind == JString:
-      case jsonNode.str
-      of "nan":
-        let b = NaN
-        dst = T(b)
-        # dst = NaN # would fail some tests because range conversions would cause CT error
-        # in some cases; but this is not a hot-spot inside this branch and backend can optimize this.
-      of "inf":
-        let b = Inf
-        dst = T(b)
-      of "-inf":
-        let b = -Inf
-        dst = T(b)
-      else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str)
-    else:
-      verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath)
-      if jsonNode.kind == JFloat:
-        dst = T(jsonNode.fnum)
-      else:
-        dst = T(jsonNode.num)
-
-  proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JString}, jsonPath)
-    dst = parseEnum[T](jsonNode.getStr)
-
-  proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JArray}, jsonPath)
-    dst.setLen jsonNode.len
-    let orignalJsonPathLen = jsonPath.len
-    for i in 0 ..< jsonNode.len:
-      jsonPath.add '['
-      jsonPath.addInt i
-      jsonPath.add ']'
-      initFromJson(dst[i], jsonNode[i], jsonPath)
-      jsonPath.setLen orignalJsonPathLen
-
-  proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JArray}, jsonPath)
-    let originalJsonPathLen = jsonPath.len
-    for i in 0 ..< jsonNode.len:
-      jsonPath.add '['
-      jsonPath.addInt i
-      jsonPath.add ']'
-      initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays
-      jsonPath.setLen originalJsonPathLen
-
-  proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) =
-    dst = initTable[string, T]()
-    verifyJsonKind(jsonNode, {JObject}, jsonPath)
-    let originalJsonPathLen = jsonPath.len
-    for key in keys(jsonNode.fields):
-      jsonPath.add '.'
-      jsonPath.add key
-      initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
-      jsonPath.setLen originalJsonPathLen
-
-  proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) =
-    dst = initOrderedTable[string,T]()
-    verifyJsonKind(jsonNode, {JObject}, jsonPath)
-    let originalJsonPathLen = jsonPath.len
-    for key in keys(jsonNode.fields):
-      jsonPath.add '.'
-      jsonPath.add key
-      initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
-      jsonPath.setLen originalJsonPathLen
-
-  proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) =
-    verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath)
-    if jsonNode.kind == JNull:
-      dst = nil
-    else:
-      dst = new(T)
-      initFromJson(dst[], jsonNode, jsonPath)
+macro isRefSkipDistinct*(arg: typed): untyped =
+  ## internal only, do not use
+  var impl = getTypeImpl(arg)
+  if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"):
+    impl = getTypeImpl(impl[1])
+  while impl.kind == nnkDistinctTy:
+    impl = getTypeImpl(impl[0])
+  result = newLit(impl.kind == nnkRefTy)
+
+# The following forward declarations don't work in older versions of Nim
+
+# forward declare all initFromJson
+
+proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string)
+proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string)
+
+# initFromJson definitions
+
+proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JString, JNull}, jsonPath)
+  # since strings don't have a nil state anymore, this mapping of
+  # JNull to the default string is questionable. `none(string)` and
+  # `some("")` have the same potentional json value `JNull`.
+  if jsonNode.kind == JNull:
+    dst = ""
+  else:
+    dst = jsonNode.str
 
-  proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) =
-    if jsonNode != nil and jsonNode.kind != JNull:
-      when T is ref:
-        dst = some(new(T))
-      else:
-        dst = some(default(T))
-      initFromJson(dst.get, jsonNode, jsonPath)
+proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JBool}, jsonPath)
+  dst = jsonNode.bval
+
+proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) =
+  if jsonNode == nil:
+    raise newException(KeyError, "key not found: " & jsonPath)
+  dst = jsonNode.copy
 
-  macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) =
-    let typInst = getTypeInst(dst)
-    let typImpl = getTypeImpl(dst)
-    let baseTyp = typImpl[0]
+proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) =
+  when T is uint|uint64 or int.sizeof == 4:
+    verifyJsonKind(jsonNode, {JInt, JString}, jsonPath)
+    case jsonNode.kind
+    of JString:
+      let x = parseBiggestUInt(jsonNode.str)
+      dst = cast[T](x)
+    else:
+      dst = T(jsonNode.num)
+  else:
+    verifyJsonKind(jsonNode, {JInt}, jsonPath)
+    dst = cast[T](jsonNode.num)
+
+proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JInt, JFloat, JString}, jsonPath)
+  if jsonNode.kind == JString:
+    case jsonNode.str
+    of "nan":
+      let b = NaN
+      dst = T(b)
+      # dst = NaN # would fail some tests because range conversions would cause CT error
+      # in some cases; but this is not a hot-spot inside this branch and backend can optimize this.
+    of "inf":
+      let b = Inf
+      dst = T(b)
+    of "-inf":
+      let b = -Inf
+      dst = T(b)
+    else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str)
+  else:
+    if jsonNode.kind == JFloat:
+      dst = T(jsonNode.fnum)
+    else:
+      dst = T(jsonNode.num)
+
+proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JString}, jsonPath)
+  dst = parseEnum[T](jsonNode.getStr)
+
+proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JArray}, jsonPath)
+  dst.setLen jsonNode.len
+  let orignalJsonPathLen = jsonPath.len
+  for i in 0 ..< jsonNode.len:
+    jsonPath.add '['
+    jsonPath.addInt i
+    jsonPath.add ']'
+    initFromJson(dst[i], jsonNode[i], jsonPath)
+    jsonPath.setLen orignalJsonPathLen
+
+proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JArray}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for i in 0 ..< jsonNode.len:
+    jsonPath.add '['
+    jsonPath.addInt i
+    jsonPath.add ']'
+    initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) =
+  dst = initTable[string, T]()
+  verifyJsonKind(jsonNode, {JObject}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for key in keys(jsonNode.fields):
+    jsonPath.add '.'
+    jsonPath.add key
+    initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) =
+  dst = initOrderedTable[string,T]()
+  verifyJsonKind(jsonNode, {JObject}, jsonPath)
+  let originalJsonPathLen = jsonPath.len
+  for key in keys(jsonNode.fields):
+    jsonPath.add '.'
+    jsonPath.add key
+    initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath)
+    jsonPath.setLen originalJsonPathLen
+
+proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) =
+  verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath)
+  if jsonNode.kind == JNull:
+    dst = nil
+  else:
+    dst = new(T)
+    initFromJson(dst[], jsonNode, jsonPath)
 
-    result = quote do:
+proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) =
+  if jsonNode != nil and jsonNode.kind != JNull:
+    when T is ref:
+      dst = some(new(T))
+    else:
+      dst = some(default(T))
+    initFromJson(dst.get, jsonNode, jsonPath)
+
+macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) =
+  let typInst = getTypeInst(dst)
+  let typImpl = getTypeImpl(dst)
+  let baseTyp = typImpl[0]
+
+  result = quote do:
+    initFromJson(`baseTyp`(`dst`), `jsonNode`, `jsonPath`)
+
+proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  assignDistinctImpl(dst, jsonNode, jsonPath)
+
+proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) =
+  if typeExpr.kind == nnkTupleConstr:
+    error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
+
+proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) =
+  case typeNode.kind
+  of nnkEmpty:
+    discard
+  of nnkRecList, nnkTupleTy:
+    for it in typeNode:
+      foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+
+  of nnkIdentDefs:
+    typeNode.expectLen 3
+    let fieldSym = typeNode[0]
+    let fieldNameLit = newLit(fieldSym.strVal)
+    let fieldPathLit = newLit("." & fieldSym.strVal)
+    let fieldType = typeNode[1]
+
+    # Detecting incompatiple tuple types in `assignObjectImpl` only
+    # would be much cleaner, but the ast for tuple types does not
+    # contain usable type information.
+    detectIncompatibleType(fieldType, fieldSym)
+
+    dst.add quote do:
+      jsonPath.add `fieldPathLit`
       when nimvm:
-        # workaround #12282
-        var tmp: `baseTyp`
-        initFromJson( tmp, `jsonNode`, `jsonPath`)
-        `dst` = `typInst`(tmp)
-      else:
-        initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`)
-
-  proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
-    assignDistinctImpl(dst, jsonNode, jsonPath)
-
-  proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) =
-    if typeExpr.kind == nnkTupleConstr:
-      error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode)
-
-  proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) =
-    case typeNode.kind
-    of nnkEmpty:
-      discard
-    of nnkRecList, nnkTupleTy:
-      for it in typeNode:
-        foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
-
-    of nnkIdentDefs:
-      typeNode.expectLen 3
-      let fieldSym = typeNode[0]
-      let fieldNameLit = newLit(fieldSym.strVal)
-      let fieldPathLit = newLit("." & fieldSym.strVal)
-      let fieldType = typeNode[1]
-
-      # Detecting incompatiple tuple types in `assignObjectImpl` only
-      # would be much cleaner, but the ast for tuple types does not
-      # contain usable type information.
-      detectIncompatibleType(fieldType, fieldSym)
-
-      dst.add quote do:
-        jsonPath.add `fieldPathLit`
-        when nimvm:
-          when isRefSkipDistinct(`tmpSym`.`fieldSym`):
-            # workaround #12489
-            var tmp: `fieldType`
-            initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
-            `tmpSym`.`fieldSym` = tmp
-          else:
-            initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+        when isRefSkipDistinct(`tmpSym`.`fieldSym`):
+          # workaround #12489
+          var tmp: `fieldType`
+          initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+          `tmpSym`.`fieldSym` = tmp
         else:
           initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
-        jsonPath.setLen `originalJsonPathLen`
-
-    of nnkRecCase:
-      let kindSym = typeNode[0][0]
-      let kindNameLit = newLit(kindSym.strVal)
-      let kindPathLit = newLit("." & kindSym.strVal)
-      let kindType = typeNode[0][1]
-      let kindOffsetLit = newLit(uint(getOffset(kindSym)))
-      dst.add quote do:
-        var kindTmp: `kindType`
-        jsonPath.add `kindPathLit`
-        initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`)
-        jsonPath.setLen `originalJsonPathLen`
-        when defined js:
+      else:
+        initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`)
+      jsonPath.setLen `originalJsonPathLen`
+
+  of nnkRecCase:
+    let kindSym = typeNode[0][0]
+    let kindNameLit = newLit(kindSym.strVal)
+    let kindPathLit = newLit("." & kindSym.strVal)
+    let kindType = typeNode[0][1]
+    let kindOffsetLit = newLit(uint(getOffset(kindSym)))
+    dst.add quote do:
+      var kindTmp: `kindType`
+      jsonPath.add `kindPathLit`
+      initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`)
+      jsonPath.setLen `originalJsonPathLen`
+      when defined js:
+        `tmpSym`.`kindSym` = kindTmp
+      else:
+        when nimvm:
           `tmpSym`.`kindSym` = kindTmp
         else:
-          when nimvm:
-            `tmpSym`.`kindSym` = kindTmp
-          else:
-            # fuck it, assign kind field anyway
-            ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
-      dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
-      for i in 1 ..< typeNode.len:
-        foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
-
-    of nnkOfBranch, nnkElse:
-      let ofBranch = newNimNode(typeNode.kind)
-      for i in 0 ..< typeNode.len-1:
-        ofBranch.add copyNimTree(typeNode[i])
-      let dstInner = newNimNode(nnkStmtListExpr)
-      foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
-      # resOuter now contains the inner stmtList
-      ofBranch.add dstInner
-      dst[^1].expectKind nnkCaseStmt
-      dst[^1].add ofBranch
-
-    of nnkObjectTy:
-      typeNode[0].expectKind nnkEmpty
-      typeNode[1].expectKind {nnkEmpty, nnkOfInherit}
-      if typeNode[1].kind == nnkOfInherit:
-        let base = typeNode[1][0]
-        var impl = getTypeImpl(base)
-        while impl.kind in {nnkRefTy, nnkPtrTy}:
-          impl = getTypeImpl(impl[0])
-        foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
-      let body = typeNode[2]
-      foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+          # fuck it, assign kind field anyway
+          ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp
+    dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
+    for i in 1 ..< typeNode.len:
+      foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+
+  of nnkOfBranch, nnkElse:
+    let ofBranch = newNimNode(typeNode.kind)
+    for i in 0 ..< typeNode.len-1:
+      ofBranch.add copyNimTree(typeNode[i])
+    let dstInner = newNimNode(nnkStmtListExpr)
+    foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+    # resOuter now contains the inner stmtList
+    ofBranch.add dstInner
+    dst[^1].expectKind nnkCaseStmt
+    dst[^1].add ofBranch
+
+  of nnkObjectTy:
+    typeNode[0].expectKind nnkEmpty
+    typeNode[1].expectKind {nnkEmpty, nnkOfInherit}
+    if typeNode[1].kind == nnkOfInherit:
+      let base = typeNode[1][0]
+      var impl = getTypeImpl(base)
+      while impl.kind in {nnkRefTy, nnkPtrTy}:
+        impl = getTypeImpl(impl[0])
+      foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
+    let body = typeNode[2]
+    foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen)
 
-    else:
-      error("unhandled kind: " & $typeNode.kind, typeNode)
-
-  macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
-    let typeSym = getTypeInst(dst)
-    let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen")
-    result = newStmtList()
-    result.add quote do:
-      let `originalJsonPathLen` = len(`jsonPath`)
-    if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
-      # both, `dst` and `typeSym` don't have good lineinfo. But nothing
-      # else is available here.
-      detectIncompatibleType(typeSym, dst)
-      foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen)
-    else:
-      foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen)
-
-  proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
-    assignObjectImpl(dst, jsonNode, jsonPath)
-
-  proc to*[T](node: JsonNode, t: typedesc[T]): T =
-    ## `Unmarshals`:idx: the specified node into the object type specified.
-    ##
-    ## Known limitations:
-    ##
-    ##   * Heterogeneous arrays are not supported.
-    ##   * Sets in object variants are not supported.
-    ##   * Not nil annotations are not supported.
-    ##
-    runnableExamples:
-      let jsonNode = parseJson("""
-        {
-          "person": {
-            "name": "Nimmer",
-            "age": 21
-          },
-          "list": [1, 2, 3, 4]
-        }
-      """)
-
-      type
-        Person = object
-          name: string
-          age: int
-
-        Data = object
-          person: Person
-          list: seq[int]
-
-      var data = to(jsonNode, Data)
-      doAssert data.person.name == "Nimmer"
-      doAssert data.person.age == 21
-      doAssert data.list == @[1, 2, 3, 4]
-
-    var jsonPath = ""
-    initFromJson(result, node, jsonPath)
+  else:
+    error("unhandled kind: " & $typeNode.kind, typeNode)
+
+macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  let typeSym = getTypeInst(dst)
+  let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen")
+  result = newStmtList()
+  result.add quote do:
+    let `originalJsonPathLen` = len(`jsonPath`)
+  if typeSym.kind in {nnkTupleTy, nnkTupleConstr}:
+    # both, `dst` and `typeSym` don't have good lineinfo. But nothing
+    # else is available here.
+    detectIncompatibleType(typeSym, dst)
+    foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen)
+  else:
+    foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen)
+
+proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) =
+  assignObjectImpl(dst, jsonNode, jsonPath)
+
+proc to*[T](node: JsonNode, t: typedesc[T]): T =
+  ## `Unmarshals`:idx: the specified node into the object type specified.
+  ##
+  ## Known limitations:
+  ##
+  ##   * Heterogeneous arrays are not supported.
+  ##   * Sets in object variants are not supported.
+  ##   * Not nil annotations are not supported.
+  ##
+  runnableExamples:
+    let jsonNode = parseJson("""
+      {
+        "person": {
+          "name": "Nimmer",
+          "age": 21
+        },
+        "list": [1, 2, 3, 4]
+      }
+    """)
+
+    type
+      Person = object
+        name: string
+        age: int
+
+      Data = object
+        person: Person
+        list: seq[int]
+
+    var data = to(jsonNode, Data)
+    doAssert data.person.name == "Nimmer"
+    doAssert data.person.age == 21
+    doAssert data.list == @[1, 2, 3, 4]
+
+  var jsonPath = ""
+  result = default(T)
+  initFromJson(result, node, jsonPath)
 
 when false:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: JsonParser
diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim
index 336a57ec1..1efd97b24 100644
--- a/lib/pure/lexbase.nim
+++ b/lib/pure/lexbase.nim
@@ -12,7 +12,7 @@
 ## needs refilling.
 
 import
-  strutils, streams
+  std/[strutils, streams]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -104,9 +104,9 @@ proc fillBaseLexer(L: var BaseLexer, pos: int): int =
     result = 0
 
 proc handleCR*(L: var BaseLexer, pos: int): int =
-  ## Call this if you scanned over '\c' in the buffer; it returns the
+  ## Call this if you scanned over `'\c'` in the buffer; it returns the
   ## position to continue the scanning from. `pos` must be the position
-  ## of the '\c'.
+  ## of the `'\c'`.
   assert(L.buf[pos] == '\c')
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos)
@@ -115,9 +115,9 @@ proc handleCR*(L: var BaseLexer, pos: int): int =
   L.lineStart = result
 
 proc handleLF*(L: var BaseLexer, pos: int): int =
-  ## Call this if you scanned over '\L' in the buffer; it returns the
+  ## Call this if you scanned over `'\L'` in the buffer; it returns the
   ## position to continue the scanning from. `pos` must be the position
-  ## of the '\L'.
+  ## of the `'\L'`.
   assert(L.buf[pos] == '\L')
   inc(L.lineNumber)
   result = fillBaseLexer(L, pos) #L.lastNL := result-1; // BUGFIX: was: result;
diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim
index cbe8a827a..c30f68af8 100644
--- a/lib/pure/logging.nim
+++ b/lib/pure/logging.nim
@@ -17,10 +17,11 @@
 ##
 ## To get started, first create a logger:
 ##
-## .. code-block::
+##   ```Nim
 ##   import std/logging
 ##
 ##   var logger = newConsoleLogger()
+##   ```
 ##
 ## The logger that was created above logs to the console, but this module
 ## also provides loggers that log to files, such as the
@@ -30,9 +31,10 @@
 ## Once a logger has been created, call its `log proc
 ## <#log.e,ConsoleLogger,Level,varargs[string,]>`_ to log a message:
 ##
-## .. code-block::
+##   ```Nim
 ##   logger.log(lvlInfo, "a log message")
 ##   # Output: INFO a log message
+##   ```
 ##
 ## The ``INFO`` within the output is the result of a format string being
 ## prepended to the message, and it will differ depending on the message's
@@ -47,9 +49,8 @@
 ##
 ## .. warning::
 ##   For loggers that log to a console or to files, only error and fatal
-##   messages will cause their output buffers to be flushed immediately.
-##   Use the `flushFile proc <syncio.html#flushFile,File>`_ to flush the buffer
-##   manually if needed.
+##   messages will cause their output buffers to be flushed immediately by default.
+##   set ``flushThreshold`` when creating the logger to change this.
 ##
 ## Handlers
 ## --------
@@ -59,7 +60,7 @@
 ## used with the `addHandler proc<#addHandler,Logger>`_, which is demonstrated
 ## in the following example:
 ##
-## .. code-block::
+##   ```Nim
 ##   import std/logging
 ##
 ##   var consoleLog = newConsoleLogger()
@@ -69,17 +70,19 @@
 ##   addHandler(consoleLog)
 ##   addHandler(fileLog)
 ##   addHandler(rollingLog)
+##   ```
 ##
 ## After doing this, use either the `log template
 ## <#log.t,Level,varargs[string,]>`_ or one of the level-specific templates,
 ## such as the `error template<#error.t,varargs[string,]>`_, to log messages
 ## to all registered handlers at once.
 ##
-## .. code-block::
+##   ```Nim
 ##   # This example uses the loggers created above
 ##   log(lvlError, "an error occurred")
 ##   error("an error occurred")  # Equivalent to the above line
 ##   info("something normal happened")  # Will not be written to errors.log
+##   ```
 ##
 ## Note that a message's level is still checked against each handler's
 ## ``levelThreshold`` and the global log filter.
@@ -117,12 +120,13 @@
 ##
 ## The following example illustrates how to use format strings:
 ##
-## .. code-block::
+##   ```Nim
 ##   import std/logging
 ##
 ##   var logger = newConsoleLogger(fmtStr="[$time] - $levelname: ")
 ##   logger.log(lvlInfo, "this is a message")
 ##   # Output: [19:50:13] - INFO: this is a message
+##   ```
 ##
 ## Notes when using multiple threads
 ## ---------------------------------
@@ -142,9 +146,9 @@
 ## * `strscans module<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which
 ##   offer easier substring extraction than regular expressions
 
-import strutils, times
+import std/[strutils, times]
 when not defined(js):
-  import os
+  import std/os
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -200,6 +204,15 @@ const
   ## If a different format string is preferred, refer to the
   ## `documentation about format strings<#basic-usage-format-strings>`_
   ## for more information, including a list of available variables.
+  defaultFlushThreshold = when NimMajor >= 2:
+      when defined(nimV1LogFlushBehavior): lvlError else: lvlAll
+    else:
+      when defined(nimFlushAllLogs): lvlAll else: lvlError
+  ## The threshold above which log messages to file-like loggers
+  ## are automatically flushed.
+  ## 
+  ## By default, only error and fatal messages are logged,
+  ## but defining ``-d:nimFlushAllLogs`` will make all levels be flushed
 
 type
   Logger* = ref object of RootObj
@@ -228,6 +241,8 @@ type
     ## * `FileLogger<#FileLogger>`_
     ## * `RollingFileLogger<#RollingFileLogger>`_
     useStderr*: bool ## If true, writes to stderr; otherwise, writes to stdout
+    flushThreshold*: Level ## Only messages that are at or above this
+                           ## threshold will be flushed immediately
 
 when not defined(js):
   type
@@ -243,6 +258,8 @@ when not defined(js):
       ## * `ConsoleLogger<#ConsoleLogger>`_
       ## * `RollingFileLogger<#RollingFileLogger>`_
       file*: File ## The wrapped file
+      flushThreshold*: Level ## Only messages that are at or above this
+                           ## threshold will be flushed immediately
 
     RollingFileLogger* = ref object of FileLogger
       ## A logger that writes log messages to a file while performing log
@@ -348,8 +365,8 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
   ## `setLogFilter proc<#setLogFilter,Level>`_.
   ##
   ## **Note:** Only error and fatal messages will cause the output buffer
-  ## to be flushed immediately. Use the `flushFile proc
-  ## <syncio.html#flushFile,File>`_ to flush the buffer manually if needed.
+  ## to be flushed immediately by default. Set ``flushThreshold`` when creating
+  ## the logger to change this.
   ##
   ## See also:
   ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_
@@ -360,10 +377,11 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var consoleLog = newConsoleLogger()
   ##   consoleLog.log(lvlInfo, "this is a message")
   ##   consoleLog.log(lvlError, "error code is: ", 404)
+  ##   ```
   if level >= logging.level and level >= logger.levelThreshold:
     let ln = substituteLog(logger.fmtStr, level, args)
     when defined(js):
@@ -380,12 +398,12 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) =
         if logger.useStderr:
           handle = stderr
         writeLine(handle, ln)
-        if level in {lvlError, lvlFatal}: flushFile(handle)
+        if level >= logger.flushThreshold: flushFile(handle)
       except IOError:
         discard
 
 proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr,
-    useStderr = false): ConsoleLogger =
+    useStderr = false, flushThreshold = defaultFlushThreshold): ConsoleLogger =
   ## Creates a new `ConsoleLogger<#ConsoleLogger>`_.
   ##
   ## By default, log messages are written to ``stdout``. If ``useStderr`` is
@@ -402,13 +420,15 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr,
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var normalLog = newConsoleLogger()
   ##   var formatLog = newConsoleLogger(fmtStr=verboseFmtStr)
   ##   var errorLog = newConsoleLogger(levelThreshold=lvlError, useStderr=true)
+  ##   ```
   new result
   result.fmtStr = fmtStr
   result.levelThreshold = levelThreshold
+  result.flushThreshold = flushThreshold
   result.useStderr = useStderr
 
 when not defined(js):
@@ -424,8 +444,8 @@ when not defined(js):
     ##
     ## **Notes:**
     ## * Only error and fatal messages will cause the output buffer
-    ##   to be flushed immediately. Use the `flushFile proc
-    ##   <syncio.html#flushFile,File>`_ to flush the buffer manually if needed.
+    ##   to be flushed immediately by default. Set ``flushThreshold`` when creating
+    ##   the logger to change this.
     ## * This method is not available for the JavaScript backend.
     ##
     ## See also:
@@ -437,13 +457,14 @@ when not defined(js):
     ##
     ## **Examples:**
     ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var fileLog = newFileLogger("messages.log")
     ##   fileLog.log(lvlInfo, "this is a message")
     ##   fileLog.log(lvlError, "error code is: ", 404)
+    ##   ```
     if level >= logging.level and level >= logger.levelThreshold:
       writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+      if level >= logger.flushThreshold: flushFile(logger.file)
 
   proc defaultFilename*(): string =
     ## Returns the filename that is used by default when naming log files.
@@ -454,7 +475,8 @@ when not defined(js):
 
   proc newFileLogger*(file: File,
                       levelThreshold = lvlAll,
-                      fmtStr = defaultFmtStr): FileLogger =
+                      fmtStr = defaultFmtStr,
+                      flushThreshold = defaultFlushThreshold): FileLogger =
     ## Creates a new `FileLogger<#FileLogger>`_ that uses the given file handle.
     ##
     ## **Note:** This proc is not available for the JavaScript backend.
@@ -467,7 +489,7 @@ when not defined(js):
     ##
     ## **Examples:**
     ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var messages = open("messages.log", fmWrite)
     ##   var formatted = open("formatted.log", fmWrite)
     ##   var errors = open("errors.log", fmWrite)
@@ -475,16 +497,19 @@ when not defined(js):
     ##   var normalLog = newFileLogger(messages)
     ##   var formatLog = newFileLogger(formatted, fmtStr=verboseFmtStr)
     ##   var errorLog = newFileLogger(errors, levelThreshold=lvlError)
+    ##   ```
     new(result)
     result.file = file
     result.levelThreshold = levelThreshold
+    result.flushThreshold = flushThreshold
     result.fmtStr = fmtStr
 
   proc newFileLogger*(filename = defaultFilename(),
                       mode: FileMode = fmAppend,
                       levelThreshold = lvlAll,
                       fmtStr = defaultFmtStr,
-                      bufSize: int = -1): FileLogger =
+                      bufSize: int = -1,
+                      flushThreshold = defaultFlushThreshold): FileLogger =
     ## Creates a new `FileLogger<#FileLogger>`_ that logs to a file with the
     ## given filename.
     ##
@@ -503,12 +528,13 @@ when not defined(js):
     ##
     ## **Examples:**
     ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var normalLog = newFileLogger("messages.log")
     ##   var formatLog = newFileLogger("formatted.log", fmtStr=verboseFmtStr)
     ##   var errorLog = newFileLogger("errors.log", levelThreshold=lvlError)
+    ##   ```
     let file = open(filename, mode, bufSize = bufSize)
-    newFileLogger(file, levelThreshold, fmtStr)
+    newFileLogger(file, levelThreshold, fmtStr, flushThreshold)
 
   # ------
 
@@ -540,7 +566,8 @@ when not defined(js):
                             levelThreshold = lvlAll,
                             fmtStr = defaultFmtStr,
                             maxLines: Positive = 1000,
-                            bufSize: int = -1): RollingFileLogger =
+                            bufSize: int = -1,
+                            flushThreshold = defaultFlushThreshold): RollingFileLogger =
     ## Creates a new `RollingFileLogger<#RollingFileLogger>`_.
     ##
     ## Once the current log file being written to contains ``maxLines`` lines,
@@ -562,11 +589,12 @@ when not defined(js):
     ##
     ## **Examples:**
     ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var normalLog = newRollingFileLogger("messages.log")
     ##   var formatLog = newRollingFileLogger("formatted.log", fmtStr=verboseFmtStr)
     ##   var shortLog = newRollingFileLogger("short.log", maxLines=200)
     ##   var errorLog = newRollingFileLogger("errors.log", levelThreshold=lvlError)
+    ##   ```
     new(result)
     result.levelThreshold = levelThreshold
     result.fmtStr = fmtStr
@@ -576,6 +604,7 @@ when not defined(js):
     result.curLine = 0
     result.baseName = filename
     result.baseMode = mode
+    result.flushThreshold = flushThreshold
 
     result.logFiles = countFiles(filename)
 
@@ -602,8 +631,8 @@ when not defined(js):
     ##
     ## **Notes:**
     ## * Only error and fatal messages will cause the output buffer
-    ##   to be flushed immediately. Use the `flushFile proc
-    ##   <syncio.html#flushFile,File>`_ to flush the buffer manually if needed.
+    ##   to be flushed immediately by default. Set ``flushThreshold`` when creating
+    ##   the logger to change this.
     ## * This method is not available for the JavaScript backend.
     ##
     ## See also:
@@ -615,10 +644,11 @@ when not defined(js):
     ##
     ## **Examples:**
     ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var rollingLog = newRollingFileLogger("messages.log")
     ##   rollingLog.log(lvlInfo, "this is a message")
     ##   rollingLog.log(lvlError, "error code is: ", 404)
+    ##   ```
     if level >= logging.level and level >= logger.levelThreshold:
       if logger.curLine >= logger.maxLines:
         logger.file.close()
@@ -629,7 +659,7 @@ when not defined(js):
             bufSize = logger.bufSize)
 
       writeLine(logger.file, substituteLog(logger.fmtStr, level, args))
-      if level in {lvlError, lvlFatal}: flushFile(logger.file)
+      if level >= logger.flushThreshold: flushFile(logger.file)
       logger.curLine.inc
 
 # --------
@@ -648,11 +678,12 @@ template log*(level: Level, args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   log(lvlInfo, "This is an example.")
+  ##   ```
   ##
   ## See also:
   ## * `debug template<#debug.t,varargs[string,]>`_
@@ -677,11 +708,12 @@ template debug*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   debug("myProc called with arguments: foo, 5")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -698,11 +730,12 @@ template info*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   info("Application started successfully.")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -719,11 +752,12 @@ template notice*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   notice("An important operation has completed.")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -739,11 +773,12 @@ template warn*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   warn("The previous operation took too long to process.")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -761,11 +796,12 @@ template error*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   error("An exception occurred while processing the form.")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -782,11 +818,12 @@ template fatal*(args: varargs[string, `$`]) =
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var logger = newConsoleLogger()
   ##   addHandler(logger)
   ##
   ##   fatal("Can't open database -- exiting.")
+  ##   ```
   ##
   ## See also:
   ## * `log template<#log.t,Level,varargs[string,]>`_
@@ -802,6 +839,7 @@ proc addHandler*(handler: Logger) =
   ##   each of those threads.
   ##
   ## See also:
+  ## * `removeHandler proc`_
   ## * `getHandlers proc<#getHandlers>`_
   runnableExamples:
     var logger = newConsoleLogger()
@@ -809,6 +847,16 @@ proc addHandler*(handler: Logger) =
     doAssert logger in getHandlers()
   handlers.add(handler)
 
+proc removeHandler*(handler: Logger) =
+  ## Removes a logger from the list of registered handlers.
+  ##
+  ## Note that for n times a logger is registered, n calls to this proc
+  ## are required to remove that logger.
+  for i, hnd in handlers:
+    if hnd == handler:
+      handlers.delete(i)
+      return
+
 proc getHandlers*(): seq[Logger] =
   ## Returns a list of all the registered handlers.
   ##
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index 848d7e3fb..f9b3d3e4c 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -54,7 +54,7 @@ Please use alternative packages for serialization.
 It is possible to reimplement this module using generics and type traits.
 Please contribute a new implementation.""".}
 
-import streams, typeinfo, json, intsets, tables, unicode
+import std/[streams, typeinfo, json, intsets, tables, unicode]
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, formatfloat]
@@ -210,7 +210,8 @@ proc loadAny(p: var JsonParser, a: Any, t: var Table[BiggestInt, pointer]) =
       setPointer(a, nil)
       next(p)
     of jsonInt:
-      setPointer(a, t.getOrDefault(p.getInt))
+      var raw = t.getOrDefault(p.getInt)
+      setPointer(a, addr raw)
       next(p)
     of jsonArrayStart:
       next(p)
@@ -304,7 +305,7 @@ proc store*[T](s: Stream, data: sink T) =
 
   var stored = initIntSet()
   var d: T
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc)or defined(gcAtomicArc):
     d = data
   else:
     shallowCopy(d, data)
@@ -333,7 +334,7 @@ proc `$$`*[T](x: sink T): string =
   else:
     var stored = initIntSet()
     var d: T
-    when defined(gcArc) or defined(gcOrc):
+    when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
       d = x
     else:
       shallowCopy(d, x)
diff --git a/lib/pure/math.nim b/lib/pure/math.nim
index 15324f882..ed7d2382f 100644
--- a/lib/pure/math.nim
+++ b/lib/pure/math.nim
@@ -47,7 +47,6 @@ runnableExamples:
 ## * `fenv module <fenv.html>`_ for handling of floating-point rounding
 ##   and exceptions (overflow, zero-divide, etc.)
 ## * `random module <random.html>`_ for a fast and tiny random number generator
-## * `mersenne module <mersenne.html>`_ for the Mersenne Twister random number generator
 ## * `stats module <stats.html>`_ for statistical analysis
 ## * `strformat module <strformat.html>`_ for formatting floats for printing
 ## * `system module <system.html>`_ for some very basic and trivial math operators
@@ -58,13 +57,14 @@ import std/private/since
 {.push debugger: off.} # the user does not want to trace a part
                        # of the standard library!
 
-import bitops, fenv
+import std/[bitops, fenv]
+import system/countbits_impl
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 
-when defined(c) or defined(cpp):
+when not defined(js) and not defined(nimscript): # C
   proc c_isnan(x: float): bool {.importc: "isnan", header: "<math.h>".}
     # a generic like `x: SomeFloat` might work too if this is implemented via a C macro.
 
@@ -78,6 +78,41 @@ when defined(c) or defined(cpp):
       importc: "frexpf", header: "<math.h>".}
   func c_frexp2(x: cdouble, exponent: var cint): cdouble {.
       importc: "frexp", header: "<math.h>".}
+  
+  type
+    div_t {.importc, header: "<stdlib.h>".} = object
+      quot: cint
+      rem: cint
+    ldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clong
+      rem: clong
+    lldiv_t {.importc, header: "<stdlib.h>".} = object
+      quot: clonglong
+      rem: clonglong
+  
+  when cint isnot clong:
+    func divmod_c(x, y: cint): div_t {.importc: "div", header: "<stdlib.h>".}
+  when clong isnot clonglong:
+    func divmod_c(x, y: clonglong): lldiv_t {.importc: "lldiv", header: "<stdlib.h>".}
+  func divmod_c(x, y: clong): ldiv_t {.importc: "ldiv", header: "<stdlib.h>".}
+  func divmod*[T: SomeInteger](x, y: T): (T, T) {.inline.} = 
+    ## Specialized instructions for computing both division and modulus.
+    ## Return structure is: (quotient, remainder)
+    runnableExamples:
+      doAssert divmod(5, 2) == (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    when T is cint | clong | clonglong:
+      when compileOption("overflowChecks"):
+        if y == 0:
+          raise new(DivByZeroDefect)
+        elif (x == T.low and y == -1.T):
+          raise new(OverflowDefect)
+      let res = divmod_c(x, y)
+      result[0] = res.quot
+      result[1] = res.rem
+    else:
+      result[0] = x div y
+      result[1] = x mod y
 
 func binom*(n, k: int): int =
   ## Computes the [binomial coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient).
@@ -121,7 +156,7 @@ func fac*(n: int): int =
 
 {.push checks: off, line_dir: off, stack_trace: off.}
 
-when defined(posix) and not defined(genode):
+when defined(posix) and not defined(genode) and not defined(macosx):
   {.passl: "-lm".}
 
 const
@@ -166,7 +201,7 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} =
   template fn: untyped = result = x != x
   when nimvm: fn()
   else:
-    when defined(js): fn()
+    when defined(js) or defined(nimscript): fn()
     else: result = c_isnan(x)
 
 when defined(js):
@@ -185,13 +220,13 @@ when defined(js):
     let a = newFloat64Array(buffer)
     let b = newUint32Array(buffer)
     a[0] = x
-    asm """
+    {.emit: """
     function updateBit(num, bitPos, bitVal) {
       return (num & ~(1 << bitPos)) | (bitVal << bitPos);
     }
     `b`[1] = updateBit(`b`[1], 31, `sgn`);
-    `result` = `a`[0]
-    """
+    `result` = `a`[0];
+    """.}
 
 proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} =
   ## Returns true if `x` is negative, false otherwise.
@@ -236,7 +271,6 @@ func classify*(x: float): FloatClass =
   ## Classifies a floating point value.
   ##
   ## Returns `x`'s class as specified by the `FloatClass enum<#FloatClass>`_.
-  ## Doesn't work with `--passc:-ffast-math`.
   runnableExamples:
     doAssert classify(0.3) == fcNormal
     doAssert classify(0.0) == fcZero
@@ -245,6 +279,7 @@ func classify*(x: float): FloatClass =
     doAssert classify(5.0e-324) == fcSubnormal
 
   # JavaScript and most C compilers have no classify:
+  if isNan(x): return fcNan
   if x == 0.0:
     if 1.0 / x == Inf:
       return fcZero
@@ -253,7 +288,6 @@ func classify*(x: float): FloatClass =
   if x * 0.5 == x:
     if x > 0.0: return fcInf
     else: return fcNegInf
-  if x != x: return fcNan
   if abs(x) < MinFloatNormal:
     return fcSubnormal
   return fcNormal
@@ -327,68 +361,8 @@ func nextPowerOfTwo*(x: int): int =
   result = result or (result shr 1)
   result += 1 + ord(x <= 0)
 
-func sum*[T](x: openArray[T]): T =
-  ## Computes the sum of the elements in `x`.
-  ##
-  ## If `x` is empty, 0 is returned.
-  ##
-  ## **See also:**
-  ## * `prod func <#prod,openArray[T]>`_
-  ## * `cumsum func <#cumsum,openArray[T]>`_
-  ## * `cumsummed func <#cumsummed,openArray[T]>`_
-  runnableExamples:
-    doAssert sum([1, 2, 3, 4]) == 10
-    doAssert sum([-4, 3, 5]) == 4
-
-  for i in items(x): result = result + i
-
-func prod*[T](x: openArray[T]): T =
-  ## Computes the product of the elements in `x`.
-  ##
-  ## If `x` is empty, 1 is returned.
-  ##
-  ## **See also:**
-  ## * `sum func <#sum,openArray[T]>`_
-  ## * `fac func <#fac,int>`_
-  runnableExamples:
-    doAssert prod([1, 2, 3, 4]) == 24
-    doAssert prod([-4, 3, 5]) == -60
-
-  result = T(1)
-  for i in items(x): result = result * i
-
-func cumsummed*[T](x: openArray[T]): seq[T] =
-  ## Returns the cumulative (aka prefix) summation of `x`.
-  ##
-  ## If `x` is empty, `@[]` is returned.
-  ##
-  ## **See also:**
-  ## * `sum func <#sum,openArray[T]>`_
-  ## * `cumsum func <#cumsum,openArray[T]>`_ for the in-place version
-  runnableExamples:
-    doAssert cumsummed([1, 2, 3, 4]) == @[1, 3, 6, 10]
-
-  let xLen = x.len
-  if xLen == 0:
-    return @[]
-  result.setLen(xLen)
-  result[0] = x[0]
-  for i in 1 ..< xLen: result[i] = result[i - 1] + x[i]
 
-func cumsum*[T](x: var openArray[T]) =
-  ## Transforms `x` in-place (must be declared as `var`) into its
-  ## cumulative (aka prefix) summation.
-  ##
-  ## **See also:**
-  ## * `sum func <#sum,openArray[T]>`_
-  ## * `cumsummed func <#cumsummed,openArray[T]>`_ for a version which
-  ##   returns a cumsummed sequence
-  runnableExamples:
-    var a = [1, 2, 3, 4]
-    cumsum(a)
-    doAssert a == @[1, 3, 6, 10]
 
-  for i in 1 ..< x.len: x[i] = x[i - 1] + x[i]
 
 when not defined(js): # C
   func sqrt*(x: float32): float32 {.importc: "sqrtf", header: "<math.h>".}
@@ -854,6 +828,14 @@ else: # JS
       doAssert -6.5 mod  2.5 == -1.5
       doAssert  6.5 mod -2.5 ==  1.5
       doAssert -6.5 mod -2.5 == -1.5
+  
+  func divmod*[T:SomeInteger](num, denom: T): (T, T) = 
+    runnableExamples:
+      doAssert  divmod(5, 2) ==  (2, 1)
+      doAssert divmod(5, -3) == (-1, 2)
+    result[0] = num div denom
+    result[1] = num mod denom
+  
 
 func round*[T: float32|float64](x: T, places: int): T =
   ## Decimal rounding on a binary floating point number.
@@ -1134,6 +1116,69 @@ func sgn*[T: SomeNumber](x: T): int {.inline.} =
 {.pop.}
 {.pop.}
 
+func sum*[T](x: openArray[T]): T =
+  ## Computes the sum of the elements in `x`.
+  ##
+  ## If `x` is empty, 0 is returned.
+  ##
+  ## **See also:**
+  ## * `prod func <#prod,openArray[T]>`_
+  ## * `cumsum func <#cumsum,openArray[T]>`_
+  ## * `cumsummed func <#cumsummed,openArray[T]>`_
+  runnableExamples:
+    doAssert sum([1, 2, 3, 4]) == 10
+    doAssert sum([-4, 3, 5]) == 4
+
+  for i in items(x): result = result + i
+
+func prod*[T](x: openArray[T]): T =
+  ## Computes the product of the elements in `x`.
+  ##
+  ## If `x` is empty, 1 is returned.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `fac func <#fac,int>`_
+  runnableExamples:
+    doAssert prod([1, 2, 3, 4]) == 24
+    doAssert prod([-4, 3, 5]) == -60
+
+  result = T(1)
+  for i in items(x): result = result * i
+
+func cumsummed*[T](x: openArray[T]): seq[T] =
+  ## Returns the cumulative (aka prefix) summation of `x`.
+  ##
+  ## If `x` is empty, `@[]` is returned.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `cumsum func <#cumsum,openArray[T]>`_ for the in-place version
+  runnableExamples:
+    doAssert cumsummed([1, 2, 3, 4]) == @[1, 3, 6, 10]
+
+  let xLen = x.len
+  if xLen == 0:
+    return @[]
+  result.setLen(xLen)
+  result[0] = x[0]
+  for i in 1 ..< xLen: result[i] = result[i - 1] + x[i]
+
+func cumsum*[T](x: var openArray[T]) =
+  ## Transforms `x` in-place (must be declared as `var`) into its
+  ## cumulative (aka prefix) summation.
+  ##
+  ## **See also:**
+  ## * `sum func <#sum,openArray[T]>`_
+  ## * `cumsummed func <#cumsummed,openArray[T]>`_ for a version which
+  ##   returns a cumsummed sequence
+  runnableExamples:
+    var a = [1, 2, 3, 4]
+    cumsum(a)
+    doAssert a == @[1, 3, 6, 10]
+
+  for i in 1 ..< x.len: x[i] = x[i - 1] + x[i]
+
 func `^`*[T: SomeNumber](x: T, y: Natural): T =
   ## Computes `x` to the power of `y`.
   ##
@@ -1185,40 +1230,42 @@ func gcd*[T](x, y: T): T =
     swap x, y
   abs x
 
-func gcd*(x, y: SomeInteger): SomeInteger =
-  ## Computes the greatest common (positive) divisor of `x` and `y`,
-  ## using the binary GCD (aka Stein's) algorithm.
-  ##
-  ## **See also:**
-  ## * `gcd func <#gcd,T,T>`_ for a float version
-  ## * `lcm func <#lcm,T,T>`_
-  runnableExamples:
-    doAssert gcd(12, 8) == 4
-    doAssert gcd(17, 63) == 1
-
-  when x is SomeSignedInt:
-    var x = abs(x)
-  else:
-    var x = x
-  when y is SomeSignedInt:
-    var y = abs(y)
-  else:
-    var y = y
-
-  if x == 0:
-    return y
-  if y == 0:
-    return x
-
-  let shift = countTrailingZeroBits(x or y)
-  y = y shr countTrailingZeroBits(y)
-  while x != 0:
-    x = x shr countTrailingZeroBits(x)
-    if y > x:
-      swap y, x
-    x -= y
-  y shl shift
-
+when useBuiltins:
+  ## this func uses bitwise comparisons from C compilers, which are not always available.
+  func gcd*(x, y: SomeInteger): SomeInteger =
+    ## Computes the greatest common (positive) divisor of `x` and `y`,
+    ## using the binary GCD (aka Stein's) algorithm.
+    ##
+    ## **See also:**
+    ## * `gcd func <#gcd,T,T>`_ for a float version
+    ## * `lcm func <#lcm,T,T>`_
+    runnableExamples:
+      doAssert gcd(12, 8) == 4
+      doAssert gcd(17, 63) == 1
+  
+    when x is SomeSignedInt:
+      var x = abs(x)
+    else:
+      var x = x
+    when y is SomeSignedInt:
+      var y = abs(y)
+    else:
+      var y = y
+  
+    if x == 0:
+      return y
+    if y == 0:
+      return x
+  
+    let shift = countTrailingZeroBits(x or y)
+    y = y shr countTrailingZeroBits(y)
+    while x != 0:
+      x = x shr countTrailingZeroBits(x)
+      if y > x:
+        swap y, x
+      x -= y
+    y shl shift
+  
 func gcd*[T](x: openArray[T]): T {.since: (1, 1).} =
   ## Computes the greatest common (positive) divisor of the elements of `x`.
   ##
diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim
index cd4d1e6b8..9c3f6d51b 100644
--- a/lib/pure/md5.nim
+++ b/lib/pure/md5.nim
@@ -14,10 +14,12 @@
 ## See also
 ## ========
 ## * `base64 module<base64.html>`_ for a Base64 encoder and decoder
-## * `std/sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
+## * `sha1 module <sha1.html>`_ for the SHA-1 checksum algorithm
 ## * `hashes module<hashes.html>`_ for efficient computations of hash values
 ##   for diverse Nim types
 
+{.deprecated: "use command `nimble install checksums` and import `checksums/md5` instead".}
+
 when defined(nimHasStyleChecks):
   {.push styleChecks: off.}
 
@@ -98,32 +100,19 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
     dest[i+3] = uint8(src[j] shr 24 and 0xff'u32)
     inc(i, 4)
 
-template slice(s: string, a, b): openArray[uint8] =
-  when nimvm:
-    # toOpenArray is not implemented in VM
-    var s2 = newSeq[uint8](s.len)
-    for i in 0 ..< s2.len:
-      s2[i] = uint8(s[i])
-    s2
-  else:
-    s.toOpenArrayByte(a, b)
-
 template slice(s: cstring, a, b): openArray[uint8] =
   when nimvm:
     # toOpenArray is not implemented in VM
-    slice($s, a, b)
+    toOpenArrayByte($s, a, b)
   else:
     when defined(js):
       # toOpenArrayByte for cstring is not implemented in JS
-      slice($s, a, b)
+      toOpenArrayByte($s, a, b)
     else:
       s.toOpenArrayByte(a, b)
 
 template slice(s: openArray[uint8], a, b): openArray[uint8] =
-  when nimvm:
-    s[a .. b]
-  else:
-    s.toOpenArray(a, b)
+  s.toOpenArray(a, b)
 
 const useMem = declared(copyMem)
 
@@ -343,4 +332,4 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
 
 
 when defined(nimHasStyleChecks):
-  {.pop.} #{.push styleChecks: off.}
+  {.pop.} #{.push styleChecks: off.}
\ No newline at end of file
diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim
index 28563b6fe..8eec551c4 100644
--- a/lib/pure/memfiles.nim
+++ b/lib/pure/memfiles.nim
@@ -16,13 +16,16 @@
 ## other "line-like", variable length, delimited records).
 
 when defined(windows):
-  import winlean
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
 elif defined(posix):
-  import posix
+  import std/posix
 else:
   {.error: "the memfiles module is not supported on your operating system!".}
 
-import os, streams
+import std/streams
+import std/oserrors
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
@@ -32,6 +35,35 @@ proc newEIO(msg: string): ref IOError =
   new(result)
   result.msg = msg
 
+proc setFileSize(fh: FileHandle, newFileSize = -1, oldSize = -1): OSErrorCode =
+  ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1,
+  ## allocating | freeing space from the file system.  This routine returns the
+  ## last OSErrorCode found rather than raising to support old rollback/clean-up
+  ## code style. [ Should maybe move to std/osfiles. ]
+  if newFileSize < 0 or newFileSize == oldSize:
+    return
+  when defined(windows):
+    var sizeHigh = int32(newFileSize shr 32)
+    let sizeLow = int32(newFileSize and 0xffffffff)
+    let status = setFilePointer(fh, sizeLow, addr(sizeHigh), FILE_BEGIN)
+    let lastErr = osLastError()
+    if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
+        setEndOfFile(fh) == 0:
+      result = lastErr
+  else:
+    if newFileSize > oldSize: # grow the file
+      var e: cint # posix_fallocate truncates up when needed.
+      when declared(posix_fallocate):
+        while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR):
+          discard
+      if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1:
+        result = osLastError() # fallback arguable; Most portable BUT allows SEGV
+      elif e != 0:
+        result = osLastError()
+    else: # shrink the file
+      if ftruncate(fh.cint, newFileSize) == -1:
+        result = osLastError()
+
 type
   MemFile* = object      ## represents a memory mapped file
     mem*: pointer        ## a pointer to the memory mapped file. The pointer
@@ -122,7 +154,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var
   ##     mm, mm_full, mm_half: MemFile
   ##
@@ -134,6 +166,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
   ##
   ##   # Read the first 512 bytes
   ##   mm_half = memfiles.open("/tmp/test.mmap", mode = fmReadWrite, mappedSize = 512)
+  ##   ```
 
   # The file can be resized only when write mode is used:
   if mode == fmAppend:
@@ -171,25 +204,13 @@ proc open*(filename: string, mode: FileMode = fmRead,
         else: FILE_ATTRIBUTE_NORMAL or flags,
         0)
 
-    when useWinUnicode:
-      result.fHandle = callCreateFile(createFileW, newWideCString(filename))
-    else:
-      result.fHandle = callCreateFile(createFileA, filename)
+    result.fHandle = callCreateFile(createFileW, newWideCString(filename))
 
     if result.fHandle == INVALID_HANDLE_VALUE:
       fail(osLastError(), "error opening file")
 
-    if newFileSize != -1:
-      var
-        sizeHigh = int32(newFileSize shr 32)
-        sizeLow = int32(newFileSize and 0xffffffff)
-
-      var status = setFilePointer(result.fHandle, sizeLow, addr(sizeHigh),
-                                  FILE_BEGIN)
-      let lastErr = osLastError()
-      if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or
-          (setEndOfFile(result.fHandle) == 0):
-        fail(lastErr, "error setting file size")
+    if (let e = setFileSize(result.fHandle.FileHandle, newFileSize);
+        e != 0.OSErrorCode): fail(e, "error setting file size")
 
     # since the strings are always 'nil', we simply always call
     # CreateFileMappingW which should be slightly faster anyway:
@@ -223,7 +244,7 @@ proc open*(filename: string, mode: FileMode = fmRead,
 
     result.wasOpened = true
     if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE:
-      if closeHandle(result.fHandle) == 0:
+      if closeHandle(result.fHandle) != 0:
         result.fHandle = INVALID_HANDLE_VALUE
 
   else:
@@ -238,42 +259,31 @@ proc open*(filename: string, mode: FileMode = fmRead,
       flags = flags or O_CREAT or O_TRUNC
       var permissionsMode = S_IRUSR or S_IWUSR
       result.handle = open(filename, flags, permissionsMode)
+      if result.handle != -1:
+        if (let e = setFileSize(result.handle.FileHandle, newFileSize);
+            e != 0.OSErrorCode): fail(e, "error setting file size")
     else:
       result.handle = open(filename, flags)
 
     if result.handle == -1:
-      # XXX: errno is supposed to be set here
-      # Is there an exception that wraps it?
       fail(osLastError(), "error opening file")
 
-    if newFileSize != -1:
-      if ftruncate(result.handle, newFileSize) == -1:
-        fail(osLastError(), "error setting file size")
-
-    if mappedSize != -1:
-      result.size = mappedSize
-    else:
-      var stat: Stat
+    if mappedSize != -1: #XXX Logic here differs from `when windows` branch ..
+      result.size = mappedSize #.. which always fstats&Uses min(mappedSize, st).
+    else: # if newFileSize!=-1: result.size=newFileSize # if trust setFileSize
+      var stat: Stat  #^^.. BUT some FSes (eg. Linux HugeTLBfs) round to 2MiB.
       if fstat(result.handle, stat) != -1:
-        # XXX: Hmm, this could be unsafe
-        # Why is mmap taking int anyway?
-        result.size = int(stat.st_size)
+        result.size = stat.st_size.int # int may be 32-bit-unsafe for 2..<4 GiB
       else:
         fail(osLastError(), "error getting file size")
 
     result.flags = if mapFlags == cint(-1): MAP_SHARED else: mapFlags
-    #Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
+    # Ensure exactly one of MAP_PRIVATE cr MAP_SHARED is set
     if int(result.flags and MAP_PRIVATE) == 0:
       result.flags = result.flags or MAP_SHARED
 
-    result.mem = mmap(
-      nil,
-      result.size,
-      if readonly: PROT_READ else: PROT_READ or PROT_WRITE,
-      result.flags,
-      result.handle,
-      offset)
-
+    let pr = if readonly: PROT_READ else: PROT_READ or PROT_WRITE
+    result.mem = mmap(nil, result.size, pr, result.flags, result.handle, offset)
     if result.mem == cast[pointer](MAP_FAILED):
       fail(osLastError(), "file mapping failed")
 
@@ -303,34 +313,58 @@ proc flush*(f: var MemFile; attempts: Natural = 3) =
       if lastErr != EBUSY.OSErrorCode:
         raiseOSError(lastErr, "error flushing mapping")
 
-when defined(posix) or defined(nimdoc):
-  proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
-    ## resize and re-map the file underlying an `allowRemap MemFile`.
-    ## **Note**: this assumes the entire file is mapped read-write at offset zero.
-    ## Also, the value of `.mem` will probably change.
-    ## **Note**: This is not (yet) available on Windows.
-    when defined(posix):
-      if f.handle == -1:
-        raise newException(IOError,
-                            "Cannot resize MemFile opened with allowRemap=false")
-      if ftruncate(f.handle, newFileSize) == -1:
-        raiseOSError(osLastError())
-      when defined(linux): #Maybe NetBSD, too?
-        #On Linux this can be over 100 times faster than a munmap,mmap cycle.
-        proc mremap(old: pointer; oldSize, newSize: csize; flags: cint):
-            pointer {.importc: "mremap", header: "<sys/mman.h>".}
-        let newAddr = mremap(f.mem, csize(f.size), csize(newFileSize), cint(1))
-        if newAddr == cast[pointer](MAP_FAILED):
-          raiseOSError(osLastError())
-      else:
-        if munmap(f.mem, f.size) != 0:
-          raiseOSError(osLastError())
-        let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
-                           f.flags, f.handle, 0)
-        if newAddr == cast[pointer](MAP_FAILED):
-          raiseOSError(osLastError())
-      f.mem = newAddr
+proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} =
+  ## Resize & re-map the file underlying an `allowRemap MemFile`.  If the OS/FS
+  ## supports it, file space is reserved to ensure room for new virtual pages.
+  ## Caller should wait often enough for `flush` to finish to limit use of
+  ## system RAM for write buffering, perhaps just prior to this call.
+  ## **Note**: this assumes the entire file is mapped read-write at offset 0.
+  ## Also, the value of `.mem` will probably change.
+  if newFileSize < 1: # Q: include system/bitmasks & use PageSize ?
+    raise newException(IOError, "Cannot resize MemFile to < 1 byte")
+  when defined(windows):
+    if not f.wasOpened:
+      raise newException(IOError, "Cannot resize unopened MemFile")
+    if f.fHandle == INVALID_HANDLE_VALUE:
+      raise newException(IOError,
+                         "Cannot resize MemFile opened with allowRemap=false")
+    if unmapViewOfFile(f.mem) == 0 or closeHandle(f.mapHandle) == 0: # Un-do map
+      raiseOSError(osLastError())
+    if newFileSize != f.size: # Seek to size & `setEndOfFile` => allocated.
+      if (let e = setFileSize(f.fHandle.FileHandle, newFileSize);
+          e != 0.OSErrorCode): raiseOSError(e)
+    f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil)
+    if f.mapHandle == 0:                                             # Re-do map
+      raiseOSError(osLastError())
+    if (let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE,
+                                0, 0, WinSizeT(newFileSize), nil); m != nil):
+      f.mem  = m
       f.size = newFileSize
+    else:
+      raiseOSError(osLastError())
+  elif defined(posix):
+    if f.handle == -1:
+      raise newException(IOError,
+                         "Cannot resize MemFile opened with allowRemap=false")
+    if newFileSize != f.size:
+      if (let e = setFileSize(f.handle.FileHandle, newFileSize, f.size);
+          e != 0.OSErrorCode): raiseOSError(e)
+    when defined(linux): #Maybe NetBSD, too?
+      # On Linux this can be over 100 times faster than a munmap,mmap cycle.
+      proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint):
+          pointer {.importc: "mremap", header: "<sys/mman.h>".}
+      let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint)
+      if newAddr == cast[pointer](MAP_FAILED):
+        raiseOSError(osLastError())
+    else:
+      if munmap(f.mem, f.size) != 0:
+        raiseOSError(osLastError())
+      let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE,
+                         f.flags, f.handle, 0)
+      if newAddr == cast[pointer](MAP_FAILED):
+        raiseOSError(osLastError())
+    f.mem = newAddr
+    f.size = newFileSize
 
 proc close*(f: var MemFile) =
   ## closes the memory mapped file `f`. All changes are written back to the
@@ -378,7 +412,7 @@ proc `==`*(x, y: MemSlice): bool =
 proc `$`*(ms: MemSlice): string {.inline.} =
   ## Return a Nim string built from a MemSlice.
   result.setLen(ms.size)
-  copyMem(addr(result[0]), ms.data, ms.size)
+  copyMem(result.cstring, ms.data, ms.size)
 
 iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} =
   ## Iterates over \[optional `eat`] `delim`-delimited slices in MemFile `mfile`.
@@ -404,15 +438,15 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline
   ## functions, not str* functions).
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var count = 0
   ##   for slice in memSlices(memfiles.open("foo")):
   ##     if slice.size > 0 and cast[cstring](slice.data)[0] != '#':
   ##       inc(count)
   ##   echo count
+  ##   ```
 
-  proc c_memchr(cstr: pointer, c: char, n: csize): pointer {.
+  proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
        importc: "memchr", header: "<string.h>".}
   proc `-!`(p, q: pointer): int {.inline.} = return cast[int](p) -% cast[int](q)
   var ms: MemSlice
@@ -420,7 +454,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline
   ms.data = mfile.mem
   var remaining = mfile.size
   while remaining > 0:
-    ending = c_memchr(ms.data, delim, remaining)
+    ending = c_memchr(ms.data, delim, csize_t(remaining))
     if ending == nil: # unterminated final slice
       ms.size = remaining # Weird case..check eat?
       yield ms
@@ -440,11 +474,11 @@ iterator lines*(mfile: MemFile, buf: var string, delim = '\l',
   ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var buffer: string = ""
   ##   for line in lines(memfiles.open("foo"), buffer):
   ##     echo line
+  ##   ```
 
   for ms in memSlices(mfile, delim, eat):
     setLen(buf, ms.size)
@@ -459,10 +493,10 @@ iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} =
   ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for line in lines(memfiles.open("foo")):
   ##     echo line
+  ##   ```
 
   var buf = newStringOfCap(80)
   for line in lines(mfile, buf, delim, eat):
@@ -492,8 +526,8 @@ proc mmsSetPosition(s: Stream, pos: int) =
 proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos
 
 proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int =
-  let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem)
-  let p = cast[ByteAddress](MemMapFileStream(s).pos)
+  let startAddress = cast[int](MemMapFileStream(s).mf.mem)
+  let p = cast[int](MemMapFileStream(s).pos)
   let l = min(bufLen, MemMapFileStream(s).mf.size - p)
   moveMem(buffer, cast[pointer](startAddress + p), l)
   result = l
@@ -508,8 +542,8 @@ proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) =
   let size = MemMapFileStream(s).mf.size
   if MemMapFileStream(s).pos + bufLen > size:
     raise newEIO("cannot write to stream")
-  let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) +
-          cast[ByteAddress](MemMapFileStream(s).pos)
+  let p = cast[int](MemMapFileStream(s).mf.mem) +
+          cast[int](MemMapFileStream(s).pos)
   moveMem(cast[pointer](p), buffer, bufLen)
   inc(MemMapFileStream(s).pos, bufLen)
 
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index 346fb39ee..ff639e8e5 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -26,8 +26,8 @@ runnableExamples:
   doAssert m.getMimetype("fakext") == "text/fakelang"
   doAssert m.getMimetype("FaKeXT") == "text/fakelang"
 
-import tables
-from strutils import startsWith, toLowerAscii, strip
+import std/tables
+from std/strutils import startsWith, toLowerAscii, strip
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -38,1848 +38,704 @@ type
     mimes: OrderedTable[string, string]
 
 const mimes* = {
-  "123": "application/vnd.lotus-1-2-3",
-  "1km": "application/vnd.1000minds.decision-model+xml",
-  "323": "text/h323",
-  "3dm": "text/vnd.in3d.3dml",
-  "3dmf": "x-world/x-3dmf",
-  "3dml": "text/vnd.in3d.3dml",
-  "3ds": "image/x-3ds",
-  "3g2": "video/3gpp2",
-  "3gp": "video/3gpp",
-  "3gpp": "audio/3gpp",
-  "3gpp2": "video/3gpp2",
-  "3mf": "application/vnd.ms-3mfdocument",
-  "669": "audio/x-mod",
-  "726": "audio/32kadpcm",
-  "7z": "application/x-7z-compressed",
-  "a": "text/plain",
-  "a2l": "application/a2l",
-  "aa3": "audio/atrac3",
-  "aab": "application/x-authorware-bin",
-  "aac": "audio/x-aac",
-  "aal": "audio/atrac-advanced-lossless",
-  "aam": "application/x-authorware-map",
-  "aas": "application/x-authorware-seg",
-  "abc": "text/vnd.abc",
-  "abw": "application/x-abiword",
+  "ez": "application/andrew-inset",
+  "aw": "application/applixware",
+  "atom": "application/atom+xml",
+  "atomcat": "application/atomcat+xml",
+  "atomsvc": "application/atomsvc+xml",
+  "ccxml": "application/ccxml+xml",
+  "cdmia": "application/cdmi-capability",
+  "cdmic": "application/cdmi-container",
+  "cdmid": "application/cdmi-domain",
+  "cdmio": "application/cdmi-object",
+  "cdmiq": "application/cdmi-queue",
+  "cu": "application/cu-seeme",
+  "davmount": "application/davmount+xml",
+  "dbk": "application/docbook+xml",
+  "dssc": "application/dssc+der",
+  "xdssc": "application/dssc+xml",
+  "ecma": "application/ecmascript",
+  "emma": "application/emma+xml",
+  "epub": "application/epub+zip",
+  "exi": "application/exi",
+  "pfr": "application/font-tdpfr",
+  "gml": "application/gml+xml",
+  "gpx": "application/gpx+xml",
+  "gxf": "application/gxf",
+  "stk": "application/hyperstudio",
+  "ink": "application/inkml+xml",
+  "inkml": "application/inkml+xml",
+  "ipfix": "application/ipfix",
+  "jar": "application/java-archive",
+  "ser": "application/java-serialized-object",
+  "class": "application/java-vm",
+  "json": "application/json",
+  "jsonml": "application/jsonml+json",
+  "lostxml": "application/lost+xml",
+  "hqx": "application/mac-binhex40",
+  "cpt": "application/mac-compactpro",
+  "mads": "application/mads+xml",
+  "mrc": "application/marc",
+  "mrcx": "application/marcxml+xml",
+  "ma": "application/mathematica",
+  "nb": "application/mathematica",
+  "mb": "application/mathematica",
+  "mathml": "application/mathml+xml",
+  "mbox": "application/mbox",
+  "mscml": "application/mediaservercontrol+xml",
+  "metalink": "application/metalink+xml",
+  "meta4": "application/metalink4+xml",
+  "mets": "application/mets+xml",
+  "mods": "application/mods+xml",
+  "m21": "application/mp21",
+  "mp21": "application/mp21",
+  "mp4s": "application/mp4",
+  "doc": "application/msword",
+  "dot": "application/msword",
+  "mxf": "application/mxf",
+  "bin": "application/octet-stream",
+  "dms": "application/octet-stream",
+  "lrf": "application/octet-stream",
+  "mar": "application/octet-stream",
+  "so": "application/octet-stream",
+  "dist": "application/octet-stream",
+  "distz": "application/octet-stream",
+  "pkg": "application/octet-stream",
+  "bpk": "application/octet-stream",
+  "dump": "application/octet-stream",
+  "elc": "application/octet-stream",
+  "deploy": "application/octet-stream",
+  "oda": "application/oda",
+  "opf": "application/oebps-package+xml",
+  "ogx": "application/ogg",
+  "omdoc": "application/omdoc+xml",
+  "onetoc": "application/onenote",
+  "onetoc2": "application/onenote",
+  "onetmp": "application/onenote",
+  "onepkg": "application/onenote",
+  "oxps": "application/oxps",
+  "xer": "application/patch-ops-error+xml",
+  "pdf": "application/pdf",
+  "pgp": "application/pgp-encrypted",
+  "asc": "application/pgp-signature",
+  "sig": "application/pgp-signature",
+  "prf": "application/pics-rules",
+  "p10": "application/pkcs10",
+  "p7m": "application/pkcs7-mime",
+  "p7c": "application/pkcs7-mime",
+  "p7s": "application/pkcs7-signature",
+  "p8": "application/pkcs8",
   "ac": "application/pkix-attr-cert",
-  "ac3": "audio/ac3",
-  "acc": "application/vnd.americandynamics.acc",
-  "ace": "application/x-ace-compressed",
-  "acn": "audio/asc",
-  "acu": "application/vnd.acucobol",
-  "acutc": "application/vnd.acucorp",
-  "acx": "application/internet-property-stream",
-  "adp": "audio/adpcm",
-  "aep": "application/vnd.audiograph",
-  "afl": "video/animaflex",
-  "afm": "application/x-font-type1",
-  "afp": "application/vnd.ibm.modcap",
-  "ahead": "application/vnd.ahead.space",
+  "cer": "application/pkix-cert",
+  "crl": "application/pkix-crl",
+  "pkipath": "application/pkix-pkipath",
+  "pki": "application/pkixcmp",
+  "pls": "application/pls+xml",
   "ai": "application/postscript",
-  "aif": "audio/x-aiff",
-  "aifc": "audio/x-aiff",
-  "aiff": "audio/x-aiff",
-  "aim": "application/x-aim",
-  "aip": "text/x-audiosoft-intra",
-  "air": "application/vnd.adobe.air-application-installer-package+zip",
-  "ait": "application/vnd.dvb.ait",
-  "alc": "chemical/x-alchemy",
-  "ami": "application/vnd.amiga.ami",
-  "aml": "application/aml",
-  "amr": "audio/amr",
-  "ani": "application/x-navi-animation",
-  "anx": "application/x-annodex",
-  "aos": "application/x-nokia-9000-communicator-add-on-software",
-  "apinotes": "text/apinotes",
-  "apk": "application/vnd.android.package-archive",
-  "apkg": "application/vnd.anki",
-  "apng": "image/apng",
-  "appcache": "text/cache-manifest",
-  "appimage": "application/appimage",
-  "application": "application/x-ms-application",
-  "apr": "application/vnd.lotus-approach",
-  "aps": "application/mime",
-  "apxml": "application/auth-policy+xml",
-  "arc": "application/x-freearc",
-  "arj": "application/x-arj",
-  "art": "message/rfc822",
-  "asar": "binary/asar",
-  "asc": "text/plain",
-  "ascii": "text/vnd.ascii-art",
-  "asf": "application/vnd.ms-asf",
-  "asice": "application/vnd.etsi.asic-e+zip",
-  "asics": "application/vnd.etsi.asic-s+zip",
-  "asm": "text/x-asm",
-  "asn": "chemical/x-ncbi-asn1-spec",
+  "eps": "application/postscript",
+  "ps": "application/postscript",
+  "cww": "application/prs.cww",
+  "pskcxml": "application/pskc+xml",
+  "rdf": "application/rdf+xml",
+  "rif": "application/reginfo+xml",
+  "rnc": "application/relax-ng-compact-syntax",
+  "rl": "application/resource-lists+xml",
+  "rld": "application/resource-lists-diff+xml",
+  "rs": "application/rls-services+xml",
+  "gbr": "application/rpki-ghostbusters",
+  "mft": "application/rpki-manifest",
+  "roa": "application/rpki-roa",
+  "rsd": "application/rsd+xml",
+  "rss": "application/rss+xml",
+  "rtf": "application/rtf",
+  "sbml": "application/sbml+xml",
+  "scq": "application/scvp-cv-request",
+  "scs": "application/scvp-cv-response",
+  "spq": "application/scvp-vp-request",
+  "spp": "application/scvp-vp-response",
+  "sdp": "application/sdp",
+  "setpay": "application/set-payment-initiation",
+  "setreg": "application/set-registration-initiation",
+  "shf": "application/shf+xml",
+  "smi": "application/smil+xml",
+  "smil": "application/smil+xml",
+  "rq": "application/sparql-query",
+  "srx": "application/sparql-results+xml",
+  "gram": "application/srgs",
+  "grxml": "application/srgs+xml",
+  "sru": "application/sru+xml",
+  "ssdl": "application/ssdl+xml",
+  "ssml": "application/ssml+xml",
+  "tei": "application/tei+xml",
+  "teicorpus": "application/tei+xml",
+  "tfi": "application/thraud+xml",
+  "tsd": "application/timestamped-data",
+  "plb": "application/vnd.3gpp.pic-bw-large",
+  "psb": "application/vnd.3gpp.pic-bw-small",
+  "pvb": "application/vnd.3gpp.pic-bw-var",
+  "tcap": "application/vnd.3gpp2.tcap",
+  "pwn": "application/vnd.3m.post-it-notes",
   "aso": "application/vnd.accpac.simply.aso",
-  "asp": "text/asp",
-  "asr": "video/x-ms-asf",
-  "asx": "video/x-ms-asf",
-  "at3": "audio/atrac3",
+  "imp": "application/vnd.accpac.simply.imp",
+  "acu": "application/vnd.acucobol",
   "atc": "application/vnd.acucorp",
-  "atf": "application/atf",
-  "atfx": "application/atfx",
-  "atom": "application/atom+xml",
-  "atomcat": "application/atomcat+xml",
-  "atomdeleted": "application/atomdeleted+xml",
-  "atomsrv": "application/atomserv+xml",
-  "atomsvc": "application/atomsvc+xml",
-  "atx": "application/vnd.antix.game-component",
-  "atxml": "application/atxml",
-  "au": "audio/basic",
-  "auc": "application/tamp-apex-update-confirm",
-  "avi": "video/x-msvideo",
-  "avs": "video/avs-video",
-  "aw": "application/applixware",
-  "awb": "audio/amr-wb",
-  "axa": "audio/x-annodex",
-  "axs": "application/olescript",
-  "axv": "video/x-annodex",
+  "acutc": "application/vnd.acucorp",
+  "air": "application/vnd.adobe.air-application-installer-package+zip",
+  "fcdt": "application/vnd.adobe.formscentral.fcdt",
+  "fxp": "application/vnd.adobe.fxp",
+  "fxpl": "application/vnd.adobe.fxp",
+  "xdp": "application/vnd.adobe.xdp+xml",
+  "xfdf": "application/vnd.adobe.xfdf",
+  "ahead": "application/vnd.ahead.space",
   "azf": "application/vnd.airzip.filesecure.azf",
   "azs": "application/vnd.airzip.filesecure.azs",
-  "azv": "image/vnd.airzip.accelerator.azv",
   "azw": "application/vnd.amazon.ebook",
-  "azw3": "application/vnd.amazon.mobi8-ebook",
-  "b": "chemical/x-molconn-Z",
-  "bak": "application/x-trash",
-  "bar": "application/vnd.qualcomm.brew-app-res",
-  "bas": "text/plain",
-  "bash": "text/shell",
-  "bat": "application/x-msdos-program",
-  "bcpio": "application/x-bcpio",
-  "bdf": "application/x-font-bdf",
-  "bdm": "application/vnd.syncml.dm+wbxml",
-  "bdoc": "application/bdoc",
-  "bed": "application/vnd.realvnc.bed",
-  "bh2": "application/vnd.fujitsu.oasysprs",
-  "bib": "text/x-bibtex",
-  "bik": "video/vnd.radgamettools.bink",
-  "bin": "application/octet-stream",
-  "bk2": "video/vnd.radgamettools.bink",
-  "bkm": "application/vnd.nervana",
-  "blb": "application/x-blorb",
-  "blend": "binary/blender",
-  "blorb": "application/x-blorb",
-  "bm": "image/bmp",
-  "bmed": "multipart/vnd.bint.med-plus",
+  "acc": "application/vnd.americandynamics.acc",
+  "ami": "application/vnd.amiga.ami",
+  "apk": "application/vnd.android.package-archive",
+  "cii": "application/vnd.anser-web-certificate-issue-initiation",
+  "fti": "application/vnd.anser-web-funds-transfer-initiation",
+  "atx": "application/vnd.antix.game-component",
+  "mpkg": "application/vnd.apple.installer+xml",
+  "m3u8": "application/vnd.apple.mpegurl",
+  "swi": "application/vnd.aristanetworks.swi",
+  "iota": "application/vnd.astraea-software.iota",
+  "aep": "application/vnd.audiograph",
+  "mpm": "application/vnd.blueice.multipass",
   "bmi": "application/vnd.bmi",
-  "bmml": "application/vnd.balsamiq.bmml+xml",
-  "bmp": "image/bmp",
-  "bmpr": "application/vnd.balsamiq.bmpr",
-  "boo": "application/book",
-  "book": "application/book",
-  "box": "application/vnd.previewsystems.box",
-  "boz": "application/x-bzip2",
-  "bpd": "application/vnd.hbci",
-  "bpk": "application/octet-stream",
-  "brf": "text/plain",
-  "bsd": "chemical/x-crossfire",
-  "bsh": "application/x-bsh",
-  "bsp": "model/vnd.valve.source.compiled-map",
-  "btf": "image/prs.btif",
-  "btif": "image/prs.btif",
-  "bz": "application/x-bzip",
-  "bz2": "application/x-bzip2",
-  "c": "text/x-csrc",
-  "c++": "text/x-c++src",
-  "c11amc": "application/vnd.cluetrust.cartomobile-config",
-  "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
-  "c3d": "chemical/x-chem3d",
-  "c3ex": "application/cccex",
+  "rep": "application/vnd.businessobjects",
+  "cdxml": "application/vnd.chemdraw+xml",
+  "mmd": "application/vnd.chipnuts.karaoke-mmd",
+  "cdy": "application/vnd.cinderella",
+  "cla": "application/vnd.claymore",
+  "rp9": "application/vnd.cloanto.rp9",
+  "c4g": "application/vnd.clonk.c4group",
   "c4d": "application/vnd.clonk.c4group",
   "c4f": "application/vnd.clonk.c4group",
-  "c4g": "application/vnd.clonk.c4group",
   "c4p": "application/vnd.clonk.c4group",
   "c4u": "application/vnd.clonk.c4group",
-  "cab": "application/vnd.ms-cab-compressed",
-  "cac": "chemical/x-cache",
-  "cache": "application/x-cache",
-  "caf": "audio/x-caf",
-  "cap": "application/vnd.tcpdump.pcap",
-  "car": "application/vnd.curl.car",
-  "cascii": "chemical/x-cactvs-binary",
-  "cat": "application/vnd.ms-pki.seccat",
-  "cb7": "application/x-cbr",
-  "cba": "application/x-cbr",
-  "cbin": "chemical/x-cactvs-binary",
-  "cbor": "application/cbor",
-  "cbr": "application/x-cbr",
-  "cbt": "application/x-cbr",
-  "cbz": "application/vnd.comicbook+zip",
-  "cc": "text/plain",
-  "ccad": "application/clariscad",
-  "ccc": "text/vnd.net2phone.commcenter.command",
-  "ccmp": "application/ccmp+xml",
-  "cco": "application/x-cocoa",
-  "cct": "application/x-director",
-  "ccxml": "application/ccxml+xml",
-  "cda": "application/x-cdf",
+  "c11amc": "application/vnd.cluetrust.cartomobile-config",
+  "c11amz": "application/vnd.cluetrust.cartomobile-config-pkg",
+  "csp": "application/vnd.commonspace",
   "cdbcmsg": "application/vnd.contact.cmsg",
-  "cdf": "application/x-netcdf",
-  "cdfx": "application/cdfx+xml",
-  "cdkey": "application/vnd.mediastation.cdkey",
-  "cdmia": "application/cdmi-capability",
-  "cdmic": "application/cdmi-container",
-  "cdmid": "application/cdmi-domain",
-  "cdmio": "application/cdmi-object",
-  "cdmiq": "application/cdmi-queue",
-  "cdr": "image/x-coreldraw",
-  "cdt": "image/x-coreldrawtemplate",
-  "cdx": "chemical/x-cdx",
-  "cdxml": "application/vnd.chemdraw+xml",
-  "cdy": "application/vnd.cinderella",
-  "cea": "application/cea",
-  "cef": "chemical/x-cxf",
-  "cellml": "application/cellml+xml",
-  "cer": "application/pkix-cert",
-  "cfg": "text/cfg",
-  "cfs": "application/x-cfs-compressed",
-  "cgm": "image/cgm",
-  "cha": "application/x-chat",
-  "chat": "application/x-chat",
-  "chm": "application/vnd.ms-htmlhelp",
-  "chrt": "application/vnd.kde.kchart",
-  "cif": "chemical/x-cif",
-  "cii": "application/vnd.anser-web-certificate-issue-initiation",
-  "cil": "application/vnd.ms-artgalry",
-  "cl": "application/simple-filter+xml",
-  "cla": "application/vnd.claymore",
-  "class": "application/java-vm",
+  "cmc": "application/vnd.cosmocaller",
+  "clkx": "application/vnd.crick.clicker",
   "clkk": "application/vnd.crick.clicker.keyboard",
   "clkp": "application/vnd.crick.clicker.palette",
   "clkt": "application/vnd.crick.clicker.template",
   "clkw": "application/vnd.crick.clicker.wordbank",
-  "clkx": "application/vnd.crick.clicker",
-  "clp": "application/x-msclip",
-  "cls": "text/x-tex",
-  "clue": "application/clue_info+xml",
-  "cmake": "text/cmake",
-  "cmc": "application/vnd.cosmocaller",
-  "cmdf": "chemical/x-cmdf",
-  "cml": "chemical/x-cml",
-  "cmp": "application/vnd.yellowriver-custom-menu",
-  "cmsc": "application/cms",
-  "cmx": "image/x-cmx",
-  "cnd": "text/jcr-cnd",
-  "cnf": "text/cnf",
-  "cod": "application/vnd.rim.cod",
-  "coffee": "application/vnd.coffeescript",
-  "com": "application/x-msdos-program",
-  "conf": "text/plain",
-  "copyright": "text/vnd.debian.copyright",
-  "cpa": "chemical/x-compass",
-  "cpio": "application/x-cpio",
-  "cpkg": "application/vnd.xmpie.cpkg",
-  "cpl": "application/cpl+xml",
-  "cpp": "text/x-c++src",
-  "cpt": "application/mac-compactpro",
-  "cr2": "image/x-canon-cr2",
-  "crd": "application/x-mscardfile",
-  "crl": "application/pkix-crl",
-  "crt": "application/x-x509-ca-cert",
-  "crtr": "application/vnd.multiad.creator",
-  "crw": "image/x-canon-crw",
-  "crx": "application/x-chrome-extension",
-  "cryptonote": "application/vnd.rig.cryptonote",
-  "cs": "text/c#",
-  "csf": "chemical/x-cache-csf",
-  "csh": "application/x-csh",
-  "csl": "application/vnd.citationstyles.style+xml",
-  "csm": "chemical/x-csml",
-  "csml": "chemical/x-csml",
-  "cson": "text/cson",
-  "csp": "application/vnd.commonspace",
-  "csrattrs": "application/csrattrs",
-  "css": "text/css",
-  "cst": "application/vnd.commonspace",
-  "csv": "text/csv",
-  "csvs": "text/csv-schema",
-  "ctab": "chemical/x-cactvs-binary",
-  "ctx": "chemical/x-ctx",
-  "cu": "application/cu-seeme",
-  "cub": "chemical/x-gaussian-cube",
-  "cuc": "application/tamp-community-update-confirm",
-  "curl": "text/vnd.curl",
-  "cw": "application/prs.cww",
-  "cww": "application/prs.cww",
-  "cxf": "chemical/x-cxf",
-  "cxt": "application/x-director",
-  "cxx": "text/plain",
-  "d": "text/x-dsrc",
-  "dae": "model/vnd.collada+xml",
-  "daf": "application/vnd.mobius.daf",
+  "wbs": "application/vnd.criticaltools.wbs+xml",
+  "pml": "application/vnd.ctc-posml",
+  "ppd": "application/vnd.cups-ppd",
+  "car": "application/vnd.curl.car",
+  "pcurl": "application/vnd.curl.pcurl",
   "dart": "application/vnd.dart",
-  "dat": "application/x-ns-proxy-autoconfig",
-  "dataless": "application/vnd.fdsn.seed",
-  "davmount": "application/davmount+xml",
-  "dbk": "application/docbook+xml",
-  "dcd": "application/dcd",
-  "dcf": "application/vnd.oma.drm.content",
-  "dcm": "application/dicom",
-  "dcr": "application/x-director",
-  "dcurl": "text/vnd.curl.dcurl",
-  "dd": "application/vnd.oma.dd+xml",
-  "dd2": "application/vnd.oma.dd2+xml",
-  "ddd": "application/vnd.fujixerox.ddd",
-  "ddf": "application/vnd.syncml.dmddf+xml",
-  "deb": "application/vnd.debian.binary-package",
-  "deepv": "application/x-deepv",
-  "def": "text/plain",
-  "deploy": "application/octet-stream",
-  "der": "application/x-x509-ca-cert",
-  "dfac": "application/vnd.dreamfactory",
-  "dgc": "application/x-dgc-compressed",
-  "dib": "image/bmp",
-  "dic": "text/x-c",
-  "dif": "video/x-dv",
-  "diff": "text/x-diff",
-  "dii": "application/dii",
-  "dim": "application/vnd.fastcopy-disk-image",
-  "dir": "application/x-director",
-  "dis": "application/vnd.mobius.dis",
-  "disposition-notification": "message/disposition-notification",
-  "dist": "application/vnd.apple.installer+xml",
-  "distz": "application/vnd.apple.installer+xml",
-  "dit": "application/dit",
-  "djv": "image/vnd.djvu",
-  "djvu": "image/vnd.djvu",
-  "dl": "video/dl",
-  "dll": "application/x-msdos-program",
-  "dls": "audio/dls",
-  "dm": "application/vnd.oma.drm.message",
-  "dmg": "application/x-apple-diskimage",
-  "dmp": "application/vnd.tcpdump.pcap",
-  "dms": "text/vnd.dmclientscript",
+  "rdz": "application/vnd.data-vision.rdz",
+  "uvf": "application/vnd.dece.data",
+  "uvvf": "application/vnd.dece.data",
+  "uvd": "application/vnd.dece.data",
+  "uvvd": "application/vnd.dece.data",
+  "uvt": "application/vnd.dece.ttml+xml",
+  "uvvt": "application/vnd.dece.ttml+xml",
+  "uvx": "application/vnd.dece.unspecified",
+  "uvvx": "application/vnd.dece.unspecified",
+  "uvz": "application/vnd.dece.zip",
+  "uvvz": "application/vnd.dece.zip",
+  "fe_launch": "application/vnd.denovo.fcselayout-link",
   "dna": "application/vnd.dna",
-  "doc": "application/msword",
-  "docjson": "application/vnd.document+json",
-  "docm": "application/vnd.ms-word.document.macroenabled.12",
-  "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-  "dor": "model/vnd.gdl",
-  "dot": "text/vnd.graphviz",
-  "dotm": "application/vnd.ms-word.template.macroenabled.12",
-  "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
-  "dp": "application/vnd.osgi.dp",
+  "mlp": "application/vnd.dolby.mlp",
   "dpg": "application/vnd.dpgraph",
-  "dpgraph": "application/vnd.dpgraph",
-  "dpkg": "application/vnd.xmpie.dpkg",
-  "dr": "application/vnd.oma.drm.rights+xml",
-  "dra": "audio/vnd.dra",
-  "drc": "application/vnd.oma.drm.rights+wbxml",
-  "drle": "image/dicom-rle",
-  "drw": "application/drafting",
-  "dsc": "text/prs.lines.tag",
-  "dsm": "application/vnd.desmume.movie",
-  "dssc": "application/dssc+der",
-  "dtb": "application/x-dtbook+xml",
-  "dtd": "application/xml-dtd",
-  "dts": "audio/vnd.dts",
-  "dtshd": "audio/vnd.dts.hd",
-  "dump": "application/octet-stream",
-  "dv": "video/x-dv",
-  "dvb": "video/vnd.dvb.file",
-  "dvc": "application/dvcs",
-  "dvi": "application/x-dvi",
-  "dwf": "model/vnd.dwf",
-  "dwg": "image/vnd.dwg",
-  "dx": "chemical/x-jcamp-dx",
-  "dxf": "image/vnd.dxf",
-  "dxp": "application/vnd.spotfire.dxp",
-  "dxr": "application/x-director",
-  "dzr": "application/vnd.dzr",
-  "ear": "binary/zip",
-  "ecelp4800": "audio/vnd.nuera.ecelp4800",
-  "ecelp7470": "audio/vnd.nuera.ecelp7470",
-  "ecelp9600": "audio/vnd.nuera.ecelp9600",
-  "ecig": "application/vnd.evolv.ecig.settings",
-  "ecigprofile": "application/vnd.evolv.ecig.profile",
-  "ecigtheme": "application/vnd.evolv.ecig.theme",
-  "ecma": "application/ecmascript",
-  "edm": "application/vnd.novadigm.edm",
-  "edx": "application/vnd.novadigm.edx",
-  "efi": "application/efi",
-  "efif": "application/vnd.picsel",
-  "ei6": "application/vnd.pg.osasli",
-  "ejs": "text/ejs",
-  "el": "text/plain",
-  "elc": "application/x-bytecode.elisp",
-  "emb": "chemical/x-embl-dl-nucleotide",
-  "embl": "chemical/x-embl-dl-nucleotide",
-  "emf": "image/emf",
-  "eml": "message/rfc822",
-  "emm": "application/vnd.ibm.electronic-media",
-  "emma": "application/emma+xml",
-  "emotionml": "application/emotionml+xml",
-  "emz": "application/x-msmetafile",
-  "ent": "text/xml-external-parsed-entity",
-  "entity": "application/vnd.nervana",
-  "env": "application/x-envoy",
-  "enw": "audio/evrcnw",
-  "eol": "audio/vnd.digital-winds",
-  "eot": "application/vnd.ms-fontobject",
-  "ep": "application/vnd.bluetooth.ep.oob",
-  "eps": "application/postscript",
-  "eps2": "application/postscript",
-  "eps3": "application/postscript",
-  "epsf": "application/postscript",
-  "epsi": "application/postscript",
-  "epub": "application/epub+zip",
-  "erb": "text/erb",
-  "erf": "image/x-epson-erf",
-  "es": "application/ecmascript",
-  "es3": "application/vnd.eszigno3+xml",
-  "esa": "application/vnd.osgi.subsystem",
-  "escn": "text/godot",
+  "dfac": "application/vnd.dreamfactory",
+  "kpxx": "application/vnd.ds-keypoint",
+  "ait": "application/vnd.dvb.ait",
+  "svc": "application/vnd.dvb.service",
+  "geo": "application/vnd.dynageo",
+  "mag": "application/vnd.ecowin.chart",
+  "nml": "application/vnd.enliven",
   "esf": "application/vnd.epson.esf",
-  "espass": "application/vnd.espass-espass+zip",
+  "msf": "application/vnd.epson.msf",
+  "qam": "application/vnd.epson.quickanime",
+  "slt": "application/vnd.epson.salt",
+  "ssf": "application/vnd.epson.ssf",
+  "es3": "application/vnd.eszigno3+xml",
   "et3": "application/vnd.eszigno3+xml",
-  "etx": "text/x-setext",
-  "eva": "application/x-eva",
-  "evb": "audio/evrcb",
-  "evc": "audio/evrc",
-  "evw": "audio/evrcwb",
-  "evy": "application/x-envoy",
-  "exe": "application/x-msdos-program",
-  "exi": "application/exi",
-  "exr": "image/aces",
-  "ext": "application/vnd.novadigm.ext",
-  "eyaml": "text/yaml",
-  "ez": "application/andrew-inset",
   "ez2": "application/vnd.ezpix-album",
   "ez3": "application/vnd.ezpix-package",
-  "f": "text/x-fortran",
-  "f4v": "video/x-f4v",
-  "f77": "text/x-fortran",
-  "f90": "text/plain",
-  "fb": "application/x-maker",
-  "fbdoc": "application/x-maker",
-  "fbs": "image/vnd.fastbidsheet",
-  "fbx": "model/filmbox",
-  "fcdt": "application/vnd.adobe.formscentral.fcdt",
-  "fch": "chemical/x-gaussian-checkpoint",
-  "fchk": "chemical/x-gaussian-checkpoint",
-  "fcs": "application/vnd.isac.fcs",
   "fdf": "application/vnd.fdf",
-  "fdt": "application/fdt+xml",
-  "fe_launch": "application/vnd.denovo.fcselayout-link",
-  "feature": "text/gherkin",
-  "fg5": "application/vnd.fujitsu.oasysgp",
-  "fgd": "application/x-director",
-  "fh": "image/x-freehand",
-  "fh4": "image/x-freehand",
-  "fh5": "image/x-freehand",
-  "fh7": "image/x-freehand",
-  "fhc": "image/x-freehand",
-  "fif": "image/fif",
-  "fig": "application/x-xfig",
-  "finf": "application/fastinfoset",
-  "fish": "text/fish",
-  "fit": "image/fits",
-  "fits": "image/fits",
-  "fla": "application/vnd.dtg.local.flash",
-  "flac": "audio/x-flac",
-  "fli": "video/x-fli",
-  "flo": "application/vnd.micrografx.flo",
-  "flr": "x-world/x-vrml",
-  "flv": "video/x-flv",
-  "flw": "application/vnd.kde.kivio",
-  "flx": "text/vnd.fmi.flexstor",
-  "fly": "text/vnd.fly",
+  "mseed": "application/vnd.fdsn.mseed",
+  "seed": "application/vnd.fdsn.seed",
+  "dataless": "application/vnd.fdsn.seed",
+  "gph": "application/vnd.flographit",
+  "ftc": "application/vnd.fluxtime.clip",
   "fm": "application/vnd.framemaker",
-  "fmf": "video/x-atomic3d-feature",
-  "fnc": "application/vnd.frogans.fnc",
-  "fo": "application/vnd.software602.filler.form+xml",
-  "for": "text/x-fortran",
-  "fpx": "image/vnd.fpx",
   "frame": "application/vnd.framemaker",
-  "frl": "application/freeloader",
-  "frm": "application/vnd.ufdl",
+  "maker": "application/vnd.framemaker",
+  "book": "application/vnd.framemaker",
+  "fnc": "application/vnd.frogans.fnc",
+  "ltf": "application/vnd.frogans.ltf",
   "fsc": "application/vnd.fsc.weblaunch",
-  "fst": "image/vnd.fst",
-  "ftc": "application/vnd.fluxtime.clip",
-  "fti": "application/vnd.anser-web-funds-transfer-initiation",
-  "fts": "image/fits",
-  "funk": "audio/make",
-  "fvt": "video/vnd.fvt",
-  "fxm": "video/x-javafx",
-  "fxp": "application/vnd.adobe.fxp",
-  "fxpl": "application/vnd.adobe.fxp",
+  "oas": "application/vnd.fujitsu.oasys",
+  "oa2": "application/vnd.fujitsu.oasys2",
+  "oa3": "application/vnd.fujitsu.oasys3",
+  "fg5": "application/vnd.fujitsu.oasysgp",
+  "bh2": "application/vnd.fujitsu.oasysprs",
+  "ddd": "application/vnd.fujixerox.ddd",
+  "xdw": "application/vnd.fujixerox.docuworks",
+  "xbd": "application/vnd.fujixerox.docuworks.binder",
   "fzs": "application/vnd.fuzzysheet",
-  "g": "text/plain",
-  "g2w": "application/vnd.geoplan",
-  "g3": "image/g3fax",
-  "g3w": "application/vnd.geospace",
-  "gac": "application/vnd.groove-account",
-  "gal": "chemical/x-gaussian-log",
-  "gam": "application/x-tads",
-  "gamin": "chemical/x-gamess-input",
-  "gau": "chemical/x-gaussian-input",
-  "gbr": "application/rpki-ghostbusters",
-  "gca": "application/x-gca-compressed",
-  "gcd": "text/x-pcs-gcd",
-  "gcf": "application/x-graphing-calculator",
-  "gcg": "chemical/x-gcg8-sequence",
-  "gdl": "model/vnd.gdl",
-  "gdoc": "application/vnd.google-apps.document",
-  "gemspec": "text/ruby",
-  "gen": "chemical/x-genbank",
-  "geo": "application/vnd.dynageo",
-  "geojson": "application/geo+json",
-  "gex": "application/vnd.geometry-explorer",
-  "gf": "application/x-tex-gf",
+  "txd": "application/vnd.genomatix.tuxedo",
   "ggb": "application/vnd.geogebra.file",
+  "ggs": "application/vnd.geogebra.slides",
   "ggt": "application/vnd.geogebra.tool",
-  "ghf": "application/vnd.groove-help",
-  "gif": "image/gif",
-  "gim": "application/vnd.groove-identity-message",
-  "gjc": "chemical/x-gaussian-input",
-  "gjf": "chemical/x-gaussian-input",
-  "gl": "video/gl",
-  "glb": "model/gltf-binary",
-  "gltf": "model/gltf+json",
-  "gml": "application/gml+xml",
+  "gex": "application/vnd.geometry-explorer",
+  "gre": "application/vnd.geometry-explorer",
+  "gxt": "application/vnd.geonext",
+  "g2w": "application/vnd.geoplan",
+  "g3w": "application/vnd.geospace",
   "gmx": "application/vnd.gmx",
-  "gnumeric": "application/x-gnumeric",
-  "go": "text/go",
-  "gotmpl": "text/gotmpl",
-  "gph": "application/vnd.flographit",
-  "gpt": "chemical/x-mopac-graph",
-  "gpx": "application/gpx+xml",
+  "kml": "application/vnd.google-earth.kml+xml",
+  "kmz": "application/vnd.google-earth.kmz",
   "gqf": "application/vnd.grafeq",
   "gqs": "application/vnd.grafeq",
-  "gradle": "text/groovy",
-  "gram": "application/srgs",
-  "gramps": "application/x-gramps-xml",
-  "gre": "application/vnd.geometry-explorer",
-  "groovy": "text/groovy",
+  "gac": "application/vnd.groove-account",
+  "ghf": "application/vnd.groove-help",
+  "gim": "application/vnd.groove-identity-message",
   "grv": "application/vnd.groove-injector",
-  "grxml": "application/srgs+xml",
-  "gsd": "audio/x-gsm",
-  "gsf": "application/x-font-ghostscript",
-  "gsheet": "application/vnd.google-apps.spreadsheet",
-  "gslides": "application/vnd.google-apps.presentation",
-  "gsm": "model/vnd.gdl",
-  "gsp": "application/x-gsp",
-  "gss": "application/x-gss",
-  "gtar": "application/x-gtar",
   "gtm": "application/vnd.groove-tool-message",
-  "gtw": "model/vnd.gtw",
-  "gv": "text/vnd.graphviz",
-  "gxf": "application/gxf",
-  "gxt": "application/vnd.geonext",
-  "gyb": "text/gyb",
-  "gyp": "text/gyp",
-  "gypi": "text/gyp",
-  "gz": "application/gzip",
-  "h": "text/x-chdr",
-  "h++": "text/x-c++hdr",
-  "h261": "video/h261",
-  "h263": "video/h263",
-  "h264": "video/h264",
+  "tpl": "application/vnd.groove-tool-template",
+  "vcg": "application/vnd.groove-vcard",
   "hal": "application/vnd.hal+xml",
-  "hbc": "application/vnd.hbci",
+  "zmm": "application/vnd.handheld-entertainment+xml",
   "hbci": "application/vnd.hbci",
-  "hbs": "text/x-handlebars-template",
-  "hdd": "application/x-virtualbox-hdd",
-  "hdf": "application/x-hdf",
-  "hdr": "image/vnd.radiance",
-  "hdt": "application/vnd.hdt",
-  "heic": "image/heic",
-  "heics": "image/heic-sequence",
-  "heif": "image/heif",
-  "heifs": "image/heif-sequence",
-  "help": "application/x-helpfile",
-  "hgl": "application/vnd.hp-hpgl",
-  "hh": "text/plain",
-  "hin": "chemical/x-hin",
-  "hjson": "application/hjson",
-  "hlb": "text/x-script",
-  "hlp": "application/winhlp",
-  "hpg": "application/vnd.hp-hpgl",
+  "les": "application/vnd.hhe.lesson-player",
   "hpgl": "application/vnd.hp-hpgl",
-  "hpi": "application/vnd.hp-hpid",
   "hpid": "application/vnd.hp-hpid",
-  "hpp": "text/x-c++hdr",
   "hps": "application/vnd.hp-hps",
-  "hpub": "application/prs.hpub+zip",
-  "hqx": "application/mac-binhex40",
-  "hs": "text/x-haskell",
-  "hta": "application/hta",
-  "htc": "text/x-component",
-  "htke": "application/vnd.kenameaapp",
-  "html": "text/html",
-  "htt": "text/webviewhtml",
-  "hvd": "application/vnd.yamaha.hv-dic",
-  "hvp": "application/vnd.yamaha.hv-voice",
-  "hvs": "application/vnd.yamaha.hv-script",
-  "hx": "text/haxe",
-  "hxml": "text/haxe",
-  "hxx": "text/plain",
-  "i2g": "application/vnd.intergeo",
-  "ic0": "application/vnd.commerce-battelle",
-  "ic1": "application/vnd.commerce-battelle",
-  "ic2": "application/vnd.commerce-battelle",
-  "ic3": "application/vnd.commerce-battelle",
-  "ic4": "application/vnd.commerce-battelle",
-  "ic5": "application/vnd.commerce-battelle",
-  "ic6": "application/vnd.commerce-battelle",
-  "ic7": "application/vnd.commerce-battelle",
-  "ic8": "application/vnd.commerce-battelle",
-  "ica": "application/vnd.commerce-battelle",
+  "jlt": "application/vnd.hp-jlyt",
+  "pcl": "application/vnd.hp-pcl",
+  "pclxl": "application/vnd.hp-pclxl",
+  "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
+  "mpy": "application/vnd.ibm.minipay",
+  "afp": "application/vnd.ibm.modcap",
+  "listafp": "application/vnd.ibm.modcap",
+  "list3820": "application/vnd.ibm.modcap",
+  "irm": "application/vnd.ibm.rights-management",
+  "sc": "application/vnd.ibm.secure-container",
   "icc": "application/vnd.iccprofile",
-  "icd": "application/vnd.commerce-battelle",
-  "ice": "x-conference/x-cooltalk",
-  "icf": "application/vnd.commerce-battelle",
   "icm": "application/vnd.iccprofile",
-  "icns": "binary/icns",
-  "ico": "image/x-icon",
-  "ics": "text/calendar",
-  "icz": "text/calendar",
-  "idc": "text/plain",
-  "idl": "text/idl",
-  "ief": "image/ief",
-  "iefs": "image/ief",
-  "ifb": "text/calendar",
-  "ifm": "application/vnd.shana.informed.formdata",
-  "iges": "model/iges",
   "igl": "application/vnd.igloader",
-  "igm": "application/vnd.insors.igm",
-  "ign": "application/vnd.coreos.ignition+json",
-  "ignition": "application/vnd.coreos.ignition+json",
-  "igs": "model/iges",
-  "igx": "application/vnd.micrografx.igx",
-  "iif": "application/vnd.shana.informed.interchange",
-  "iii": "application/x-iphone",
-  "ima": "application/x-ima",
-  "imap": "application/x-httpd-imap",
-  "imf": "application/vnd.imagemeter.folder+zip",
-  "img": "application/octet-stream",
-  "imgcal": "application/vnd.3lightssoftware.imagescal",
-  "imi": "application/vnd.imagemeter.image+zip",
-  "imp": "application/vnd.accpac.simply.imp",
-  "ims": "application/vnd.ms-ims",
-  "imscc": "application/vnd.ims.imsccv1p1",
-  "in": "text/plain",
-  "inc": "text/inc",
-  "inf": "application/inf",
-  "info": "application/x-info",
-  "ini": "text/ini",
-  "ink": "application/inkml+xml",
-  "inkml": "application/inkml+xml",
-  "inp": "chemical/x-gamess-input",
-  "ins": "application/x-internet-signup",
-  "install": "application/x-install-instructions",
-  "iota": "application/vnd.astraea-software.iota",
-  "ip": "application/x-ip2",
-  "ipfix": "application/ipfix",
-  "ipk": "application/vnd.shana.informed.package",
-  "irm": "application/vnd.ibm.rights-management",
-  "irp": "application/vnd.irepository.package+xml",
-  "ism": "model/vnd.gdl",
-  "iso": "application/x-iso9660-image",
-  "isp": "application/x-internet-signup",
-  "ist": "chemical/x-isostar",
-  "istr": "chemical/x-isostar",
-  "isu": "video/x-isvideo",
-  "it": "audio/it",
-  "itp": "application/vnd.shana.informed.formtemplate",
-  "its": "application/its+xml",
-  "iv": "application/x-inventor",
   "ivp": "application/vnd.immervision-ivp",
-  "ivr": "i-world/i-vrml",
   "ivu": "application/vnd.immervision-ivu",
-  "ivy": "application/x-livescreen",
-  "j2": "text/jinja",
-  "jad": "text/vnd.sun.j2me.app-descriptor",
-  "jade": "text/jade",
+  "igm": "application/vnd.insors.igm",
+  "xpw": "application/vnd.intercon.formnet",
+  "xpx": "application/vnd.intercon.formnet",
+  "i2g": "application/vnd.intergeo",
+  "qbo": "application/vnd.intu.qbo",
+  "qfx": "application/vnd.intu.qfx",
+  "rcprofile": "application/vnd.ipunplugged.rcprofile",
+  "irp": "application/vnd.irepository.package+xml",
+  "xpr": "application/vnd.is-xpr",
+  "fcs": "application/vnd.isac.fcs",
   "jam": "application/vnd.jam",
-  "jar": "application/x-java-archive",
-  "jardiff": "application/x-java-archive-diff",
-  "java": "text/x-java-source",
-  "jcm": "application/x-java-commerce",
-  "jdx": "chemical/x-jcamp-dx",
-  "jenkinsfile": "text/groovy",
-  "jfif": "image/jpeg",
-  "jinja": "text/jinja",
-  "jinja2": "text/jinja",
+  "rms": "application/vnd.jcp.javame.midlet-rms",
   "jisp": "application/vnd.jisp",
-  "jls": "image/jls",
-  "jlt": "application/vnd.hp-jlyt",
-  "jl": "text/julia",
-  "jmz": "application/x-jmol",
-  "jng": "image/x-jng",
-  "jnlp": "application/x-java-jnlp-file",
   "joda": "application/vnd.joost.joda-archive",
-  "jp2": "image/jp2",
-  "jpe": "image/jpeg",
-  "jpeg": "image/jpeg",
-  "jpf": "image/jpx",
-  "jpg": "image/jpeg",
-  "jpg2": "image/jp2",
-  "jpgm": "image/jpm",
-  "jpgv": "video/jpeg",
-  "jpm": "image/jpm",
-  "jps": "image/x-jps",
-  "jpx": "image/jpx",
-  "jrd": "application/jrd+json",
-  "js": "application/javascript",
-  "json": "application/json",
-  "json-patch": "application/json-patch+json",
-  "json5": "application/json5",
-  "jsonld": "application/ld+json",
-  "jsonml": "application/jsonml+json",
-  "jsx": "text/jsx",
-  "jtd": "text/vnd.esmertec.theme-descriptor",
-  "jut": "image/jutvision",
-  "kar": "audio/midi",
+  "ktz": "application/vnd.kahootz",
+  "ktr": "application/vnd.kahootz",
   "karbon": "application/vnd.kde.karbon",
-  "kcm": "application/vnd.nervana",
-  "key": "application/pgp-keys",
-  "keynote": "application/vnd.apple.keynote",
+  "chrt": "application/vnd.kde.kchart",
   "kfo": "application/vnd.kde.kformula",
-  "kia": "application/vnd.kidspiration",
-  "kil": "application/x-killustrator",
-  "kin": "chemical/x-kinemage",
-  "kml": "application/vnd.google-earth.kml+xml",
-  "kmz": "application/vnd.google-earth.kmz",
-  "kne": "application/vnd.kinar",
-  "knp": "application/vnd.kinar",
-  "kom": "application/vnd.hbci",
+  "flw": "application/vnd.kde.kivio",
   "kon": "application/vnd.kde.kontour",
-  "koz": "audio/vnd.audikoz",
   "kpr": "application/vnd.kde.kpresenter",
   "kpt": "application/vnd.kde.kpresenter",
-  "kpxx": "application/vnd.ds-keypoint",
-  "ksh": "application/x-ksh",
   "ksp": "application/vnd.kde.kspread",
-  "kt": "text/kotlin",
-  "ktr": "application/vnd.kahootz",
-  "ktx": "image/ktx",
-  "ktz": "application/vnd.kahootz",
   "kwd": "application/vnd.kde.kword",
   "kwt": "application/vnd.kde.kword",
-  "l16": "audio/l16",
-  "la": "audio/nspaudio",
-  "lam": "audio/x-liveaudio",
-  "lasjson": "application/vnd.las.las+json",
+  "htke": "application/vnd.kenameaapp",
+  "kia": "application/vnd.kidspiration",
+  "kne": "application/vnd.kinar",
+  "knp": "application/vnd.kinar",
+  "skp": "application/vnd.koan",
+  "skd": "application/vnd.koan",
+  "skt": "application/vnd.koan",
+  "skm": "application/vnd.koan",
+  "sse": "application/vnd.kodak-descriptor",
   "lasxml": "application/vnd.las.las+xml",
-  "latex": "application/x-latex",
-  "lbc": "audio/ilbc",
   "lbd": "application/vnd.llamagraphics.life-balance.desktop",
   "lbe": "application/vnd.llamagraphics.life-balance.exchange+xml",
-  "le": "application/vnd.bluetooth.le.oob",
-  "les": "application/vnd.hhe.lesson-player",
-  "less": "text/less",
-  "lgr": "application/lgr+xml",
-  "lha": "application/octet-stream",
-  "lhs": "text/x-literate-haskell",
-  "lhx": "application/octet-stream",
-  "lin": "application/bbolin",
-  "link66": "application/vnd.route66.link66+xml",
-  "list": "text/plain",
-  "list3820": "application/vnd.ibm.modcap",
-  "listafp": "application/vnd.ibm.modcap",
-  "lmp": "model/vnd.gdl",
-  "lnk": "application/x-ms-shortcut",
-  "log": "text/plain",
-  "lostsyncxml": "application/lostsync+xml",
-  "lostxml": "application/lost+xml",
-  "lrf": "application/octet-stream",
-  "lrm": "application/vnd.ms-lrm",
-  "lsf": "video/x-la-asf",
-  "lsp": "text/x-script.lisp",
-  "lst": "text/plain",
-  "lsx": "video/x-la-asf",
-  "ltf": "application/vnd.frogans.ltf",
-  "ltx": "application/x-latex",
-  "lua": "text/x-lua",
-  "luac": "application/x-lua-bytecode",
-  "lvp": "audio/vnd.lucent.voice",
+  "123": "application/vnd.lotus-1-2-3",
+  "apr": "application/vnd.lotus-approach",
+  "pre": "application/vnd.lotus-freelance",
+  "nsf": "application/vnd.lotus-notes",
+  "org": "application/vnd.lotus-organizer",
+  "scm": "application/vnd.lotus-screencam",
   "lwp": "application/vnd.lotus-wordpro",
-  "lxf": "application/lxf",
-  "lyx": "application/x-lyx",
-  "lzh": "application/octet-stream",
-  "lzx": "application/x-lzx",
-  "m": "application/vnd.wolfram.mathematica.package",
-  "m13": "application/x-msmediaview",
-  "m14": "application/x-msmediaview",
-  "m15": "audio/x-mod",
-  "m1v": "video/mpeg",
-  "m21": "application/mp21",
-  "m2a": "audio/mpeg",
-  "m2v": "video/mpeg",
-  "m3a": "audio/mpeg",
-  "m3g": "application/m3g",
-  "m3u": "audio/x-mpegurl",
-  "m3u8": "application/vnd.apple.mpegurl",
-  "m4a": "audio/x-m4a",
-  "m4s": "video/iso.segment",
-  "m4u": "video/vnd.mpegurl",
-  "m4v": "video/x-m4v",
-  "ma": "application/mathematica",
-  "mads": "application/mads+xml",
-  "mag": "application/vnd.ecowin.chart",
-  "mail": "message/rfc822",
-  "maker": "application/vnd.framemaker",
-  "man": "application/x-troff-man",
-  "manifest": "text/cache-manifest",
-  "map": "application/x-navimap",
-  "mar": "text/plain",
-  "markdown": "text/markdown",
-  "mathml": "application/mathml+xml",
-  "mb": "application/mathematica",
-  "mbd": "application/mbedlet",
-  "mbk": "application/vnd.mobius.mbk",
-  "mbox": "application/mbox",
-  "mc$": "application/x-magic-cap-package-1.0",
-  "mc1": "application/vnd.medcalcdata",
+  "portpkg": "application/vnd.macports.portpkg",
   "mcd": "application/vnd.mcd",
-  "mcf": "image/vasa",
-  "mcif": "chemical/x-mmcif",
-  "mcm": "chemical/x-macmolecule",
-  "mcp": "application/netmc",
-  "mcurl": "text/vnd.curl.mcurl",
-  "md": "text/markdown",
-  "mdb": "application/x-msaccess",
-  "mdc": "application/vnd.marlin.drm.mdcf",
-  "mdi": "image/vnd.ms-modi",
-  "me": "application/x-troff-me",
-  "med": "audio/x-mod",
-  "mesh": "model/mesh",
-  "meta4": "application/metalink4+xml",
-  "metalink": "application/metalink+xml",
-  "mets": "application/mets+xml",
-  "mf4": "application/mf4",
+  "mc1": "application/vnd.medcalcdata",
+  "cdkey": "application/vnd.mediastation.cdkey",
+  "mwf": "application/vnd.mfer",
   "mfm": "application/vnd.mfmp",
-  "mft": "application/rpki-manifest",
-  "mgp": "application/vnd.osgeo.mapguide.package",
-  "mgz": "application/vnd.proteus.magazine",
-  "mht": "message/rfc822",
-  "mhtml": "message/rfc822",
-  "mib": "text/mib",
-  "mid": "audio/midi",
-  "midi": "audio/midi",
-  "mie": "application/x-mie",
-  "mif": "application/x-mif",
-  "mime": "message/rfc822",
-  "miz": "text/mizar",
-  "mj2": "video/mj2",
-  "mjf": "audio/x-vnd.audioexplosion.mjuicemediafile",
-  "mjp2": "video/mj2",
-  "mjpg": "video/x-motion-jpeg",
-  "mjs": "application/javascript",
-  "mk": "text/makefile",
-  "mk3d": "video/x-matroska-3d",
-  "mka": "audio/x-matroska",
-  "mkd": "text/x-markdown",
-  "mks": "video/x-matroska",
-  "mkv": "video/x-matroska",
-  "mlp": "application/vnd.dolby.mlp",
-  "mm": "application/x-freemind",
-  "mmd": "application/vnd.chipnuts.karaoke-mmd",
-  "mmdb": "application/vnd.maxmind.maxmind-db",
-  "mme": "application/base64",
-  "mmf": "application/vnd.smaf",
-  "mml": "text/mathml",
-  "mmod": "chemical/x-macromodel-input",
-  "mmr": "image/vnd.fujixerox.edmics-mmr",
-  "mms": "application/vnd.wap.mms-message",
-  "mng": "video/x-mng",
-  "mny": "application/x-msmoney",
-  "mobi": "application/x-mobipocket-ebook",
-  "moc": "text/x-moc",
-  "mod": "audio/x-mod",
-  "model-inter": "application/vnd.vd-study",
-  "mods": "application/mods+xml",
-  "modulemap": "text/modulemap",
-  "mol": "chemical/x-mdl-molfile",
-  "mol2": "chemical/x-mol2",
-  "moml": "model/vnd.moml+xml",
-  "moo": "chemical/x-mopac-out",
-  "moov": "video/quicktime",
-  "mop": "chemical/x-mopac-input",
-  "mopcrt": "chemical/x-mopac-input",
-  "mov": "video/quicktime",
-  "movie": "video/x-sgi-movie",
-  "mp1": "audio/mpeg",
-  "mp2": "audio/mpeg",
-  "mp21": "application/mp21",
-  "mp2a": "audio/mpeg",
-  "mp3": "audio/mp3",
-  "mp4": "video/mp4",
-  "mp4a": "audio/mp4",
-  "mp4s": "application/mp4",
-  "mp4v": "video/mp4",
-  "mpa": "video/mpeg",
-  "mpc": "application/vnd.mophun.certificate",
-  "mpd": "application/dash+xml",
-  "mpdd": "application/dashdelta",
-  "mpe": "video/mpeg",
-  "mpeg": "video/mpeg",
-  "mpega": "audio/mpeg",
-  "mpf": "text/vnd.ms-mediapackage",
-  "mpg": "video/mpeg",
-  "mpg4": "video/mp4",
-  "mpga": "audio/mpeg",
-  "mpkg": "application/vnd.apple.installer+xml",
-  "mpm": "application/vnd.blueice.multipass",
+  "flo": "application/vnd.micrografx.flo",
+  "igx": "application/vnd.micrografx.igx",
+  "mif": "application/vnd.mif",
+  "daf": "application/vnd.mobius.daf",
+  "dis": "application/vnd.mobius.dis",
+  "mbk": "application/vnd.mobius.mbk",
+  "mqy": "application/vnd.mobius.mqy",
+  "msl": "application/vnd.mobius.msl",
+  "plc": "application/vnd.mobius.plc",
+  "txf": "application/vnd.mobius.txf",
   "mpn": "application/vnd.mophun.application",
+  "mpc": "application/vnd.mophun.certificate",
+  "xul": "application/vnd.mozilla.xul+xml",
+  "cil": "application/vnd.ms-artgalry",
+  "cab": "application/vnd.ms-cab-compressed",
+  "xls": "application/vnd.ms-excel",
+  "xlm": "application/vnd.ms-excel",
+  "xla": "application/vnd.ms-excel",
+  "xlc": "application/vnd.ms-excel",
+  "xlt": "application/vnd.ms-excel",
+  "xlw": "application/vnd.ms-excel",
+  "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
+  "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
+  "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
+  "xltm": "application/vnd.ms-excel.template.macroenabled.12",
+  "eot": "application/vnd.ms-fontobject",
+  "chm": "application/vnd.ms-htmlhelp",
+  "ims": "application/vnd.ms-ims",
+  "lrm": "application/vnd.ms-lrm",
+  "thmx": "application/vnd.ms-officetheme",
+  "cat": "application/vnd.ms-pki.seccat",
+  "stl": "application/vnd.ms-pki.stl",
+  "ppt": "application/vnd.ms-powerpoint",
+  "pps": "application/vnd.ms-powerpoint",
+  "pot": "application/vnd.ms-powerpoint",
+  "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
+  "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
+  "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
+  "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
+  "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
   "mpp": "application/vnd.ms-project",
   "mpt": "application/vnd.ms-project",
-  "mpv": "application/x-project",
-  "mpv2": "video/mpeg",
-  "mpx": "application/x-project",
-  "mpy": "application/vnd.ibm.minipay",
-  "mqy": "application/vnd.mobius.mqy",
-  "mrc": "application/marc",
-  "mrcx": "application/marcxml+xml",
-  "ms": "application/x-troff-ms",
-  "msa": "application/vnd.msa-disk-image",
-  "mscml": "application/mediaservercontrol+xml",
-  "msd": "application/vnd.fdsn.mseed",
-  "mseed": "application/vnd.fdsn.mseed",
+  "docm": "application/vnd.ms-word.document.macroenabled.12",
+  "dotm": "application/vnd.ms-word.template.macroenabled.12",
+  "wps": "application/vnd.ms-works",
+  "wks": "application/vnd.ms-works",
+  "wcm": "application/vnd.ms-works",
+  "wdb": "application/vnd.ms-works",
+  "wpl": "application/vnd.ms-wpl",
+  "xps": "application/vnd.ms-xpsdocument",
   "mseq": "application/vnd.mseq",
-  "msf": "application/vnd.epson.msf",
-  "msg": "application/vnd.ms-outlook",
-  "msh": "model/mesh",
-  "msi": "application/x-msi",
-  "msl": "application/vnd.mobius.msl",
-  "msm": "model/vnd.gdl",
-  "msty": "application/vnd.muvee.style",
-  "mtm": "audio/x-mod",
-  "mts": "model/vnd.mts",
-  "multitrack": "audio/vnd.presonus.multitrack",
   "mus": "application/vnd.musician",
-  "musd": "application/mmt-usd+xml",
-  "musicxml": "application/vnd.recordare.musicxml+xml",
-  "mv": "video/x-sgi-movie",
-  "mvb": "application/x-msmediaview",
-  "mvt": "application/vnd.mapbox-vector-tile",
-  "mwc": "application/vnd.dpgraph",
-  "mwf": "application/vnd.mfer",
-  "mxf": "application/mxf",
-  "mxi": "application/vnd.vd-study",
-  "mxl": "application/vnd.recordare.musicxml",
-  "mxmf": "audio/mobile-xmf",
-  "mxml": "application/xv+xml",
-  "mxs": "application/vnd.triscape.mxs",
-  "mxu": "video/vnd.mpegurl",
-  "my": "audio/make",
-  "mzz": "application/x-vnd.audioexplosion.mzz",
-  "n-gage": "application/vnd.nokia.n-gage.symbian.install",
-  "n3": "text/n3",
-  "nap": "image/naplps",
-  "naplps": "image/naplps",
-  "nb": "application/mathematica",
-  "nbp": "application/vnd.wolfram.player",
-  "nc": "application/x-netcdf",
-  "ncm": "application/vnd.nokia.configuration-message",
-  "ncx": "application/x-dtbncx+xml",
-  "ndc": "application/vnd.osa.netdeploy",
-  "ndjson": "application/json",
-  "ndl": "application/vnd.lotus-notes",
-  "nds": "application/vnd.nintendo.nitro.rom",
-  "nef": "image/x-nikon-nef",
-  "nfo": "text/x-nfo",
-  "ngdat": "application/vnd.nokia.n-gage.data",
-  "ngdoc": "text/ngdoc",
-  "nif": "image/x-niff",
-  "niff": "image/x-niff",
+  "msty": "application/vnd.muvee.style",
+  "taglet": "application/vnd.mynfc",
+  "nlu": "application/vnd.neurolanguage.nlu",
   "nim": "text/nim",
   "nimble": "text/nimble",
   "nimf": "text/nim",
   "nims": "text/nim",
+  "ntf": "application/vnd.nitf",
   "nitf": "application/vnd.nitf",
-  "nix": "application/x-mix-transfer",
-  "nlu": "application/vnd.neurolanguage.nlu",
-  "nml": "application/vnd.enliven",
   "nnd": "application/vnd.noblenet-directory",
   "nns": "application/vnd.noblenet-sealer",
   "nnw": "application/vnd.noblenet-web",
-  "notebook": "application/vnd.smart.notebook",
-  "npx": "image/vnd.net-fpx",
-  "nq": "application/n-quads",
-  "ns2": "application/vnd.lotus-notes",
-  "ns3": "application/vnd.lotus-notes",
-  "ns4": "application/vnd.lotus-notes",
-  "nsc": "application/x-conference",
-  "nsf": "application/vnd.lotus-notes",
-  "nsg": "application/vnd.lotus-notes",
-  "nsh": "application/vnd.lotus-notes",
-  "nt": "application/n-triples",
-  "ntf": "application/vnd.lotus-notes",
-  "numbers": "application/vnd.apple.numbers",
-  "nvd": "application/x-navidoc",
-  "nwc": "application/x-nwc",
-  "nws": "message/rfc822",
-  "nzb": "application/x-nzb",
-  "o": "application/x-object",
-  "o4a": "application/vnd.oma.drm.dcf",
-  "o4v": "application/vnd.oma.drm.dcf",
-  "oa2": "application/vnd.fujitsu.oasys2",
-  "oa3": "application/vnd.fujitsu.oasys3",
-  "oas": "application/vnd.fujitsu.oasys",
-  "obd": "application/x-msbinder",
-  "obg": "application/vnd.openblox.game-binary",
-  "obgx": "application/vnd.openblox.game+xml",
-  "obj": "application/x-tgif",
-  "oda": "application/oda",
-  "odb": "application/vnd.oasis.opendocument.database",
+  "ngdat": "application/vnd.nokia.n-gage.data",
+  "n-gage": "application/vnd.nokia.n-gage.symbian.install",
+  "rpst": "application/vnd.nokia.radio-preset",
+  "rpss": "application/vnd.nokia.radio-presets",
+  "edm": "application/vnd.novadigm.edm",
+  "edx": "application/vnd.novadigm.edx",
+  "ext": "application/vnd.novadigm.ext",
   "odc": "application/vnd.oasis.opendocument.chart",
-  "odd": "application/tei+xml",
+  "otc": "application/vnd.oasis.opendocument.chart-template",
+  "odb": "application/vnd.oasis.opendocument.database",
   "odf": "application/vnd.oasis.opendocument.formula",
   "odft": "application/vnd.oasis.opendocument.formula-template",
   "odg": "application/vnd.oasis.opendocument.graphics",
-  "odi": "application/vnd.oasis.opendocument.image",
-  "odm": "application/vnd.oasis.opendocument.text-master",
-  "odp": "application/vnd.oasis.opendocument.presentation",
-  "ods": "application/vnd.oasis.opendocument.spreadsheet",
-  "odt": "application/vnd.oasis.opendocument.text",
-  "odx": "application/odx",
-  "oeb": "application/vnd.openeye.oeb",
-  "oga": "audio/ogg",
-  "ogex": "model/vnd.opengex",
-  "ogg": "audio/ogg",
-  "ogv": "video/ogg",
-  "ogx": "application/ogg",
-  "old": "application/x-trash",
-  "omc": "application/x-omc",
-  "omcd": "application/x-omcdatamaker",
-  "omcr": "application/x-omcregerator",
-  "omdoc": "application/omdoc+xml",
-  "omg": "audio/atrac3",
-  "onepkg": "application/onenote",
-  "onetmp": "application/onenote",
-  "onetoc": "application/onenote",
-  "onetoc2": "application/onenote",
-  "opf": "application/oebps-package+xml",
-  "opml": "text/x-opml",
-  "oprc": "application/vnd.palm",
-  "opus": "audio/ogg",
-  "or2": "application/vnd.lotus-organizer",
-  "or3": "application/vnd.lotus-organizer",
-  "orf": "image/x-olympus-orf",
-  "org": "text/x-org",
-  "orq": "application/ocsp-request",
-  "ors": "application/ocsp-response",
-  "osf": "application/vnd.yamaha.openscoreformat",
-  "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
-  "osm": "application/vnd.openstreetmap.data+xml",
-  "otc": "application/vnd.oasis.opendocument.chart-template",
-  "otf": "font/otf",
   "otg": "application/vnd.oasis.opendocument.graphics-template",
-  "oth": "application/vnd.oasis.opendocument.text-web",
+  "odi": "application/vnd.oasis.opendocument.image",
   "oti": "application/vnd.oasis.opendocument.image-template",
+  "odp": "application/vnd.oasis.opendocument.presentation",
   "otp": "application/vnd.oasis.opendocument.presentation-template",
+  "ods": "application/vnd.oasis.opendocument.spreadsheet",
   "ots": "application/vnd.oasis.opendocument.spreadsheet-template",
+  "odt": "application/vnd.oasis.opendocument.text",
+  "odm": "application/vnd.oasis.opendocument.text-master",
   "ott": "application/vnd.oasis.opendocument.text-template",
-  "ova": "application/x-virtualbox-ova",
-  "ovf": "application/x-virtualbox-ovf",
-  "owx": "application/owl+xml",
-  "oxlicg": "application/vnd.oxli.countgraph",
-  "oxps": "application/oxps",
+  "oth": "application/vnd.oasis.opendocument.text-web",
+  "xo": "application/vnd.olpc-sugar",
+  "dd2": "application/vnd.oma.dd2+xml",
   "oxt": "application/vnd.openofficeorg.extension",
-  "oza": "application/x-oz-application",
-  "p": "text/x-pascal",
-  "p10": "application/pkcs10",
-  "p12": "application/pkcs12",
-  "p2p": "application/vnd.wfa.p2p",
-  "p7a": "application/x-pkcs7-signature",
-  "p7b": "application/x-pkcs7-certificates",
-  "p7c": "application/pkcs7-mime",
-  "p7m": "application/pkcs7-mime",
-  "p7r": "application/x-pkcs7-certreqresp",
-  "p7s": "application/pkcs7-signature",
-  "p8": "application/pkcs8",
-  "pac": "application/x-ns-proxy-autoconfig",
-  "pack": "application/x-java-pack200",
-  "package": "application/vnd.autopackage",
-  "pages": "application/vnd.apple.pages",
-  "par": "text/plain-bas",
-  "part": "application/pro_eng",
-  "pas": "text/pascal",
-  "pat": "image/x-coreldrawpattern",
-  "patch": "text/x-diff",
-  "paw": "application/vnd.pawaafile",
-  "pbd": "application/vnd.powerbuilder6",
-  "pbm": "image/x-portable-bitmap",
-  "pcap": "application/vnd.tcpdump.pcap",
-  "pcf": "application/x-font-pcf",
-  "pcl": "application/vnd.hp-pcl",
-  "pclxl": "application/vnd.hp-pclxl",
-  "pct": "image/x-pict",
-  "pcurl": "application/vnd.curl.pcurl",
-  "pcx": "image/x-pcx",
-  "pdb": "application/vnd.palm",
-  "pde": "text/x-processing",
-  "pdf": "application/pdf",
-  "pdx": "application/pdx",
-  "pem": "text/pem",
-  "pfa": "application/x-font-type1",
-  "pfb": "application/x-font-type1",
-  "pfm": "application/x-font-type1",
-  "pfr": "application/font-tdpfr",
-  "pfunk": "audio/make",
-  "pfx": "application/pkcs12",
-  "pgb": "image/vnd.globalgraphics.pgb",
-  "pgm": "image/x-portable-graymap",
-  "pgn": "application/x-chess-pgn",
-  "pgp": "application/pgp-encrypted",
-  "php": "application/x-httpd-php",
-  "php3": "application/x-httpd-php3",
-  "php3p": "application/x-httpd-php3-preprocessed",
-  "php4": "application/x-httpd-php4",
-  "php5": "application/x-httpd-php5",
-  "phps": "application/x-httpd-php-source",
-  "pht": "application/x-httpd-php",
-  "phtml": "application/x-httpd-php",
-  "pic": "image/pict",
-  "pict": "image/pict",
-  "pil": "application/vnd.piaccess.application-license",
-  "pk": "application/x-tex-pk",
-  "pkd": "application/vnd.hbci",
-  "pkg": "application/vnd.apple.installer+xml",
-  "pki": "application/pkixcmp",
-  "pkipath": "application/pkix-pkipath",
-  "pko": "application/ynd.ms-pkipko",
-  "pkpass": "application/vnd.apple.pkpass",
-  "pl": "application/x-perl",
-  "plantuml": "text/plantuml",
-  "plb": "application/vnd.3gpp.pic-bw-large",
-  "plc": "application/vnd.mobius.plc",
-  "plf": "application/vnd.pocketlearn",
-  "plj": "audio/vnd.everad.plj",
-  "plp": "application/vnd.panoply",
-  "pls": "application/pls+xml",
-  "plx": "application/x-pixclscript",
-  "ply": "model/stanford",
-  "pm": "text/plain",
-  "pm4": "application/x-pagemaker",
-  "pm5": "application/x-pagemaker",
-  "pma": "application/x-perfmon",
-  "pmc": "application/x-perfmon",
-  "pml": "application/vnd.ctc-posml",
-  "pmr": "application/x-perfmon",
-  "pmw": "application/x-perfmon",
-  "png": "image/png",
-  "pnm": "image/x-portable-anymap",
-  "po": "text/pofile",
-  "pod": "text/x-pod",
-  "portpkg": "application/vnd.macports.portpkg",
-  "pot": "application/vnd.ms-powerpoint",
-  "potm": "application/vnd.ms-powerpoint.template.macroenabled.12",
-  "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
-  "pov": "model/x-pov",
-  "pp": "text/puppet",
-  "ppa": "application/vnd.ms-powerpoint",
-  "ppam": "application/vnd.ms-powerpoint.addin.macroenabled.12",
-  "ppd": "application/vnd.cups-ppd",
-  "ppkg": "application/vnd.xmpie.ppkg",
-  "ppm": "image/x-portable-pixmap",
-  "pps": "application/vnd.ms-powerpoint",
-  "ppsm": "application/vnd.ms-powerpoint.slideshow.macroenabled.12",
-  "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
-  "ppt": "application/vnd.ms-powerpoint",
-  "pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12",
   "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
-  "ppz": "application/mspowerpoint",
+  "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
+  "ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+  "potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
+  "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+  "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+  "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+  "dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+  "mgp": "application/vnd.osgeo.mapguide.package",
+  "dp": "application/vnd.osgi.dp",
+  "esa": "application/vnd.osgi.subsystem",
+  "pdb": "application/vnd.palm",
   "pqa": "application/vnd.palm",
-  "prc": "application/vnd.palm",
-  "pre": "application/vnd.lotus-freelance",
-  "preminet": "application/vnd.preminet",
-  "prf": "application/pics-rules",
-  "proto": "text/proto",
-  "provn": "text/provenance-notation",
-  "provx": "application/provenance+xml",
-  "prt": "application/pro_eng",
-  "prz": "application/vnd.lotus-freelance",
-  "ps": "application/postscript",
-  "psb": "application/vnd.3gpp.pic-bw-small",
-  "psd": "image/vnd.adobe.photoshop",
-  "pseg3820": "application/vnd.ibm.modcap",
-  "psf": "application/x-font-linux-psf",
-  "psid": "audio/prs.sid",
-  "pskcxml": "application/pskc+xml",
-  "pti": "image/prs.pti",
-  "ptid": "application/vnd.pvi.ptid1",
-  "pub": "application/x-mspublisher",
-  "purs": "text/purescript",
-  "pvb": "application/vnd.3gpp.pic-bw-var",
-  "pvu": "paleovu/x-pv",
-  "pwn": "application/vnd.3m.post-it-notes",
-  "pwz": "application/vnd.ms-powerpoint",
-  "pxd": "text/cython",
-  "pxi": "text/cython",
-  "py": "text/x-script.phyton",
-  "pya": "audio/vnd.ms-playready.media.pya",
-  "pyc": "application/x-python-code",
-  "pyi": "text/pyi",
-  "pyo": "application/x-python-code",
-  "pyv": "video/vnd.ms-playready.media.pyv",
-  "pyx": "text/cython",
-  "qam": "application/vnd.epson.quickanime",
-  "qbo": "application/vnd.intu.qbo",
-  "qca": "application/vnd.ericsson.quickcall",
-  "qcall": "application/vnd.ericsson.quickcall",
-  "qcp": "audio/qcelp",
-  "qd3": "x-world/x-3dmf",
-  "qd3d": "x-world/x-3dmf",
-  "qfx": "application/vnd.intu.qfx",
-  "qgs": "application/x-qgis",
-  "qif": "image/x-quicktime",
+  "oprc": "application/vnd.palm",
+  "paw": "application/vnd.pawaafile",
+  "str": "application/vnd.pg.format",
+  "ei6": "application/vnd.pg.osasli",
+  "efif": "application/vnd.picsel",
+  "wg": "application/vnd.pmi.widget",
+  "plf": "application/vnd.pocketlearn",
+  "pbd": "application/vnd.powerbuilder6",
+  "box": "application/vnd.previewsystems.box",
+  "mgz": "application/vnd.proteus.magazine",
   "qps": "application/vnd.publishare-delta-tree",
-  "qt": "video/quicktime",
-  "qtc": "video/x-qtc",
-  "qti": "image/x-quicktime",
-  "qtif": "image/x-quicktime",
-  "qtl": "application/x-quicktimeplayer",
-  "quiz": "application/vnd.quobject-quoxdocument",
-  "quox": "application/vnd.quobject-quoxdocument",
-  "qvd": "application/vnd.theqvd",
+  "ptid": "application/vnd.pvi.ptid1",
+  "qxd": "application/vnd.quark.quarkxpress",
+  "qxt": "application/vnd.quark.quarkxpress",
   "qwd": "application/vnd.quark.quarkxpress",
   "qwt": "application/vnd.quark.quarkxpress",
-  "qxb": "application/vnd.quark.quarkxpress",
-  "qxd": "application/vnd.quark.quarkxpress",
   "qxl": "application/vnd.quark.quarkxpress",
-  "qxt": "application/vnd.quark.quarkxpress",
-  "r": "text/r",
-  "ra": "audio/x-realaudio",
-  "ram": "audio/x-pn-realaudio",
-  "raml": "application/raml+yaml",
-  "rapd": "application/route-apd+xml",
-  "rar": "application/x-rar-compressed",
-  "ras": "image/x-cmu-raster",
-  "rast": "image/cmu-raster",
-  "rb": "application/x-ruby",
-  "rcprofile": "application/vnd.ipunplugged.rcprofile",
-  "rct": "application/prs.nprend",
-  "rd": "chemical/x-mdl-rdfile",
-  "rda": "text/r",
-  "rdata": "text/r",
-  "rds": "text/r",
-  "rdf": "application/rdf+xml",
-  "rdf-crypt": "application/prs.rdf-xml-crypt",
-  "rdz": "application/vnd.data-vision.rdz",
-  "relo": "application/p2p-overlay+xml",
-  "rep": "application/vnd.businessobjects",
-  "request": "application/vnd.nervana",
-  "res": "application/x-dtbresource+xml",
-  "rexx": "text/x-script.rexx",
-  "rf": "image/vnd.rn-realflash",
-  "rfcxml": "application/rfc+xml",
-  "rgb": "image/x-rgb",
-  "rgbe": "image/vnd.radiance",
-  "rhtml": "application/x-httpd-eruby",
-  "rif": "application/reginfo+xml",
-  "rip": "audio/vnd.rip",
-  "ris": "application/x-research-info-systems",
-  "rl": "application/resource-lists+xml",
-  "rlc": "image/vnd.fujixerox.edmics-rlc",
-  "rld": "application/resource-lists-diff+xml",
-  "rlib": "text/rust",
+  "qxb": "application/vnd.quark.quarkxpress",
+  "bed": "application/vnd.realvnc.bed",
+  "mxl": "application/vnd.recordare.musicxml",
+  "musicxml": "application/vnd.recordare.musicxml+xml",
+  "cryptonote": "application/vnd.rig.cryptonote",
+  "cod": "application/vnd.rim.cod",
   "rm": "application/vnd.rn-realmedia",
-  "rmi": "audio/mid",
-  "rmm": "audio/x-pn-realaudio",
-  "rmp": "audio/x-pn-realaudio-plugin",
-  "rms": "application/vnd.jcp.javame.midlet-rms",
   "rmvb": "application/vnd.rn-realmedia-vbr",
-  "rnc": "application/relax-ng-compact-syntax",
-  "rnd": "application/prs.nprend",
-  "rng": "text/xml",
-  "rnx": "application/vnd.rn-realplayer",
-  "roa": "application/rpki-roa",
-  "roff": "text/troff",
-  "ros": "chemical/x-rosdal",
-  "rp": "image/vnd.rn-realpix",
-  "rp9": "application/vnd.cloanto.rp9",
-  "rpm": "application/x-redhat-package-manager",
-  "rpss": "application/vnd.nokia.radio-presets",
-  "rpst": "application/vnd.nokia.radio-preset",
-  "rq": "application/sparql-query",
-  "rs": "application/rls-services+xml",
-  "rsd": "application/rsd+xml",
-  "rsheet": "application/urc-ressheet+xml",
-  "rsm": "model/vnd.gdl",
-  "rss": "application/rss+xml",
-  "rst": "text/prs.fallenstein.rst",
-  "rt": "text/richtext",
-  "rtf": "text/rtf",
-  "rtx": "text/richtext",
-  "run": "application/x-makeself",
-  "rusd": "application/route-usd+xml",
-  "rv": "video/vnd.rn-realvideo",
-  "rxn": "chemical/x-mdl-rxnfile",
-  "s": "text/x-asm",
-  "s11": "video/vnd.sealed.mpeg1",
-  "s14": "video/vnd.sealed.mpeg4",
-  "s1a": "application/vnd.sealedmedia.softseal.pdf",
-  "s1e": "application/vnd.sealed.xls",
-  "s1g": "image/vnd.sealedmedia.softseal.gif",
-  "s1h": "application/vnd.sealedmedia.softseal.html",
-  "s1j": "image/vnd.sealedmedia.softseal.jpg",
-  "s1m": "audio/vnd.sealedmedia.softseal.mpeg",
-  "s1n": "image/vnd.sealed.png",
-  "s1p": "application/vnd.sealed.ppt",
-  "s1q": "video/vnd.sealedmedia.softseal.mov",
-  "s1w": "application/vnd.sealed.doc",
-  "s3df": "application/vnd.sealed.3df",
-  "s3m": "audio/s3m",
-  "sac": "application/tamp-sequence-adjust-confirm",
-  "saf": "application/vnd.yamaha.smaf-audio",
-  "sam": "application/vnd.lotus-wordpro",
-  "sandboxed": "text/html-sandboxed",
-  "sass": "text/x-sass",
-  "saveme": "application/octet-stream",
-  "sbk": "application/x-tbook",
-  "sbml": "application/sbml+xml",
-  "sc": "application/vnd.ibm.secure-container",
-  "scala": "text/x-scala",
-  "scd": "application/x-msschedule",
-  "sce": "application/vnd.etsi.asic-e+zip",
-  "scim": "application/scim+json",
-  "scld": "application/vnd.doremir.scorecloud-binary-document",
-  "scm": "application/vnd.lotus-screencam",
-  "scq": "application/scvp-cv-request",
-  "scr": "application/x-silverlight",
-  "scs": "application/scvp-cv-response",
-  "scsf": "application/vnd.sealed.csf",
-  "scss": "text/x-scss",
-  "sct": "text/scriptlet",
-  "scurl": "text/vnd.curl.scurl",
-  "sd": "chemical/x-mdl-sdfile",
-  "sd2": "audio/x-sd2",
-  "sda": "application/vnd.stardivision.draw",
-  "sdc": "application/vnd.stardivision.calc",
-  "sdd": "application/vnd.stardivision.impress",
-  "sdf": "application/vnd.kinar",
-  "sdkd": "application/vnd.solent.sdkm+xml",
-  "sdkm": "application/vnd.solent.sdkm+xml",
-  "sdml": "text/plain",
-  "sdo": "application/vnd.sealed.doc",
-  "sdoc": "application/vnd.sealed.doc",
-  "sdp": "application/sdp",
-  "sdr": "application/sounder",
-  "sdw": "application/vnd.stardivision.writer",
-  "sea": "application/x-sea",
+  "link66": "application/vnd.route66.link66+xml",
+  "st": "application/vnd.sailingtracker.track",
   "see": "application/vnd.seemail",
-  "seed": "application/vnd.fdsn.seed",
-  "sem": "application/vnd.sealed.eml",
   "sema": "application/vnd.sema",
   "semd": "application/vnd.semd",
   "semf": "application/vnd.semf",
-  "seml": "application/vnd.sealed.eml",
-  "ser": "application/java-serialized-object",
-  "set": "application/set",
-  "setpay": "application/set-payment-initiation",
-  "setreg": "application/set-registration-initiation",
-  "sfc": "application/vnd.nintendo.snes.rom",
-  "sfd": "application/vnd.font-fontforge-sfd",
-  "sfd-hdstx": "application/vnd.hydrostatix.sof-data",
+  "ifm": "application/vnd.shana.informed.formdata",
+  "itp": "application/vnd.shana.informed.formtemplate",
+  "iif": "application/vnd.shana.informed.interchange",
+  "ipk": "application/vnd.shana.informed.package",
+  "twd": "application/vnd.simtech-mindmapper",
+  "twds": "application/vnd.simtech-mindmapper",
+  "mmf": "application/vnd.smaf",
+  "teacher": "application/vnd.smart.teacher",
+  "sdkm": "application/vnd.solent.sdkm+xml",
+  "sdkd": "application/vnd.solent.sdkm+xml",
+  "dxp": "application/vnd.spotfire.dxp",
   "sfs": "application/vnd.spotfire.sfs",
-  "sfv": "text/x-sfv",
-  "sgf": "application/x-go-sgf",
-  "sgi": "image/sgi",
-  "sgif": "image/vnd.sealedmedia.softseal.gif",
-  "sgl": "application/vnd.stardivision.writer-global",
-  "sgm": "text/sgml",
-  "sgml": "text/sgml",
-  "sh": "application/x-sh",
-  "shar": "application/x-shar",
-  "shex": "text/shex",
-  "shf": "application/shf+xml",
-  "shp": "application/x-qgis",
-  "shx": "application/x-qgis",
-  "si": "text/vnd.wap.si",
-  "sic": "application/vnd.wap.sic",
-  "sid": "image/x-mrsid-image",
-  "sieve": "application/sieve",
-  "sig": "application/pgp-signature",
-  "sik": "application/x-trash",
-  "sil": "audio/silk",
-  "silo": "model/mesh",
-  "sis": "application/vnd.symbian.install",
-  "sisx": "x-epoc/x-sisx-app",
-  "sit": "application/x-stuffit",
-  "sitx": "application/x-stuffitx",
-  "siv": "application/sieve",
-  "sjp": "image/vnd.sealedmedia.softseal.jpg",
-  "sjpg": "image/vnd.sealedmedia.softseal.jpg",
-  "skd": "application/vnd.koan",
-  "skm": "application/vnd.koan",
-  "skp": "application/vnd.koan",
-  "skt": "application/vnd.koan",
-  "sl": "text/vnd.wap.sl",
-  "sla": "application/vnd.scribus",
-  "slaz": "application/vnd.scribus",
-  "slc": "application/vnd.wap.slc",
-  "sldm": "application/vnd.ms-powerpoint.slide.macroenabled.12",
-  "sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
-  "sls": "application/route-s-tsid+xml",
-  "slt": "application/vnd.epson.salt",
-  "sm": "application/vnd.stepmania.stepchart",
-  "smc": "application/vnd.nintendo.snes.rom",
+  "sdc": "application/vnd.stardivision.calc",
+  "sda": "application/vnd.stardivision.draw",
+  "sdd": "application/vnd.stardivision.impress",
   "smf": "application/vnd.stardivision.math",
-  "smh": "application/vnd.sealed.mht",
-  "smht": "application/vnd.sealed.mht",
-  "smi": "application/smil+xml",
-  "smil": "application/smil+xml",
-  "smk": "video/vnd.radgamettools.smacker",
-  "sml": "application/smil+xml",
-  "smo": "video/vnd.sealedmedia.softseal.mov",
-  "smov": "video/vnd.sealedmedia.softseal.mov",
-  "smp": "audio/vnd.sealedmedia.softseal.mpeg",
-  "smp3": "audio/vnd.sealedmedia.softseal.mpeg",
-  "smpg": "video/vnd.sealed.mpeg1",
-  "sms": "application/vnd.3gpp2.sms",
-  "smv": "video/x-smv",
+  "sdw": "application/vnd.stardivision.writer",
+  "vor": "application/vnd.stardivision.writer",
+  "sgl": "application/vnd.stardivision.writer-global",
   "smzip": "application/vnd.stepmania.package",
-  "snd": "audio/basic",
-  "snf": "application/x-font-snf",
-  "so": "application/octet-stream",
-  "soa": "text/dns",
-  "soc": "application/sgml-open-catalog",
-  "sol": "application/solids",
-  "spc": "text/x-speech",
-  "spd": "application/vnd.sealedmedia.softseal.pdf",
-  "spdf": "application/vnd.sealedmedia.softseal.pdf",
-  "spec": "text/spec",
-  "spf": "application/vnd.yamaha.smaf-phrase",
-  "spl": "application/x-futuresplash",
-  "spn": "image/vnd.sealed.png",
-  "spng": "image/vnd.sealed.png",
-  "spo": "text/vnd.in3d.spot",
-  "spot": "text/vnd.in3d.spot",
-  "spp": "application/scvp-vp-response",
-  "sppt": "application/vnd.sealed.ppt",
-  "spq": "application/scvp-vp-request",
-  "spr": "application/x-sprite",
-  "sprite": "application/x-sprite",
-  "spx": "audio/ogg",
-  "sql": "application/x-sql",
-  "sr": "application/vnd.sigrok.session",
-  "src": "application/x-wais-source",
-  "srt": "application/x-subrip",
-  "sru": "application/sru+xml",
-  "srx": "application/sparql-results+xml",
-  "ssdl": "application/ssdl+xml",
-  "sse": "application/vnd.kodak-descriptor",
-  "ssf": "application/vnd.epson.ssf",
-  "ssi": "text/x-server-parsed-html",
-  "ssm": "application/streamingmedia",
-  "ssml": "application/ssml+xml",
-  "sst": "application/vnd.ms-pki.certstore",
-  "ssw": "video/vnd.sealed.swf",
-  "sswf": "video/vnd.sealed.swf",
-  "st": "application/vnd.sailingtracker.track",
+  "sm": "application/vnd.stepmania.stepchart",
+  "sxc": "application/vnd.sun.xml.calc",
   "stc": "application/vnd.sun.xml.calc.template",
+  "sxd": "application/vnd.sun.xml.draw",
   "std": "application/vnd.sun.xml.draw.template",
-  "step": "application/step",
-  "stf": "application/vnd.wt.stf",
+  "sxi": "application/vnd.sun.xml.impress",
   "sti": "application/vnd.sun.xml.impress.template",
-  "stif": "application/vnd.sealed.tiff",
-  "stk": "application/hyperstudio",
-  "stl": "application/vnd.ms-pki.stl",
-  "stm": "audio/x-stm",
-  "stml": "application/vnd.sealedmedia.softseal.html",
-  "stp": "application/step",
-  "str": "application/vnd.pg.format",
-  "study-inter": "application/vnd.vd-study",
+  "sxm": "application/vnd.sun.xml.math",
+  "sxw": "application/vnd.sun.xml.writer",
+  "sxg": "application/vnd.sun.xml.writer.global",
   "stw": "application/vnd.sun.xml.writer.template",
-  "sty": "text/x-tex",
-  "styl": "text/stylus",
-  "sub": "text/vnd.dvb.subtitle",
   "sus": "application/vnd.sus-calendar",
   "susp": "application/vnd.sus-calendar",
-  "sv4cpio": "application/x-sv4cpio",
-  "sv4crc": "application/x-sv4crc",
-  "svc": "application/vnd.dvb.service",
   "svd": "application/vnd.svd",
-  "svf": "image/x-dwg",
-  "svg": "image/svg+xml",
-  "svgz": "image/svg+xml",
-  "sw": "chemical/x-swissprot",
-  "swa": "application/x-director",
-  "swf": "application/x-shockwave-flash",
-  "swfl": "application/x-shockwave-flash",
-  "swi": "application/vnd.aristanetworks.swi",
-  "swift": "text/swift",
-  "swiftdeps": "text/swiftdeps",
-  "sxc": "application/vnd.sun.xml.calc",
-  "sxd": "application/vnd.sun.xml.draw",
-  "sxg": "application/vnd.sun.xml.writer.global",
-  "sxi": "application/vnd.sun.xml.impress",
-  "sxl": "application/vnd.sealed.xls",
-  "sxls": "application/vnd.sealed.xls",
-  "sxm": "application/vnd.sun.xml.math",
-  "sxw": "application/vnd.sun.xml.writer",
-  "t": "text/troff",
-  "t3": "application/x-t3vm-image",
-  "t38": "image/t38",
-  "tac": "text/twisted",
-  "tag": "text/prs.lines.tag",
-  "taglet": "application/vnd.mynfc",
-  "talk": "text/x-speech",
-  "tam": "application/vnd.onepager",
-  "tamp": "application/vnd.onepagertamp",
-  "tamx": "application/vnd.onepagertamx",
+  "sis": "application/vnd.symbian.install",
+  "sisx": "application/vnd.symbian.install",
+  "xsm": "application/vnd.syncml+xml",
+  "bdm": "application/vnd.syncml.dm+wbxml",
+  "xdm": "application/vnd.syncml.dm+xml",
   "tao": "application/vnd.tao.intent-module-archive",
-  "tap": "image/vnd.tencent.tap",
-  "tar": "application/x-tar",
-  "tat": "application/vnd.onepagertat",
-  "tatp": "application/vnd.onepagertatp",
-  "tatx": "application/vnd.onepagertatx",
-  "tau": "application/tamp-apex-update",
-  "taz": "application/x-gtar",
-  "tbk": "application/toolbook",
-  "tcap": "application/vnd.3gpp2.tcap",
-  "tcl": "application/x-tcl",
-  "tcsh": "text/x-script.tcsh",
-  "tcu": "application/tamp-community-update",
-  "td": "application/urc-targetdesc+xml",
-  "teacher": "application/vnd.smart.teacher",
-  "tei": "application/tei+xml",
-  "teicorpus": "application/tei+xml",
-  "ter": "application/tamp-error",
-  "tex": "application/x-tex",
-  "texi": "application/x-texinfo",
-  "texinfo": "application/x-texinfo",
-  "text": "text/plain",
-  "tf": "text/terraform",
-  "tfi": "application/thraud+xml",
-  "tfm": "application/x-tex-tfm",
-  "tfx": "image/tiff-fx",
-  "tga": "image/x-tga",
-  "tgf": "chemical/x-mdl-tgf",
-  "tgz": "application/gzip",
-  "thmx": "application/vnd.ms-officetheme",
-  "thrift": "text/thrift",
-  "tif": "image/tiff",
-  "tiff": "image/tiff",
-  "tk": "text/x-tcl",
-  "tlclient": "application/vnd.cendio.thinlinc.clientconf",
-  "tm": "text/texmacs",
+  "pcap": "application/vnd.tcpdump.pcap",
+  "cap": "application/vnd.tcpdump.pcap",
+  "dmp": "application/vnd.tcpdump.pcap",
   "tmo": "application/vnd.tmobile-livetv",
-  "tnef": "application/vnd.ms-tnef",
-  "tnf": "application/vnd.ms-tnef",
-  "toml": "text/toml",
-  "torrent": "application/x-bittorrent",
-  "tpl": "application/vnd.groove-tool-template",
   "tpt": "application/vnd.trid.tpt",
-  "tr": "text/troff",
+  "mxs": "application/vnd.triscape.mxs",
   "tra": "application/vnd.trueapp",
-  "tree": "application/vnd.rainstor.data",
-  "trig": "application/trig",
-  "trm": "application/x-msterminal",
-  "ts": "video/mp2t",
-  "tsa": "application/tamp-sequence-adjust",
-  "tscn": "text/godot",
-  "tsd": "application/timestamped-data",
-  "tsi": "audio/tsp-audio",
-  "tsp": "audio/tsplayer",
-  "tsq": "application/timestamp-query",
-  "tsr": "application/timestamp-reply",
-  "tst": "application/vnd.etsi.timestamp-token",
-  "tsv": "text/tab-separated-values",
-  "tsx": "text/tsx",
-  "ttc": "font/collection",
-  "ttf": "font/ttf",
-  "ttl": "text/turtle",
-  "ttml": "application/ttml+xml",
-  "tuc": "application/tamp-update-confirm",
-  "tur": "application/tamp-update",
-  "turbot": "image/florian",
-  "twd": "application/vnd.simtech-mindmapper",
-  "twds": "application/vnd.simtech-mindmapper",
-  "txd": "application/vnd.genomatix.tuxedo",
-  "txf": "application/vnd.mobius.txf",
-  "txt": "text/plain",
-  "u32": "application/x-authorware-bin",
-  "u8dsn": "message/global-delivery-status",
-  "u8hdr": "message/global-headers",
-  "u8mdn": "message/global-disposition-notification",
-  "u8msg": "message/global",
-  "udeb": "application/vnd.debian.binary-package",
   "ufd": "application/vnd.ufdl",
   "ufdl": "application/vnd.ufdl",
-  "uil": "text/x-uil",
-  "uis": "application/urc-uisocketdesc+xml",
-  "uls": "text/iuls",
-  "ult": "audio/x-mod",
-  "ulx": "application/x-glulx",
+  "utz": "application/vnd.uiq.theme",
   "umj": "application/vnd.umajin",
-  "uni": "audio/x-mod",
-  "unis": "text/uri-list",
   "unityweb": "application/vnd.unity",
-  "unv": "application/i-deas",
-  "uo": "application/vnd.uoml+xml",
   "uoml": "application/vnd.uoml+xml",
-  "upa": "application/vnd.hbci",
-  "uri": "text/uri-list",
-  "uric": "text/vnd.si.uricatalogue",
-  "urim": "application/vnd.uri-map",
-  "urimap": "application/vnd.uri-map",
-  "uris": "text/uri-list",
-  "urls": "text/uri-list",
-  "ustar": "application/x-ustar",
-  "utz": "application/vnd.uiq.theme",
-  "uu": "text/x-uuencode",
-  "uue": "text/x-uuencode",
-  "uva": "audio/vnd.dece.audio",
-  "uvd": "application/vnd.dece.data",
-  "uvf": "application/vnd.dece.data",
-  "uvg": "image/vnd.dece.graphic",
-  "uvh": "video/vnd.dece.hd",
-  "uvi": "image/vnd.dece.graphic",
-  "uvm": "video/vnd.dece.mobile",
-  "uvp": "video/vnd.dece.pd",
-  "uvs": "video/vnd.dece.sd",
-  "uvt": "application/vnd.dece.ttml+xml",
-  "uvu": "video/vnd.dece.mp4",
-  "uvv": "video/vnd.dece.video",
-  "uvva": "audio/vnd.dece.audio",
-  "uvvd": "application/vnd.dece.data",
-  "uvvf": "application/vnd.dece.data",
-  "uvvg": "image/vnd.dece.graphic",
-  "uvvh": "video/vnd.dece.hd",
-  "uvvi": "image/vnd.dece.graphic",
-  "uvvm": "video/vnd.dece.mobile",
-  "uvvp": "video/vnd.dece.pd",
-  "uvvs": "video/vnd.dece.sd",
-  "uvvt": "application/vnd.dece.ttml+xml",
-  "uvvu": "video/vnd.dece.mp4",
-  "uvvv": "video/vnd.dece.video",
-  "uvvx": "application/vnd.dece.unspecified",
-  "uvvz": "application/vnd.dece.zip",
-  "uvx": "application/vnd.dece.unspecified",
-  "uvz": "application/vnd.dece.zip",
-  "val": "chemical/x-ncbi-asn1-binary",
-  "vbk": "audio/vnd.nortel.vbk",
-  "vbox": "application/x-virtualbox-vbox",
-  "vbox-extpack": "application/x-virtualbox-vbox-extpack",
-  "vcard": "text/vcard",
-  "vcd": "application/x-cdlink",
-  "vcf": "text/x-vcard",
-  "vcg": "application/vnd.groove-vcard",
-  "vcs": "text/x-vcalendar",
   "vcx": "application/vnd.vcx",
-  "vda": "application/vda",
-  "vdi": "application/x-virtualbox-vdi",
-  "vdo": "video/vdo",
-  "vdx": "text/vdx",
-  "vew": "application/vnd.lotus-approach",
-  "vfr": "application/vnd.tml",
-  "vhd": "application/x-virtualbox-vhd",
-  "viaframe": "application/vnd.tml",
-  "vim": "text/vim",
-  "vis": "application/vnd.visionary",
-  "viv": "video/vnd.vivo",
-  "vivo": "video/vivo",
-  "vmd": "application/vocaltec-media-desc",
-  "vmdk": "application/x-virtualbox-vmdk",
-  "vmf": "application/vocaltec-media-file",
-  "vms": "chemical/x-vamas-iso14976",
-  "vmt": "application/vnd.valve.source.material",
-  "vob": "video/x-ms-vob",
-  "voc": "audio/voc",
-  "vor": "application/vnd.stardivision.writer",
-  "vos": "video/vosaic",
-  "vox": "audio/voxware",
-  "vpm": "multipart/voice-message",
-  "vqe": "audio/x-twinvq-plugin",
-  "vqf": "audio/x-twinvq",
-  "vql": "audio/x-twinvq-plugin",
-  "vrm": "x-world/x-vrml",
-  "vrml": "model/vrml",
-  "vrt": "x-world/x-vrt",
-  "vsc": "application/vnd.vidsoft.vidconference",
   "vsd": "application/vnd.visio",
-  "vsf": "application/vnd.vsf",
-  "vss": "application/vnd.visio",
   "vst": "application/vnd.visio",
+  "vss": "application/vnd.visio",
   "vsw": "application/vnd.visio",
-  "vtf": "image/vnd.valve.source.texture",
-  "vtt": "text/vtt",
-  "vtu": "model/vnd.vtu",
-  "vue": "text/vue",
-  "vwx": "application/vnd.vectorworks",
-  "vxml": "application/voicexml+xml",
-  "w3d": "application/x-director",
-  "w60": "application/wordperfect6.0",
-  "w61": "application/wordperfect6.1",
-  "w6w": "application/msword",
-  "wad": "application/x-doom",
-  "wadl": "application/vnd.sun.wadl+xml",
-  "war": "binary/zip",
-  "wasm": "application/wasm",
-  "wav": "audio/wave",
-  "wax": "audio/x-ms-wax",
-  "wb1": "application/x-qpro",
-  "wbmp": "image/vnd.wap.wbmp",
-  "wbs": "application/vnd.criticaltools.wbs+xml",
+  "vis": "application/vnd.visionary",
+  "vsf": "application/vnd.vsf",
   "wbxml": "application/vnd.wap.wbxml",
-  "wcm": "application/vnd.ms-works",
-  "wdb": "application/vnd.ms-works",
-  "wdp": "image/vnd.ms-photo",
-  "web": "application/vnd.xara",
-  "weba": "audio/webm",
-  "webapp": "application/x-web-app-manifest+json",
-  "webm": "video/webm",
-  "webmanifest": "application/manifest+json",
-  "webp": "image/webp",
-  "wg": "application/vnd.pmi.widget",
-  "wgt": "application/widget",
-  "whl": "binary/wheel",
-  "wif": "application/watcherinfo+xml",
-  "win": "model/vnd.gdl",
-  "wiz": "application/msword",
-  "wk": "application/x-123",
-  "wk1": "application/vnd.lotus-1-2-3",
-  "wk3": "application/vnd.lotus-1-2-3",
-  "wk4": "application/vnd.lotus-1-2-3",
-  "wks": "application/vnd.ms-works",
-  "wkt": "text/wkt",
-  "wlnk": "application/link-format",
-  "wm": "video/x-ms-wm",
-  "wma": "audio/x-ms-wma",
-  "wmc": "application/vnd.wmc",
-  "wmd": "application/x-ms-wmd",
-  "wmf": "image/wmf",
-  "wml": "text/vnd.wap.wml",
   "wmlc": "application/vnd.wap.wmlc",
-  "wmls": "text/vnd.wap.wmlscript",
   "wmlsc": "application/vnd.wap.wmlscriptc",
-  "wmv": "video/x-ms-wmv",
-  "wmx": "video/x-ms-wmx",
-  "wmz": "application/x-ms-wmz",
-  "woff": "font/woff",
-  "woff2": "font/woff2",
-  "word": "application/msword",
-  "wp": "application/wordperfect",
-  "wp5": "application/wordperfect",
-  "wp6": "application/wordperfect",
+  "wtb": "application/vnd.webturbo",
+  "nbp": "application/vnd.wolfram.player",
   "wpd": "application/vnd.wordperfect",
-  "wpl": "application/vnd.ms-wpl",
-  "wps": "application/vnd.ms-works",
-  "wq1": "application/x-lotus",
   "wqd": "application/vnd.wqd",
-  "wri": "application/x-mswrite",
-  "wrl": "model/vrml",
-  "wrz": "model/vrml",
-  "wsc": "message/vnd.wfa.wsc",
+  "stf": "application/vnd.wt.stf",
+  "xar": "application/vnd.xara",
+  "xfdl": "application/vnd.xfdl",
+  "hvd": "application/vnd.yamaha.hv-dic",
+  "hvs": "application/vnd.yamaha.hv-script",
+  "hvp": "application/vnd.yamaha.hv-voice",
+  "osf": "application/vnd.yamaha.openscoreformat",
+  "osfpvg": "application/vnd.yamaha.openscoreformat.osfpvg+xml",
+  "saf": "application/vnd.yamaha.smaf-audio",
+  "spf": "application/vnd.yamaha.smaf-phrase",
+  "cmp": "application/vnd.yellowriver-custom-menu",
+  "zir": "application/vnd.zul",
+  "zirz": "application/vnd.zul",
+  "zaz": "application/vnd.zzazz.deck+xml",
+  "vxml": "application/voicexml+xml",
+  "wasm": "application/wasm",
+  "wgt": "application/widget",
+  "hlp": "application/winhlp",
   "wsdl": "application/wsdl+xml",
-  "wsgi": "text/wsgi",
   "wspolicy": "application/wspolicy+xml",
-  "wsrc": "application/x-wais-source",
-  "wtb": "application/vnd.webturbo",
-  "wtk": "application/x-wintalk",
-  "wv": "application/vnd.wv.csp+wbxml",
-  "wvx": "video/x-ms-wvx",
-  "wz": "application/x-wingz",
-  "x-png": "image/png",
+  "7z": "application/x-7z-compressed",
+  "abw": "application/x-abiword",
+  "ace": "application/x-ace-compressed",
+  "dmg": "application/x-apple-diskimage",
+  "aab": "application/x-authorware-bin",
   "x32": "application/x-authorware-bin",
-  "x3d": "application/vnd.hzn-3d-crossword",
-  "x3db": "model/x3d+xml",
-  "x3dbz": "model/x3d+binary",
-  "x3dv": "model/x3d-vrml",
-  "x3dvz": "model/x3d-vrml",
-  "x3dz": "model/x3d+xml",
-  "x_b": "model/vnd.parasolid.transmit.binary",
-  "x_t": "model/vnd.parasolid.transmit.text",
-  "xaf": "x-world/x-vrml",
-  "xaml": "application/xaml+xml",
-  "xap": "application/x-silverlight-app",
-  "xar": "application/vnd.xara",
-  "xav": "application/xcap-att+xml",
+  "u32": "application/x-authorware-bin",
+  "vox": "application/x-authorware-bin",
+  "aam": "application/x-authorware-map",
+  "aas": "application/x-authorware-seg",
+  "bcpio": "application/x-bcpio",
+  "torrent": "application/x-bittorrent",
+  "blb": "application/x-blorb",
+  "blorb": "application/x-blorb",
+  "bz": "application/x-bzip",
+  "bz2": "application/x-bzip2",
+  "boz": "application/x-bzip2",
+  "cbr": "application/x-cbr",
+  "cba": "application/x-cbr",
+  "cbt": "application/x-cbr",
+  "cbz": "application/x-cbr",
+  "cb7": "application/x-cbr",
+  "vcd": "application/x-cdlink",
+  "cfs": "application/x-cfs-compressed",
+  "chat": "application/x-chat",
+  "pgn": "application/x-chess-pgn",
+  "nsc": "application/x-conference",
+  "cpio": "application/x-cpio",
+  "csh": "application/x-csh",
+  "deb": "application/x-debian-package",
+  "udeb": "application/x-debian-package",
+  "dgc": "application/x-dgc-compressed",
+  "dir": "application/x-director",
+  "dcr": "application/x-director",
+  "dxr": "application/x-director",
+  "cst": "application/x-director",
+  "cct": "application/x-director",
+  "cxt": "application/x-director",
+  "w3d": "application/x-director",
+  "fgd": "application/x-director",
+  "swa": "application/x-director",
+  "wad": "application/x-doom",
+  "ncx": "application/x-dtbncx+xml",
+  "dtb": "application/x-dtbook+xml",
+  "res": "application/x-dtbresource+xml",
+  "dvi": "application/x-dvi",
+  "evy": "application/x-envoy",
+  "eva": "application/x-eva",
+  "bdf": "application/x-font-bdf",
+  "gsf": "application/x-font-ghostscript",
+  "psf": "application/x-font-linux-psf",
+  "pcf": "application/x-font-pcf",
+  "snf": "application/x-font-snf",
+  "pfa": "application/x-font-type1",
+  "pfb": "application/x-font-type1",
+  "pfm": "application/x-font-type1",
+  "afm": "application/x-font-type1",
+  "arc": "application/x-freearc",
+  "spl": "application/x-futuresplash",
+  "gca": "application/x-gca-compressed",
+  "ulx": "application/x-glulx",
+  "gnumeric": "application/x-gnumeric",
+  "gramps": "application/x-gramps-xml",
+  "gtar": "application/x-gtar",
+  "hdf": "application/x-hdf",
+  "install": "application/x-install-instructions",
+  "iso": "application/x-iso9660-image",
+  "jnlp": "application/x-java-jnlp-file",
+  "latex": "application/x-latex",
+  "lzh": "application/x-lzh-compressed",
+  "lha": "application/x-lzh-compressed",
+  "mie": "application/x-mie",
+  "prc": "application/x-mobipocket-ebook",
+  "mobi": "application/x-mobipocket-ebook",
+  "application": "application/x-ms-application",
+  "lnk": "application/x-ms-shortcut",
+  "wmd": "application/x-ms-wmd",
+  "wmz": "application/x-ms-wmz",
   "xbap": "application/x-ms-xbap",
-  "xbd": "application/vnd.fujixerox.docuworks.binder",
-  "xbm": "image/x-xbitmap",
-  "xca": "application/xcap-caps+xml",
-  "xcf": "application/x-xcf",
-  "xcs": "application/calendar+xml",
-  "xct": "application/vnd.fujixerox.docuworks.container",
-  "xdd": "application/bacnet-xdd+zip",
-  "xdf": "application/xcap-diff+xml",
-  "xdm": "application/vnd.syncml.dm+xml",
-  "xdp": "application/vnd.adobe.xdp+xml",
-  "xdr": "video/x-amt-demorun",
-  "xdssc": "application/dssc+xml",
-  "xdw": "application/vnd.fujixerox.docuworks",
-  "xel": "application/xcap-el+xml",
-  "xenc": "application/xenc+xml",
-  "xer": "application/patch-ops-error+xml",
-  "xfd": "application/vnd.xfdl",
-  "xfdf": "application/vnd.adobe.xfdf",
-  "xfdl": "application/vnd.xfdl",
-  "xgz": "xgl/drawing",
-  "xht": "application/xhtml+xml",
-  "xhtm": "application/xhtml+xml",
-  "xhtml": "application/xhtml+xml",
-  "xhvml": "application/xv+xml",
-  "xif": "image/vnd.xiff",
-  "xl": "application/excel",
-  "xla": "application/vnd.ms-excel",
-  "xlam": "application/vnd.ms-excel.addin.macroenabled.12",
-  "xlb": "application/vndms-excel",
-  "xlc": "application/vnd.ms-excel",
+  "mdb": "application/x-msaccess",
+  "obd": "application/x-msbinder",
+  "crd": "application/x-mscardfile",
+  "clp": "application/x-msclip",
+  "exe": "application/x-msdownload",
+  "dll": "application/x-msdownload",
+  "com": "application/x-msdownload",
+  "bat": "application/x-msdownload",
+  "msi": "application/x-msdownload",
+  "mvb": "application/x-msmediaview",
+  "m13": "application/x-msmediaview",
+  "m14": "application/x-msmediaview",
+  "wmf": "application/x-msmetafile",
+  "wmz": "application/x-msmetafile",
+  "emf": "application/x-msmetafile",
+  "emz": "application/x-msmetafile",
+  "mny": "application/x-msmoney",
+  "pub": "application/x-mspublisher",
+  "scd": "application/x-msschedule",
+  "trm": "application/x-msterminal",
+  "wri": "application/x-mswrite",
+  "nc": "application/x-netcdf",
+  "cdf": "application/x-netcdf",
+  "nzb": "application/x-nzb",
+  "p12": "application/x-pkcs12",
+  "pfx": "application/x-pkcs12",
+  "p7b": "application/x-pkcs7-certificates",
+  "spc": "application/x-pkcs7-certificates",
+  "p7r": "application/x-pkcs7-certreqresp",
+  "rar": "application/x-rar-compressed",
+  "ris": "application/x-research-info-systems",
+  "sh": "application/x-sh",
+  "shar": "application/x-shar",
+  "swf": "application/x-shockwave-flash",
+  "xap": "application/x-silverlight-app",
+  "sql": "application/x-sql",
+  "sit": "application/x-stuffit",
+  "sitx": "application/x-stuffitx",
+  "srt": "application/x-subrip",
+  "sv4cpio": "application/x-sv4cpio",
+  "sv4crc": "application/x-sv4crc",
+  "t3": "application/x-t3vm-image",
+  "gam": "application/x-tads",
+  "tar": "application/x-tar",
+  "tcl": "application/x-tcl",
+  "tex": "application/x-tex",
+  "tfm": "application/x-tex-tfm",
+  "texinfo": "application/x-texinfo",
+  "texi": "application/x-texinfo",
+  "obj": "application/x-tgif",
+  "ustar": "application/x-ustar",
+  "src": "application/x-wais-source",
+  "der": "application/x-x509-ca-cert",
+  "crt": "application/x-x509-ca-cert",
+  "fig": "application/x-xfig",
   "xlf": "application/x-xliff+xml",
-  "xlim": "application/vnd.xmpie.xlim",
-  "xlm": "application/vnd.ms-excel",
-  "xls": "application/vnd.ms-excel",
-  "xlsb": "application/vnd.ms-excel.sheet.binary.macroenabled.12",
-  "xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
-  "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-  "xlt": "application/vnd.ms-excel",
-  "xltm": "application/vnd.ms-excel.template.macroenabled.12",
-  "xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
-  "xlw": "application/vnd.ms-excel",
-  "xm": "audio/xm",
-  "xml": "text/xml",
-  "xmls": "application/dskpp+xml",
-  "xmt_bin": "model/vnd.parasolid.transmit.binary",
-  "xmt_txt": "model/vnd.parasolid.transmit.text",
-  "xmz": "xgl/movie",
-  "xns": "application/xcap-ns+xml",
-  "xo": "application/vnd.olpc-sugar",
-  "xof": "x-world/x-vrml",
-  "xop": "application/xop+xml",
-  "xpdl": "application/xml",
   "xpi": "application/x-xpinstall",
-  "xpix": "application/x-vnd.ls-xpix",
-  "xpl": "application/xproc+xml",
-  "xpm": "image/x-xpixmap",
-  "xpr": "application/vnd.is-xpr",
-  "xps": "application/vnd.ms-xpsdocument",
-  "xpw": "application/vnd.intercon.formnet",
-  "xpx": "application/vnd.intercon.formnet",
-  "xq": "text/xquery",
-  "xql": "text/xquery",
-  "xqm": "text/xquery",
-  "xqu": "text/xquery",
-  "xquery": "text/xquery",
-  "xqy": "text/xquery",
-  "xsd": "text/xml",
-  "xsf": "application/prs.xsf+xml",
-  "xsl": "application/xslt+xml",
-  "xslt": "application/xslt+xml",
-  "xsm": "application/vnd.syncml+xml",
-  "xspf": "application/xspf+xml",
-  "xsr": "video/x-amt-showrun",
-  "xtel": "chemical/x-xtel",
-  "xul": "application/vnd.mozilla.xul+xml",
-  "xvm": "application/xv+xml",
-  "xvml": "application/xv+xml",
-  "xwd": "image/x-xwindowdump",
-  "xyz": "chemical/x-xyz",
-  "xyze": "image/vnd.radiance",
   "xz": "application/x-xz",
-  "yaml": "text/yaml",
-  "yang": "application/yang",
-  "yin": "application/yin+xml",
-  "yme": "application/vnd.yaoweme",
-  "yml": "text/yaml",
-  "ymp": "text/x-suse-ymp",
   "z1": "application/x-zmachine",
   "z2": "application/x-zmachine",
   "z3": "application/x-zmachine",
@@ -1888,19 +744,293 @@ const mimes* = {
   "z6": "application/x-zmachine",
   "z7": "application/x-zmachine",
   "z8": "application/x-zmachine",
-  "zaz": "application/vnd.zzazz.deck+xml",
-  "zfc": "application/vnd.filmit.zfc",
-  "zfo": "application/vnd.software602.filler.form-xml-zip",
-  "zig": "text/zig",
+  "xaml": "application/xaml+xml",
+  "xdf": "application/xcap-diff+xml",
+  "xenc": "application/xenc+xml",
+  "xhtml": "application/xhtml+xml",
+  "xht": "application/xhtml+xml",
+  "xml": "application/xml",
+  "xsl": "application/xml",
+  "dtd": "application/xml-dtd",
+  "xop": "application/xop+xml",
+  "xpl": "application/xproc+xml",
+  "xslt": "application/xslt+xml",
+  "xspf": "application/xspf+xml",
+  "mxml": "application/xv+xml",
+  "xhvml": "application/xv+xml",
+  "xvml": "application/xv+xml",
+  "xvm": "application/xv+xml",
+  "yang": "application/yang",
+  "yin": "application/yin+xml",
   "zip": "application/zip",
-  "zir": "application/vnd.zul",
-  "zirz": "application/vnd.zul",
-  "zmm": "application/vnd.handheld-entertainment+xml",
-  "zmt": "chemical/x-mopac-input",
-  "zone": "text/dns",
-  "zoo": "application/octet-stream",
-  "zsh": "text/x-script.zsh",
-  "~": "application/x-trash"
+  "adp": "audio/adpcm",
+  "au": "audio/basic",
+  "snd": "audio/basic",
+  "mid": "audio/midi",
+  "midi": "audio/midi",
+  "kar": "audio/midi",
+  "rmi": "audio/midi",
+  "m4a": "audio/mp4",
+  "mp4a": "audio/mp4",
+  "mpga": "audio/mpeg",
+  "mp2": "audio/mpeg",
+  "mp2a": "audio/mpeg",
+  "mp3": "audio/mpeg",
+  "m2a": "audio/mpeg",
+  "m3a": "audio/mpeg",
+  "oga": "audio/ogg",
+  "ogg": "audio/ogg",
+  "spx": "audio/ogg",
+  "opus": "audio/ogg",
+  "s3m": "audio/s3m",
+  "sil": "audio/silk",
+  "uva": "audio/vnd.dece.audio",
+  "uvva": "audio/vnd.dece.audio",
+  "eol": "audio/vnd.digital-winds",
+  "dra": "audio/vnd.dra",
+  "dts": "audio/vnd.dts",
+  "dtshd": "audio/vnd.dts.hd",
+  "lvp": "audio/vnd.lucent.voice",
+  "pya": "audio/vnd.ms-playready.media.pya",
+  "ecelp4800": "audio/vnd.nuera.ecelp4800",
+  "ecelp7470": "audio/vnd.nuera.ecelp7470",
+  "ecelp9600": "audio/vnd.nuera.ecelp9600",
+  "rip": "audio/vnd.rip",
+  "weba": "audio/webm",
+  "aac": "audio/x-aac",
+  "aif": "audio/x-aiff",
+  "aiff": "audio/x-aiff",
+  "aifc": "audio/x-aiff",
+  "caf": "audio/x-caf",
+  "flac": "audio/x-flac",
+  "mka": "audio/x-matroska",
+  "m3u": "audio/x-mpegurl",
+  "wax": "audio/x-ms-wax",
+  "wma": "audio/x-ms-wma",
+  "ram": "audio/x-pn-realaudio",
+  "ra": "audio/x-pn-realaudio",
+  "rmp": "audio/x-pn-realaudio-plugin",
+  "wav": "audio/x-wav",
+  "xm": "audio/xm",
+  "cdx": "chemical/x-cdx",
+  "cif": "chemical/x-cif",
+  "cmdf": "chemical/x-cmdf",
+  "cml": "chemical/x-cml",
+  "csml": "chemical/x-csml",
+  "xyz": "chemical/x-xyz",
+  "ttc": "font/collection",
+  "otf": "font/otf",
+  "ttf": "font/ttf",
+  "woff": "font/woff",
+  "woff2": "font/woff2",
+  "bmp": "image/bmp",
+  "cgm": "image/cgm",
+  "g3": "image/g3fax",
+  "gif": "image/gif",
+  "ief": "image/ief",
+  "jpeg": "image/jpeg",
+  "jpg": "image/jpeg",
+  "jpe": "image/jpeg",
+  "ktx": "image/ktx",
+  "png": "image/png",
+  "btif": "image/prs.btif",
+  "sgi": "image/sgi",
+  "svg": "image/svg+xml",
+  "svgz": "image/svg+xml",
+  "tiff": "image/tiff",
+  "tif": "image/tiff",
+  "psd": "image/vnd.adobe.photoshop",
+  "uvi": "image/vnd.dece.graphic",
+  "uvvi": "image/vnd.dece.graphic",
+  "uvg": "image/vnd.dece.graphic",
+  "uvvg": "image/vnd.dece.graphic",
+  "djvu": "image/vnd.djvu",
+  "djv": "image/vnd.djvu",
+  "sub": "image/vnd.dvb.subtitle",
+  "dwg": "image/vnd.dwg",
+  "dxf": "image/vnd.dxf",
+  "fbs": "image/vnd.fastbidsheet",
+  "fpx": "image/vnd.fpx",
+  "fst": "image/vnd.fst",
+  "mmr": "image/vnd.fujixerox.edmics-mmr",
+  "rlc": "image/vnd.fujixerox.edmics-rlc",
+  "mdi": "image/vnd.ms-modi",
+  "wdp": "image/vnd.ms-photo",
+  "npx": "image/vnd.net-fpx",
+  "wbmp": "image/vnd.wap.wbmp",
+  "xif": "image/vnd.xiff",
+  "webp": "image/webp",
+  "3ds": "image/x-3ds",
+  "ras": "image/x-cmu-raster",
+  "cmx": "image/x-cmx",
+  "fh": "image/x-freehand",
+  "fhc": "image/x-freehand",
+  "fh4": "image/x-freehand",
+  "fh5": "image/x-freehand",
+  "fh7": "image/x-freehand",
+  "ico": "image/x-icon",
+  "sid": "image/x-mrsid-image",
+  "pcx": "image/x-pcx",
+  "pic": "image/x-pict",
+  "pct": "image/x-pict",
+  "pnm": "image/x-portable-anymap",
+  "pbm": "image/x-portable-bitmap",
+  "pgm": "image/x-portable-graymap",
+  "ppm": "image/x-portable-pixmap",
+  "rgb": "image/x-rgb",
+  "tga": "image/x-tga",
+  "xbm": "image/x-xbitmap",
+  "xpm": "image/x-xpixmap",
+  "xwd": "image/x-xwindowdump",
+  "eml": "message/rfc822",
+  "mime": "message/rfc822",
+  "igs": "model/iges",
+  "iges": "model/iges",
+  "msh": "model/mesh",
+  "mesh": "model/mesh",
+  "silo": "model/mesh",
+  "dae": "model/vnd.collada+xml",
+  "dwf": "model/vnd.dwf",
+  "gdl": "model/vnd.gdl",
+  "gtw": "model/vnd.gtw",
+  "mts": "model/vnd.mts",
+  "vtu": "model/vnd.vtu",
+  "wrl": "model/vrml",
+  "vrml": "model/vrml",
+  "x3db": "model/x3d+binary",
+  "x3dbz": "model/x3d+binary",
+  "x3dv": "model/x3d+vrml",
+  "x3dvz": "model/x3d+vrml",
+  "x3d": "model/x3d+xml",
+  "x3dz": "model/x3d+xml",
+  "appcache": "text/cache-manifest",
+  "ics": "text/calendar",
+  "ifb": "text/calendar",
+  "css": "text/css",
+  "csv": "text/csv",
+  "html": "text/html",
+  "htm": "text/html",
+  "js": "text/javascript",
+  "mjs": "text/javascript",
+  "n3": "text/n3",
+  "txt": "text/plain",
+  "text": "text/plain",
+  "conf": "text/plain",
+  "def": "text/plain",
+  "list": "text/plain",
+  "log": "text/plain",
+  "in": "text/plain",
+  "dsc": "text/prs.lines.tag",
+  "rtx": "text/richtext",
+  "sgml": "text/sgml",
+  "sgm": "text/sgml",
+  "tsv": "text/tab-separated-values",
+  "t": "text/troff",
+  "tr": "text/troff",
+  "roff": "text/troff",
+  "man": "text/troff",
+  "me": "text/troff",
+  "ms": "text/troff",
+  "ttl": "text/turtle",
+  "uri": "text/uri-list",
+  "uris": "text/uri-list",
+  "urls": "text/uri-list",
+  "vcard": "text/vcard",
+  "curl": "text/vnd.curl",
+  "dcurl": "text/vnd.curl.dcurl",
+  "mcurl": "text/vnd.curl.mcurl",
+  "scurl": "text/vnd.curl.scurl",
+  "sub": "text/vnd.dvb.subtitle",
+  "fly": "text/vnd.fly",
+  "flx": "text/vnd.fmi.flexstor",
+  "gv": "text/vnd.graphviz",
+  "3dml": "text/vnd.in3d.3dml",
+  "spot": "text/vnd.in3d.spot",
+  "jad": "text/vnd.sun.j2me.app-descriptor",
+  "wml": "text/vnd.wap.wml",
+  "wmls": "text/vnd.wap.wmlscript",
+  "s": "text/x-asm",
+  "asm": "text/x-asm",
+  "c": "text/x-c",
+  "cc": "text/x-c",
+  "cxx": "text/x-c",
+  "cpp": "text/x-c",
+  "h": "text/x-c",
+  "hh": "text/x-c",
+  "dic": "text/x-c",
+  "f": "text/x-fortran",
+  "for": "text/x-fortran",
+  "f77": "text/x-fortran",
+  "f90": "text/x-fortran",
+  "java": "text/x-java-source",
+  "nfo": "text/x-nfo",
+  "opml": "text/x-opml",
+  "p": "text/x-pascal",
+  "pas": "text/x-pascal",
+  "etx": "text/x-setext",
+  "sfv": "text/x-sfv",
+  "uu": "text/x-uuencode",
+  "vcs": "text/x-vcalendar",
+  "vcf": "text/x-vcard",
+  "3gp": "video/3gpp",
+  "3g2": "video/3gpp2",
+  "h261": "video/h261",
+  "h263": "video/h263",
+  "h264": "video/h264",
+  "jpgv": "video/jpeg",
+  "jpm": "video/jpm",
+  "jpgm": "video/jpm",
+  "mj2": "video/mj2",
+  "mjp2": "video/mj2",
+  "mp4": "video/mp4",
+  "mp4v": "video/mp4",
+  "mpg4": "video/mp4",
+  "mpeg": "video/mpeg",
+  "mpg": "video/mpeg",
+  "mpe": "video/mpeg",
+  "m1v": "video/mpeg",
+  "m2v": "video/mpeg",
+  "ogv": "video/ogg",
+  "qt": "video/quicktime",
+  "mov": "video/quicktime",
+  "uvh": "video/vnd.dece.hd",
+  "uvvh": "video/vnd.dece.hd",
+  "uvm": "video/vnd.dece.mobile",
+  "uvvm": "video/vnd.dece.mobile",
+  "uvp": "video/vnd.dece.pd",
+  "uvvp": "video/vnd.dece.pd",
+  "uvs": "video/vnd.dece.sd",
+  "uvvs": "video/vnd.dece.sd",
+  "uvv": "video/vnd.dece.video",
+  "uvvv": "video/vnd.dece.video",
+  "dvb": "video/vnd.dvb.file",
+  "fvt": "video/vnd.fvt",
+  "mxu": "video/vnd.mpegurl",
+  "m4u": "video/vnd.mpegurl",
+  "pyv": "video/vnd.ms-playready.media.pyv",
+  "uvu": "video/vnd.uvvu.mp4",
+  "uvvu": "video/vnd.uvvu.mp4",
+  "viv": "video/vnd.vivo",
+  "webm": "video/webm",
+  "f4v": "video/x-f4v",
+  "fli": "video/x-fli",
+  "flv": "video/x-flv",
+  "m4v": "video/x-m4v",
+  "mkv": "video/x-matroska",
+  "mk3d": "video/x-matroska",
+  "mks": "video/x-matroska",
+  "mng": "video/x-mng",
+  "asf": "video/x-ms-asf",
+  "asx": "video/x-ms-asf",
+  "vob": "video/x-ms-vob",
+  "wm": "video/x-ms-wm",
+  "wmv": "video/x-ms-wmv",
+  "wmx": "video/x-ms-wmx",
+  "wvx": "video/x-ms-wvx",
+  "avi": "video/x-msvideo",
+  "movie": "video/x-sgi-movie",
+  "smv": "video/x-smv",
+  "ice": "x-conference/x-cooltalk",
 }
 
 
diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim
index dcba996a4..656c98a20 100644
--- a/lib/pure/nativesockets.nim
+++ b/lib/pure/nativesockets.nim
@@ -12,7 +12,7 @@
 
 # TODO: Clean up the exports a bit and everything else in general.
 
-import os, options
+import std/[os, options]
 import std/private/since
 import std/strbasics
 
@@ -23,15 +23,16 @@ when hostOS == "solaris":
   {.passl: "-lsocket -lnsl".}
 
 const useWinVersion = defined(windows) or defined(nimdoc)
-const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 
 when useWinVersion:
-  import winlean
+  import std/winlean
   export WSAEWOULDBLOCK, WSAECONNRESET, WSAECONNABORTED, WSAENETRESET,
          WSANOTINITIALISED, WSAENOTSOCK, WSAEINPROGRESS, WSAEINTR,
          WSAEDISCON, ERROR_NETNAME_DELETED
 else:
-  import posix
+  import std/posix
   export fcntl, F_GETFL, O_NONBLOCK, F_SETFL, EAGAIN, EWOULDBLOCK, MSG_NOSIGNAL,
     EINTR, EINPROGRESS, ECONNRESET, EPIPE, ENETRESET, EBADF
   export Sockaddr_storage, Sockaddr_un, Sockaddr_un_path_length
@@ -66,7 +67,7 @@ type
                    ## some procedures, such as getaddrinfo)
     AF_UNIX = 1,   ## for local socket (using a file). Unsupported on Windows.
     AF_INET = 2,   ## for network protocol IPv4 or
-    AF_INET6 = when defined(macosx): 30 else: 23 ## for network protocol IPv6.
+    AF_INET6 = when defined(macosx): 30 elif defined(windows): 23 else: 10 ## for network protocol IPv6.
 
   SockType* = enum     ## second argument to `socket` proc
     SOCK_STREAM = 1,   ## reliable stream-oriented service or Stream Sockets
@@ -96,6 +97,8 @@ type
     length*: int
     addrList*: seq[string]
 
+const IPPROTO_NONE* = IPPROTO_IP ## Use this if your socket type requires a protocol value of zero (e.g. Unix sockets).
+
 when useWinVersion:
   let
     osInvalidSocket* = winlean.INVALID_SOCKET
@@ -169,7 +172,7 @@ when not useWinVersion:
 
 else:
   proc toInt(domain: Domain): cint =
-    result = toU32(ord(domain)).cint
+    result = cast[cint](uint32(ord(domain)))
 
   proc toKnownDomain*(family: cint): Option[Domain] =
     ## Converts the platform-dependent `cint` to the Domain or none(),
@@ -304,7 +307,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET,
   let socketPort = if sockType == SOCK_RAW: "" else: $port
   var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result)
   if gaiResult != 0'i32:
-    when useWinVersion or defined(freertos):
+    when useWinVersion or defined(freertos) or defined(nuttx):
       raiseOSError(osLastError())
     else:
       raiseOSError(osLastError(), $gai_strerror(gaiResult))
@@ -351,7 +354,7 @@ proc getSockDomain*(socket: SocketHandle): Domain =
   else:
     raise newException(IOError, "Unknown socket family in getSockDomain")
 
-when not useNimNetLite: 
+when not useNimNetLite:
   proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} =
     ## Searches the database from the beginning and finds the first entry for
     ## which the service name specified by `name` matches the s_name member
@@ -375,9 +378,9 @@ when not useNimNetLite:
     ##
     ## On posix this will search through the `/etc/services` file.
     when useWinVersion:
-      var s = winlean.getservbyport(ze(int16(port)).cint, proto)
+      var s = winlean.getservbyport(uint16(port).cint, proto)
     else:
-      var s = posix.getservbyport(ze(int16(port)).cint, proto)
+      var s = posix.getservbyport(uint16(port).cint, proto)
     if s == nil: raiseOSError(osLastError(), "Service not found.")
     result.name = $s.s_name
     result.aliases = cstringArrayToSeq(s.s_aliases)
@@ -386,21 +389,37 @@ when not useNimNetLite:
 
   proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} =
     ## This function will lookup the hostname of an IP Address.
-    var myaddr: InAddr
-    myaddr.s_addr = inet_addr(ip)
+    var
+      addrInfo = getAddrInfo(ip, Port(0), AF_UNSPEC)
+      myAddr: pointer
+      addrLen = 0
+      family = 0
+    
+    defer: freeAddrInfo(addrInfo)
+
+    if addrInfo.ai_addr.sa_family.cint == nativeAfInet:
+      family = nativeAfInet
+      myAddr = addr cast[ptr Sockaddr_in](addrInfo.ai_addr).sin_addr
+      addrLen = 4
+    elif addrInfo.ai_addr.sa_family.cint == nativeAfInet6:
+      family = nativeAfInet6
+      myAddr = addr cast[ptr Sockaddr_in6](addrInfo.ai_addr).sin6_addr
+      addrLen = 16
+    else:
+      raise newException(IOError, "Unknown socket family in `getHostByAddr()`")
 
     when useWinVersion:
-      var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint,
-                                    cint(AF_INET))
+      var s = winlean.gethostbyaddr(cast[ptr InAddr](myAddr), addrLen.cuint,
+                                    cint(family))
       if s == nil: raiseOSError(osLastError())
     else:
       var s =
         when defined(android4):
-          posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint,
-                              cint(posix.AF_INET))
+          posix.gethostbyaddr(cast[cstring](myAddr), addrLen.cint,
+                              cint(family))
         else:
-          posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen,
-                              cint(posix.AF_INET))
+          posix.gethostbyaddr(myAddr, addrLen.SockLen,
+                              cint(family))
       if s == nil:
         raiseOSError(osLastError(), $hstrerror(h_errno))
 
@@ -423,7 +442,20 @@ when not useNimNetLite:
         result.addrList.add($inet_ntoa(inaddrPtr[]))
         inc(i)
     else:
-      result.addrList = cstringArrayToSeq(s.h_addr_list)
+      let strAddrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int
+                       else: 46
+      var i = 0
+      while not isNil(s.h_addr_list[i]):
+        var ipStr = newString(strAddrLen)
+        if inet_ntop(nativeAfInet6, cast[pointer](s.h_addr_list[i]),
+                     cstring(ipStr), len(ipStr).int32) == nil:
+          raiseOSError(osLastError())
+        when not useWinVersion:
+          if posix.IN6_IS_ADDR_V4MAPPED(cast[ptr In6Addr](s.h_addr_list[i])) != 0:
+            ipStr.setSlice("::ffff:".len..<strAddrLen)
+        setLen(ipStr, len(cstring(ipStr)))
+        result.addrList.add(ipStr)
+        inc(i)
     result.length = int(s.h_length)
 
   proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} =
@@ -481,13 +513,13 @@ when not useNimNetLite:
       result = newString(addrLen)
       let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
       when not useWinVersion:
-        if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0],
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr result[0]),
                           result.len.int32) == nil:
           raiseOSError(osLastError())
         if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
           result.setSlice("::ffff:".len..<addrLen)
       else:
-        if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr result[0]),
                             result.len.int32) == nil:
           raiseOSError(osLastError())
       setLen(result, len(cstring(result)))
@@ -509,23 +541,23 @@ when not useNimNetLite:
     if sockAddr.sa_family.cint == nativeAfInet:
       let addr4 = addr cast[ptr Sockaddr_in](sockAddr).sin_addr
       when not useWinVersion:
-        if posix.inet_ntop(posix.AF_INET, addr4, addr strAddress[0],
+        if posix.inet_ntop(posix.AF_INET, addr4, cast[cstring](addr strAddress[0]),
                           strAddress.len.int32) == nil:
           raiseOSError(osLastError())
       else:
-        if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0],
+        if winlean.inet_ntop(winlean.AF_INET, addr4, cast[cstring](addr strAddress[0]),
                             strAddress.len.int32) == nil:
           raiseOSError(osLastError())
     elif sockAddr.sa_family.cint == nativeAfInet6:
       let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
       when not useWinVersion:
-        if posix.inet_ntop(posix.AF_INET6, addr6, addr strAddress[0],
+        if posix.inet_ntop(posix.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
                           strAddress.len.int32) == nil:
           raiseOSError(osLastError())
         if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
           strAddress.setSlice("::ffff:".len..<length)
       else:
-        if winlean.inet_ntop(winlean.AF_INET6, addr6, addr strAddress[0],
+        if winlean.inet_ntop(winlean.AF_INET6, addr6, cast[cstring](addr strAddress[0]),
                             strAddress.len.int32) == nil:
           raiseOSError(osLastError())
     else:
@@ -584,7 +616,7 @@ when not useNimNetLite:
       # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
       result[0] = newString(64)
       if inet_ntop(name.sin6_family.cint,
-          addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
         raiseOSError(osLastError())
       setLen(result[0], result[0].cstring.len)
       result[1] = Port(nativesockets.ntohs(name.sin6_port))
@@ -621,7 +653,7 @@ when not useNimNetLite:
       # Cannot use INET6_ADDRSTRLEN here, because it's a C define.
       result[0] = newString(64)
       if inet_ntop(name.sin6_family.cint,
-          addr name.sin6_addr, addr result[0][0], (result[0].len+1).int32).isNil:
+          addr name.sin6_addr, cast[cstring](addr result[0][0]), (result[0].len+1).int32).isNil:
         raiseOSError(osLastError())
       setLen(result[0], result[0].cstring.len)
       result[1] = Port(nativesockets.ntohs(name.sin6_port))
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 16390d9af..24c94b651 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -93,23 +93,24 @@ import std/private/since
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-import nativesockets
-import os, strutils, times, sets, options, std/monotimes
-import ssl_config
+import std/nativesockets
+import std/[os, strutils, times, sets, options, monotimes]
+import std/ssl_config
 export nativesockets.Port, nativesockets.`$`, nativesockets.`==`
-export Domain, SockType, Protocol
+export Domain, SockType, Protocol, IPPROTO_NONE
 
 const useWinVersion = defined(windows) or defined(nimdoc)
-const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr)
+const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or
+    defined(nuttx)
 const defineSsl = defined(ssl) or defined(nimdoc)
 
 when useWinVersion:
-  from winlean import WSAESHUTDOWN
+  from std/winlean import WSAESHUTDOWN
 
 when defineSsl:
-  import openssl
+  import std/openssl
   when not defined(nimDisableCertificateValidation):
-    from ssl_certs import scanSSLCertificates
+    from std/ssl_certs import scanSSLCertificates
 
 # Note: The enumerations are mapped to Window's constants.
 
@@ -208,7 +209,7 @@ when defined(nimHasStyleChecks):
 
 
 when defined(posix) and not defined(lwip):
-  from posix import TPollfd, POLLIN, POLLPRI, POLLOUT, POLLWRBAND, Tnfds
+  from std/posix import TPollfd, POLLIN, POLLPRI, POLLOUT, POLLWRBAND, Tnfds
 
   template monitorPollEvent(x: var SocketHandle, y: cint, timeout: int): int =
     var tpollfd: TPollfd
@@ -480,8 +481,8 @@ proc parseIPv6Address(addressStr: string): IpAddress =
 proc parseIpAddress*(addressStr: string): IpAddress =
   ## Parses an IP address
   ##
-  ## Raises ValueError on error. 
-  ## 
+  ## Raises ValueError on error.
+  ##
   ## For IPv4 addresses, only the strict form as
   ## defined in RFC 6943 is considered valid, see
   ## https://datatracker.ietf.org/doc/html/rfc6943#section-3.1.1.
@@ -621,10 +622,11 @@ when defineSsl:
 
   proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer,
                    certFile = "", keyFile = "", cipherList = CiphersIntermediate,
-                   caDir = "", caFile = ""): SslContext =
+                   caDir = "", caFile = "", ciphersuites = CiphersModern): SslContext =
     ## Creates an SSL context.
     ##
-    ## protVersion is currently unsed.
+    ## Protocol version is currently ignored by default and TLS is used.
+    ## With `-d:openssl10`, only SSLv23 and TLSv1 may be used.
     ##
     ## There are three options for verify mode:
     ## `CVerifyNone`: certificates are not verified;
@@ -651,7 +653,19 @@ when defineSsl:
     ## or using ECDSA:
     ## - `openssl ecparam -out mykey.pem -name secp256k1 -genkey`
     ## - `openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`
-    let mtd = TLS_method()
+    var mtd: PSSL_METHOD
+    when defined(openssl10):
+      case protVersion
+      of protSSLv23:
+        mtd = SSLv23_method()
+      of protSSLv2:
+        raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23")
+      of protSSLv3:
+        raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23")
+      of protTLSv1:
+        mtd = TLSv1_method()
+    else:
+      mtd = TLS_method()
     if mtd == nil:
       raiseSSLError("Failed to create TLS context")
     var newCTX = SSL_CTX_new(mtd)
@@ -662,16 +676,16 @@ when defineSsl:
       raiseSSLError()
     when not defined(openssl10) and not defined(libressl):
       let sslVersion = getOpenSSLVersion()
-      if sslVersion >= 0x010101000 and not sslVersion == 0x020000000:
+      if sslVersion >= 0x010101000 and sslVersion != 0x020000000:
         # In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via
         # this API.
-        if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1:
+        if newCTX.SSL_CTX_set_ciphersuites(ciphersuites) != 1:
           raiseSSLError()
     # Automatically the best ECDH curve for client exchange. Without this, ECDH
     # ciphers will be ignored by the server.
     #
     # From OpenSSL >= 1.1.0, this setting is set by default and can't be
-    # overriden.
+    # overridden.
     if newCTX.SSL_CTX_set_ecdh_auto(1) != 1:
       raiseSSLError()
 
@@ -1025,7 +1039,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {.
 proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string,
                  flags = {SocketFlag.SafeDisconn},
                  inheritable = defined(nimInheritHandles)) {.
-                 tags: [ReadIOEffect], gcsafe, locks: 0.} =
+                 tags: [ReadIOEffect], gcsafe.} =
   ## Blocks until a connection is being made from a client. When a connection
   ## is made sets `client` to the client socket and `address` to the address
   ## of the connecting client.
@@ -1140,7 +1154,7 @@ proc accept*(server: Socket, client: var owned(Socket),
   acceptAddr(server, client, addrDummy, flags)
 
 when defined(posix) and not defined(lwip):
-  from posix import Sigset, sigwait, sigismember, sigemptyset, sigaddset,
+  from std/posix import Sigset, sigwait, sigismember, sigemptyset, sigaddset,
     sigprocmask, pthread_sigmask, SIGPIPE, SIG_BLOCK, SIG_UNBLOCK
 
 template blockSigpipe(body: untyped): untyped =
@@ -1254,9 +1268,9 @@ proc close*(socket: Socket, flags = {SocketFlag.SafeDisconn}) =
     socket.fd = osInvalidSocket
 
 when defined(posix):
-  from posix import TCP_NODELAY
+  from std/posix import TCP_NODELAY
 else:
-  from winlean import TCP_NODELAY
+  from std/winlean import TCP_NODELAY
 
 proc toCInt*(opt: SOBool): cint =
   ## Converts a `SOBool` into its Socket Option cint representation.
@@ -1307,7 +1321,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite):
     when not defined(nimdoc):
       var socketAddr = makeUnixAddr(path)
       if socket.fd.connect(cast[ptr SockAddr](addr socketAddr),
-          (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
         raiseOSError(osLastError())
 
   proc bindUnix*(socket: Socket, path: string) =
@@ -1316,7 +1330,7 @@ when defined(nimdoc) or (defined(posix) and not useNimNetLite):
     when not defined(nimdoc):
       var socketAddr = makeUnixAddr(path)
       if socket.fd.bindAddr(cast[ptr SockAddr](addr socketAddr),
-          (sizeof(socketAddr.sun_family) + path.len).SockLen) != 0'i32:
+          (offsetOf(socketAddr, sun_path) + path.len + 1).SockLen) != 0'i32:
         raiseOSError(osLastError())
 
 when defineSsl:
@@ -1722,15 +1736,36 @@ proc send*(socket: Socket, data: pointer, size: int): int {.
     result = send(socket.fd, data, size, int32(MSG_NOSIGNAL))
 
 proc send*(socket: Socket, data: string,
-           flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} =
-  ## sends data to a socket.
-  let sent = send(socket, cstring(data), data.len)
-  if sent < 0:
-    let lastError = osLastError()
-    socketError(socket, lastError = lastError, flags = flags)
+           flags = {SocketFlag.SafeDisconn}, maxRetries = 100) {.tags: [WriteIOEffect].} =
+  ## Sends data to a socket. Will try to send all the data by handling interrupts
+  ## and incomplete writes up to `maxRetries`.
+  var written = 0
+  var attempts = 0
+  while data.len - written > 0:
+    let sent = send(socket, cstring(data), data.len)
+
+    if sent < 0:
+      let lastError = osLastError()
+      let isBlockingErr =
+        when defined(nimdoc):
+          false
+        elif useWinVersion:
+          lastError.int32 == WSAEINTR or
+          lastError.int32 == WSAEWOULDBLOCK
+        else:
+          lastError.int32 == EINTR or
+          lastError.int32 == EWOULDBLOCK or
+          lastError.int32 == EAGAIN
 
-  if sent != data.len:
-    raiseOSError(osLastError(), "Could not send all data.")
+      if not isBlockingErr:
+        let lastError = osLastError()
+        socketError(socket, lastError = lastError, flags = flags)
+      else:
+        attempts.inc()
+        if attempts > maxRetries:
+          raiseOSError(osLastError(), "Could not send all data.")
+    else:
+      written.inc(sent)
 
 template `&=`*(socket: Socket; data: typed) =
   ## an alias for 'send'.
@@ -1781,7 +1816,7 @@ proc sendTo*(socket: Socket, address: string, port: Port,
   ## This proc sends `data` to the specified `address`,
   ## which may be an IP address or a hostname, if a hostname is specified
   ## this function will try each IP of that hostname.
-  ## 
+  ##
   ## Generally for use with connection-less (UDP) sockets.
   ##
   ## If an error occurs an OSError exception will be raised.
@@ -1793,9 +1828,9 @@ proc sendTo*(socket: Socket, address: IpAddress, port: Port,
              data: string, flags = 0'i32): int {.
               discardable, tags: [WriteIOEffect].} =
   ## This proc sends `data` to the specified `IpAddress` and returns
-  ## the number of bytes written. 
+  ## the number of bytes written.
   ##
-  ## Generally for use with connection-less (UDP) sockets. 
+  ## Generally for use with connection-less (UDP) sockets.
   ##
   ## If an error occurs an OSError exception will be raised.
   ##
@@ -1885,14 +1920,18 @@ proc `==`*(lhs, rhs: IpAddress): bool =
 
 proc `$`*(address: IpAddress): string =
   ## Converts an IpAddress into the textual representation
-  result = ""
   case address.family
   of IpAddressFamily.IPv4:
-    for i in 0 .. 3:
-      if i != 0:
-        result.add('.')
-      result.add($address.address_v4[i])
+    result = newStringOfCap(15)
+    result.addInt address.address_v4[0]
+    result.add '.'
+    result.addInt address.address_v4[1]
+    result.add '.'
+    result.addInt address.address_v4[2]
+    result.add '.'
+    result.addInt address.address_v4[3]
   of IpAddressFamily.IPv6:
+    result = newStringOfCap(39)
     var
       currentZeroStart = -1
       currentZeroCount = 0
@@ -2001,8 +2040,10 @@ proc dial*(address: string, port: Port,
   if success:
     result = newSocket(lastFd, domain, sockType, protocol, buffered)
   elif lastError != 0.OSErrorCode:
+    lastFd.close()
     raiseOSError(lastError)
   else:
+    lastFd.close()
     raise newException(IOError, "Couldn't resolve address: " & address)
 
 proc connect*(socket: Socket, address: string,
diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim
index 3b1f703e3..bf8367d1d 100644
--- a/lib/pure/nimprof.nim
+++ b/lib/pure/nimprof.nim
@@ -15,13 +15,15 @@
 when not defined(profiler) and not defined(memProfiler):
   {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".}
 
-when defined(nimHasUsed):
-  {.used.}
+{.used.}
 
 # We don't want to profile the profiling code ...
 {.push profiler: off.}
 
-import hashes, algorithm, strutils, tables, sets
+import std/[hashes, algorithm, strutils, tables, sets]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, sysatomics]
 
 when not defined(memProfiler):
   include "system/timers"
@@ -67,7 +69,7 @@ when not defined(memProfiler):
     else: interval = intervalInUs * 1000 - tickCountCorrection
 
 when withThreads:
-  import locks
+  import std/locks
   var
     profilingLock: Lock
 
@@ -122,13 +124,13 @@ when defined(memProfiler):
   var
     gTicker {.threadvar.}: int
 
-  proc requestedHook(): bool {.nimcall, locks: 0.} =
+  proc requestedHook(): bool {.nimcall.} =
     if gTicker == 0:
       gTicker = SamplingInterval
       result = true
     dec gTicker
 
-  proc hook(st: StackTrace, size: int) {.nimcall, locks: 0.} =
+  proc hook(st: StackTrace, size: int) {.nimcall.} =
     when defined(ignoreAllocationSize):
       hookAux(st, 1)
     else:
@@ -140,7 +142,7 @@ else:
     gTicker: int # we use an additional counter to
                  # avoid calling 'getTicks' too frequently
 
-  proc requestedHook(): bool {.nimcall, locks: 0.} =
+  proc requestedHook(): bool {.nimcall.} =
     if interval == 0: result = true
     elif gTicker == 0:
       gTicker = 500
@@ -149,7 +151,7 @@ else:
     else:
       dec gTicker
 
-  proc hook(st: StackTrace) {.nimcall, locks: 0.} =
+  proc hook(st: StackTrace) {.nimcall.} =
     #echo "profiling! ", interval
     if interval == 0:
       hookAux(st, 1)
diff --git a/lib/pure/nimtracker.nim b/lib/pure/nimtracker.nim
deleted file mode 100644
index a66dfc2ea..000000000
--- a/lib/pure/nimtracker.nim
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2016 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Memory tracking support for Nim.
-
-when not defined(memTracker) and not isMainModule:
-  {.error: "Memory tracking support is turned off!".}
-
-{.push memtracker: off.}
-# we import the low level wrapper and are careful not to use Nim's
-# memory manager for anything here.
-import sqlite3
-
-var
-  dbHandle: PSqlite3
-  insertStmt {.threadvar.}: Pstmt
-
-const insertQuery = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
-
-template sbind(x: int; value) =
-  when value is cstring:
-    let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT)
-    if ret != SQLITE_OK:
-      quit "could not bind value"
-  else:
-    let ret = insertStmt.bindInt64(x, value)
-    if ret != SQLITE_OK:
-      quit "could not bind value"
-
-when defined(memTracker):
-  proc logEntries(log: TrackLog) {.nimcall, locks: 0, tags: [], gcsafe.} =
-    if insertStmt.isNil:
-      if prepare_v2(dbHandle, insertQuery,
-          insertQuery.len, insertStmt, nil) != SQLITE_OK:
-        quit "could not bind query to insertStmt " & $sqlite3.errmsg(dbHandle)
-    for i in 0..log.count-1:
-      var success = false
-      let e = log.data[i]
-      discard sqlite3.reset(insertStmt)
-      discard clearBindings(insertStmt)
-      sbind 1, e.op
-      sbind(2, cast[int](e.address))
-      sbind 3, e.size
-      sbind 4, e.file
-      sbind 5, e.line
-      if step(insertStmt) == SQLITE_DONE:
-        success = true
-      if not success:
-        quit "could not write to database! " & $sqlite3.errmsg(dbHandle)
-
-proc execQuery(q: string) =
-  var s: Pstmt
-  if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK:
-    discard step(s)
-    if finalize(s) != SQLITE_OK:
-      quit "could not finalize " & $sqlite3.errmsg(dbHandle)
-  else:
-    quit "could not prepare statement " & $sqlite3.errmsg(dbHandle)
-
-proc setupDb() =
-  execQuery """create table if not exists tracking(
-       id integer primary key,
-       op varchar not null,
-       address integer not null,
-       size integer not null,
-       file varchar not null,
-       line integer not null
-     )"""
-  execQuery "delete from tracking"
-
-if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK:
-  setupDb()
-  const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
-  if prepare_v2(dbHandle, insertQuery,
-      insertQuery.len, insertStmt, nil) == SQLITE_OK:
-    when defined(memTracker):
-      setTrackLogger logEntries
-  else:
-    quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle)
-else:
-  quit "could not setup sqlite " & $sqlite3.errmsg(dbHandle)
-{.pop.}
diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim
index 43eadad27..4d6ceefd7 100644
--- a/lib/pure/oids.nim
+++ b/lib/pure/oids.nim
@@ -14,9 +14,12 @@
 ## This implementation calls `initRand()` for the first call of
 ## `genOid`.
 
-import hashes, times, endians, random
+import std/[hashes, times, endians, random]
 from std/private/decode_helpers import handleHexChar
 
+when defined(nimPreviewSlimSystem):
+  import std/sysatomics
+
 type
   Oid* = object ## An OID.
     time: int64
@@ -41,9 +44,9 @@ proc hexbyte*(hex: char): int {.inline.} =
 
 proc parseOid*(str: cstring): Oid =
   ## Parses an OID.
-  var bytes = cast[cstring](addr(result.time))
+  var bytes = cast[cstring](cast[pointer](cast[int](addr(result.time)) + 4))
   var i = 0
-  while i < 16:
+  while i < 12:
     bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
     inc(i)
 
@@ -51,12 +54,12 @@ proc `$`*(oid: Oid): string =
   ## Converts an OID to a string.
   const hex = "0123456789abcdef"
 
-  result.setLen 32
+  result.setLen 24
 
   var o = oid
-  var bytes = cast[cstring](addr(o))
+  var bytes = cast[cstring](cast[pointer](cast[int](addr(o)) + 4))
   var i = 0
-  while i < 16:
+  while i < 12:
     let b = bytes[i].ord
     result[2 * i] = hex[(b and 0xF0) shr 4]
     result[2 * i + 1] = hex[b and 0xF]
@@ -83,9 +86,9 @@ template genOid(result: var Oid, incr: var int, fuzz: int32) =
 proc genOid*(): Oid =
   ## Generates a new OID.
   runnableExamples:
-    doAssert ($genOid()).len == 32
+    doAssert ($genOid()).len == 24
   runnableExamples("-r:off"):
-    echo $genOid() # for example, "00000000632c452db08c3d19ee9073e5"
+    echo $genOid() # for example, "5fc7f546ddbbc84800006aaf"
   genOid(result, incr, fuzz)
 
 proc generatedTime*(oid: Oid): Time =
diff --git a/lib/pure/options.nim b/lib/pure/options.nim
index 9dc4e096b..b34ff72c0 100644
--- a/lib/pure/options.nim
+++ b/lib/pure/options.nim
@@ -53,7 +53,7 @@ Pattern matching
 supports pattern matching on `Option`s, with the `Some(<pattern>)` and
 `None()` patterns.
 
-.. code-block:: nim
+  ```nim
   {.experimental: "caseStmtMacros".}
 
   import fusion/matching
@@ -65,6 +65,7 @@ supports pattern matching on `Option`s, with the `Some(<pattern>)` and
     assert false
 
   assertMatch(some(some(none(int))), Some(Some(None())))
+  ```
 ]##
 # xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule`
 
@@ -73,7 +74,7 @@ when defined(nimHasEffectsOf):
 else:
   {.pragma: effectsOf.}
 
-import typetraits
+import std/typetraits
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -81,7 +82,7 @@ when defined(nimPreviewSlimSystem):
 
 when (NimMajor, NimMinor) >= (1, 1):
   type
-    SomePointer = ref | ptr | pointer | proc
+    SomePointer = ref | ptr | pointer | proc | iterator {.closure.}
 else:
   type
     SomePointer = ref | ptr | pointer
@@ -89,7 +90,7 @@ else:
 type
   Option*[T] = object
     ## An optional type that may or may not contain a value of type `T`.
-    ## When `T` is a a pointer type (`ptr`, `pointer`, `ref` or `proc`),
+    ## When `T` is a a pointer type (`ptr`, `pointer`, `ref`, `proc` or `iterator {.closure.}`),
     ## `none(T)` is represented as `nil`.
     when T is SomePointer:
       val: T
@@ -116,9 +117,10 @@ proc option*[T](val: sink T): Option[T] {.inline.} =
     assert option[Foo](nil).isNone
     assert option(42).isSome
 
-  result.val = val
-  when T isnot SomePointer:
-    result.has = true
+  when T is SomePointer:
+    result = Option[T](val: val)
+  else:
+    result = Option[T](has: true, val: val)
 
 proc some*[T](val: sink T): Option[T] {.inline.} =
   ## Returns an `Option` that has the value `val`.
@@ -135,10 +137,9 @@ proc some*[T](val: sink T): Option[T] {.inline.} =
 
   when T is SomePointer:
     assert not val.isNil
-    result.val = val
+    result = Option[T](val: val)
   else:
-    result.has = true
-    result.val = val
+    result = Option[T](has: true, val: val)
 
 proc none*(T: typedesc): Option[T] {.inline.} =
   ## Returns an `Option` for this type that has no value.
@@ -151,7 +152,7 @@ proc none*(T: typedesc): Option[T] {.inline.} =
     assert none(int).isNone
 
   # the default is the none type
-  discard
+  result = Option[T]()
 
 proc none*[T]: Option[T] {.inline.} =
   ## Alias for `none(T) <#none,typedesc>`_.
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 5c387ec03..78ebb1c88 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -8,8 +8,10 @@
 #
 
 ## This module contains basic operating system facilities like
-## retrieving environment variables, reading command line arguments,
-## working with directories, running shell commands, etc.
+## retrieving environment variables, working with directories,
+## running shell commands, etc.
+
+## .. importdoc:: symlinks.nim, appdirs.nim, dirs.nim, ospaths2.nim
 
 runnableExamples:
   let myFile = "/path/to/my/file.nim"
@@ -20,22 +22,40 @@ runnableExamples:
   assert myFile.changeFileExt("c") == "/path/to/my/file.c"
 
 ## **See also:**
+## * `paths <paths.html>`_ and `files <files.html>`_ modules for high-level file manipulation
 ## * `osproc module <osproc.html>`_ for process communication beyond
 ##   `execShellCmd proc`_
-## * `parseopt module <parseopt.html>`_ for command-line parser beyond
-##   `parseCmdLine proc`_
 ## * `uri module <uri.html>`_
 ## * `distros module <distros.html>`_
 ## * `dynlib module <dynlib.html>`_
 ## * `streams module <streams.html>`_
+import std/private/ospaths2
+export ospaths2
+
+import std/private/osfiles
+export osfiles
+
+import std/private/osdirs
+export osdirs
+
+import std/private/ossymlinks
+export ossymlinks
+
+import std/private/osappdirs
+export osappdirs
+
+import std/private/oscommon
 
 include system/inclrtl
 import std/private/since
 
-import strutils, pathnorm
+import std/cmdline
+export cmdline
+
+import std/[strutils, pathnorm]
 
 when defined(nimPreviewSlimSystem):
-  import std/[syncio, assertions]
+  import std/[syncio, assertions, widestrs]
 
 const weirdTarget = defined(nimscript) or defined(js)
 
@@ -56,9 +76,9 @@ since (1, 1):
 when weirdTarget:
   discard
 elif defined(windows):
-  import winlean, times
+  import std/[winlean, times]
 elif defined(posix):
-  import posix, times
+  import std/[posix, times]
 
   proc toTime(ts: Timespec): times.Time {.inline.} =
     result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
@@ -78,955 +98,17 @@ elif defined(js):
 else:
   {.pragma: noNimJs.}
 
-proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
-
-type
-  ReadEnvEffect* = object of ReadIOEffect   ## Effect that denotes a read
-                                            ## from an environment variable.
-  WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write
-                                            ## to an environment variable.
-
-  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
-                                            ## operation from the directory
-                                            ## structure.
-  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
-                                            ## operation to
-                                            ## the directory structure.
-
-  OSErrorCode* = distinct int32 ## Specifies an OS Error Code.
-
-include "includes/osseps"
-
-proc absolutePathInternal(path: string): string {.gcsafe.}
-
-proc normalizePathEnd(path: var string, trailingSep = false) =
-  ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
-  ## ``trailingSep``, and taking care of edge cases: it preservers whether
-  ## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
-  ## not `AltSep`. Trailing `/.` are compressed, see examples.
-  if path.len == 0: return
-  var i = path.len
-  while i >= 1:
-    if path[i-1] in {DirSep, AltSep}: dec(i)
-    elif path[i-1] == '.' and i >= 2 and path[i-2] in {DirSep, AltSep}: dec(i)
-    else: break
-  if trailingSep:
-    # foo// => foo
-    path.setLen(i)
-    # foo => foo/
-    path.add DirSep
-  elif i > 0:
-    # foo// => foo
-    path.setLen(i)
-  else:
-    # // => / (empty case was already taken care of)
-    path = $DirSep
-
-proc normalizePathEnd(path: string, trailingSep = false): string =
-  ## outplace overload
-  runnableExamples:
-    when defined(posix):
-      assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/"
-      assert normalizePathEnd("lib/./.", trailingSep = false) == "lib"
-      assert normalizePathEnd(".//./.", trailingSep = false) == "."
-      assert normalizePathEnd("", trailingSep = true) == "" # not / !
-      assert normalizePathEnd("/", trailingSep = false) == "/" # not "" !
-  result = path
-  result.normalizePathEnd(trailingSep)
-
-since((1, 1)):
-  export normalizePathEnd
-
-template endsWith(a: string, b: set[char]): bool =
-  a.len > 0 and a[^1] in b
-
-proc joinPathImpl(result: var string, state: var int, tail: string) =
-  let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep})
-  normalizePathEnd(result, trailingSep=false)
-  addNormalizePath(tail, result, state, DirSep)
-  normalizePathEnd(result, trailingSep=trailingSep)
-
-proc joinPath*(head, tail: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Joins two directory names to one.
-  ##
-  ## returns normalized path concatenation of `head` and `tail`, preserving
-  ## whether or not `tail` has a trailing slash (or, if tail if empty, whether
-  ## head has one).
-  ##
-  ## See also:
-  ## * `joinPath(parts: varargs[string]) proc`_
-  ## * `/ proc`_
-  ## * `splitPath proc`_
-  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
-  ## * `uri./ proc <uri.html#/,Uri,string>`_
-  runnableExamples:
-    when defined(posix):
-      assert joinPath("usr", "lib") == "usr/lib"
-      assert joinPath("usr", "lib/") == "usr/lib/"
-      assert joinPath("usr", "") == "usr"
-      assert joinPath("usr/", "") == "usr/"
-      assert joinPath("", "") == ""
-      assert joinPath("", "lib") == "lib"
-      assert joinPath("", "/lib") == "/lib"
-      assert joinPath("usr/", "/lib") == "usr/lib"
-      assert joinPath("usr/lib", "../bin") == "usr/bin"
-
-  result = newStringOfCap(head.len + tail.len)
-  var state = 0
-  joinPathImpl(result, state, head)
-  joinPathImpl(result, state, tail)
-  when false:
-    if len(head) == 0:
-      result = tail
-    elif head[len(head)-1] in {DirSep, AltSep}:
-      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
-        result = head & substr(tail, 1)
-      else:
-        result = head & tail
-    else:
-      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
-        result = head & tail
-      else:
-        result = head & DirSep & tail
-
-proc joinPath*(parts: varargs[string]): string {.noSideEffect,
-  rtl, extern: "nos$1OpenArray".} =
-  ## The same as `joinPath(head, tail) proc`_,
-  ## but works with any number of directory parts.
-  ##
-  ## You need to pass at least one element or the proc
-  ## will assert in debug builds and crash on release builds.
-  ##
-  ## See also:
-  ## * `joinPath(head, tail) proc`_
-  ## * `/ proc`_
-  ## * `/../ proc`_
-  ## * `splitPath proc`_
-  runnableExamples:
-    when defined(posix):
-      assert joinPath("a") == "a"
-      assert joinPath("a", "b", "c") == "a/b/c"
-      assert joinPath("usr/lib", "../../var", "log") == "var/log"
-
-  var estimatedLen = 0
-  for p in parts: estimatedLen += p.len
-  result = newStringOfCap(estimatedLen)
-  var state = 0
-  for i in 0..high(parts):
-    joinPathImpl(result, state, parts[i])
-
-proc `/`*(head, tail: string): string {.noSideEffect, inline.} =
-  ## The same as `joinPath(head, tail) proc`_.
-  ##
-  ## See also:
-  ## * `/../ proc`_
-  ## * `joinPath(head, tail) proc`_
-  ## * `joinPath(parts: varargs[string]) proc`_
-  ## * `splitPath proc`_
-  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
-  ## * `uri./ proc <uri.html#/,Uri,string>`_
-  runnableExamples:
-    when defined(posix):
-      assert "usr" / "" == "usr"
-      assert "" / "lib" == "lib"
-      assert "" / "/lib" == "/lib"
-      assert "usr/" / "/lib/" == "usr/lib/"
-      assert "usr" / "lib" / "../bin" == "usr/bin"
-
-  result = joinPath(head, tail)
-
-when doslikeFileSystem:
-  import std/private/ntpath
-
-proc splitPath*(path: string): tuple[head, tail: string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a directory into `(head, tail)` tuple, so that
-  ## ``head / tail == path`` (except for edge cases like "/usr").
-  ##
-  ## See also:
-  ## * `joinPath(head, tail) proc`_
-  ## * `joinPath(parts: varargs[string]) proc`_
-  ## * `/ proc`_
-  ## * `/../ proc`_
-  ## * `relativePath proc`_
-  runnableExamples:
-    assert splitPath("usr/local/bin") == ("usr/local", "bin")
-    assert splitPath("usr/local/bin/") == ("usr/local/bin", "")
-    assert splitPath("/bin/") == ("/bin", "")
-    when (NimMajor, NimMinor) <= (1, 0):
-      assert splitPath("/bin") == ("", "bin")
-    else:
-      assert splitPath("/bin") == ("/", "bin")
-    assert splitPath("bin") == ("", "bin")
-    assert splitPath("") == ("", "")
-
-  when doslikeFileSystem:
-    let (drive, splitpath) = splitDrive(path)
-    let stop = drive.len
-  else:
-    const stop = 0
-
-  var sepPos = -1
-  for i in countdown(len(path)-1, stop):
-    if path[i] in {DirSep, AltSep}:
-      sepPos = i
-      break
-  if sepPos >= 0:
-    result.head = substr(path, 0,
-      when (NimMajor, NimMinor) <= (1, 0):
-        sepPos-1
-      else:
-        if likely(sepPos >= 1): sepPos-1 else: 0
-    )
-    result.tail = substr(path, sepPos+1)
-  else:
-    when doslikeFileSystem:
-      result.head = drive
-      result.tail = splitpath
-    else:
-      result.head = ""
-      result.tail = path
-
-proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
-  ## Checks whether a given `path` is absolute.
-  ##
-  ## On Windows, network paths are considered absolute too.
-  runnableExamples:
-    assert not "".isAbsolute
-    assert not ".".isAbsolute
-    when defined(posix):
-      assert "/".isAbsolute
-      assert not "a/".isAbsolute
-      assert "/a/".isAbsolute
-
-  if len(path) == 0: return false
-
-  when doslikeFileSystem:
-    var len = len(path)
-    result = (path[0] in {'/', '\\'}) or
-              (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
-  elif defined(macos):
-    # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
-    result = path[0] != ':'
-  elif defined(RISCOS):
-    result = path[0] == '$'
-  elif defined(posix) or defined(js):
-    # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469
-    # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs
-    result = path[0] == '/'
-  else:
-    doAssert false # if ever hits here, adapt as needed
-
-when FileSystemCaseSensitive:
-  template `!=?`(a, b: char): bool = a != b
-else:
-  template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
-
-when doslikeFileSystem:
-  proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: [].} =
-    ## An absolute path from the root of the current drive (e.g. "\foo")
-    path.len > 0 and
-    (path[0] == AltSep or
-     (path[0] == DirSep and
-      (path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
-
-  proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} =
-    ## Return true if path1 and path2 have a same root.
-    ##
-    ## Detail of Windows path formats:
-    ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
-
-    assert(isAbsolute(path1))
-    assert(isAbsolute(path2))
-
-    if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
-      result = true
-    elif cmpIgnoreCase(splitDrive(path1).drive, splitDrive(path2).drive) == 0:
-      result = true
-    else:
-      result = false
-
-proc relativePath*(path, base: string, sep = DirSep): string {.
-  rtl, extern: "nos$1".} =
-  ## Converts `path` to a path relative to `base`.
-  ##
-  ## The `sep` (default: DirSep_) is used for the path normalizations,
-  ## this can be useful to ensure the relative path only contains `'/'`
-  ## so that it can be used for URL constructions.
-  ##
-  ## On Windows, if a root of `path` and a root of `base` are different,
-  ## returns `path` as is because it is impossible to make a relative path.
-  ## That means an absolute path can be returned.
-  ##
-  ## See also:
-  ## * `splitPath proc`_
-  ## * `parentDir proc`_
-  ## * `tailDir proc`_
-  runnableExamples:
-    assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
-    assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
-    when not doslikeFileSystem: # On Windows, UNC-paths start with `//`
-      assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
-    assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
-    assert relativePath("", "/users/moo", '/') == ""
-    assert relativePath("foo", ".", '/') == "foo"
-    assert relativePath("foo", "foo", '/') == "."
-
-  if path.len == 0: return ""
-  var base = if base == ".": "" else: base
-  var path = path
-  path.normalizePathAux
-  base.normalizePathAux
-  let a1 = isAbsolute(path)
-  let a2 = isAbsolute(base)
-  if a1 and not a2:
-    base = absolutePathInternal(base)
-  elif a2 and not a1:
-    path = absolutePathInternal(path)
-
-  when doslikeFileSystem:
-    if isAbsolute(path) and isAbsolute(base):
-      if not sameRoot(path, base):
-        return path
-
-  var f = default PathIter
-  var b = default PathIter
-  var ff = (0, -1)
-  var bb = (0, -1) # (int, int)
-  result = newStringOfCap(path.len)
-  # skip the common prefix:
-  while f.hasNext(path) and b.hasNext(base):
-    ff = next(f, path)
-    bb = next(b, base)
-    let diff = ff[1] - ff[0]
-    if diff != bb[1] - bb[0]: break
-    var same = true
-    for i in 0..diff:
-      if path[i + ff[0]] !=? base[i + bb[0]]:
-        same = false
-        break
-    if not same: break
-    ff = (0, -1)
-    bb = (0, -1)
-  #  for i in 0..diff:
-  #    result.add base[i + bb[0]]
-
-  # /foo/bar/xxx/ -- base
-  # /foo/bar/baz  -- path path
-  #   ../baz
-  # every directory that is in 'base', needs to add '..'
-  while true:
-    if bb[1] >= bb[0]:
-      if result.len > 0 and result[^1] != sep:
-        result.add sep
-      result.add ".."
-    if not b.hasNext(base): break
-    bb = b.next(base)
-
-  # add the rest of 'path':
-  while true:
-    if ff[1] >= ff[0]:
-      if result.len > 0 and result[^1] != sep:
-        result.add sep
-      for i in 0..ff[1] - ff[0]:
-        result.add path[i + ff[0]]
-    if not f.hasNext(path): break
-    ff = f.next(path)
-
-  when not defined(nimOldRelativePathBehavior):
-    if result.len == 0: result.add "."
-
-proc isRelativeTo*(path: string, base: string): bool {.since: (1, 1).} =
-  ## Returns true if `path` is relative to `base`.
-  runnableExamples:
-    doAssert isRelativeTo("./foo//bar", "foo")
-    doAssert isRelativeTo("foo/bar", ".")
-    doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim")
-    doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim")
-  let path = path.normalizePath
-  let base = base.normalizePath
-  let ret = relativePath(path, base)
-  result = path.len > 0 and not ret.startsWith ".."
-
-proc parentDirPos(path: string): int =
-  var q = 1
-  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
-  for i in countdown(len(path)-q, 0):
-    if path[i] in {DirSep, AltSep}: return i
-  result = -1
-
-proc parentDir*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Returns the parent directory of `path`.
-  ##
-  ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end
-  ## in a dir separator, but also takes care of path normalizations.
-  ## The remainder can be obtained with `lastPathPart(path) proc`_.
-  ##
-  ## See also:
-  ## * `relativePath proc`_
-  ## * `splitPath proc`_
-  ## * `tailDir proc`_
-  ## * `parentDirs iterator`_
-  runnableExamples:
-    assert parentDir("") == ""
-    when defined(posix):
-      assert parentDir("/usr/local/bin") == "/usr/local"
-      assert parentDir("foo/bar//") == "foo"
-      assert parentDir("//foo//bar//.") == "/foo"
-      assert parentDir("./foo") == "."
-      assert parentDir("/./foo//./") == "/"
-      assert parentDir("a//./") == "."
-      assert parentDir("a/b/c/..") == "a"
-  result = pathnorm.normalizePath(path)
-  when doslikeFileSystem:
-    let (drive, splitpath) = splitDrive(result)
-    result = splitpath
-  var sepPos = parentDirPos(result)
-  if sepPos >= 0:
-    result = substr(result, 0, sepPos)
-    normalizePathEnd(result)
-  elif result == ".." or result == "." or result.len == 0 or result[^1] in {DirSep, AltSep}:
-    # `.` => `..` and .. => `../..`(etc) would be a sensible alternative
-    # `/` => `/` (as done with splitFile) would be a sensible alternative
-    result = ""
-  else:
-    result = "."
-  when doslikeFileSystem:
-    if result.len == 0:
-      discard
-    elif drive.len > 0 and result.len == 1 and result[0] in {DirSep, AltSep}:
-      result = drive
-    else:
-      result = drive & result
-
-proc tailDir*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Returns the tail part of `path`.
-  ##
-  ## See also:
-  ## * `relativePath proc`_
-  ## * `splitPath proc`_
-  ## * `parentDir proc`_
-  runnableExamples:
-    assert tailDir("/bin") == "bin"
-    assert tailDir("bin") == ""
-    assert tailDir("bin/") == ""
-    assert tailDir("/usr/local/bin") == "usr/local/bin"
-    assert tailDir("//usr//local//bin//") == "usr//local//bin//"
-    assert tailDir("./usr/local/bin") == "usr/local/bin"
-    assert tailDir("usr/local/bin") == "local/bin"
-
-  var i = 0
-  when doslikeFileSystem:
-    let (drive, splitpath) = path.splitDrive
-    if drive != "":
-      return splitpath.strip(chars = {DirSep, AltSep}, trailing = false)
-  while i < len(path):
-    if path[i] in {DirSep, AltSep}:
-      while i < len(path) and path[i] in {DirSep, AltSep}: inc i
-      return substr(path, i)
-    inc i
-  result = ""
-
-proc isRootDir*(path: string): bool {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Checks whether a given `path` is a root directory.
-  runnableExamples:
-    assert isRootDir("")
-    assert isRootDir(".")
-    assert isRootDir("/")
-    assert isRootDir("a")
-    assert not isRootDir("/a")
-    assert not isRootDir("a/b/c")
-
-  when doslikeFileSystem:
-    if splitDrive(path).path == "":
-      return true
-  result = parentDirPos(path) < 0
-
-iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
-  ## Walks over all parent directories of a given `path`.
-  ##
-  ## If `fromRoot` is true (default: false), the traversal will start from
-  ## the file system root directory.
-  ## If `inclusive` is true (default), the original argument will be included
-  ## in the traversal.
-  ##
-  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
-  ## only the directories appearing in the relative path.
-  ##
-  ## See also:
-  ## * `parentDir proc`_
-  ##
-  runnableExamples:
-    let g = "a/b/c"
-
-    for p in g.parentDirs:
-      echo p
-      # a/b/c
-      # a/b
-      # a
-
-    for p in g.parentDirs(fromRoot=true):
-      echo p
-      # a/
-      # a/b/
-      # a/b/c
-
-    for p in g.parentDirs(inclusive=false):
-      echo p
-      # a/b
-      # a
-
-  if not fromRoot:
-    var current = path
-    if inclusive: yield path
-    while true:
-      if current.isRootDir: break
-      current = current.parentDir
-      yield current
-  else:
-    when doslikeFileSystem:
-      let start = path.splitDrive.drive.len
-    else:
-      const start = 0
-    for i in countup(start, path.len - 2): # ignore the last /
-      # deal with non-normalized paths such as /foo//bar//baz
-      if path[i] in {DirSep, AltSep} and
-          (i == 0 or path[i-1] notin {DirSep, AltSep}):
-        yield path.substr(0, i)
-
-    if inclusive: yield path
-
-proc `/../`*(head, tail: string): string {.noSideEffect.} =
-  ## The same as ``parentDir(head) / tail``, unless there is no parent
-  ## directory. Then ``head / tail`` is performed instead.
-  ##
-  ## See also:
-  ## * `/ proc`_
-  ## * `parentDir proc`_
-  runnableExamples:
-    when defined(posix):
-      assert "a/b/c" /../ "d/e" == "a/b/d/e"
-      assert "a" /../ "d/e" == "a/d/e"
-
-  when doslikeFileSystem:
-    let (drive, head) = splitDrive(head)
-  let sepPos = parentDirPos(head)
-  if sepPos >= 0:
-    result = substr(head, 0, sepPos-1) / tail
-  else:
-    result = head / tail
-  when doslikeFileSystem:
-    result = drive / result
-
-proc normExt(ext: string): string =
-  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
-  else: result = ExtSep & ext
-
-proc searchExtPos*(path: string): int =
-  ## Returns index of the `'.'` char in `path` if it signifies the beginning
-  ## of extension. Returns -1 otherwise.
-  ##
-  ## See also:
-  ## * `splitFile proc`_
-  ## * `extractFilename proc`_
-  ## * `lastPathPart proc`_
-  ## * `changeFileExt proc`_
-  ## * `addFileExt proc`_
-  runnableExamples:
-    assert searchExtPos("a/b/c") == -1
-    assert searchExtPos("c.nim") == 1
-    assert searchExtPos("a/b/c.nim") == 5
-    assert searchExtPos("a.b.c.nim") == 5
-
-  # BUGFIX: do not search until 0! .DS_Store is no file extension!
-  result = -1
-  for i in countdown(len(path)-1, 1):
-    if path[i] == ExtSep:
-      result = i
-      break
-    elif path[i] in {DirSep, AltSep}:
-      break # do not skip over path
-
-proc splitFile*(path: string): tuple[dir, name, ext: string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a filename into `(dir, name, extension)` tuple.
-  ##
-  ## `dir` does not end in DirSep_ unless it's `/`.
-  ## `extension` includes the leading dot.
-  ##
-  ## If `path` has no extension, `ext` is the empty string.
-  ## If `path` has no directory component, `dir` is the empty string.
-  ## If `path` has no filename component, `name` and `ext` are empty strings.
-  ##
-  ## See also:
-  ## * `searchExtPos proc`_
-  ## * `extractFilename proc`_
-  ## * `lastPathPart proc`_
-  ## * `changeFileExt proc`_
-  ## * `addFileExt proc`_
-  runnableExamples:
-    var (dir, name, ext) = splitFile("usr/local/nimc.html")
-    assert dir == "usr/local"
-    assert name == "nimc"
-    assert ext == ".html"
-    (dir, name, ext) = splitFile("/usr/local/os")
-    assert dir == "/usr/local"
-    assert name == "os"
-    assert ext == ""
-    (dir, name, ext) = splitFile("/usr/local/")
-    assert dir == "/usr/local"
-    assert name == ""
-    assert ext == ""
-    (dir, name, ext) = splitFile("/tmp.txt")
-    assert dir == "/"
-    assert name == "tmp"
-    assert ext == ".txt"
-
-  var namePos = 0
-  var dotPos = 0
-  when doslikeFileSystem:
-    let (drive, _) = splitDrive(path)
-    let stop = len(drive)
-    result.dir = drive
-  else:
-    const stop = 0
-  for i in countdown(len(path) - 1, stop):
-    if path[i] in {DirSep, AltSep} or i == 0:
-      if path[i] in {DirSep, AltSep}:
-        result.dir = substr(path, 0, if likely(i >= 1): i - 1 else: 0)
-        namePos = i + 1
-      if dotPos > i:
-        result.name = substr(path, namePos, dotPos - 1)
-        result.ext = substr(path, dotPos)
-      else:
-        result.name = substr(path, namePos)
-      break
-    elif path[i] == ExtSep and i > 0 and i < len(path) - 1 and
-         path[i - 1] notin {DirSep, AltSep} and
-         path[i + 1] != ExtSep and dotPos == 0:
-      dotPos = i
-
-proc extractFilename*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Extracts the filename of a given `path`.
-  ##
-  ## This is the same as ``name & ext`` from `splitFile(path) proc`_.
-  ##
-  ## See also:
-  ## * `searchExtPos proc`_
-  ## * `splitFile proc`_
-  ## * `lastPathPart proc`_
-  ## * `changeFileExt proc`_
-  ## * `addFileExt proc`_
-  runnableExamples:
-    assert extractFilename("foo/bar/") == ""
-    assert extractFilename("foo/bar") == "bar"
-    assert extractFilename("foo/bar.baz") == "bar.baz"
-
-  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
-    result = ""
-  else:
-    result = splitPath(path).tail
-
-proc lastPathPart*(path: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Like `extractFilename proc`_, but ignores
-  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
-  ##
-  ## See also:
-  ## * `searchExtPos proc`_
-  ## * `splitFile proc`_
-  ## * `extractFilename proc`_
-  ## * `changeFileExt proc`_
-  ## * `addFileExt proc`_
-  runnableExamples:
-    assert lastPathPart("foo/bar/") == "bar"
-    assert lastPathPart("foo/bar") == "bar"
-
-  let path = path.normalizePathEnd(trailingSep = false)
-  result = extractFilename(path)
-
-proc changeFileExt*(filename, ext: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Changes the file extension to `ext`.
-  ##
-  ## If the `filename` has no extension, `ext` will be added.
-  ## If `ext` == "" then any extension is removed.
-  ##
-  ## `Ext` should be given without the leading `'.'`, because some
-  ## filesystems may use a different character. (Although I know
-  ## of none such beast.)
-  ##
-  ## See also:
-  ## * `searchExtPos proc`_
-  ## * `splitFile proc`_
-  ## * `extractFilename proc`_
-  ## * `lastPathPart proc`_
-  ## * `addFileExt proc`_
-  runnableExamples:
-    assert changeFileExt("foo.bar", "baz") == "foo.baz"
-    assert changeFileExt("foo.bar", "") == "foo"
-    assert changeFileExt("foo", "baz") == "foo.baz"
-
-  var extPos = searchExtPos(filename)
-  if extPos < 0: result = filename & normExt(ext)
-  else: result = substr(filename, 0, extPos-1) & normExt(ext)
-
-proc addFileExt*(filename, ext: string): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Adds the file extension `ext` to `filename`, unless
-  ## `filename` already has an extension.
-  ##
-  ## `Ext` should be given without the leading `'.'`, because some
-  ## filesystems may use a different character.
-  ## (Although I know of none such beast.)
-  ##
-  ## See also:
-  ## * `searchExtPos proc`_
-  ## * `splitFile proc`_
-  ## * `extractFilename proc`_
-  ## * `lastPathPart proc`_
-  ## * `changeFileExt proc`_
-  runnableExamples:
-    assert addFileExt("foo.bar", "baz") == "foo.bar"
-    assert addFileExt("foo.bar", "") == "foo.bar"
-    assert addFileExt("foo", "baz") == "foo.baz"
-
-  var extPos = searchExtPos(filename)
-  if extPos < 0: result = filename & normExt(ext)
-  else: result = filename
-
-proc cmpPaths*(pathA, pathB: string): int {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Compares two paths.
-  ##
-  ## On a case-sensitive filesystem this is done
-  ## case-sensitively otherwise case-insensitively. Returns:
-  ##
-  ## | 0 if pathA == pathB
-  ## | < 0 if pathA < pathB
-  ## | > 0 if pathA > pathB
-  runnableExamples:
-    when defined(macosx):
-      assert cmpPaths("foo", "Foo") == 0
-    elif defined(posix):
-      assert cmpPaths("foo", "Foo") > 0
-
-  let a = normalizePath(pathA)
-  let b = normalizePath(pathB)
-  if FileSystemCaseSensitive:
-    result = cmp(a, b)
-  else:
-    when defined(nimscript):
-      result = cmpic(a, b)
-    elif defined(nimdoc): discard
-    else:
-      result = cmpIgnoreCase(a, b)
-
-proc unixToNativePath*(path: string, drive=""): string {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Converts an UNIX-like path to a native one.
-  ##
-  ## On an UNIX system this does nothing. Else it converts
-  ## `'/'`, `'.'`, `'..'` to the appropriate things.
-  ##
-  ## On systems with a concept of "drives", `drive` is used to determine
-  ## which drive label to use during absolute path conversion.
-  ## `drive` defaults to the drive of the current working directory, and is
-  ## ignored on systems that do not have a concept of "drives".
-  when defined(unix):
-    result = path
-  else:
-    if path.len == 0: return ""
-
-    var start: int
-    if path[0] == '/':
-      # an absolute path
-      when doslikeFileSystem:
-        if drive != "":
-          result = drive & ":" & DirSep
-        else:
-          result = $DirSep
-      elif defined(macos):
-        result = "" # must not start with ':'
-      else:
-        result = $DirSep
-      start = 1
-    elif path[0] == '.' and (path.len == 1 or path[1] == '/'):
-      # current directory
-      result = $CurDir
-      start = when doslikeFileSystem: 1 else: 2
-    else:
-      result = ""
-      start = 0
-
-    var i = start
-    while i < len(path): # ../../../ --> ::::
-      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)] == ':':
-            add result, ':'
-          else:
-            add result, ParDir
-        else:
-          add result, ParDir & DirSep
-        inc(i, 3)
-      elif path[i] == '/':
-        add result, DirSep
-        inc(i)
-      else:
-        add result, path[i]
-        inc(i)
-
-include "includes/oserr"
-include "includes/osenv"
 
-proc getHomeDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the home directory of the current user.
-  ##
-  ## This proc is wrapped by the `expandTilde proc`_
-  ## for the convenience of processing paths coming from user configuration files.
-  ##
-  ## See also:
-  ## * `getConfigDir proc`_
-  ## * `getTempDir proc`_
-  ## * `expandTilde proc`_
-  ## * `getCurrentDir proc`_
-  ## * `setCurrentDir proc`_
-  runnableExamples:
-    assert getHomeDir() == expandTilde("~")
+import std/oserrors
+export oserrors
+import std/envvars
+export envvars
 
-  when defined(windows): return getEnv("USERPROFILE") & "\\"
-  else: return getEnv("HOME") & "/"
+import std/private/osseps
+export osseps
 
-proc getConfigDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the config directory of the current user for applications.
-  ##
-  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
-  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
-  ## variable if it is set, otherwise it returns the default configuration
-  ## directory ("~/.config/").
-  ##
-  ## An OS-dependent trailing slash is always present at the end of the
-  ## returned string: `\\` on Windows and `/` on all other OSs.
-  ##
-  ## See also:
-  ## * `getHomeDir proc`_
-  ## * `getTempDir proc`_
-  ## * `expandTilde proc`_
-  ## * `getCurrentDir proc`_
-  ## * `setCurrentDir proc`_
-  when defined(windows):
-    result = getEnv("APPDATA")
-  else:
-    result = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config")
-  result.normalizePathEnd(trailingSep = true)
 
 
-proc getCacheDir*(): string =
-  ## Returns the cache directory of the current user for applications.
-  ##
-  ## This makes use of the following environment variables:
-  ##
-  ## * On Windows: `getEnv("LOCALAPPDATA")`
-  ##
-  ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")`
-  ##
-  ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")`
-  ##
-  ## **See also:**
-  ## * `getHomeDir proc`_
-  ## * `getTempDir proc`_
-  ## * `getConfigDir proc`_
-  # follows https://crates.io/crates/platform-dirs
-  when defined(windows):
-    result = getEnv("LOCALAPPDATA")
-  elif defined(osx):
-    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")
-  else:
-    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")
-  result.normalizePathEnd(false)
-
-proc getCacheDir*(app: string): string =
-  ## Returns the cache directory for an application `app`.
-  ##
-  ## * On Windows, this uses: `getCacheDir() / app / "cache"`
-  ## * On other platforms, this uses: `getCacheDir() / app`
-  when defined(windows):
-    getCacheDir() / app / "cache"
-  else:
-    getCacheDir() / app
-
-
-when defined(windows):
-  type DWORD = uint32
-
-  proc getTempPath(
-    nBufferLength: DWORD, lpBuffer: WideCString
-  ): DWORD {.stdcall, dynlib: "kernel32.dll", importc: "GetTempPathW".} =
-    ## Retrieves the path of the directory designated for temporary files.
-
-template getEnvImpl(result: var string, tempDirList: openArray[string]) =
-  for dir in tempDirList:
-    if existsEnv(dir):
-      result = getEnv(dir)
-      break
-
-template getTempDirImpl(result: var string) =
-  when defined(windows):
-    getEnvImpl(result, ["TMP", "TEMP", "USERPROFILE"])
-  else:
-    getEnvImpl(result, ["TMPDIR", "TEMP", "TMP", "TEMPDIR"])
-
-proc getTempDir*(): string {.rtl, extern: "nos$1",
-  tags: [ReadEnvEffect, ReadIOEffect].} =
-  ## Returns the temporary directory of the current user for applications to
-  ## save temporary files in.
-  ##
-  ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw).
-  ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order.
-  ## On all platforms, `/tmp` will be returned if the procs fails.
-  ##
-  ## You can override this implementation
-  ## by adding `-d:tempDir=mytempname` to your compiler invocation.
-  ##
-  ## **Note:** This proc does not check whether the returned path exists.
-  ##
-  ## See also:
-  ## * `getHomeDir proc`_
-  ## * `getConfigDir proc`_
-  ## * `expandTilde proc`_
-  ## * `getCurrentDir proc`_
-  ## * `setCurrentDir proc`_
-  const tempDirDefault = "/tmp"
-  when defined(tempDir):
-    const tempDir {.strdefine.}: string = tempDirDefault
-    result = tempDir
-  else:
-    when nimvm:
-      getTempDirImpl(result)
-    else:
-      when defined(windows):
-        let size = getTempPath(0, nil)
-        # If the function fails, the return value is zero.
-        if size > 0:
-          let buffer = newWideCString(size.int)
-          if getTempPath(size, buffer) > 0:
-            result = $buffer
-      elif defined(android): result = "/data/local/tmp"
-      else:
-        getTempDirImpl(result)
-    if result.len == 0:
-      result = tempDirDefault
-  normalizePathEnd(result, trailingSep=true)
-
 proc expandTilde*(path: string): string {.
   tags: [ReadEnvEffect, ReadIOEffect].} =
   ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
@@ -1130,106 +212,10 @@ when not weirdTarget:
     importc: "system", header: "<stdlib.h>".}
 
   when not defined(windows):
-    proc c_rename(oldname, newname: cstring): cint {.
-      importc: "rename", header: "<stdio.h>".}
-    proc c_strlen(a: cstring): cint {.
-      importc: "strlen", header: "<string.h>", noSideEffect.}
     proc c_free(p: pointer) {.
       importc: "free", header: "<stdlib.h>".}
 
 
-when defined(windows) and not weirdTarget:
-  when useWinUnicode:
-    template wrapUnary(varname, winApiProc, arg: untyped) =
-      var varname = winApiProc(newWideCString(arg))
-
-    template wrapBinary(varname, winApiProc, arg, arg2: untyped) =
-      var varname = winApiProc(newWideCString(arg), arg2)
-    proc findFirstFile(a: string, b: var WIN32_FIND_DATA): Handle =
-      result = findFirstFileW(newWideCString(a), b)
-    template findNextFile(a, b: untyped): untyped = findNextFileW(a, b)
-    template getCommandLine(): untyped = getCommandLineW()
-
-    template getFilename(f: untyped): untyped =
-      $cast[WideCString](addr(f.cFileName[0]))
-  else:
-    template findFirstFile(a, b: untyped): untyped = findFirstFileA(a, b)
-    template findNextFile(a, b: untyped): untyped = findNextFileA(a, b)
-    template getCommandLine(): untyped = getCommandLineA()
-
-    template getFilename(f: untyped): untyped = $cstring(addr f.cFileName)
-
-  proc skipFindData(f: WIN32_FIND_DATA): bool {.inline.} =
-    # Note - takes advantage of null delimiter in the cstring
-    const dot = ord('.')
-    result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
-             f.cFileName[1].int == dot and f.cFileName[2].int == 0)
-
-proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
-                                          tags: [ReadDirEffect], noNimJs.} =
-  ## Returns true if `filename` exists and is a regular file or symlink.
-  ##
-  ## Directories, device files, named pipes and sockets return false.
-  ##
-  ## See also:
-  ## * `dirExists proc`_
-  ## * `symlinkExists proc`_
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, filename)
-    else:
-      var a = getFileAttributesA(filename)
-    if a != -1'i32:
-      result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
-  else:
-    var res: Stat
-    return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
-
-proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
-                                     noNimJs.} =
-  ## Returns true if the directory `dir` exists. If `dir` is a file, false
-  ## is returned. Follows symlinks.
-  ##
-  ## See also:
-  ## * `fileExists proc`_
-  ## * `symlinkExists proc`_
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, dir)
-    else:
-      var a = getFileAttributesA(dir)
-    if a != -1'i32:
-      result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
-  else:
-    var res: Stat
-    result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
-
-proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
-                                          tags: [ReadDirEffect],
-                                          noWeirdTarget.} =
-  ## Returns true if the symlink `link` exists. Will return true
-  ## regardless of whether the link points to a directory or file.
-  ##
-  ## See also:
-  ## * `fileExists proc`_
-  ## * `dirExists proc`_
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(a, getFileAttributesW, link)
-    else:
-      var a = getFileAttributesA(link)
-    if a != -1'i32:
-      # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
-      # may also be needed.
-      result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
-  else:
-    var res: Stat
-    result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
-
-
-when not defined(windows):
-  const maxSymlinkLen = 1024
-
 const
   ExeExts* = ## Platform specific file extension for executables.
     ## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
@@ -1269,7 +255,7 @@ proc findExe*(exe: string, followSymlinks: bool = true;
     for ext in extensions:
       var x = addFileExt(x, ext)
       if fileExists(x):
-        when not defined(windows):
+        when not (defined(windows) or defined(nintendoswitch)):
           while followSymlinks: # doubles as if here
             if x.symlinkExists:
               var r = newString(maxSymlinkLen)
@@ -1368,356 +354,6 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} =
   else:
     result = getLastModificationTime(a) > getLastModificationTime(b)
 
-when not defined(nimscript):
-  proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
-    ## Returns the `current working directory`:idx: i.e. where the built
-    ## binary is run.
-    ##
-    ## So the path returned by this proc is determined at run time.
-    ##
-    ## See also:
-    ## * `getHomeDir proc`_
-    ## * `getConfigDir proc`_
-    ## * `getTempDir proc`_
-    ## * `setCurrentDir proc`_
-    ## * `currentSourcePath template <system.html#currentSourcePath.t>`_
-    ## * `getProjectPath proc <macros.html#getProjectPath>`_
-    when defined(nodejs):
-      var ret: cstring
-      {.emit: "`ret` = process.cwd();".}
-      return $ret
-    elif defined(js):
-      doAssert false, "use -d:nodejs to have `getCurrentDir` defined"
-    elif defined(windows):
-      var bufsize = MAX_PATH.int32
-      when useWinUnicode:
-        var res = newWideCString("", bufsize)
-        while true:
-          var L = getCurrentDirectoryW(bufsize, res)
-          if L == 0'i32:
-            raiseOSError(osLastError())
-          elif L > bufsize:
-            res = newWideCString("", L)
-            bufsize = L
-          else:
-            result = res$L
-            break
-      else:
-        result = newString(bufsize)
-        while true:
-          var L = getCurrentDirectoryA(bufsize, result)
-          if L == 0'i32:
-            raiseOSError(osLastError())
-          elif L > bufsize:
-            result = newString(L)
-            bufsize = L
-          else:
-            setLen(result, L)
-            break
-    else:
-      var bufsize = 1024 # should be enough
-      result = newString(bufsize)
-      while true:
-        if getcwd(result.cstring, bufsize) != nil:
-          setLen(result, c_strlen(result.cstring))
-          break
-        else:
-          let err = osLastError()
-          if err.int32 == ERANGE:
-            bufsize = bufsize shl 1
-            doAssert(bufsize >= 0)
-            result = newString(bufsize)
-          else:
-            raiseOSError(osLastError())
-
-proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
-  ## Sets the `current working directory`:idx:; `OSError`
-  ## is raised if `newDir` cannot been set.
-  ##
-  ## See also:
-  ## * `getHomeDir proc`_
-  ## * `getConfigDir proc`_
-  ## * `getTempDir proc`_
-  ## * `getCurrentDir proc`_
-  when defined(windows):
-    when useWinUnicode:
-      if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
-        raiseOSError(osLastError(), newDir)
-    else:
-      if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir)
-  else:
-    if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
-
-
-proc absolutePath*(path: string, root = getCurrentDir()): string =
-  ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
-  ## default: current directory).
-  ## If `path` is absolute, return it, ignoring `root`.
-  ##
-  ## See also:
-  ## * `normalizedPath proc`_
-  ## * `normalizePath proc`_
-  runnableExamples:
-    assert absolutePath("a") == getCurrentDir() / "a"
-
-  if isAbsolute(path): path
-  else:
-    if not root.isAbsolute:
-      raise newException(ValueError, "The specified root is not absolute: " & root)
-    joinPath(root, path)
-
-proc absolutePathInternal(path: string): string =
-  absolutePath(path, getCurrentDir())
-
-proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
-  ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`.
-  runnableExamples:
-    import std/sugar
-    when defined(posix):
-      doAssert "foo".dup(normalizeExe) == "./foo"
-      doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
-    doAssert "".dup(normalizeExe) == ""
-  when defined(posix):
-    if file.len > 0 and DirSep notin file and file != "." and file != "..":
-      file = "./" & file
-
-proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
-  ## Normalize a path.
-  ##
-  ## Consecutive directory separators are collapsed, including an initial double slash.
-  ##
-  ## On relative paths, double dot (`..`) sequences are collapsed if possible.
-  ## On absolute paths they are always collapsed.
-  ##
-  ## .. warning:: URL-encoded and Unicode attempts at directory traversal are not detected.
-  ##   Triple dot is not handled.
-  ##
-  ## See also:
-  ## * `absolutePath proc`_
-  ## * `normalizedPath proc`_ for outplace version
-  ## * `normalizeExe proc`_
-  runnableExamples:
-    when defined(posix):
-      var a = "a///b//..//c///d"
-      a.normalizePath()
-      assert a == "a/c/d"
-
-  path = pathnorm.normalizePath(path)
-  when false:
-    let isAbs = isAbsolute(path)
-    var stack: seq[string] = @[]
-    for p in split(path, {DirSep}):
-      case p
-      of "", ".":
-        continue
-      of "..":
-        if stack.len == 0:
-          if isAbs:
-            discard  # collapse all double dots on absoluta paths
-          else:
-            stack.add(p)
-        elif stack[^1] == "..":
-          stack.add(p)
-        else:
-          discard stack.pop()
-      else:
-        stack.add(p)
-
-    if isAbs:
-      path = DirSep & join(stack, $DirSep)
-    elif stack.len > 0:
-      path = join(stack, $DirSep)
-    else:
-      path = "."
-
-proc normalizePathAux(path: var string) = normalizePath(path)
-
-proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} =
-  ## Returns a normalized path for the current OS.
-  ##
-  ## See also:
-  ## * `absolutePath proc`_
-  ## * `normalizePath proc`_ for the in-place version
-  runnableExamples:
-    when defined(posix):
-      assert normalizedPath("a///b//..//c///d") == "a/c/d"
-  result = pathnorm.normalizePath(path)
-
-when defined(windows) and not weirdTarget:
-  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), access,
-        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
-        nil, OPEN_EXISTING, flags, 0
-        )
-    else:
-      result = createFileA(
-        path, access,
-        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
-        nil, OPEN_EXISTING, flags, 0
-        )
-
-proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect], noWeirdTarget.} =
-  ## Returns true if both pathname arguments refer to the same physical
-  ## file or directory.
-  ##
-  ## Raises `OSError` if any of the files does not
-  ## exist or information about it can not be obtained.
-  ##
-  ## This proc will return true if given two alternative hard-linked or
-  ## sym-linked paths to the same file or directory.
-  ##
-  ## See also:
-  ## * `sameFileContent proc`_
-  when defined(windows):
-    var success = true
-    var f1 = openHandle(path1)
-    var f2 = openHandle(path2)
-
-    var lastErr: OSErrorCode
-    if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
-      var fi1, fi2: BY_HANDLE_FILE_INFORMATION
-
-      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
-         getFileInformationByHandle(f2, addr(fi2)) != 0:
-        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
-                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
-                 fi1.nFileIndexLow == fi2.nFileIndexLow
-      else:
-        lastErr = osLastError()
-        success = false
-    else:
-      lastErr = osLastError()
-      success = false
-
-    discard closeHandle(f1)
-    discard closeHandle(f2)
-
-    if not success: raiseOSError(lastErr, $(path1, path2))
-  else:
-    var a, b: Stat
-    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
-      raiseOSError(osLastError(), $(path1, path2))
-    else:
-      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
-
-type
-  FilePermission* = enum   ## File access permission, modelled after UNIX.
-    ##
-    ## See also:
-    ## * `getFilePermissions`_
-    ## * `setFilePermissions`_
-    ## * `FileInfo object`_
-    fpUserExec,            ## execute access for the file owner
-    fpUserWrite,           ## write access for the file owner
-    fpUserRead,            ## read access for the file owner
-    fpGroupExec,           ## execute access for the group
-    fpGroupWrite,          ## write access for the group
-    fpGroupRead,           ## read access for the group
-    fpOthersExec,          ## execute access for others
-    fpOthersWrite,         ## write access for others
-    fpOthersRead           ## read access for others
-
-proc getFilePermissions*(filename: string): set[FilePermission] {.
-  rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} =
-  ## Retrieves file permissions for `filename`.
-  ##
-  ## `OSError` is raised in case of an error.
-  ## On Windows, only the ``readonly`` flag is checked, every other
-  ## permission is available in any case.
-  ##
-  ## See also:
-  ## * `setFilePermissions proc`_
-  ## * `FilePermission enum`_
-  when defined(posix):
-    var a: Stat
-    if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename)
-    result = {}
-    if (a.st_mode and S_IRUSR.Mode) != 0.Mode: result.incl(fpUserRead)
-    if (a.st_mode and S_IWUSR.Mode) != 0.Mode: result.incl(fpUserWrite)
-    if (a.st_mode and S_IXUSR.Mode) != 0.Mode: result.incl(fpUserExec)
-
-    if (a.st_mode and S_IRGRP.Mode) != 0.Mode: result.incl(fpGroupRead)
-    if (a.st_mode and S_IWGRP.Mode) != 0.Mode: result.incl(fpGroupWrite)
-    if (a.st_mode and S_IXGRP.Mode) != 0.Mode: result.incl(fpGroupExec)
-
-    if (a.st_mode and S_IROTH.Mode) != 0.Mode: result.incl(fpOthersRead)
-    if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite)
-    if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec)
-  else:
-    when useWinUnicode:
-      wrapUnary(res, getFileAttributesW, filename)
-    else:
-      var res = getFileAttributesA(filename)
-    if res == -1'i32: raiseOSError(osLastError(), filename)
-    if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
-      result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
-                fpOthersExec, fpOthersRead}
-    else:
-      result = {fpUserExec..fpOthersRead}
-
-proc setFilePermissions*(filename: string, permissions: set[FilePermission],
-                         followSymlinks = true)
-  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect],
-   noWeirdTarget.} =
-  ## Sets the file permissions for `filename`.
-  ##
-  ## If `followSymlinks` set to true (default) and ``filename`` points to a
-  ## symlink, permissions are set to the file symlink points to.
-  ## `followSymlinks` set to false is a noop on Windows and some POSIX
-  ## systems (including Linux) on which `lchmod` is either unavailable or always
-  ## fails, given that symlinks permissions there are not observed.
-  ##
-  ## `OSError` is raised in case of an error.
-  ## On Windows, only the ``readonly`` flag is changed, depending on
-  ## ``fpUserWrite`` permission.
-  ##
-  ## See also:
-  ## * `getFilePermissions proc`_
-  ## * `FilePermission enum`_
-  when defined(posix):
-    var p = 0.Mode
-    if fpUserRead in permissions: p = p or S_IRUSR.Mode
-    if fpUserWrite in permissions: p = p or S_IWUSR.Mode
-    if fpUserExec in permissions: p = p or S_IXUSR.Mode
-
-    if fpGroupRead in permissions: p = p or S_IRGRP.Mode
-    if fpGroupWrite in permissions: p = p or S_IWGRP.Mode
-    if fpGroupExec in permissions: p = p or S_IXGRP.Mode
-
-    if fpOthersRead in permissions: p = p or S_IROTH.Mode
-    if fpOthersWrite in permissions: p = p or S_IWOTH.Mode
-    if fpOthersExec in permissions: p = p or S_IXOTH.Mode
-
-    if not followSymlinks and filename.symlinkExists:
-      when declared(lchmod):
-        if lchmod(filename, cast[Mode](p)) != 0:
-          raiseOSError(osLastError(), $(filename, permissions))
-    else:
-      if chmod(filename, cast[Mode](p)) != 0:
-        raiseOSError(osLastError(), $(filename, permissions))
-  else:
-    when useWinUnicode:
-      wrapUnary(res, getFileAttributesW, filename)
-    else:
-      var res = getFileAttributesA(filename)
-    if res == -1'i32: raiseOSError(osLastError(), filename)
-    if fpUserWrite in permissions:
-      res = res and not FILE_ATTRIBUTE_READONLY
-    else:
-      res = res or FILE_ATTRIBUTE_READONLY
-    when useWinUnicode:
-      wrapBinary(res2, setFileAttributesW, filename, res)
-    else:
-      var res2 = setFileAttributesA(filename, res)
-    if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
 
 proc isAdmin*: bool {.noWeirdTarget.} =
   ## Returns whether the caller's process is a member of the Administrators local
@@ -1750,310 +386,6 @@ proc isAdmin*: bool {.noWeirdTarget.} =
   else:
     result = geteuid() == 0
 
-proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
-  ## Create a symbolic link at `dest` which points to the item specified
-  ## by `src`. On most operating systems, will fail if a link already exists.
-  ##
-  ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation
-  ##   of symlinks to root users (administrators) or users with developer mode enabled.
-  ##
-  ## See also:
-  ## * `createHardlink proc`_
-  ## * `expandSymlink proc`_
-
-  when defined(windows):
-    const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2
-    # allows anyone with developer mode on to create a link
-    let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
-    when useWinUnicode:
-      var wSrc = newWideCString(src)
-      var wDst = newWideCString(dest)
-      if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
-        raiseOSError(osLastError(), $(src, dest))
-    else:
-      if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0:
-        raiseOSError(osLastError(), $(src, dest))
-  else:
-    if symlink(src, dest) != 0:
-      raiseOSError(osLastError(), $(src, dest))
-
-proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
-  ## Returns a string representing the path to which the symbolic link points.
-  ##
-  ## On Windows this is a noop, `symlinkPath` is simply returned.
-  ##
-  ## See also:
-  ## * `createSymlink proc`_
-  when defined(windows):
-    result = symlinkPath
-  else:
-    result = newString(maxSymlinkLen)
-    var len = readlink(symlinkPath, result.cstring, maxSymlinkLen)
-    if len < 0:
-      raiseOSError(osLastError(), symlinkPath)
-    if len > maxSymlinkLen:
-      result = newString(len+1)
-      len = readlink(symlinkPath, result.cstring, len)
-    setLen(result, len)
-
-const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile)
-  # xxx instead of `nimLegacyCopyFile`, support something like: `when osxVersion >= (10, 5)`
-
-when hasCCopyfile:
-  # `copyfile` API available since osx 10.5.
-  {.push nodecl, header: "<copyfile.h>".}
-  type
-    copyfile_state_t {.nodecl.} = pointer
-    copyfile_flags_t = cint
-  proc copyfile_state_alloc(): copyfile_state_t
-  proc copyfile_state_free(state: copyfile_state_t): cint
-  proc c_copyfile(src, dst: cstring,  state: copyfile_state_t, flags: copyfile_flags_t): cint {.importc: "copyfile".}
-  # replace with `let` pending bootstrap >= 1.4.0
-  var
-    COPYFILE_DATA {.nodecl.}: copyfile_flags_t
-    COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
-  {.pop.}
-
-type
-  CopyFlag* = enum    ## Copy options.
-    cfSymlinkAsIs,    ## Copy symlinks as symlinks
-    cfSymlinkFollow,  ## Copy the files symlinks point to
-    cfSymlinkIgnore   ## Ignore symlinks
-
-const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore}
-
-proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl,
-  extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect],
-  noWeirdTarget.} =
-  ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
-  ##
-  ## On non-Windows OSes, `options` specify the way file is copied; by default,
-  ## if `source` is a symlink, copies the file symlink points to. `options` is
-  ## ignored on Windows: symlinks are skipped.
-  ##
-  ## If this fails, `OSError` is raised.
-  ##
-  ## On the Windows platform this proc will
-  ## copy the source file's attributes into dest.
-  ##
-  ## On other platforms you need
-  ## to use `getFilePermissions`_ and
-  ## `setFilePermissions`_
-  ## procs
-  ## to copy them by hand (or use the convenience `copyFileWithPermissions
-  ## proc`_),
-  ## otherwise `dest` will inherit the default permissions of a newly
-  ## created file for the user.
-  ##
-  ## If `dest` already exists, the file attributes
-  ## will be preserved and the content overwritten.
-  ##
-  ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless
-  ## `-d:nimLegacyCopyFile` is used.
-  ##
-  ## See also:
-  ## * `CopyFlag enum`_
-  ## * `copyDir proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `tryRemoveFile proc`_
-  ## * `removeFile proc`_
-  ## * `moveFile proc`_
-
-  doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " &
-                                                 "one cfSymlink* in options"
-  let isSymlink = source.symlinkExists
-  if isSymlink and (cfSymlinkIgnore in options or defined(windows)):
-    return
-  when defined(windows):
-    when useWinUnicode:
-      let s = newWideCString(source)
-      let d = newWideCString(dest)
-      if copyFileW(s, d, 0'i32) == 0'i32:
-        raiseOSError(osLastError(), $(source, dest))
-    else:
-      if copyFileA(source, dest, 0'i32) == 0'i32:
-        raiseOSError(osLastError(), $(source, dest))
-  else:
-    if isSymlink and cfSymlinkAsIs in options:
-      createSymlink(expandSymlink(source), dest)
-    else:
-      when hasCCopyfile:
-        let state = copyfile_state_alloc()
-        # xxx `COPYFILE_STAT` could be used for one-shot
-        # `copyFileWithPermissions`.
-        let status = c_copyfile(source.cstring, dest.cstring, state,
-                                COPYFILE_DATA)
-        if status != 0:
-          let err = osLastError()
-          discard copyfile_state_free(state)
-          raiseOSError(err, $(source, dest))
-        let status2 = copyfile_state_free(state)
-        if status2 != 0: raiseOSError(osLastError(), $(source, dest))
-      else:
-        # generic version of copyFile which works for any platform:
-        const bufSize = 8000 # better for memory manager
-        var d, s: File
-        if not open(s, source):raiseOSError(osLastError(), source)
-        if not open(d, dest, fmWrite):
-          close(s)
-          raiseOSError(osLastError(), dest)
-        var buf = alloc(bufSize)
-        while true:
-          var bytesread = readBuffer(s, buf, bufSize)
-          if bytesread > 0:
-            var byteswritten = writeBuffer(d, buf, bytesread)
-            if bytesread != byteswritten:
-              dealloc(buf)
-              close(s)
-              close(d)
-              raiseOSError(osLastError(), dest)
-          if bytesread != bufSize: break
-        dealloc(buf)
-        close(s)
-        flushFile(d)
-        close(d)
-
-proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow})
-  {.noWeirdTarget, since: (1,3,7).} =
-  ## Copies a file `source` into directory `dir`, which must exist.
-  ##
-  ## On non-Windows OSes, `options` specify the way file is copied; by default,
-  ## if `source` is a symlink, copies the file symlink points to. `options` is
-  ## ignored on Windows: symlinks are skipped.
-  ##
-  ## See also:
-  ## * `CopyFlag enum`_
-  ## * `copyFile proc`_
-  if dir.len == 0: # treating "" as "." is error prone
-    raise newException(ValueError, "dest is empty")
-  copyFile(source, dir / source.lastPathPart, options)
-
-when not declared(ENOENT) and not defined(windows):
-  when defined(nimscript):
-    when not defined(haiku):
-      const ENOENT = cint(2) # 2 on most systems including Solaris
-    else:
-      const ENOENT = cint(-2147459069)
-  else:
-    var ENOENT {.importc, header: "<errno.h>".}: cint
-
-when defined(windows) and not weirdTarget:
-  when useWinUnicode:
-    template deleteFile(file: untyped): untyped  = deleteFileW(file)
-    template setFileAttributes(file, attrs: untyped): untyped =
-      setFileAttributesW(file, attrs)
-  else:
-    template deleteFile(file: untyped): untyped = deleteFileA(file)
-    template setFileAttributes(file, attrs: untyped): untyped =
-      setFileAttributesA(file, attrs)
-
-proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
-  ## Removes the `file`.
-  ##
-  ## If this fails, returns `false`. This does not fail
-  ## if the file never existed in the first place.
-  ##
-  ## On Windows, ignores the read-only attribute.
-  ##
-  ## See also:
-  ## * `copyFile proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `removeFile proc`_
-  ## * `moveFile proc`_
-  result = true
-  when defined(windows):
-    when useWinUnicode:
-      let f = newWideCString(file)
-    else:
-      let f = file
-    if deleteFile(f) == 0:
-      result = false
-      let err = getLastError()
-      if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
-        result = true
-      elif err == ERROR_ACCESS_DENIED and
-         setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
-         deleteFile(f) != 0:
-        result = true
-  else:
-    if unlink(file) != 0'i32 and errno != ENOENT:
-      result = false
-
-proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
-  ## Removes the `file`.
-  ##
-  ## If this fails, `OSError` is raised. This does not fail
-  ## if the file never existed in the first place.
-  ##
-  ## On Windows, ignores the read-only attribute.
-  ##
-  ## See also:
-  ## * `removeDir proc`_
-  ## * `copyFile proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `tryRemoveFile proc`_
-  ## * `moveFile proc`_
-  if not tryRemoveFile(file):
-    raiseOSError(osLastError(), file)
-
-proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
-  ## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
-  ##
-  ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
-  ## In case of other errors `OSError` is raised.
-  ## Returns true in case of success.
-  when defined(windows):
-    when useWinUnicode:
-      let s = newWideCString(source)
-      let d = newWideCString(dest)
-      result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
-    else:
-      result = moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
-  else:
-    result = c_rename(source, dest) == 0'i32
-
-  if not result:
-    let err = osLastError()
-    let isAccessDeniedError =
-      when defined(windows):
-        const AccessDeniedError = OSErrorCode(5)
-        isDir and err == AccessDeniedError
-      else:
-        err == EXDEV.OSErrorCode
-    if not isAccessDeniedError:
-      raiseOSError(err, $(source, dest))
-
-proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
-  ## Moves a file from `source` to `dest`.
-  ##
-  ## Symlinks are not followed: if `source` is a symlink, it is itself moved,
-  ## not its target.
-  ##
-  ## If this fails, `OSError` is raised.
-  ## If `dest` already exists, it will be overwritten.
-  ##
-  ## Can be used to `rename files`:idx:.
-  ##
-  ## See also:
-  ## * `moveDir proc`_
-  ## * `copyFile proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `removeFile proc`_
-  ## * `tryRemoveFile proc`_
-
-  if not tryMoveFSObject(source, dest, isDir = false):
-    when defined(windows):
-      doAssert false
-    else:
-      # Fallback to copy & del
-      copyFile(source, dest, {cfSymlinkAsIs})
-      try:
-        removeFile(source)
-      except:
-        discard tryRemoveFile(dest)
-        raise
-
 
 proc exitStatusLikeShell*(status: cint): cint =
   ## Converts exit code from `c_system` into a shell exit code.
@@ -2079,122 +411,11 @@ proc execShellCmd*(command: string): int {.rtl, extern: "nos$1",
   ## <osproc.html#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_.
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   discard execShellCmd("ls -la")
+  ##   ```
   result = exitStatusLikeShell(c_system(command))
 
-# Templates for filtering directories and files
-when defined(windows) and not weirdTarget:
-  template isDir(f: WIN32_FIND_DATA): bool =
-    (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
-  template isFile(f: WIN32_FIND_DATA): bool =
-    not isDir(f)
-else:
-  template isDir(f: string): bool {.dirty.} =
-    dirExists(f)
-  template isFile(f: string): bool {.dirty.} =
-    fileExists(f)
-
-template defaultWalkFilter(item): bool =
-  ## Walk filter used to return true on both
-  ## files and directories
-  true
-
-template walkCommon(pattern: string, filter) =
-  ## Common code for getting the files and directories with the
-  ## specified `pattern`
-  when defined(windows):
-    var
-      f: WIN32_FIND_DATA
-      res: int
-    res = findFirstFile(pattern, f)
-    if res != -1:
-      defer: findClose(res)
-      let dotPos = searchExtPos(pattern)
-      while true:
-        if not skipFindData(f) and filter(f):
-          # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check
-          # that the file extensions have the same length ...
-          let ff = getFilename(f)
-          let idx = ff.len - pattern.len + dotPos
-          if dotPos < 0 or idx >= ff.len or (idx >= 0 and ff[idx] == '.') or
-              (dotPos >= 0 and dotPos+1 < pattern.len and pattern[dotPos+1] == '*'):
-            yield splitFile(pattern).dir / extractFilename(ff)
-        if findNextFile(res, f) == 0'i32:
-          let errCode = getLastError()
-          if errCode == ERROR_NO_MORE_FILES: break
-          else: raiseOSError(errCode.OSErrorCode)
-  else: # here we use glob
-    var
-      f: Glob
-      res: int
-    f.gl_offs = 0
-    f.gl_pathc = 0
-    f.gl_pathv = nil
-    res = glob(pattern, 0, nil, addr(f))
-    defer: globfree(addr(f))
-    if res == 0:
-      for i in 0.. f.gl_pathc - 1:
-        assert(f.gl_pathv[i] != nil)
-        let path = $f.gl_pathv[i]
-        if filter(path):
-          yield path
-
-iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
-  ## Iterate over all the files and directories that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkFiles iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  runnableExamples:
-    import std/sequtils
-    let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too
-    assert "lib/pure/concurrency".unixToNativePath in paths
-    assert "lib/pure/os.nim".unixToNativePath in paths
-  walkCommon(pattern, defaultWalkFilter)
-
-iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
-  ## Iterate over all the files that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  runnableExamples:
-    import std/sequtils
-    assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too
-  walkCommon(pattern, isFile)
-
-iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
-  ## Iterate over all the directories that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkFiles iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  runnableExamples:
-    import std/sequtils
-    let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too
-    assert "lib/pure/concurrency".unixToNativePath in paths
-  walkCommon(pattern, isDir)
-
 proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
   tags: [ReadDirEffect], noWeirdTarget.} =
   ## Returns the full (`absolute`:idx:) path of an existing file `filename`.
@@ -2202,32 +423,18 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
   ## Raises `OSError` in case of an error. Follows symlinks.
   when defined(windows):
     var bufsize = MAX_PATH.int32
-    when useWinUnicode:
-      var unused: WideCString = nil
-      var res = newWideCString("", bufsize)
-      while true:
-        var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError(), filename)
-        elif L > bufsize:
-          res = newWideCString("", L)
-          bufsize = L
-        else:
-          result = res$L
-          break
-    else:
-      var unused: cstring = nil
-      result = newString(bufsize)
-      while true:
-        var L = getFullPathNameA(filename, bufsize, result, unused)
-        if L == 0'i32:
-          raiseOSError(osLastError(), filename)
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
+    var unused: WideCString = nil
+    var res = newWideCString(bufsize)
+    while true:
+      var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused)
+      if L == 0'i32:
+        raiseOSError(osLastError(), filename)
+      elif L > bufsize:
+        res = newWideCString(L)
+        bufsize = L
+      else:
+        result = res$L
+        break
     # getFullPathName doesn't do case corrections, so we have to use this convoluted
     # way of retrieving the true filename
     for x in walkFiles(result):
@@ -2245,376 +452,14 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
       result = $r
       c_free(cast[pointer](r))
 
-type
-  PathComponent* = enum   ## Enumeration specifying a path component.
-    ##
-    ## See also:
-    ## * `walkDirRec iterator`_
-    ## * `FileInfo object`_
-    pcFile,               ## path refers to a file
-    pcLinkToFile,         ## path refers to a symbolic link to a file
-    pcDir,                ## path refers to a directory
-    pcLinkToDir           ## path refers to a symbolic link to a directory
-
 proc getCurrentCompilerExe*(): string {.compileTime.} = discard
-  ## This is `getAppFilename()`_ at compile time.
+  ## Returns the path of the currently running Nim compiler or nimble executable.
   ##
   ## Can be used to retrieve the currently executing
   ## Nim compiler from a Nim or nimscript program, or the nimble binary
   ## inside a nimble program (likewise with other binaries built from
   ## compiler API).
 
-when defined(posix) and not weirdTarget:
-  proc getSymlinkFileKind(path: string): PathComponent =
-    # Helper function.
-    var s: Stat
-    assert(path != "")
-    if stat(path, s) == 0'i32 and S_ISDIR(s.st_mode):
-      result = pcLinkToDir
-    else:
-      result = pcLinkToFile
-
-proc staticWalkDir(dir: string; relative: bool): seq[
-                  tuple[kind: PathComponent, path: string]] =
-  discard
-
-iterator walkDir*(dir: string; relative = false, checkDir = false):
-  tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
-  ## Walks over the directory `dir` and yields for each directory or file in
-  ## `dir`. The component type and full path for each item are returned.
-  ##
-  ## Walking is not recursive. If ``relative`` is true (default: false)
-  ## the resulting path is shortened to be relative to ``dir``.
-  ##
-  ## If `checkDir` is true, `OSError` is raised when `dir`
-  ## doesn't exist.
-  ##
-  ## **Example:**
-  ##
-  ## This directory structure::
-  ##   dirA / dirB / fileB1.txt
-  ##        / dirC
-  ##        / fileA1.txt
-  ##        / fileA2.txt
-  ##
-  ## and this code:
-  runnableExamples("-r:off"):
-    import std/[strutils, sugar]
-    # note: order is not guaranteed
-    # this also works at compile time
-    assert collect(for k in walkDir("dirA"): k.path).join(" ") ==
-                          "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt"
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkFiles iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDirRec iterator`_
-
-  when nimvm:
-    for k, v in items(staticWalkDir(dir, relative)):
-      yield (k, v)
-  else:
-    when weirdTarget:
-      for k, v in items(staticWalkDir(dir, relative)):
-        yield (k, v)
-    elif defined(windows):
-      var f: WIN32_FIND_DATA
-      var h = findFirstFile(dir / "*", f)
-      if h == -1:
-        if checkDir:
-          raiseOSError(osLastError(), dir)
-      else:
-        defer: findClose(h)
-        while true:
-          var k = pcFile
-          if not skipFindData(f):
-            if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
-              k = pcDir
-            if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
-              k = succ(k)
-            let xx = if relative: extractFilename(getFilename(f))
-                     else: dir / extractFilename(getFilename(f))
-            yield (k, xx)
-          if findNextFile(h, f) == 0'i32:
-            let errCode = getLastError()
-            if errCode == ERROR_NO_MORE_FILES: break
-            else: raiseOSError(errCode.OSErrorCode)
-    else:
-      var d = opendir(dir)
-      if d == nil:
-        if checkDir:
-          raiseOSError(osLastError(), dir)
-      else:
-        defer: discard closedir(d)
-        while true:
-          var x = readdir(d)
-          if x == nil: break
-          var y = $cstring(addr x.d_name)
-          if y != "." and y != "..":
-            var s: Stat
-            let path = dir / y
-            if not relative:
-              y = path
-            var k = pcFile
-
-            template kSetGeneric() =  # pure Posix component `k` resolution
-              if lstat(path.cstring, s) < 0'i32: continue  # don't yield
-              elif S_ISDIR(s.st_mode):
-                k = pcDir
-              elif S_ISLNK(s.st_mode):
-                k = getSymlinkFileKind(path)
-
-            when defined(linux) or defined(macosx) or
-                 defined(bsd) or defined(genode) or defined(nintendoswitch):
-              case x.d_type
-              of DT_DIR: k = pcDir
-              of DT_LNK:
-                if dirExists(path): k = pcLinkToDir
-                else: k = pcLinkToFile
-              of DT_UNKNOWN:
-                kSetGeneric()
-              else: # e.g. DT_REG etc
-                discard # leave it as pcFile
-            else:  # assuming that field `d_type` is not present
-              kSetGeneric()
-
-            yield (k, y)
-
-iterator walkDirRec*(dir: string,
-                     yieldFilter = {pcFile}, followFilter = {pcDir},
-                     relative = false, checkDir = false): string {.tags: [ReadDirEffect].} =
-  ## Recursively walks over the directory `dir` and yields for each file
-  ## or directory in `dir`.
-  ##
-  ## If ``relative`` is true (default: false) the resulting path is
-  ## shortened to be relative to ``dir``, otherwise the full path is returned.
-  ##
-  ## If `checkDir` is true, `OSError` is raised when `dir`
-  ## doesn't exist.
-  ##
-  ## .. warning:: Modifying the directory structure while the iterator
-  ##   is traversing may result in undefined behavior!
-  ##
-  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
-  ##
-  ## =====================   =============================================
-  ## yieldFilter             meaning
-  ## =====================   =============================================
-  ## ``pcFile``              yield real files (default)
-  ## ``pcLinkToFile``        yield symbolic links to files
-  ## ``pcDir``               yield real directories
-  ## ``pcLinkToDir``         yield symbolic links to directories
-  ## =====================   =============================================
-  ##
-  ## =====================   =============================================
-  ## followFilter            meaning
-  ## =====================   =============================================
-  ## ``pcDir``               follow real directories (default)
-  ## ``pcLinkToDir``         follow symbolic links to directories
-  ## =====================   =============================================
-  ##
-  ##
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkFiles iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDir iterator`_
-
-  var stack = @[""]
-  var checkDir = checkDir
-  while stack.len > 0:
-    let d = stack.pop()
-    for k, p in walkDir(dir / d, relative = true, checkDir = checkDir):
-      let rel = d / p
-      if k in {pcDir, pcLinkToDir} and k in followFilter:
-        stack.add rel
-      if k in yieldFilter:
-        yield if relative: rel else: dir / rel
-    checkDir = false
-      # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong
-      # permissions), it'll abort iteration and there would be no way to
-      # continue iteration.
-      # Future work can provide a way to customize this and do error reporting.
-
-proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
-  when defined(windows):
-    when useWinUnicode:
-      wrapUnary(res, removeDirectoryW, dir)
-    else:
-      var res = removeDirectoryA(dir)
-    let lastError = osLastError()
-    if res == 0'i32 and lastError.int32 != 3'i32 and
-        lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
-      raiseOSError(lastError, dir)
-  else:
-    if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir)
-
-proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [
-  WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} =
-  ## Removes the directory `dir` including all subdirectories and files
-  ## in `dir` (recursively).
-  ##
-  ## If this fails, `OSError` is raised. This does not fail if the directory never
-  ## existed in the first place, unless `checkDir` = true.
-  ##
-  ## See also:
-  ## * `tryRemoveFile proc`_
-  ## * `removeFile proc`_
-  ## * `existsOrCreateDir proc`_
-  ## * `createDir proc`_
-  ## * `copyDir proc`_
-  ## * `copyDirWithPermissions proc`_
-  ## * `moveDir proc`_
-  for kind, path in walkDir(dir, checkDir = checkDir):
-    case kind
-    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
-    of pcDir: removeDir(path, true)
-      # for subdirectories there is no benefit in `checkDir = false`
-      # (unless perhaps for edge case of concurrent processes also deleting
-      # the same files)
-  rawRemoveDir(dir)
-
-proc rawCreateDir(dir: string): bool {.noWeirdTarget.} =
-  # Try to create one directory (not the whole path).
-  # returns `true` for success, `false` if the path has previously existed
-  #
-  # This is a thin wrapper over mkDir (or alternatives on other systems),
-  # so in case of a pre-existing path we don't check that it is a directory.
-  when defined(solaris):
-    let res = mkdir(dir, 0o777)
-    if res == 0'i32:
-      result = true
-    elif errno in {EEXIST, ENOSYS}:
-      result = false
-    else:
-      raiseOSError(osLastError(), dir)
-  elif defined(haiku):
-    let res = mkdir(dir, 0o777)
-    if res == 0'i32:
-      result = true
-    elif errno == EEXIST or errno == EROFS:
-      result = false
-    else:
-      raiseOSError(osLastError(), dir)
-  elif defined(posix):
-    let res = mkdir(dir, 0o777)
-    if res == 0'i32:
-      result = true
-    elif errno == EEXIST:
-      result = false
-    else:
-      #echo res
-      raiseOSError(osLastError(), dir)
-  else:
-    when useWinUnicode:
-      wrapUnary(res, createDirectoryW, dir)
-    else:
-      let res = createDirectoryA(dir)
-
-    if res != 0'i32:
-      result = true
-    elif getLastError() == 183'i32:
-      result = false
-    else:
-      raiseOSError(osLastError(), dir)
-
-proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
-  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
-  ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
-  ##
-  ## Does not create parent directories (raises `OSError` if parent directories do not exist).
-  ## Returns `true` if the directory already exists, and `false` otherwise.
-  ##
-  ## See also:
-  ## * `removeDir proc`_
-  ## * `createDir proc`_
-  ## * `copyDir proc`_
-  ## * `copyDirWithPermissions proc`_
-  ## * `moveDir proc`_
-  result = not rawCreateDir(dir)
-  if result:
-    # path already exists - need to check that it is indeed a directory
-    if not dirExists(dir):
-      raise newException(IOError, "Failed to create '" & dir & "'")
-
-proc createDir*(dir: string) {.rtl, extern: "nos$1",
-  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
-  ## Creates the `directory`:idx: `dir`.
-  ##
-  ## The directory may contain several subdirectories that do not exist yet.
-  ## The full path is created. If this fails, `OSError` is raised.
-  ##
-  ## It does **not** fail if the directory already exists because for
-  ## most usages this does not indicate an error.
-  ##
-  ## See also:
-  ## * `removeDir proc`_
-  ## * `existsOrCreateDir proc`_
-  ## * `copyDir proc`_
-  ## * `copyDirWithPermissions proc`_
-  ## * `moveDir proc`_
-  if dir == "":
-    return
-  var omitNext = isAbsolute(dir)
-  for p in parentDirs(dir, fromRoot=true):
-    if omitNext:
-      omitNext = false
-    else:
-      discard existsOrCreateDir(p)
-
-proc copyDir*(source, dest: string) {.rtl, extern: "nos$1",
-  tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
-  ## Copies a directory from `source` to `dest`.
-  ##
-  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
-  ## are skipped.
-  ##
-  ## If this fails, `OSError` is raised.
-  ##
-  ## On the Windows platform this proc will copy the attributes from
-  ## `source` into `dest`.
-  ##
-  ## On other platforms created files and directories will inherit the
-  ## default permissions of a newly created file/directory for the user.
-  ## Use `copyDirWithPermissions proc`_
-  ## to preserve attributes recursively on these platforms.
-  ##
-  ## See also:
-  ## * `copyDirWithPermissions proc`_
-  ## * `copyFile proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `removeDir proc`_
-  ## * `existsOrCreateDir proc`_
-  ## * `createDir proc`_
-  ## * `moveDir proc`_
-  createDir(dest)
-  for kind, path in walkDir(source):
-    var noSource = splitPath(path).tail
-    if kind == pcDir:
-      copyDir(path, dest / noSource)
-    else:
-      copyFile(path, dest / noSource, {cfSymlinkAsIs})
-
-proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
-  ## Moves a directory from `source` to `dest`.
-  ##
-  ## Symlinks are not followed: if `source` contains symlinks, they themself are
-  ## moved, not their target.
-  ##
-  ## If this fails, `OSError` is raised.
-  ##
-  ## See also:
-  ## * `moveFile proc`_
-  ## * `copyDir proc`_
-  ## * `copyDirWithPermissions proc`_
-  ## * `removeDir proc`_
-  ## * `existsOrCreateDir proc`_
-  ## * `createDir proc`_
-  if not tryMoveFSObject(source, dest, isDir = true):
-    # Fallback to copy & del
-    copyDir(source, dest)
-    removeDir(source)
-
 proc createHardlink*(src, dest: string) {.noWeirdTarget.} =
   ## Create a hard link at `dest` which points to the item specified
   ## by `src`.
@@ -2625,386 +470,32 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} =
   ## See also:
   ## * `createSymlink proc`_
   when defined(windows):
-    when useWinUnicode:
-      var wSrc = newWideCString(src)
-      var wDst = newWideCString(dest)
-      if createHardLinkW(wDst, wSrc, nil) == 0:
-        raiseOSError(osLastError(), $(src, dest))
-    else:
-      if createHardLinkA(dest, src, nil) == 0:
-        raiseOSError(osLastError(), $(src, dest))
+    var wSrc = newWideCString(src)
+    var wDst = newWideCString(dest)
+    if createHardLinkW(wDst, wSrc, nil) == 0:
+      raiseOSError(osLastError(), $(src, dest))
   else:
     if link(src, dest) != 0:
       raiseOSError(osLastError(), $(src, dest))
 
-proc copyFileWithPermissions*(source, dest: string,
-                              ignorePermissionErrors = true,
-                              options = {cfSymlinkFollow}) {.noWeirdTarget.} =
-  ## Copies a file from `source` to `dest` preserving file permissions.
-  ##
-  ## On non-Windows OSes, `options` specify the way file is copied; by default,
-  ## if `source` is a symlink, copies the file symlink points to. `options` is
-  ## ignored on Windows: symlinks are skipped.
-  ##
-  ## This is a wrapper proc around `copyFile`_,
-  ## `getFilePermissions`_ and `setFilePermissions`_
-  ## procs on non-Windows platforms.
-  ##
-  ## On Windows this proc is just a wrapper for `copyFile proc`_ since
-  ## that proc already copies attributes.
-  ##
-  ## On non-Windows systems permissions are copied after the file itself has
-  ## been copied, which won't happen atomically and could lead to a race
-  ## condition. If `ignorePermissionErrors` is true (default), errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  ##
-  ## See also:
-  ## * `CopyFlag enum`_
-  ## * `copyFile proc`_
-  ## * `copyDir proc`_
-  ## * `tryRemoveFile proc`_
-  ## * `removeFile proc`_
-  ## * `moveFile proc`_
-  ## * `copyDirWithPermissions proc`_
-  copyFile(source, dest, options)
-  when not defined(windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
-                         (cfSymlinkFollow in options))
-    except:
-      if not ignorePermissionErrors:
-        raise
-
-proc copyDirWithPermissions*(source, dest: string,
-                             ignorePermissionErrors = true)
-  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
-   benign, noWeirdTarget.} =
-  ## Copies a directory from `source` to `dest` preserving file permissions.
-  ##
-  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
-  ## are skipped.
-  ##
-  ## If this fails, `OSError` is raised. This is a wrapper proc around
-  ## `copyDir`_ and `copyFileWithPermissions`_ procs
-  ## on non-Windows platforms.
-  ##
-  ## On Windows this proc is just a wrapper for `copyDir proc`_ since
-  ## that proc already copies attributes.
-  ##
-  ## On non-Windows systems permissions are copied after the file or directory
-  ## itself has been copied, which won't happen atomically and could lead to a
-  ## race condition. If `ignorePermissionErrors` is true (default), errors while
-  ## reading/setting file attributes will be ignored, otherwise will raise
-  ## `OSError`.
-  ##
-  ## See also:
-  ## * `copyDir proc`_
-  ## * `copyFile proc`_
-  ## * `copyFileWithPermissions proc`_
-  ## * `removeDir proc`_
-  ## * `moveDir proc`_
-  ## * `existsOrCreateDir proc`_
-  ## * `createDir proc`_
-  createDir(dest)
-  when not defined(windows):
-    try:
-      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
-                         false)
-    except:
-      if not ignorePermissionErrors:
-        raise
-  for kind, path in walkDir(source):
-    var noSource = splitPath(path).tail
-    if kind == pcDir:
-      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors)
-    else:
-      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs})
-
 proc inclFilePermissions*(filename: string,
                           permissions: set[FilePermission]) {.
   rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} =
   ## A convenience proc for:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   setFilePermissions(filename, getFilePermissions(filename)+permissions)
+  ##   ```
   setFilePermissions(filename, getFilePermissions(filename)+permissions)
 
 proc exclFilePermissions*(filename: string,
                           permissions: set[FilePermission]) {.
   rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} =
   ## A convenience proc for:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   setFilePermissions(filename, getFilePermissions(filename)-permissions)
+  ##   ```
   setFilePermissions(filename, getFilePermissions(filename)-permissions)
 
-proc parseCmdLine*(c: string): seq[string] {.
-  noSideEffect, rtl, extern: "nos$1".} =
-  ## Splits a `command line`:idx: into several components.
-  ##
-  ## **Note**: This proc is only occasionally useful, better use the
-  ## `parseopt module <parseopt.html>`_.
-  ##
-  ## On Windows, it uses the `following parsing rules
-  ## <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_:
-  ##
-  ## * Arguments are delimited by white space, which is either a space or a tab.
-  ## * The caret character (^) is not recognized as an escape character or
-  ##   delimiter. The character is handled completely by the command-line parser
-  ##   in the operating system before being passed to the argv array in the
-  ##   program.
-  ## * A string surrounded by double quotation marks ("string") is interpreted
-  ##   as a single argument, regardless of white space contained within. A
-  ##   quoted string can be embedded in an argument.
-  ## * A double quotation mark preceded by a backslash (\") is interpreted as a
-  ##   literal double quotation mark character (").
-  ## * Backslashes are interpreted literally, unless they immediately precede
-  ##   a double quotation mark.
-  ## * If an even number of backslashes is followed by a double quotation mark,
-  ##   one backslash is placed in the argv array for every pair of backslashes,
-  ##   and the double quotation mark is interpreted as a string delimiter.
-  ## * If an odd number of backslashes is followed by a double quotation mark,
-  ##   one backslash is placed in the argv array for every pair of backslashes,
-  ##   and the double quotation mark is "escaped" by the remaining backslash,
-  ##   causing a literal double quotation mark (") to be placed in argv.
-  ##
-  ## On Posix systems, it uses the following parsing rules:
-  ## Components are separated by whitespace unless the whitespace
-  ## occurs within ``"`` or ``'`` quotes.
-  ##
-  ## See also:
-  ## * `parseopt module <parseopt.html>`_
-  ## * `paramCount proc`_
-  ## * `paramStr proc`_
-  ## * `commandLineParams proc`_
-
-  result = @[]
-  var i = 0
-  var a = ""
-  while true:
-    setLen(a, 0)
-    # eat all delimiting whitespace
-    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:
-      var inQuote = false
-      while i < c.len:
-        case c[i]
-        of '\\':
-          var j = i
-          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
-            else:
-              a.add('"')
-              i = j+1
-          else:
-            a.add(c[i])
-            inc(i)
-        of '"':
-          inc(i)
-          if not inQuote: inQuote = true
-          elif i < c.len and c[i] == '"':
-            a.add(c[i])
-            inc(i)
-          else:
-            inQuote = false
-            break
-        of ' ', '\t':
-          if not inQuote: break
-          a.add(c[i])
-          inc(i)
-        else:
-          a.add(c[i])
-          inc(i)
-    else:
-      case c[i]
-      of '\'', '\"':
-        var delim = c[i]
-        inc(i) # skip ' or "
-        while i < c.len and c[i] != delim:
-          add a, c[i]
-          inc(i)
-        if i < c.len: inc(i)
-      else:
-        while i < c.len and c[i] > ' ':
-          add(a, c[i])
-          inc(i)
-    add(result, a)
-
-when defined(nimdoc):
-  # Common forward declaration docstring block for parameter retrieval procs.
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    ## Returns the number of `command line arguments`:idx: given to the
-    ## application.
-    ##
-    ## Unlike `argc`:idx: in C, if your binary was called without parameters this
-    ## will return zero.
-    ## You can query each individual parameter with `paramStr proc`_
-    ## or retrieve all of them in one go with `commandLineParams proc`_.
-    ##
-    ## **Availability**: When generating a dynamic library (see `--app:lib`) on
-    ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared,untyped>`_.
-    ##
-    ## See also:
-    ## * `parseopt module <parseopt.html>`_
-    ## * `parseCmdLine proc`_
-    ## * `paramStr proc`_
-    ## * `commandLineParams proc`_
-    ##
-    ## **Examples:**
-    ##
-    ## .. code-block:: nim
-    ##   when declared(paramCount):
-    ##     # Use paramCount() here
-    ##   else:
-    ##     # Do something else!
-
-  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
-    ## Returns the `i`-th `command line argument`:idx: given to the application.
-    ##
-    ## `i` should be in the range `1..paramCount()`, the `IndexDefect`
-    ## exception will be raised for invalid values. Instead of iterating
-    ## over `paramCount()`_ with this proc you can
-    ## call the convenience `commandLineParams()`_.
-    ##
-    ## Similarly to `argv`:idx: in C,
-    ## it is possible to call `paramStr(0)` but this will return OS specific
-    ## contents (usually the name of the invoked executable). You should avoid
-    ## this and call `getAppFilename()`_ instead.
-    ##
-    ## **Availability**: When generating a dynamic library (see `--app:lib`) on
-    ## Posix this proc is not defined.
-    ## Test for availability using `declared() <system.html#declared,untyped>`_.
-    ##
-    ## See also:
-    ## * `parseopt module <parseopt.html>`_
-    ## * `parseCmdLine proc`_
-    ## * `paramCount proc`_
-    ## * `commandLineParams proc`_
-    ## * `getAppFilename proc`_
-    ##
-    ## **Examples:**
-    ##
-    ## .. code-block:: nim
-    ##   when declared(paramStr):
-    ##     # Use paramStr() here
-    ##   else:
-    ##     # Do something else!
-
-elif defined(nimscript): discard
-elif defined(nodejs):
-  type Argv = object of JsRoot
-  let argv {.importjs: "process.argv".} : Argv
-  proc len(argv: Argv): int {.importjs: "#.length".}
-  proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".}
-
-  proc paramCount*(): int {.tags: [ReadDirEffect].} =
-    result = argv.len - 2
-
-  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
-    let i = i + 1
-    if i < argv.len and i >= 0:
-      result = $argv[i]
-    else:
-      raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2))
-elif defined(windows):
-  # Since we support GUI applications with Nim, we sometimes generate
-  # a WinMain entry proc. But a WinMain proc has no access to the parsed
-  # command line arguments. The way to get them differs. Thus we parse them
-  # ourselves. This has the additional benefit that the program's behaviour
-  # is always the same -- independent of the used C compiler.
-  var
-    ownArgv {.threadvar.}: seq[string]
-    ownParsedArgv {.threadvar.}: bool
-
-  proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if not ownParsedArgv:
-      ownArgv = parseCmdLine($getCommandLine())
-      ownParsedArgv = true
-    result = ownArgv.len-1
-
-  proc paramStr*(i: int): string {.rtl, extern: "nos$1",
-    tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if not ownParsedArgv:
-      ownArgv = parseCmdLine($getCommandLine())
-      ownParsedArgv = true
-    if i < ownArgv.len and i >= 0:
-      result = ownArgv[i]
-    else:
-      raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1))
-
-elif defined(genode):
-  proc paramStr*(i: int): string =
-    raise newException(OSError, "paramStr is not implemented on Genode")
-
-  proc paramCount*(): int =
-    raise newException(OSError, "paramCount is not implemented on Genode")
-elif weirdTarget or (defined(posix) and appType == "lib"):
-  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
-    raise newException(OSError, "paramStr is not implemented on current platform")
-
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    raise newException(OSError, "paramCount is not implemented on current platform")
-elif not defined(createNimRtl) and
-  not(defined(posix) and appType == "lib"):
-  # On Posix, there is no portable way to get the command line from a DLL.
-  var
-    cmdCount {.importc: "cmdCount".}: cint
-    cmdLine {.importc: "cmdLine".}: cstringArray
-
-  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    if i < cmdCount and i >= 0:
-      result = $cmdLine[i]
-    else:
-      raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1))
-
-  proc paramCount*(): int {.tags: [ReadIOEffect].} =
-    # Docstring in nimdoc block.
-    result = cmdCount-1
-
-when declared(paramCount) or defined(nimdoc):
-  proc commandLineParams*(): seq[string] =
-    ## Convenience proc which returns the command line parameters.
-    ##
-    ## This returns **only** the parameters. If you want to get the application
-    ## executable filename, call `getAppFilename()`_.
-    ##
-    ## **Availability**: On Posix there is no portable way to get the command
-    ## line from a DLL and thus the proc isn't defined in this environment. You
-    ## can test for its availability with `declared()
-    ## <system.html#declared,untyped>`_.
-    ##
-    ## See also:
-    ## * `parseopt module <parseopt.html>`_
-    ## * `parseCmdLine proc`_
-    ## * `paramCount proc`_
-    ## * `paramStr proc`_
-    ## * `getAppFilename proc`_
-    ##
-    ## **Examples:**
-    ##
-    ## .. code-block:: nim
-    ##   when declared(commandLineParams):
-    ##     # Use commandLineParams() here
-    ##   else:
-    ##     # Do something else!
-    result = @[]
-    for i in 1..paramCount():
-      result.add(paramStr(i))
-else:
-  proc commandLineParams*(): seq[string] {.error:
-  "commandLineParams() unsupported by dynamic libraries".} =
-    discard
-
 when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)):
   proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t,
               newp: pointer, newplen: csize_t): cint
@@ -3121,7 +612,7 @@ when defined(haiku):
     B_FIND_PATH_IMAGE_PATH = 1000
 
   proc find_path(codePointer: pointer, baseDirectory: cint, subPath: cstring,
-                 pathBuffer: cstring, bufferSize: csize): int32
+                 pathBuffer: cstring, bufferSize: csize_t): int32
                 {.importc, header: "<FindDirectory.h>".}
 
   proc getApplHaiku(): string =
@@ -3133,10 +624,12 @@ when defined(haiku):
     else:
       result = ""
 
-proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} =
+proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget, raises: [].} =
   ## Returns the filename of the application's executable.
   ## This proc will resolve symlinks.
   ##
+  ## Returns empty string when name is unavailable
+  ##
   ## See also:
   ## * `getAppDir proc`_
   ## * `getCurrentCompilerExe proc`_
@@ -3147,32 +640,18 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
   # /proc/<pid>/path/a.out (complete pathname)
   when defined(windows):
     var bufsize = int32(MAX_PATH)
-    when useWinUnicode:
-      var buf = newWideCString("", bufsize)
-      while true:
-        var L = getModuleFileNameW(0, buf, bufsize)
-        if L == 0'i32:
-          result = "" # error!
-          break
-        elif L > bufsize:
-          buf = newWideCString("", L)
-          bufsize = L
-        else:
-          result = buf$L
-          break
-    else:
-      result = newString(bufsize)
-      while true:
-        var L = getModuleFileNameA(0, result, bufsize)
-        if L == 0'i32:
-          result = "" # error!
-          break
-        elif L > bufsize:
-          result = newString(L)
-          bufsize = L
-        else:
-          setLen(result, L)
-          break
+    var buf = newWideCString(bufsize)
+    while true:
+      var L = getModuleFileNameW(0, buf, bufsize)
+      if L == 0'i32:
+        result = "" # error!
+        break
+      elif L > bufsize:
+        buf = newWideCString(L)
+        bufsize = L
+      else:
+        result = buf$L
+        break
   elif defined(macosx):
     var size = cuint32(0)
     getExecPath1(nil, size)
@@ -3180,24 +659,29 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW
     if getExecPath2(result.cstring, size):
       result = "" # error!
     if result.len > 0:
-      result = result.expandFilename
+      try:
+        result = result.expandFilename
+      except OSError:
+        result = ""
   else:
     when defined(linux) or defined(aix):
       result = getApplAux("/proc/self/exe")
     elif defined(solaris):
       result = getApplAux("/proc/" & $getpid() & "/path/a.out")
     elif defined(genode):
-      raiseOSError(OSErrorCode(-1), "POSIX command line not supported")
+      result = "" # Not supported
     elif defined(freebsd) or defined(dragonfly) or defined(netbsd):
       result = getApplFreebsd()
     elif defined(haiku):
       result = getApplHaiku()
     elif defined(openbsd):
-      result = getApplOpenBsd()
+      result = try: getApplOpenBsd() except OSError: ""
+    elif defined(nintendoswitch):
+      result = ""
 
     # little heuristic that may work on other POSIX-like systems:
     if result.len == 0:
-      result = getApplHeuristic()
+      result = try: getApplHeuristic() except OSError: ""
 
 proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} =
   ## Returns the directory of the application's executable.
@@ -3208,7 +692,10 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT
 
 proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} =
   ## Sleeps `milsecs` milliseconds.
+  ## A negative `milsecs` causes sleep to return immediately.
   when defined(windows):
+    if milsecs < 0:
+      return  # fixes #23732
     winlean.sleep(int32(milsecs))
   else:
     var a, b: Timespec
@@ -3259,20 +746,25 @@ type
     creationTime*: times.Time         ## Time file was created. Not supported on all systems!
     blockSize*: int                   ## Preferred I/O block size for this object.
                                       ## In some filesystems, this may vary from file to file.
+    isSpecial*: bool                  ## Is file special? (on Unix some "files"
+                                      ## can be special=non-regular like FIFOs,
+                                      ## devices); for directories `isSpecial`
+                                      ## is always `false`, for symlinks it is
+                                      ## the same as for the link's target.
 
 template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
   ## Transforms the native file info structure into the one nim uses.
   ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows,
   ## or a 'Stat' structure on posix
   when defined(windows):
-    template merge(a, b): untyped =
-      int64(
+    template merge[T](a, b): untyped =
+       cast[T](
         (uint64(cast[uint32](a))) or
         (uint64(cast[uint32](b)) shl 32)
        )
     formalInfo.id.device = rawInfo.dwVolumeSerialNumber
-    formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
-    formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
+    formalInfo.id.file = merge[FileId](rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh)
+    formalInfo.size = merge[BiggestInt](rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh)
     formalInfo.linkCount = rawInfo.nNumberOfLinks
     formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime))
     formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime))
@@ -3319,14 +811,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped =
     checkAndIncludeMode(S_IWOTH, fpOthersWrite)
     checkAndIncludeMode(S_IXOTH, fpOthersExec)
 
-    formalInfo.kind =
+    (formalInfo.kind, formalInfo.isSpecial) =
       if S_ISDIR(rawInfo.st_mode):
-        pcDir
+        (pcDir, false)
       elif S_ISLNK(rawInfo.st_mode):
         assert(path != "") # symlinks can't occur for file handles
         getSymlinkFileKind(path)
       else:
-        pcFile
+        (pcFile, not S_ISREG(rawInfo.st_mode))
 
 when defined(js):
   when not declared(FileHandle):
@@ -3379,7 +871,8 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget.
   ##
   ## When `followSymlink` is true (default), symlinks are followed and the
   ## information retrieved is information related to the symlink's target.
-  ## Otherwise, information on the symlink itself is retrieved.
+  ## Otherwise, information on the symlink itself is retrieved (however,
+  ## field `isSpecial` is still determined from the target on Unix).
   ##
   ## If the information cannot be retrieved, such as when the path doesn't
   ## exist, or when permission restrictions prevent the program from retrieving
@@ -3461,10 +954,7 @@ proc isHidden*(path: string): bool {.noWeirdTarget.} =
       assert ".foo/".isHidden
 
   when defined(windows):
-    when useWinUnicode:
-      wrapUnary(attributes, getFileAttributesW, path)
-    else:
-      var attributes = getFileAttributesA(path)
+    wrapUnary(attributes, getFileAttributesW, path)
     if attributes != -1'i32:
       result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32
   else:
@@ -3500,14 +990,21 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} =
     discard h.closeHandle
     if res == 0'i32: raiseOSError(osLastError(), file)
 
-func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1), deprecated: "Deprecated since v1.5.1".} =
-  ## Returns true if ``filename`` is valid for crossplatform use.
+
+func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1).} =
+  ## Returns `true` if `filename` is valid for crossplatform use.
   ##
   ## This is useful if you want to copy or save files across Windows, Linux, Mac, etc.
-  ## You can pass full paths as argument too, but func only checks filenames.
-  ##
   ## It uses `invalidFilenameChars`, `invalidFilenames` and `maxLen` to verify the specified `filename`.
   ##
+  ## See also:
+  ##
+  ## * https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception
+  ## * https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
+  ## * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
+  ##
+  ## .. warning:: This only checks filenames, not whole paths
+  ##    (because basically you can mount anything as a path on Linux).
   runnableExamples:
     assert not isValidFilename(" foo")     # Leading white space
     assert not isValidFilename("foo ")     # Trailing white space
@@ -3518,9 +1015,6 @@ func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1
     assert not isValidFilename("")         # Empty string
     assert not isValidFilename("foo/")     # Filename is empty
 
-  # https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception
-  # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
-  # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
   result = true
   let f = filename.splitFile()
   if unlikely(f.name.len + f.ext.len > maxLen or f.name.len == 0 or
@@ -3529,11 +1023,10 @@ func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1
   for invalid in invalidFilenames:
     if cmpIgnoreCase(f.name, invalid) == 0: return false
 
+
 # deprecated declarations
-when not defined(nimscript):
-  when not defined(js): # `noNimJs` doesn't work with templates, this should improve.
-    template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} =
-      fileExists(args)
-    template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} =
-      dirExists(args)
-  # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue
+when not weirdTarget:
+  template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} =
+    fileExists(args)
+  template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} =
+    dirExists(args)
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index a0079cf95..c304ecca6 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -18,21 +18,23 @@
 include "system/inclrtl"
 
 import
-  strutils, os, strtabs, streams, cpuinfo, streamwrapper,
-  std/private/since
+  std/[strutils, os, strtabs, streams, cpuinfo, streamwrapper,
+  private/since]
 
 export quoteShell, quoteShellWindows, quoteShellPosix
 
 when defined(windows):
-  import winlean
+  import std/winlean
 else:
-  import posix
+  import std/posix
 
 when defined(linux) and defined(useClone):
-  import linux
+  import std/linux
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
+  when defined(windows):
+    import std/widestrs
 
 
 type
@@ -74,7 +76,7 @@ type
 proc execProcess*(command: string, workingDir: string = "",
     args: openArray[string] = [], env: StringTableRef = nil,
     options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}):
-  string {.rtl, extern: "nosp$1",
+  string {.rtl, extern: "nosp$1", raises: [OSError, IOError],
                   tags: [ExecIOEffect, ReadIOEffect, RootEffect].}
   ## A convenience procedure that executes ``command`` with ``startProcess``
   ## and returns its output as a string.
@@ -89,12 +91,12 @@ proc execProcess*(command: string, workingDir: string = "",
   ## * `execCmd proc <#execCmd,string>`_
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
-  ##  let outp = execProcess("nim", args=["c", "-r", "mytestfile.nim"], options={poUsePath})
-  ##  let outp_shell = execProcess("nim c -r mytestfile.nim")
-  ##  # Note: outp may have an interleave of text from the nim compile
-  ##  # and any output from mytestfile when it runs
+  ##   ```Nim
+  ##   let outp = execProcess("nim", args=["c", "-r", "mytestfile.nim"], options={poUsePath})
+  ##   let outp_shell = execProcess("nim c -r mytestfile.nim")
+  ##   # Note: outp may have an interleave of text from the nim compile
+  ##   # and any output from mytestfile when it runs
+  ##   ```
 
 proc execCmd*(command: string): int {.rtl, extern: "nosp$1",
     tags: [ExecIOEffect, ReadIOEffect, RootEffect].}
@@ -111,14 +113,14 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1",
   ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
-  ##  let errC = execCmd("nim c -r mytestfile.nim")
+  ##   ```Nim
+  ##   let errC = execCmd("nim c -r mytestfile.nim")
+  ##   ```
 
 proc startProcess*(command: string, workingDir: string = "",
     args: openArray[string] = [], env: StringTableRef = nil,
     options: set[ProcessOption] = {poStdErrToStdOut}):
-  owned(Process) {.rtl, extern: "nosp$1",
+  owned(Process) {.rtl, extern: "nosp$1", raises: [OSError, IOError],
                    tags: [ExecIOEffect, ReadEnvEffect, RootEffect].}
   ## Starts a process. `Command` is the executable file, `workingDir` is the
   ## process's working directory. If ``workingDir == ""`` the current directory
@@ -150,7 +152,7 @@ proc startProcess*(command: string, workingDir: string = "",
   ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
   ## * `execCmd proc <#execCmd,string>`_
 
-proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].}
+proc close*(p: Process) {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [WriteIOEffect].}
   ## When the process has finished executing, cleanup related handles.
   ##
   ## .. warning:: If the process has not finished executing, this will forcibly
@@ -199,7 +201,7 @@ proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].}
   ## * `terminate proc <#terminate,Process>`_
   ## * `posix_utils.sendSignal(pid: Pid, signal: int) <posix_utils.html#sendSignal,Pid,int>`_
 
-proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].}
+proc running*(p: Process): bool {.rtl, extern: "nosp$1", raises: [OSError], tags: [].}
   ## Returns true if the process `p` is still running. Returns immediately.
 
 proc processID*(p: Process): int {.rtl, extern: "nosp$1".} =
@@ -210,7 +212,7 @@ proc processID*(p: Process): int {.rtl, extern: "nosp$1".} =
   return p.id
 
 proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
-    extern: "nosp$1", tags: [].}
+    extern: "nosp$1", raises: [OSError, ValueError], tags: [TimeEffect].}
   ## Waits for the process to finish and returns `p`'s error code.
   ##
   ## .. warning:: Be careful when using `waitForExit` for processes created without
@@ -218,8 +220,12 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
   ##
   ## On posix, if the process has exited because of a signal, 128 + signal
   ## number will be returned.
+  ##
+  ## .. warning:: When working with `timeout` parameters, remember that the value is 
+  ##   typically expressed in milliseconds, and ensure that the correct unit of time
+  ##   is used to avoid unexpected behavior.
 
-proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].}
+proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], tags: [].}
   ## Return `-1` if the process is still running. Otherwise the process' exit code.
   ##
   ## On posix, if the process has exited because of a signal, 128 + signal
@@ -235,7 +241,7 @@ proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
   ## * `outputStream proc <#outputStream,Process>`_
   ## * `errorStream proc <#errorStream,Process>`_
 
-proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
+proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [].}
   ## Returns ``p``'s output stream for reading from.
   ##
   ## You cannot perform peek/write/setOption operations to this stream.
@@ -287,7 +293,7 @@ proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [],
   ## * `errorStream proc <#errorStream,Process>`_
   ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_
 
-proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
+proc inputHandle*(p: Process): FileHandle {.rtl, raises: [], extern: "nosp$1",
   tags: [].} =
   ## Returns ``p``'s input file handle for writing to.
   ##
@@ -300,7 +306,7 @@ proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
   result = p.inHandle
 
 proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
-    tags: [].} =
+    raises: [], tags: [].} =
   ## Returns ``p``'s output file handle for reading from.
   ##
   ## .. warning:: The returned `FileHandle` should not be closed manually as
@@ -312,7 +318,7 @@ proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
   result = p.outHandle
 
 proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
-    tags: [].} =
+    raises: [], tags: [].} =
   ## Returns ``p``'s error file handle for reading from.
   ##
   ## .. warning:: The returned `FileHandle` should not be closed manually as
@@ -323,7 +329,7 @@ proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1",
   ## * `outputHandle proc <#outputHandle,Process>`_
   result = p.errHandle
 
-proc countProcessors*(): int {.rtl, extern: "nosp$1".} =
+proc countProcessors*(): int {.rtl, extern: "nosp$1", raises: [].} =
   ## Returns the number of the processors/cores the machine has.
   ## Returns 0 if it cannot be detected.
   ## It is implemented just calling `cpuinfo.countProcessors`.
@@ -337,6 +343,7 @@ proc execProcesses*(cmds: openArray[string],
     beforeRunEvent: proc(idx: int) = nil,
     afterRunEvent: proc(idx: int, p: Process) = nil):
   int {.rtl, extern: "nosp$1",
+        raises: [ValueError, OSError, IOError],
         tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect],
         effectsOf: [beforeRunEvent, afterRunEvent].} =
   ## Executes the commands `cmds` in parallel.
@@ -450,7 +457,7 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
-iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags: [ReadIOEffect].} =
+iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
   ## Convenience iterator for working with `startProcess` to read data from a
   ## background process.
   ##
@@ -458,8 +465,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags:
   ## * `readLines proc <#readLines,Process>`_
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
   ##   var ps: seq[Process]
   ##   for prog in ["a", "b"]: # run 2 progs in parallel
@@ -471,6 +477,7 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags:
   ##       i.inc
   ##       if i > 100: break
   ##     p.close
+  ##   ```
   var outp = p.outputStream
   var line = newStringOfCap(120)
   while outp.readLine(line):
@@ -479,7 +486,8 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags:
     yield line
   discard waitForExit(p)
 
-proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} =
+proc readLines*(p: Process): (seq[string], int) {.since: (1, 3),
+    raises: [OSError, IOError, ValueError], tags: [ReadIOEffect, TimeEffect].} =
   ## Convenience function for working with `startProcess` to read data from a
   ## background process.
   ##
@@ -487,8 +495,7 @@ proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} =
   ## * `lines iterator <#lines.i,Process>`_
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   const opts = {poUsePath, poDaemon, poStdErrToStdOut}
   ##   var ps: seq[Process]
   ##   for prog in ["a", "b"]: # run 2 progs in parallel
@@ -498,6 +505,7 @@ proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} =
   ##     if exCode != 0:
   ##       for line in lines: echo line
   ##     p.close
+  ##   ```
   for line in p.lines: result[0].add(line)
   result[1] = p.peekExitCode
 
@@ -566,12 +574,8 @@ when defined(windows) and not defined(useNimRtl):
     if a == 0: raiseOSError(osLastError())
 
   proc newFileHandleStream(handle: Handle): owned FileHandleStream =
-    new(result)
-    result.handle = handle
-    result.closeImpl = hsClose
-    result.atEndImpl = hsAtEnd
-    result.readDataImpl = hsReadData
-    result.writeDataImpl = hsWriteData
+    result = FileHandleStream(handle: handle, closeImpl: hsClose, atEndImpl: hsAtEnd,
+      readDataImpl: hsReadData, writeDataImpl: hsWriteData)
 
   proc buildCommandLine(a: string, args: openArray[string]): string =
     result = quoteShell(a)
@@ -714,22 +718,15 @@ when defined(windows) and not defined(useNimRtl):
     if len(workingDir) > 0: wd = workingDir
     if env != nil: e = buildEnv(env)
     if poEchoCmd in options: echo($cmdl)
-    when useWinUnicode:
-      var tmp = newWideCString(cmdl)
-      var ee =
-        if e.str.isNil: newWideCString(cstring(nil))
-        else: newWideCString(e.str, e.len)
-      var wwd = newWideCString(wd)
-      var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT
-      if poDaemon in options: flags = flags or CREATE_NO_WINDOW
-      success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
-        ee, wwd, si, procInfo)
-    else:
-      var ee =
-        if e.str.isNil: cstring(nil)
-        else: cstring(e.str)
-      success = winlean.createProcessA(nil,
-        cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wd, si, procInfo)
+    var tmp = newWideCString(cmdl)
+    var ee =
+      if e.str.isNil: newWideCString(cstring(nil))
+      else: newWideCString(e.str, e.len)
+    var wwd = newWideCString(wd)
+    var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT
+    if poDaemon in options: flags = flags or CREATE_NO_WINDOW
+    success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags,
+      ee, wwd, si, procInfo)
     let lastError = osLastError()
 
     if poParentStreams notin options:
@@ -745,7 +742,7 @@ when defined(windows) and not defined(useNimRtl):
       const errFileNotFound = 2.int
       if lastError.int in {errInvalidParameter, errFileNotFound}:
         raiseOSError(lastError,
-            "Requested command not found: '$1'. OS error:" % command)
+              "Requested command not found: '" & command & "'. OS error:")
       else:
         raiseOSError(lastError, command)
     result.fProcessHandle = procInfo.hProcess
@@ -870,13 +867,9 @@ when defined(windows) and not defined(useNimRtl):
     si.hStdError = getStdHandle(STD_ERROR_HANDLE)
     si.hStdInput = getStdHandle(STD_INPUT_HANDLE)
     si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE)
-    when useWinUnicode:
-      var c = newWideCString(command)
-      var res = winlean.createProcessW(nil, c, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
-    else:
-      var res = winlean.createProcessA(nil, command, nil, nil, 0,
-        NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
+    var c = newWideCString(command)
+    var res = winlean.createProcessW(nil, c, nil, nil, 0,
+      NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo)
     if res == 0:
       raiseOSError(osLastError())
     else:
@@ -953,13 +946,13 @@ elif not defined(useNimRtl):
                              not defined(useClone) and not defined(linux)
   when useProcessAuxSpawn:
     proc startProcessAuxSpawn(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
   else:
     proc startProcessAuxFork(data: StartProcessData): Pid {.
-      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.}
     {.push stacktrace: off, profiler: off.}
     proc startProcessAfterFork(data: ptr StartProcessData) {.
-      tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.}
+      raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.}
     {.pop.}
 
   proc startProcess(command: string, workingDir: string = "",
@@ -1060,13 +1053,15 @@ elif not defined(useNimRtl):
       var mask: Sigset
       chck sigemptyset(mask)
       chck posix_spawnattr_setsigmask(attr, mask)
-      if poDaemon in data.options:
-        chck posix_spawnattr_setpgroup(attr, 0'i32)
+      when not defined(nuttx):
+        if poDaemon in data.options:
+          chck posix_spawnattr_setpgroup(attr, 0'i32)
 
       var flags = POSIX_SPAWN_USEVFORK or
                   POSIX_SPAWN_SETSIGMASK
-      if poDaemon in data.options:
-        flags = flags or POSIX_SPAWN_SETPGROUP
+      when not defined(nuttx):
+        if poDaemon in data.options:
+          flags = flags or POSIX_SPAWN_SETPGROUP
       chck posix_spawnattr_setflags(attr, flags)
 
       if not (poParentStreams in data.options):
@@ -1128,15 +1123,13 @@ elif not defined(useNimRtl):
       var error: cint
       let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error))
       if sizeRead == sizeof(error):
-        raiseOSError(osLastError(),
-                     "Could not find command: '$1'. OS error: $2" %
-                      [$data.sysCommand, $strerror(error)])
+        raiseOSError(OSErrorCode(error),
+                      "Could not find command: '" & $data.sysCommand & "'. OS error: " & $strerror(error))
 
       return pid
 
     {.push stacktrace: off, profiler: off.}
-    proc startProcessFail(data: ptr StartProcessData) =
-      var error: cint = errno
+    proc startProcessFail(data: ptr StartProcessData, error: cint = errno) =
       discard write(data.pErrorPipe[writeIdx], addr error, sizeof(error))
       exitnow(1)
 
@@ -1173,7 +1166,11 @@ elif not defined(useNimRtl):
       if (poUsePath in data.options):
         when defined(uClibc) or defined(linux) or defined(haiku):
           # uClibc environment (OpenWrt included) doesn't have the full execvpe
-          let exe = findExe(data.sysCommand)
+          var exe: string
+          try:
+            exe = findExe(data.sysCommand)
+          except OSError as e:
+            startProcessFail(data, e.errorCode)
           discard execve(exe.cstring, data.sysArgs, data.sysEnv)
         else:
           # MacOSX doesn't have execvpe, so we need workaround.
@@ -1237,7 +1234,7 @@ elif not defined(useNimRtl):
 
   when defined(macosx) or defined(freebsd) or defined(netbsd) or
        defined(openbsd) or defined(dragonfly):
-    import kqueue
+    import std/kqueue
 
     proc waitForExit(p: Process, timeout: int = -1): int =
       if p.exitFlag:
@@ -1354,119 +1351,68 @@ elif not defined(useNimRtl):
             p.exitStatus = status
             break
           else:
-            doAssert false, "unreachable!"
+            raiseAssert "unreachable!"
 
       result = exitStatusLikeShell(p.exitStatus)
 
   else:
-    import times
-
-    const
-      hasThreadSupport = compileOption("threads") and not defined(nimscript)
+    import std/times except getTime
+    import std/monotimes
 
     proc waitForExit(p: Process, timeout: int = -1): int =
-      template adjustTimeout(t, s, e: Timespec) =
-        var diff: int
-        var b: Timespec
-        b.tv_sec = e.tv_sec
-        b.tv_nsec = e.tv_nsec
-        e.tv_sec = e.tv_sec - s.tv_sec
-        if e.tv_nsec >= s.tv_nsec:
-          e.tv_nsec -= s.tv_nsec
-        else:
-          if e.tv_sec == posix.Time(0):
-            raise newException(ValueError, "System time was modified")
-          else:
-            diff = s.tv_nsec - e.tv_nsec
-            e.tv_nsec = 1_000_000_000 - diff
-        t.tv_sec = t.tv_sec - e.tv_sec
-        if t.tv_nsec >= e.tv_nsec:
-          t.tv_nsec -= e.tv_nsec
-        else:
-          t.tv_sec = t.tv_sec - posix.Time(1)
-          diff = e.tv_nsec - t.tv_nsec
-          t.tv_nsec = 1_000_000_000 - diff
-        s.tv_sec = b.tv_sec
-        s.tv_nsec = b.tv_nsec
-
       if p.exitFlag:
         return exitStatusLikeShell(p.exitStatus)
 
-      if timeout == -1:
-        var status: cint = 1
+      if timeout < 0:
+        # Backwards compatibility with previous verison to
+        # handle cases where timeout == -1, but extend
+        # to handle cases where timeout < 0
+        var status: cint
         if waitpid(p.id, status, 0) < 0:
           raiseOSError(osLastError())
         p.exitFlag = true
         p.exitStatus = status
       else:
-        var nmask, omask: Sigset
-        var sinfo: SigInfo
-        var stspec, enspec, tmspec: Timespec
-
-        discard sigemptyset(nmask)
-        discard sigemptyset(omask)
-        discard sigaddset(nmask, SIGCHLD)
-
-        when hasThreadSupport:
-          if pthread_sigmask(SIG_BLOCK, nmask, omask) == -1:
-            raiseOSError(osLastError())
-        else:
-          if sigprocmask(SIG_BLOCK, nmask, omask) == -1:
-            raiseOSError(osLastError())
-
-        if timeout >= 1000:
-          tmspec.tv_sec = posix.Time(timeout div 1_000)
-          tmspec.tv_nsec = (timeout %% 1_000) * 1_000_000
-        else:
-          tmspec.tv_sec = posix.Time(0)
-          tmspec.tv_nsec = (timeout * 1_000_000)
-
-        try:
-          if clock_gettime(CLOCK_REALTIME, stspec) == -1:
-            raiseOSError(osLastError())
-          while true:
-            let res = sigtimedwait(nmask, sinfo, tmspec)
-            if res == SIGCHLD:
-              if sinfo.si_pid == p.id:
-                var status: cint = 1
-                if waitpid(p.id, status, 0) < 0:
-                  raiseOSError(osLastError())
-                p.exitFlag = true
-                p.exitStatus = status
-                break
-              else:
-                # we have SIGCHLD, but not for process we are waiting,
-                # so we need to adjust timeout value and continue
-                if clock_gettime(CLOCK_REALTIME, enspec) == -1:
-                  raiseOSError(osLastError())
-                adjustTimeout(tmspec, stspec, enspec)
-            elif res < 0:
-              let err = osLastError()
-              if err.cint == EINTR:
-                # we have received another signal, so we need to
-                # adjust timeout and continue
-                if clock_gettime(CLOCK_REALTIME, enspec) == -1:
-                  raiseOSError(osLastError())
-                adjustTimeout(tmspec, stspec, enspec)
-              elif err.cint == EAGAIN:
-                # timeout expired, so we trying to kill process
-                if posix.kill(p.id, SIGKILL) == -1:
-                  raiseOSError(osLastError())
-                var status: cint = 1
-                if waitpid(p.id, status, 0) < 0:
-                  raiseOSError(osLastError())
-                p.exitFlag = true
-                p.exitStatus = status
-                break
-              else:
-                raiseOSError(err)
-        finally:
-          when hasThreadSupport:
-            if pthread_sigmask(SIG_UNBLOCK, nmask, omask) == -1:
-              raiseOSError(osLastError())
+        # Max 50ms delay
+        const maxWait = initDuration(milliseconds = 50)
+        let wait = initDuration(milliseconds = timeout)
+        let deadline = getMonoTime() + wait
+        # starting 50μs delay
+        var delay = initDuration(microseconds = 50)
+        
+        while true:
+          var status: cint
+          let pid = waitpid(p.id, status, WNOHANG)
+          if p.id == pid :
+            p.exitFlag = true
+            p.exitStatus = status
+            break
+          elif pid.int == -1:
+            raiseOsError(osLastError())
           else:
-            if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
-              raiseOSError(osLastError())
+            # Continue waiting if needed
+            if getMonoTime() >= deadline:
+              # Previous version of `waitForExit`
+              # foricibly killed the process.
+              # We keep this so we don't break programs
+              # that depend on this behavior
+              if posix.kill(p.id, SIGKILL) < 0:
+                raiseOSError(osLastError())
+            else:
+              const max = 1_000_000_000
+              let 
+                newWait = getMonoTime() + delay
+                ticks = newWait.ticks()
+                ns = ticks mod max
+                secs = ticks div max
+              var 
+                waitSpec: TimeSpec
+                unused: Timespec
+              waitSpec.tv_sec = posix.Time(secs)
+              waitSpec.tv_nsec = clong ns 
+              discard posix.clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, waitSpec, unused)
+              let remaining = deadline - getMonoTime()
+              delay = min([delay * 2, remaining, maxWait])
 
       result = exitStatusLikeShell(p.exitStatus)
 
@@ -1576,7 +1522,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
                 poStdErrToStdOut, poUsePath}, env: StringTableRef = nil,
                 workingDir = "", input = ""): tuple[
                 output: string,
-                exitCode: int] {.tags:
+                exitCode: int] {.raises: [OSError, IOError], tags:
                 [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} =
   ## A convenience proc that runs the `command`, and returns its `output` and
   ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`.
@@ -1593,8 +1539,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
   ##   <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4")
   ##   import std/[strutils, strtabs]
   ##   stripLineEnd(result[0]) ## portable way to remove trailing newline, if any
@@ -1603,6 +1548,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = {
   ##   when defined(posix):
   ##     assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0)
   ##     assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0)
+  ##   ```
 
   when (NimMajor, NimMinor, NimPatch) < (1, 3, 5):
     doAssert input.len == 0
diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim
index 54584a253..8a43daf54 100644
--- a/lib/pure/parsecfg.nim
+++ b/lib/pure/parsecfg.nim
@@ -45,9 +45,7 @@ runnableExamples("-r:off"):
 ## Configuration file example
 ]##
 
-##
-## .. code-block:: nim
-##
+##     ```none
 ##     charset = "utf-8"
 ##     [Package]
 ##     name = "hello"
@@ -55,6 +53,7 @@ runnableExamples("-r:off"):
 ##     [Author]
 ##     name = "nim-lang"
 ##     website = "nim-lang.org"
+##     ```
 
 ##[
 ## Creating a configuration file
@@ -171,7 +170,7 @@ runnableExamples:
   assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False"
   assert dict.getSectionValue(section4, "purpose") == "formatting for readability"
 
-import strutils, lexbase, streams, tables
+import std/[strutils, lexbase, streams, tables]
 import std/private/decode_helpers
 import std/private/since
 
@@ -453,7 +452,7 @@ proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent =
   if c.tok.kind == tkSymbol:
     case kind
     of cfgOption, cfgKeyValuePair:
-      result = CfgEvent(kind: kind, key: c.tok.literal, value: "")
+      result = CfgEvent(kind: kind, key: c.tok.literal.move, value: "")
     else: discard
     rawGetTok(c, c.tok)
     if c.tok.kind in {tkEquals, tkColon}:
@@ -482,7 +481,7 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} =
   of tkBracketLe:
     rawGetTok(c, c.tok)
     if c.tok.kind == tkSymbol:
-      result = CfgEvent(kind: cfgSectionStart, section: c.tok.literal)
+      result = CfgEvent(kind: cfgSectionStart, section: c.tok.literal.move)
     else:
       result = CfgEvent(kind: cfgError,
         msg: errorStr(c, "symbol expected, but found: " & c.tok.literal))
diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim
index a8d1cfaab..c7bf0c9c1 100644
--- a/lib/pure/parsecsv.nim
+++ b/lib/pure/parsecsv.nim
@@ -13,7 +13,7 @@
 ## Basic usage
 ## ===========
 ##
-## .. code-block:: nim
+##   ```nim
 ##   import std/parsecsv
 ##   from std/os import paramStr
 ##   from std/streams import newFileStream
@@ -29,11 +29,12 @@
 ##     for val in items(x.row):
 ##       echo "##", val, "##"
 ##   close(x)
+##   ```
 ##
 ## For CSV files with a header row, the header can be read and then used as a
 ## reference for item access with `rowEntry <#rowEntry,CsvParser,string>`_:
 ##
-## .. code-block:: nim
+##   ```nim
 ##   import std/parsecsv
 ##
 ##   # Prepare a file
@@ -52,6 +53,7 @@
 ##     for col in items(p.headers):
 ##       echo "##", col, ":", p.rowEntry(col), "##"
 ##   p.close()
+##   ```
 ##
 ## See also
 ## ========
@@ -65,7 +67,7 @@
 ## * `parsesql module <parsesql.html>`_ for a SQL parser
 ## * `other parsers <lib.html#pure-libraries-parsers>`_ for other parsers
 
-import lexbase, streams
+import std/[lexbase, streams]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -347,7 +349,7 @@ proc rowEntry*(self: var CsvParser, entry: string): var string =
     raise newException(KeyError, "Entry `" & entry & "` doesn't exist")
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: CsvParser
diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim
index fcbcf8e36..9292a8596 100644
--- a/lib/pure/parsejson.nim
+++ b/lib/pure/parsejson.nim
@@ -11,7 +11,7 @@
 ## and exported by the `json` standard library
 ## module, but can also be used in its own right.
 
-import strutils, lexbase, streams, unicode
+import std/[strutils, lexbase, streams, unicode]
 import std/private/decode_helpers
 
 when defined(nimPreviewSlimSystem):
diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim
index f8d03e092..03f151b66 100644
--- a/lib/pure/parseopt.nim
+++ b/lib/pure/parseopt.nim
@@ -48,7 +48,7 @@
 ##
 ## Here is an example:
 ##
-## .. code-block::
+##   ```Nim
 ##   import std/parseopt
 ##
 ##   var p = initOptParser("-ab -e:5 --foo --bar=20 file.txt")
@@ -71,10 +71,34 @@
 ##   # Option: foo
 ##   # Option and value: bar, 20
 ##   # Argument: file.txt
+##   ```
 ##
 ## The `getopt iterator<#getopt.i,OptParser>`_, which is provided for
 ## convenience, can be used to iterate through all command line options as well.
 ##
+## To set a default value for a variable assigned through `getopt` and accept arguments from the cmd line.
+## Assign the default value to a variable before parsing.
+## Then set the variable to the new value while parsing.
+##
+## Here is an example:
+##
+##   ```Nim
+##   import std/parseopt
+##
+##   var varName: string = "defaultValue"
+##
+##   for kind, key, val in getopt():
+##     case kind
+##     of cmdArgument:
+##       discard
+##     of cmdLongOption, cmdShortOption:
+##       case key:
+##       of "varName": # --varName:<value> in the console when executing
+##         varName = val # do input sanitization in production systems
+##     of cmdEnd:
+##       discard
+##   ```
+##
 ## `shortNoVal` and `longNoVal`
 ## ============================
 ##
@@ -98,7 +122,7 @@
 ## `shortNoVal` and `longNoVal`, which is the default, and providing
 ## arguments for those two parameters:
 ##
-## .. code-block::
+##   ```Nim
 ##   import std/parseopt
 ##
 ##   proc printToken(kind: CmdLineKind, key: string, val: string) =
@@ -132,6 +156,7 @@
 ##   # Output:
 ##   # Option and value: j, 4
 ##   # Option and value: first, bar
+##   ```
 ##
 ## See also
 ## ========
@@ -151,7 +176,8 @@
 
 include "system/inclrtl"
 
-import os
+import std/strutils
+import std/os
 
 type
   CmdLineKind* = enum ## The detected command line token.
@@ -194,34 +220,6 @@ proc parseWord(s: string, i: int, w: var string,
 
 proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
                     longNoVal: seq[string] = @[];
-                    allowWhitespaceAfterColon = true): OptParser
-
-proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
-                    longNoVal: seq[string] = @[];
-                    allowWhitespaceAfterColon = true): OptParser =
-  ## Initializes the command line parser.
-  ##
-  ## If `cmdline == ""`, the real command line as provided by the
-  ## `os` module is retrieved instead if it is available. If the
-  ## command line is not available, a `ValueError` will be raised.
-  ##
-  ## `shortNoVal` and `longNoVal` are used to specify which options
-  ## do not take values. See the `documentation about these
-  ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
-  ## how this affects parsing.
-  ##
-  ## See also:
-  ## * `getopt iterator<#getopt.i,OptParser>`_
-  runnableExamples:
-    var p = initOptParser()
-    p = initOptParser("--left --debug:3 -l -r:2")
-    p = initOptParser("--left --debug:3 -l -r:2",
-                      shortNoVal = {'l'}, longNoVal = @["left"])
-
-  initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon)
-
-proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
-                    longNoVal: seq[string] = @[];
                     allowWhitespaceAfterColon = true): OptParser =
   ## Initializes the command line parser.
   ##
@@ -252,18 +250,55 @@ proc initOptParser*(cmdline: seq[string], shortNoVal: set[char] = {},
       result.cmds[i] = cmdline[i]
   else:
     when declared(paramCount):
-      result.cmds = newSeq[string](paramCount())
-      for i in countup(1, paramCount()):
-        result.cmds[i-1] = paramStr(i)
+      when defined(nimscript):
+        var ctr = 0
+        var firstNimsFound = false
+        for i in countup(0, paramCount()):
+          if firstNimsFound: 
+            result.cmds[ctr] = paramStr(i)
+            inc ctr, 1
+          if paramStr(i).endsWith(".nims") and not firstNimsFound:
+            firstNimsFound = true 
+            result.cmds = newSeq[string](paramCount()-i)
+      else:
+        result.cmds = newSeq[string](paramCount())
+        for i in countup(1, paramCount()):
+          result.cmds[i-1] = paramStr(i)
     else:
       # we cannot provide this for NimRtl creation on Posix, because we can't
       # access the command line arguments then!
-      doAssert false, "empty command line given but" &
+      raiseAssert "empty command line given but" &
         " real command line is not accessible"
   result.kind = cmdEnd
   result.key = ""
   result.val = ""
 
+proc initOptParser*(cmdline = "", shortNoVal: set[char] = {},
+                    longNoVal: seq[string] = @[];
+                    allowWhitespaceAfterColon = true): OptParser =
+  ## Initializes the command line parser.
+  ##
+  ## If `cmdline == ""`, the real command line as provided by the
+  ## `os` module is retrieved instead if it is available. If the
+  ## command line is not available, a `ValueError` will be raised.
+  ##
+  ## `shortNoVal` and `longNoVal` are used to specify which options
+  ## do not take values. See the `documentation about these
+  ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
+  ## how this affects parsing.
+  ##
+  ## This does not provide a way of passing default values to arguments.
+  ##
+  ## See also:
+  ## * `getopt iterator<#getopt.i,OptParser>`_
+  runnableExamples:
+    var p = initOptParser()
+    p = initOptParser("--left --debug:3 -l -r:2")
+    p = initOptParser("--left --debug:3 -l -r:2",
+                      shortNoVal = {'l'}, longNoVal = @["left"])
+
+  initOptParser(parseCmdLine(cmdline), shortNoVal, longNoVal, allowWhitespaceAfterColon)
+
 proc handleShortOption(p: var OptParser; cmd: string) =
   var i = p.pos
   p.kind = cmdShortOption
@@ -356,7 +391,7 @@ proc next*(p: var OptParser) {.rtl, extern: "npo$1".} =
       handleShortOption(p, p.cmds[p.idx])
   else:
     p.kind = cmdArgument
-    p.key =  p.cmds[p.idx]
+    p.key = p.cmds[p.idx]
     inc p.idx
     p.pos = 0
 
@@ -368,15 +403,14 @@ when declared(quoteShellCommand):
     ## * `remainingArgs proc<#remainingArgs,OptParser>`_
     ##
     ## **Examples:**
-    ##
-    ## .. code-block::
+    ##   ```Nim
     ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
     ##   while true:
     ##     p.next()
     ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
     ##       break
-    ##     else: continue
     ##   doAssert p.cmdLineRest == "foo.txt bar.txt"
+    ##   ```
     result = p.cmds[p.idx .. ^1].quoteShellCommand
 
 proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} =
@@ -386,15 +420,14 @@ proc remainingArgs*(p: OptParser): seq[string] {.rtl, extern: "npo$1".} =
   ## * `cmdLineRest proc<#cmdLineRest,OptParser>`_
   ##
   ## **Examples:**
-  ##
-  ## .. code-block::
+  ##   ```Nim
   ##   var p = initOptParser("--left -r:2 -- foo.txt bar.txt")
   ##   while true:
   ##     p.next()
   ##     if p.kind == cmdLongOption and p.key == "":  # Look for "--"
   ##       break
-  ##     else: continue
   ##   doAssert p.remainingArgs == @["foo.txt", "bar.txt"]
+  ##   ```
   result = @[]
   for i in p.idx..<p.cmds.len: result.add p.cmds[i]
 
@@ -403,14 +436,15 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
   ## Convenience iterator for iterating over the given
   ## `OptParser<#OptParser>`_.
   ##
-  ## There is no need to check for `cmdEnd` while iterating.
+  ## There is no need to check for `cmdEnd` while iterating. If using `getopt`
+  ## with case switching, checking for `cmdEnd` is required.
   ##
   ## See also:
   ## * `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
+  ##   ```Nim
   ##   # these are placeholders, of course
   ##   proc writeHelp() = discard
   ##   proc writeVersion() = discard
@@ -430,6 +464,7 @@ iterator getopt*(p: var OptParser): tuple[kind: CmdLineKind, key,
   ##   if filename == "":
   ##     # no filename has been given, so we show the help
   ##     writeHelp()
+  ##   ```
   p.pos = 0
   p.idx = 0
   while true:
@@ -451,15 +486,15 @@ iterator getopt*(cmdline: seq[string] = @[],
   ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on
   ## how this affects parsing.
   ##
-  ## There is no need to check for `cmdEnd` while iterating.
+  ## There is no need to check for `cmdEnd` while iterating. If using `getopt`
+  ## with case switching, checking for `cmdEnd` is required.
   ##
   ## See also:
   ## * `initOptParser proc<#initOptParser,seq[string],set[char],seq[string]>`_
   ##
   ## **Examples:**
   ##
-  ## .. code-block::
-  ##
+  ##   ```Nim
   ##   # these are placeholders, of course
   ##   proc writeHelp() = discard
   ##   proc writeVersion() = discard
@@ -479,6 +514,7 @@ iterator getopt*(cmdline: seq[string] = @[],
   ##   if filename == "":
   ##     # no filename has been written, so we show the help
   ##     writeHelp()
+  ##   ```
   var p = initOptParser(cmdline, shortNoVal = shortNoVal,
       longNoVal = longNoVal)
   while true:
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index 3af840e29..a7c938d01 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -12,7 +12,7 @@
 ##
 ## Unstable API.
 
-import strutils, lexbase
+import std/[strutils, lexbase]
 import std/private/decode_helpers
 
 when defined(nimPreviewSlimSystem):
@@ -60,7 +60,7 @@ const
 
   reservedKeywords = @[
     # statements
-    "select", "from", "where", "group", "limit", "having",
+    "select", "from", "where", "group", "limit", "offset", "having",
     # functions
     "count",
   ]
@@ -509,6 +509,7 @@ type
     nkFromItemPair,
     nkGroup,
     nkLimit,
+    nkOffset,
     nkHaving,
     nkOrder,
     nkJoin,
@@ -1126,6 +1127,11 @@ proc parseSelect(p: var SqlParser): SqlNode =
     var l = newNode(nkLimit)
     l.add(parseExpr(p))
     result.add(l)
+  if isKeyw(p, "offset"):
+    getTok(p)
+    var o = newNode(nkOffset)
+    o.add(parseExpr(p))
+    result.add(o)
 
 proc parseStmt(p: var SqlParser; parent: SqlNode) =
   if isKeyw(p, "create"):
@@ -1388,6 +1394,9 @@ proc ra(n: SqlNode, s: var SqlWriter) =
   of nkLimit:
     s.addKeyw("limit")
     s.addMulti(n)
+  of nkOffset:
+    s.addKeyw("offset")
+    s.addMulti(n)
   of nkHaving:
     s.addKeyw("having")
     s.addMulti(n)
@@ -1482,7 +1491,7 @@ proc treeRepr*(s: SqlNode): string =
   result = newStringOfCap(128)
   treeReprAux(s, 0, result)
 
-import streams
+import std/streams
 
 proc open(L: var SqlLexer, input: Stream, filename: string) =
   lexbase.open(L, input)
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 64949428f..2ca255fa0 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -12,29 +12,28 @@
 ##
 ## To unpack raw bytes look at the `streams <streams.html>`_ module.
 ##
-## .. code-block:: nim
-##    :test:
+##   ```nim test
+##   let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
+##   var outp: seq[string]
 ##
-##    let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
-##    var outp: seq[string]
+##   for log in logs:
+##     var res: string
+##     if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
+##       outp.add(res & " - " & captureBetween(log, ' ', '_'))
+##   doAssert outp == @["2019-01-10 - OK", "2019-01-11 - FAIL"]
+##   ```
 ##
-##    for log in logs:
-##      var res: string
-##      if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
-##        outp.add(res & " - " & captureBetween(log, ' ', '_'))
-##    doAssert outp == @["2019-01-10 - OK", "2019-01-11 - FAIL"]
+##   ```nim test
+##   from std/strutils import Digits, parseInt
 ##
-## .. code-block:: nim
-##    :test:
-##    from std/strutils import Digits, parseInt
-##
-##    let
-##      input1 = "2019 school start"
-##      input2 = "3 years back"
-##      startYear = input1[0 .. skipWhile(input1, Digits)-1] # 2019
-##      yearsBack = input2[0 .. skipWhile(input2, Digits)-1] # 3
-##      examYear = parseInt(startYear) + parseInt(yearsBack)
-##    doAssert "Examination is in " & $examYear == "Examination is in 2022"
+##   let
+##     input1 = "2019 school start"
+##     input2 = "3 years back"
+##     startYear = input1[0 .. skipWhile(input1, Digits)-1] # 2019
+##     yearsBack = input2[0 .. skipWhile(input2, Digits)-1] # 3
+##     examYear = parseInt(startYear) + parseInt(yearsBack)
+##   doAssert "Examination is in " & $examYear == "Examination is in 2022"
+##   ```
 ##
 ## **See also:**
 ## * `strutils module<strutils.html>`_ for combined and identical parsing proc's
@@ -50,6 +49,8 @@
 
 include "system/inclrtl"
 
+template toOa(s: string): openArray[char] = openArray[char](s)
+
 const
   Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
@@ -59,8 +60,7 @@ const
 proc toLower(c: char): char {.inline.} =
   result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
 
-proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseBin*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses a binary number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -89,7 +89,7 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
     doAssert num64 == 336784608873
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -104,10 +104,9 @@ proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseOct*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses an octal number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -136,7 +135,7 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseOct("2346475523464755", num64) == 16
     doAssert num64 == 86216859871725
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -151,10 +150,9 @@ proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
-    maxLen = 0): int {.noSideEffect.} =
+proc parseHex*[T: SomeInteger](s: openArray[char], number: var T, maxLen = 0): int {.noSideEffect.} =
   ## Parses a hexadecimal number and stores its value in ``number``.
   ##
   ## Returns the number of the parsed characters or 0 in case of an error.
@@ -184,7 +182,7 @@ proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
     var num64: int64
     doAssert parseHex("4E69ED4E69ED", num64) == 12
     doAssert num64 == 86216859871725
-  var i = start
+  var i = 0
   var output = T(0)
   var foundDigit = false
   let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
@@ -206,9 +204,9 @@ proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
     inc(i)
   if foundDigit:
     number = output
-    result = i - start
+    result = i
 
-proc parseIdent*(s: string, ident: var string, start = 0): int =
+proc parseIdent*(s: openArray[char], ident: var string): int =
   ## Parses an identifier and stores it in ``ident``. Returns
   ## the number of the parsed characters or 0 in case of an error.
   ## If error, the value of `ident` is not changed.
@@ -220,14 +218,14 @@ proc parseIdent*(s: string, ident: var string, start = 0): int =
     doAssert res == "ello"
     doAssert parseIdent("Hello World", res, 6) == 5
     doAssert res == "World"
-  var i = start
+  var i = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    ident = substr(s, start, i-1)
-    result = i-start
+    ident = substr(s.toOpenArray(0, i-1))
+    result = i
 
-proc parseIdent*(s: string, start = 0): string =
+proc parseIdent*(s: openArray[char]): string =
   ## Parses an identifier and returns it or an empty string in
   ## case of an error.
   runnableExamples:
@@ -236,13 +234,13 @@ proc parseIdent*(s: string, start = 0): string =
     doAssert parseIdent("Hello World", 5) == ""
     doAssert parseIdent("Hello World", 6) == "World"
   result = ""
-  var i = start
+  var i = 0
   if i < s.len and s[i] in IdentStartChars:
     inc(i)
     while i < s.len and s[i] in IdentChars: inc(i)
-    result = substr(s, start, i-1)
+    result = substr(s.toOpenArray(0, i - 1))
 
-proc parseChar*(s: string, c: var char, start = 0): int =
+proc parseChar*(s: openArray[char], c: var char): int =
   ## Parses a single character, stores it in `c` and returns 1.
   ## In case of error (if start >= s.len) it returns 0
   ## and the value of `c` is unchanged.
@@ -252,11 +250,11 @@ proc parseChar*(s: string, c: var char, start = 0): int =
     doAssert c == '\0'
     doAssert "nim".parseChar(c, 0) == 1
     doAssert c == 'n'
-  if start < s.len:
-    c = s[start]
+  if s.len > 0:
+    c = s[0]
     result = 1
 
-proc skipWhitespace*(s: string, start = 0): int {.inline.} =
+proc skipWhitespace*(s: openArray[char]): int {.inline.} =
   ## Skips the whitespace starting at ``s[start]``. Returns the number of
   ## skipped characters.
   runnableExamples:
@@ -265,9 +263,9 @@ proc skipWhitespace*(s: string, start = 0): int {.inline.} =
     doAssert skipWhitespace("Hello World", 5) == 1
     doAssert skipWhitespace("Hello  World", 5) == 2
   result = 0
-  while start+result < s.len and s[start+result] in Whitespace: inc(result)
+  while result < s.len and s[result] in Whitespace: inc(result)
 
-proc skip*(s, token: string, start = 0): int {.inline.} =
+proc skip*(s, token: openArray[char]): int {.inline.} =
   ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
   ## or 0 if there was no `token` at ``s[start]``.
   runnableExamples:
@@ -277,22 +275,22 @@ proc skip*(s, token: string, start = 0): int {.inline.} =
     doAssert skip("CAPlow", "CAP", 0) == 3
     doAssert skip("CAPlow", "cap", 0) == 0
   result = 0
-  while start+result < s.len and result < token.len and
-      s[result+start] == token[result]:
+  while result < s.len and result < token.len and
+      s[result] == token[result]:
     inc(result)
   if result != token.len: result = 0
 
-proc skipIgnoreCase*(s, token: string, start = 0): int =
+proc skipIgnoreCase*(s, token: openArray[char]): int =
   ## Same as `skip` but case is ignored for token matching.
   runnableExamples:
     doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
     doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
   result = 0
-  while start+result < s.len and result < token.len and
-      toLower(s[result+start]) == toLower(token[result]): inc(result)
+  while result < s.len and result < token.len and
+      toLower(s[result]) == toLower(token[result]): inc(result)
   if result != token.len: result = 0
 
-proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: set[char]): int {.inline.} =
   ## Skips all characters until one char from the set `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
@@ -301,9 +299,9 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
     doAssert skipUntil("Hello World", {'W'}, 0) == 6
     doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
   result = 0
-  while start+result < s.len and s[result+start] notin until: inc(result)
+  while result < s.len and s[result] notin until: inc(result)
 
-proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
+proc skipUntil*(s: openArray[char], until: char): int {.inline.} =
   ## Skips all characters until the char `until` is found
   ## or the end is reached.
   ## Returns number of characters skipped.
@@ -313,24 +311,23 @@ proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
     doAssert skipUntil("Hello World", 'W', 0) == 6
     doAssert skipUntil("Hello World", 'w', 0) == 11
   result = 0
-  while start+result < s.len and s[result+start] != until: inc(result)
+  while result < s.len and s[result] != 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.
+proc skipWhile*(s: openArray[char], toSkip: set[char]): int {.inline.} =
+  ## Skips all characters while one char from the set `toSkip` is found.
   ## Returns number of characters skipped.
   runnableExamples:
     doAssert skipWhile("Hello World", {'H', 'e'}) == 2
     doAssert skipWhile("Hello World", {'e'}) == 0
     doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
   result = 0
-  while start+result < s.len and s[result+start] in toSkip: inc(result)
+  while result < s.len and s[result] in toSkip: inc(result)
 
-proc fastSubstr(s: string; token: var string; start, length: int) =
+proc fastSubstr(s: openArray[char]; token: var string; length: int) =
   token.setLen length
-  for i in 0 ..< length: token[i] = s[i+start]
+  for i in 0 ..< length: token[i] = s[i]
 
-proc parseUntil*(s: string, token: var string, until: set[char],
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: set[char]): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters notin `until`.
@@ -342,14 +339,13 @@ proc parseUntil*(s: string, token: var string, until: set[char],
     doAssert myToken == "Hello "
     doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
     doAssert myToken == "lo "
-  var i = start
+  var i = 0
   while i < s.len and s[i] notin until: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: char,
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: char): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that is not the `until` character.
@@ -361,14 +357,13 @@ proc parseUntil*(s: string, token: var string, until: char,
     doAssert myToken == "Hell"
     doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
     doAssert myToken == "ll"
-  var i = start
+  var i = 0
   while i < s.len and s[i] != until: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseUntil*(s: string, token: var string, until: string,
-                 start = 0): int {.inline.} =
+proc parseUntil*(s: openArray[char], token: var string, until: string): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of any character that comes before the `until`  token.
@@ -382,7 +377,7 @@ proc parseUntil*(s: string, token: var string, until: string,
     if until.len == 0:
       token.setLen(0)
       return 0
-  var i = start
+  var i = 0
   while i < s.len:
     if until.len > 0 and s[i] == until[0]:
       var u = 1
@@ -390,12 +385,11 @@ proc parseUntil*(s: string, token: var string, until: string,
         inc u
       if u >= until.len: break
     inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc parseWhile*(s: string, token: var string, validChars: set[char],
-                 start = 0): int {.inline.} =
+proc parseWhile*(s: openArray[char], token: var string, validChars: set[char]): int {.inline.} =
   ## Parses a token and stores it in ``token``. Returns
   ## the number of the parsed characters or 0 in case of an error. A token
   ## consists of the characters in `validChars`.
@@ -405,22 +399,22 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
     doAssert myToken.len() == 0
     doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
     doAssert myToken == "Wor"
-  var i = start
+  var i = 0
   while i < s.len and s[i] in validChars: inc(i)
-  result = i-start
-  fastSubstr(s, token, start, result)
+  result = i
+  fastSubstr(s, token, result)
   #token = substr(s, start, i-1)
 
-proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
+proc captureBetween*(s: openArray[char], first: char, second = '\0'): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
   ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
   runnableExamples:
     doAssert captureBetween("Hello World", 'e') == "llo World"
     doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
-    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
-  var i = skipUntil(s, first, start)+1+start
+    doAssert captureBetween("Hello World".toOpenArray(6, "Hello World".high), 'l') == "d"
+  var i = skipUntil(s, first) + 1
   result = ""
-  discard s.parseUntil(result, if second == '\0': first else: second, i)
+  discard parseUntil(s.toOpenArray(i, s.high), result, if second == '\0': first else: second)
 
 proc integerOutOfRangeError() {.noinline.} =
   raise newException(ValueError, "Parsed integer outside of valid range")
@@ -429,10 +423,10 @@ proc integerOutOfRangeError() {.noinline.} =
 when defined(js):
   {.push overflowChecks: off.}
 
-proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
+proc rawParseInt(s: openArray[char], b: var BiggestInt): int =
   var
     sign: BiggestInt = -1
-    i = start
+    i = 0
   if i < s.len:
     if s[i] == '+': inc(i)
     elif s[i] == '-':
@@ -452,47 +446,47 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
       integerOutOfRangeError()
     else:
       b = b * sign
-      result = i - start
+      result = i
 
 when defined(js):
   {.pop.} # overflowChecks: off
 
-proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
+proc parseBiggestInt*(s: openArray[char], number: var BiggestInt): int {.
   rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
     var res: BiggestInt
-    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert parseBiggestInt("9223372036854775807", res) == 19
     doAssert res == 9223372036854775807
+    doAssert parseBiggestInt("-2024_05_09", res) == 11
+    doAssert res == -20240509
   var res = BiggestInt(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseInt(s, res, start)
+  result = rawParseInt(s, res)
   if result != 0:
     number = res
 
-proc parseInt*(s: string, number: var int, start = 0): int {.
+proc parseInt*(s: openArray[char], number: var int): int {.
   rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Parses an integer and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there is no integer.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
     var res: int
-    doAssert parseInt("2019", res, 0) == 4
-    doAssert res == 2019
-    doAssert parseInt("2019", res, 2) == 2
-    doAssert res == 19
+    doAssert parseInt("-2024_05_02", res) == 11
+    doAssert res == -20240502
   var res = BiggestInt(0)
-  result = parseBiggestInt(s, res, start)
+  result = parseBiggestInt(s, res)
   when sizeof(int) <= 4:
     if res < low(int) or res > high(int):
       integerOutOfRangeError()
   if result != 0:
     number = int(res)
 
-proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+proc parseSaturatedNatural*(s: openArray[char], b: var int): int {.
     raises: [].} =
   ## Parses a natural number into ``b``. This cannot raise an overflow
   ## error. ``high(int)`` is returned for an overflow.
@@ -502,7 +496,7 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
     var res = 0
     discard parseSaturatedNatural("848", res)
     doAssert res == 848
-  var i = start
+  var i = 0
   if i < s.len and s[i] == '+': inc(i)
   if i < s.len and s[i] in {'0'..'9'}:
     b = 0
@@ -514,13 +508,13 @@ proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
         b = high(int)
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
-    result = i - start
+    result = i
 
-proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
+proc rawParseUInt(s: openArray[char], b: var BiggestUInt): int =
   var
     res = 0.BiggestUInt
     prev = 0.BiggestUInt
-    i = start
+    i = 0
   if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
     integerOutOfRangeError()
   if i < s.len and s[i] == '+': inc(i) # Allow
@@ -534,11 +528,11 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
       inc(i)
       while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
     b = res
-    result = i - start
+    result = i
 
-proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
+proc parseBiggestUInt*(s: openArray[char], number: var BiggestUInt): int {.
   rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an unsigned integer starting at `start` and stores the value
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
@@ -550,13 +544,13 @@ proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
   var res = BiggestUInt(0)
   # use 'res' for exception safety (don't write to 'number' in case of an
   # overflow exception):
-  result = rawParseUInt(s, res, start)
+  result = rawParseUInt(s, res)
   if result != 0:
     number = res
 
-proc parseUInt*(s: string, number: var uint, start = 0): int {.
+proc parseUInt*(s: openArray[char], number: var uint): int {.
   rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
-  ## Parses an unsigned integer starting at `start` and stores the value
+  ## Parses an unsigned integer and stores the value
   ## into `number`.
   ## `ValueError` is raised if the parsed integer is out of the valid range.
   runnableExamples:
@@ -566,22 +560,22 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {.
     doAssert parseUInt("3450", res, 2) == 2
     doAssert res == 50
   var res = BiggestUInt(0)
-  result = parseBiggestUInt(s, res, start)
+  result = parseBiggestUInt(s, res)
   when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
     if res > 0xFFFF_FFFF'u64:
       integerOutOfRangeError()
   if result != 0:
     number = uint(res)
 
-proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
+proc parseBiggestFloat*(s: openArray[char], number: var BiggestFloat): int {.
   magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
-  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float and stores the value into `number`.
   ## Result is the number of processed chars or 0 if a parsing error
   ## occurred.
 
-proc parseFloat*(s: string, number: var float, start = 0): int {.
+proc parseFloat*(s: openArray[char], number: var float): int {.
   rtl, extern: "npuParseFloat", noSideEffect.} =
-  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Parses a float and stores the value into `number`.
   ## Result is the number of processed chars or 0 if there occurred a parsing
   ## error.
   runnableExamples:
@@ -593,10 +587,75 @@ proc parseFloat*(s: string, number: var float, start = 0): int {.
     doAssert parseFloat("32.57", res, 3) == 2
     doAssert res == 57.00
   var bf = BiggestFloat(0.0)
-  result = parseBiggestFloat(s, bf, start)
+  result = parseBiggestFloat(s, bf)
   if result != 0:
     number = bf
 
+func toLowerAscii(c: char): char =
+  if c in {'A'..'Z'}: char(uint8(c) xor 0b0010_0000'u8) else: c
+
+func parseSize*(s: openArray[char], size: var int64, alwaysBin=false): int =
+  ## Parse a size qualified by binary or metric units into `size`.  This format
+  ## is often called "human readable".  Result is the number of processed chars
+  ## or 0 on parse errors and size is rounded to the nearest integer.  Trailing
+  ## garbage like "/s" in "1k/s" is allowed and detected by `result < s.len`.
+  ##
+  ## To simplify use, following non-rare wild conventions, and since fractional
+  ## data like milli-bytes is so rare, unit matching is case-insensitive but for
+  ## the 'i' distinguishing binary-metric from metric (which cannot be 'I').
+  ##
+  ## An optional trailing 'B|b' is ignored but processed.  I.e., you must still
+  ## know if units are bytes | bits or infer this fact via the case of s[^1] (if
+  ## users can even be relied upon to use 'B' for byte and 'b' for bit or have
+  ## that be s[^1]).
+  ##
+  ## If `alwaysBin==true` then scales are always binary-metric, but e.g. "KiB"
+  ## is still accepted for clarity.  If the value would exceed the range of
+  ## `int64`, `size` saturates to `int64.high`.  Supported metric prefix chars
+  ## include k, m, g, t, p, e, z, y (but z & y saturate unless the number is a
+  ## small fraction).
+  ##
+  ## **See also:**
+  ## * https://en.wikipedia.org/wiki/Binary_prefix
+  ## * `formatSize module<strutils.html>`_ for formatting
+  runnableExamples:
+    var res: int64  # caller must still know if 'b' refers to bytes|bits
+    doAssert parseSize("10.5 MB", res) == 7
+    doAssert res == 10_500_000  # decimal metric Mega prefix
+    doAssert parseSize("64 mib", res) == 6
+    doAssert res == 67108864    # 64 shl 20
+    doAssert parseSize("1G/h", res, true) == 2 # '/' stops parse
+    doAssert res == 1073741824  # 1 shl 30, forced binary metric
+  const prefix = "b" & "kmgtpezy"       # byte|bit & lowCase metric-ish prefixes
+  const scaleM = [1.0, 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24] # 10^(3*idx)
+  const scaleB = [1.0, 1024, 1048576, 1073741824, 1099511627776.0,  # 2^(10*idx)
+                  1125899906842624.0, 1152921504606846976.0,        # ldexp?
+                  1.180591620717411303424e21, 1.208925819614629174706176e24]
+  var number: float
+  var scale = 1.0
+  result = parseFloat(s, number)
+  if number < 0:                        # While parseFloat accepts negatives ..
+    result = 0                          #.. we do not since sizes cannot be < 0
+  if result > 0:
+    let start = result                  # Save spot to maybe unwind white to EOS
+    while result < s.len and s[result] in Whitespace:
+      inc result
+    if result < s.len:                  # Illegal starting char => unity
+      if (let si = prefix.find(s[result].toLowerAscii); si >= 0):
+        inc result                      # Now parse the scale
+        scale = if alwaysBin: scaleB[si] else: scaleM[si]
+        if result < s.len and s[result] == 'i':
+          scale = scaleB[si]            # Switch from default to binary-metric
+          inc result
+        if result < s.len and s[result].toLowerAscii == 'b':
+          inc result                    # Skip optional '[bB]'
+    else:                               # Unwind result advancement when there..
+      result = start                    #..is no unit to the end of `s`.
+    var sizeF = number * scale + 0.5    # Saturate to int64.high when too big
+    size = if sizeF > 9223372036854774784.0: int64.high else: sizeF.int64
+# Above constant=2^63-1024 avoids C UB; github.com/nim-lang/Nim/issues/20102 or
+# stackoverflow.com/questions/20923556/math-pow2-63-1-math-pow2-63-512-is-true
+
 type
   InterpolatedKind* = enum ## Describes for `interpolatedFragments`
                            ## which part of the interpolated string is
@@ -606,7 +665,7 @@ type
     ikVar,                 ## ``var`` part of the interpolated string
     ikExpr                 ## ``expr`` part of the interpolated string
 
-iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+iterator interpolatedFragments*(s: openArray[char]): tuple[kind: InterpolatedKind,
   value: string] =
   ## Tokenizes the string `s` into substrings for interpolation purposes.
   ##
@@ -641,7 +700,7 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
             else: discard
             inc j
           raise newException(ValueError,
-            "Expected closing '}': " & substr(s, i, s.high))
+            "Expected closing '}': " & substr(s.toOpenArray(i, s.high)))
         inc i, 2 # skip ${
         kind = ikExpr
       elif j+1 < s.len and s[j+1] in IdentStartChars:
@@ -655,15 +714,374 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
         kind = ikDollar
       else:
         raise newException(ValueError,
-          "Unable to parse a variable name at " & substr(s, i, s.high))
+          "Unable to parse a variable name at " & substr(s.toOpenArray(i, s.high)))
     else:
       while j < s.len and s[j] != '$': inc j
       kind = ikStr
     if j > i:
       # do not copy the trailing } for ikExpr:
-      yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
+      yield (kind, substr(s.toOpenArray(i, j-1-ord(kind == ikExpr))))
     else:
       break
     i = j
 
 {.pop.}
+
+
+proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a binary number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-bin character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
+    doAssert num == 5138925
+    doAssert parseBin("3", num) == 0
+    var num8: int8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
+    doAssert num8 == 0b1110_1101'i8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
+    doAssert num8 == 0b0100_1110'i8
+    var num8u: uint8
+    doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
+    doAssert num64 == 336784608873
+  parseBin(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses an octal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-oct character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseOct("0o23464755", num) == 10
+    doAssert num == 5138925
+    doAssert parseOct("8", num) == 0
+    var num8: int8
+    doAssert parseOct("0o_1464_755", num8) == 11
+    doAssert num8 == -19
+    doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
+    doAssert num8 == 102
+    var num8u: uint8
+    doAssert parseOct("1464755", num8u) == 7
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseOct("2346475523464755", num64) == 16
+    doAssert num64 == 86216859871725
+  parseOct(s.toOpenArray(start, s.high), number, maxLen)
+
+proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
+    maxLen = 0): int {.noSideEffect.} =
+  ## Parses a hexadecimal number and stores its value in ``number``.
+  ##
+  ## Returns the number of the parsed characters or 0 in case of an error.
+  ## If error, the value of ``number`` is not changed.
+  ##
+  ## If ``maxLen == 0``, the parsing continues until the first non-hex character
+  ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
+  ## are parsed starting from the ``start`` position.
+  ##
+  ## It does not check for overflow. If the value represented by the string is
+  ## too big to fit into ``number``, only the value of last fitting characters
+  ## will be stored in ``number`` without producing an error.
+  runnableExamples:
+    var num: int
+    doAssert parseHex("4E_69_ED", num) == 8
+    doAssert num == 5138925
+    doAssert parseHex("X", num) == 0
+    doAssert parseHex("#ABC", num) == 4
+    var num8: int8
+    doAssert parseHex("0x_4E_69_ED", num8) == 11
+    doAssert num8 == 0xED'i8
+    doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
+    doAssert num8 == 0x4E'i8
+    var num8u: uint8
+    doAssert parseHex("0x_4E_69_ED", num8u) == 11
+    doAssert num8u == 237
+    var num64: int64
+    doAssert parseHex("4E69ED4E69ED", num64) == 12
+    doAssert num64 == 86216859871725
+  parseHex(s.toOpenArray(start, s.high), number, maxLen)
+
+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.
+  ## If error, the value of `ident` is not changed.
+  runnableExamples:
+    var res: string
+    doAssert parseIdent("Hello World", res, 0) == 5
+    doAssert res == "Hello"
+    doAssert parseIdent("Hello World", res, 1) == 4
+    doAssert res == "ello"
+    doAssert parseIdent("Hello World", res, 6) == 5
+    doAssert res == "World"
+  parseIdent(s.toOpenArray(start, s.high), ident)
+
+proc parseIdent*(s: string, start = 0): string =
+  ## Parses an identifier and returns it or an empty string in
+  ## case of an error.
+  runnableExamples:
+    doAssert parseIdent("Hello World", 0) == "Hello"
+    doAssert parseIdent("Hello World", 1) == "ello"
+    doAssert parseIdent("Hello World", 5) == ""
+    doAssert parseIdent("Hello World", 6) == "World"
+  parseIdent(s.toOpenArray(start, s.high))
+
+proc parseChar*(s: string, c: var char, start = 0): int =
+  ## Parses a single character, stores it in `c` and returns 1.
+  ## In case of error (if start >= s.len) it returns 0
+  ## and the value of `c` is unchanged.
+  runnableExamples:
+    var c: char
+    doAssert "nim".parseChar(c, 3) == 0
+    doAssert c == '\0'
+    doAssert "nim".parseChar(c, 0) == 1
+    doAssert c == 'n'
+  parseChar(s.toOpenArray(start, s.high), c)
+
+proc skipWhitespace*(s: string, start = 0): int {.inline.} =
+  ## Skips the whitespace starting at ``s[start]``. Returns the number of
+  ## skipped characters.
+  runnableExamples:
+    doAssert skipWhitespace("Hello World", 0) == 0
+    doAssert skipWhitespace(" Hello World", 0) == 1
+    doAssert skipWhitespace("Hello World", 5) == 1
+    doAssert skipWhitespace("Hello  World", 5) == 2
+  skipWhitespace(s.toOpenArray(start, s.high))
+
+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]``.
+  runnableExamples:
+    doAssert skip("2019-01-22", "2019", 0) == 4
+    doAssert skip("2019-01-22", "19", 0) == 0
+    doAssert skip("2019-01-22", "19", 2) == 2
+    doAssert skip("CAPlow", "CAP", 0) == 3
+    doAssert skip("CAPlow", "cap", 0) == 0
+  skip(s.toOpenArray(start, s.high), token)
+
+proc skipIgnoreCase*(s, token: string, start = 0): int =
+  ## Same as `skip` but case is ignored for token matching.
+  runnableExamples:
+    doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
+    doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
+  skipIgnoreCase(s.toOpenArray(start, s.high), token)
+
+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.
+  runnableExamples:
+    doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
+    doAssert skipUntil("Hello World", {'W'}, 0) == 6
+    doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+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.
+  runnableExamples:
+    doAssert skipUntil("Hello World", 'o', 0) == 4
+    doAssert skipUntil("Hello World", 'o', 4) == 0
+    doAssert skipUntil("Hello World", 'W', 0) == 6
+    doAssert skipUntil("Hello World", 'w', 0) == 11
+  skipUntil(s.toOpenArray(start, s.high), until)
+
+proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
+  ## Skips all characters while one char from the set `toSkip` is found.
+  ## Returns number of characters skipped.
+  runnableExamples:
+    doAssert skipWhile("Hello World", {'H', 'e'}) == 2
+    doAssert skipWhile("Hello World", {'e'}) == 0
+    doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
+  skipWhile(s.toOpenArray(start, s.high), toSkip)
+
+proc parseUntil*(s: string, token: var string, until: set[char],
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of the characters notin `until`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
+    doAssert myToken == "lo "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: char,
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of any character that is not the `until` character.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, 'W') == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, 'o') == 4
+    doAssert myToken == "Hell"
+    doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
+    doAssert myToken == "ll"
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseUntil*(s: string, token: var string, until: string,
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of any character that comes before the `until`  token.
+  runnableExamples:
+    var myToken: string
+    doAssert parseUntil("Hello World", myToken, "Wor") == 6
+    doAssert myToken == "Hello "
+    doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4
+    doAssert myToken == "llo "
+  parseUntil(s.toOpenArray(start, s.high), token, until)
+
+proc parseWhile*(s: string, token: var string, validChars: set[char],
+                 start = 0): int {.inline.} =
+  ## Parses a token and stores it in ``token``. Returns
+  ## the number of the parsed characters or 0 in case of an error. A token
+  ## consists of the characters in `validChars`.
+  runnableExamples:
+    var myToken: string
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0
+    doAssert myToken.len() == 0
+    doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
+    doAssert myToken == "Wor"
+  parseWhile(s.toOpenArray(start, s.high), token, validChars)
+
+proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
+  ## Finds the first occurrence of ``first``, then returns everything from there
+  ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
+  runnableExamples:
+    doAssert captureBetween("Hello World", 'e') == "llo World"
+    doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
+    doAssert captureBetween("Hello World", 'l', start = 6) == "d"
+  captureBetween(s.toOpenArray(start, s.high), first, second)
+
+proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there is no integer.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestInt
+    doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
+    doAssert res == 9223372036854775807
+    doAssert parseBiggestInt("-2024_05_09", res) == 11
+    doAssert res == -20240509
+    doAssert parseBiggestInt("-2024_05_02", res, 7) == 4
+    doAssert res == 502
+  parseBiggestInt(s.toOpenArray(start, s.high), number)
+
+proc parseInt*(s: string, number: var int, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an integer starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there is no integer.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: int
+    doAssert parseInt("-2024_05_02", res) == 11
+    doAssert res == -20240502
+    doAssert parseInt("-2024_05_02", res, 7) == 4
+    doAssert res == 502
+  parseInt(s.toOpenArray(start, s.high), number)
+
+
+proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
+    raises: [].} =
+  ## Parses a natural number into ``b``. This cannot raise an overflow
+  ## error. ``high(int)`` is returned for an overflow.
+  ## The number of processed character is returned.
+  ## This is usually what you really want to use instead of `parseInt`:idx:.
+  runnableExamples:
+    var res = 0
+    discard parseSaturatedNatural("848", res)
+    doAssert res == 848
+  parseSaturatedNatural(s.toOpenArray(start, s.high), b)
+
+
+proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: BiggestUInt
+    doAssert parseBiggestUInt("12", res, 0) == 2
+    doAssert res == 12
+    doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
+    doAssert res == 1111111111111111111'u64
+  parseBiggestUInt(s.toOpenArray(start, s.high), number)
+
+proc parseUInt*(s: string, number: var uint, start = 0): int {.noSideEffect, raises: [ValueError].} =
+  ## Parses an unsigned integer starting at `start` and stores the value
+  ## into `number`.
+  ## `ValueError` is raised if the parsed integer is out of the valid range.
+  runnableExamples:
+    var res: uint
+    doAssert parseUInt("3450", res) == 4
+    doAssert res == 3450
+    doAssert parseUInt("3450", res, 2) == 2
+    doAssert res == 50
+  parseUInt(s.toOpenArray(start, s.high), number)
+
+proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.noSideEffect.} =
+  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if a parsing error
+  ## occurred.
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+proc parseFloat*(s: string, number: var float, start = 0): int {.noSideEffect.} =
+  ## Parses a float starting at `start` and stores the value into `number`.
+  ## Result is the number of processed chars or 0 if there occurred a parsing
+  ## error.
+  runnableExamples:
+    var res: float
+    doAssert parseFloat("32", res, 0) == 2
+    doAssert res == 32.0
+    doAssert parseFloat("32.57", res, 0) == 5
+    doAssert res == 32.57
+    doAssert parseFloat("32.57", res, 3) == 2
+    doAssert res == 57.00
+  parseFloat(s.toOpenArray(start, s.high), number)
+
+iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
+  value: string] =
+  ## Tokenizes the string `s` into substrings for interpolation purposes.
+  ##
+  runnableExamples:
+    var outp: seq[tuple[kind: InterpolatedKind, value: string]]
+    for k, v in interpolatedFragments("  $this is ${an  example}  $$"):
+      outp.add (k, v)
+    doAssert outp == @[(ikStr, "  "),
+                       (ikVar, "this"),
+                       (ikStr, " is "),
+                       (ikExpr, "an  example"),
+                       (ikStr, "  "),
+                       (ikDollar, "$")]
+  for x in s.toOa.interpolatedFragments:
+    yield x
+
diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim
index 3ba5a19d5..c760799a2 100644
--- a/lib/pure/parsexml.nim
+++ b/lib/pure/parsexml.nim
@@ -36,43 +36,43 @@ The file ``examples/htmltitle.nim`` demonstrates how to use the
 XML parser to accomplish a simple task: To determine the title of an HTML
 document.
 
-.. code-block:: nim
+  ```nim
+  # Example program to show the parsexml module
+  # This program reads an HTML file and writes its title to stdout.
+  # Errors and whitespace are ignored.
 
-    # Example program to show the parsexml module
-    # This program reads an HTML file and writes its title to stdout.
-    # Errors and whitespace are ignored.
+  import std/[os, streams, parsexml, strutils]
 
-    import os, streams, parsexml, strutils
+  if paramCount() < 1:
+    quit("Usage: htmltitle filename[.html]")
 
-    if paramCount() < 1:
-      quit("Usage: htmltitle filename[.html]")
-
-    var filename = addFileExt(paramStr(1), "html")
-    var s = newFileStream(filename, fmRead)
-    if s == nil: quit("cannot open the file " & filename)
-    var x: XmlParser
-    open(x, s, filename)
-    while true:
-      x.next()
-      case x.kind
-      of xmlElementStart:
-        if cmpIgnoreCase(x.elementName, "title") == 0:
-          var title = ""
-          x.next()  # skip "<title>"
-          while x.kind == xmlCharData:
-            title.add(x.charData)
-            x.next()
-          if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
-            echo("Title: " & title)
-            quit(0) # Success!
-          else:
-            echo(x.errorMsgExpected("/title"))
+  var filename = addFileExt(paramStr(1), "html")
+  var s = newFileStream(filename, fmRead)
+  if s == nil: quit("cannot open the file " & filename)
+  var x: XmlParser
+  open(x, s, filename)
+  while true:
+    x.next()
+    case x.kind
+    of xmlElementStart:
+      if cmpIgnoreCase(x.elementName, "title") == 0:
+        var title = ""
+        x.next()  # skip "<title>"
+        while x.kind == xmlCharData:
+          title.add(x.charData)
+          x.next()
+        if x.kind == xmlElementEnd and cmpIgnoreCase(x.elementName, "title") == 0:
+          echo("Title: " & title)
+          quit(0) # Success!
+        else:
+          echo(x.errorMsgExpected("/title"))
 
-      of xmlEof: break # end of file reached
-      else: discard # ignore other events
+    of xmlEof: break # end of file reached
+    else: discard # ignore other events
 
-    x.close()
-    quit("Could not determine title!")
+  x.close()
+  quit("Could not determine title!")
+  ```
 
 ]##
 
@@ -85,72 +85,72 @@ The file ``examples/htmlrefs.nim`` demonstrates how to use the
 XML parser to accomplish another simple task: To determine all the links
 an HTML document contains.
 
-.. code-block:: nim
+  ```nim
+  # Example program to show the new parsexml module
+  # This program reads an HTML file and writes all its used links to stdout.
+  # Errors and whitespace are ignored.
 
-    # Example program to show the new parsexml module
-    # This program reads an HTML file and writes all its used links to stdout.
-    # Errors and whitespace are ignored.
+  import std/[os, streams, parsexml, strutils]
 
-    import os, streams, parsexml, strutils
+  proc `=?=` (a, b: string): bool =
+    # little trick: define our own comparator that ignores case
+    return cmpIgnoreCase(a, b) == 0
 
-    proc `=?=` (a, b: string): bool =
-      # little trick: define our own comparator that ignores case
-      return cmpIgnoreCase(a, b) == 0
+  if paramCount() < 1:
+    quit("Usage: htmlrefs filename[.html]")
 
-    if paramCount() < 1:
-      quit("Usage: htmlrefs filename[.html]")
-
-    var links = 0 # count the number of links
-    var filename = addFileExt(paramStr(1), "html")
-    var s = newFileStream(filename, fmRead)
-    if s == nil: quit("cannot open the file " & filename)
-    var x: XmlParser
-    open(x, s, filename)
-    next(x) # get first event
-    block mainLoop:
-      while true:
-        case x.kind
-        of xmlElementOpen:
-          # the <a href = "xyz"> tag we are interested in always has an attribute,
-          # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
-          if x.elementName =?= "a":
-            x.next()
-            if x.kind == xmlAttribute:
-              if x.attrKey =?= "href":
-                var link = x.attrValue
-                inc(links)
-                # skip until we have an ``xmlElementClose`` event
-                while true:
-                  x.next()
-                  case x.kind
-                  of xmlEof: break mainLoop
-                  of xmlElementClose: break
-                  else: discard
-                x.next() # skip ``xmlElementClose``
-                # now we have the description for the ``a`` element
-                var desc = ""
-                while x.kind == xmlCharData:
-                  desc.add(x.charData)
-                  x.next()
-                echo(desc & ": " & link)
-          else:
-            x.next()
-        of xmlEof: break # end of file reached
-        of xmlError:
-          echo(errorMsg(x))
+  var links = 0 # count the number of links
+  var filename = addFileExt(paramStr(1), "html")
+  var s = newFileStream(filename, fmRead)
+  if s == nil: quit("cannot open the file " & filename)
+  var x: XmlParser
+  open(x, s, filename)
+  next(x) # get first event
+  block mainLoop:
+    while true:
+      case x.kind
+      of xmlElementOpen:
+        # the <a href = "xyz"> tag we are interested in always has an attribute,
+        # thus we search for ``xmlElementOpen`` and not for ``xmlElementStart``
+        if x.elementName =?= "a":
+          x.next()
+          if x.kind == xmlAttribute:
+            if x.attrKey =?= "href":
+              var link = x.attrValue
+              inc(links)
+              # skip until we have an ``xmlElementClose`` event
+              while true:
+                x.next()
+                case x.kind
+                of xmlEof: break mainLoop
+                of xmlElementClose: break
+                else: discard
+              x.next() # skip ``xmlElementClose``
+              # now we have the description for the ``a`` element
+              var desc = ""
+              while x.kind == xmlCharData:
+                desc.add(x.charData)
+                x.next()
+              echo(desc & ": " & link)
+        else:
           x.next()
-        else: x.next() # skip other events
+      of xmlEof: break # end of file reached
+      of xmlError:
+        echo(errorMsg(x))
+        x.next()
+      else: x.next() # skip other events
 
-    echo($links & " link(s) found!")
-    x.close()
+  echo($links & " link(s) found!")
+  x.close()
+  ```
 
 ]##
 
 import
-  strutils, lexbase, streams, unicode
+  std/[strutils, lexbase, streams, unicode]
 
 when defined(nimPreviewSlimSystem):
-  import std/assertions
+  import std/[assertions, syncio]
 
 # the parser treats ``<br />`` as ``<br></br>``
 
@@ -792,7 +792,7 @@ proc next*(my: var XmlParser) =
     my.state = stateNormal
 
 when not defined(testing) and isMainModule:
-  import os
+  import std/os
   var s = newFileStream(paramStr(1), fmRead)
   if s == nil: quit("cannot open the file" & paramStr(1))
   var x: XmlParser
diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim
index a71ae0762..4cdc02303 100644
--- a/lib/pure/pathnorm.nim
+++ b/lib/pure/pathnorm.nim
@@ -14,7 +14,7 @@
 
 # Yes, this uses import here, not include so that
 # we don't end up exporting these symbols from pathnorm and os:
-import includes/osseps
+import std/private/osseps
 
 type
   PathIter* = object
diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim
index a95700825..2969fd6d7 100644
--- a/lib/pure/pegs.nim
+++ b/lib/pure/pegs.nim
@@ -22,11 +22,11 @@ when defined(nimPreviewSlimSystem):
 const
   useUnicode = true ## change this to deactivate proper UTF-8 support
 
-import strutils, macros
+import std/[strutils, macros]
 import std/private/decode_helpers
 
 when useUnicode:
-  import unicode
+  import std/unicode
   export unicode.`==`
 
 const
@@ -168,8 +168,9 @@ func charSet*(s: set[char]): Peg {.rtl, extern: "npegs$1".} =
   ## constructs a PEG from a character set `s`
   assert '\0' notin s
   result = Peg(kind: pkCharChoice)
-  new(result.charChoice)
-  result.charChoice[] = s
+  {.cast(noSideEffect).}:
+    new(result.charChoice)
+    result.charChoice[] = s
 
 func len(a: Peg): int {.inline.} = return a.sons.len
 func add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s)
@@ -313,21 +314,21 @@ func capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.rtl, extern: "npegsCapture".}
 func backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
   rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. `reverse` specifies wether indexing starts from the end of the
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
   ## capture list.
   result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1))
 
 func backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
   rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. `reverse` specifies wether indexing starts from the end of the
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
   ## capture list. Ignores case for matching.
   result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1))
 
 func backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {.
   rtl, extern: "npegs$1".} =
   ## constructs a back reference of the given `index`. `index` starts counting
-  ## from 1. `reverse` specifies wether indexing starts from the end of the
+  ## from 1. `reverse` specifies whether indexing starts from the end of the
   ## capture list. Ignores style for matching.
   result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1))
 
@@ -561,7 +562,7 @@ template matchOrParse(mopProc: untyped) =
   # procs. For the former, *enter* and *leave* event handler code generators
   # are provided which just return *discard*.
 
-  proc mopProc(s: string, p: Peg, start: int, c: var Captures): int =
+  proc mopProc(s: string, p: Peg, start: int, c: var Captures): int {.gcsafe, raises: [].} =
     proc matchBackRef(s: string, p: Peg, start: int, c: var Captures): int =
       # Parse handler code must run in an *of* clause of its own for each
       # *PegKind*, so we encapsulate the identical clause body for
@@ -888,7 +889,7 @@ macro mkHandlerTplts(handlers: untyped): untyped =
   # Transforms the handler spec in *handlers* into handler templates.
   # The AST structure of *handlers[0]*:
   #
-  # .. code-block::
+  #   ```
   #   StmtList
   #     Call
   #       Ident "pkNonTerminal"
@@ -909,6 +910,7 @@ macro mkHandlerTplts(handlers: untyped): untyped =
   #           StmtList
   #             <handler code block>
   #     ...
+  #   ```
   func mkEnter(hdName, body: NimNode): NimNode =
     template helper(hdName, body) {.dirty.} =
       template hdName(s, p, start) =
@@ -958,60 +960,61 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
   ## match, else the length of the total match. The following example code
   ## evaluates an arithmetic expression defined by a simple PEG:
   ##
-  ## .. code-block:: nim
-  ##  import std/[strutils, pegs]
+  ##   ```nim
+  ##   import std/[strutils, pegs]
   ##
-  ##  let
-  ##    pegAst = """
-  ##  Expr    <- Sum
-  ##  Sum     <- Product (('+' / '-')Product)*
-  ##  Product <- Value (('*' / '/')Value)*
-  ##  Value   <- [0-9]+ / '(' Expr ')'
-  ##    """.peg
-  ##    txt = "(5+3)/2-7*22"
+  ##   let
+  ##     pegAst = """
+  ##   Expr    <- Sum
+  ##   Sum     <- Product (('+' / '-')Product)*
+  ##   Product <- Value (('*' / '/')Value)*
+  ##   Value   <- [0-9]+ / '(' Expr ')'
+  ##     """.peg
+  ##     txt = "(5+3)/2-7*22"
   ##
-  ##  var
-  ##    pStack: seq[string] = @[]
-  ##    valStack: seq[float] = @[]
-  ##    opStack = ""
-  ##  let
-  ##    parseArithExpr = pegAst.eventParser:
-  ##      pkNonTerminal:
-  ##        enter:
-  ##          pStack.add p.nt.name
-  ##        leave:
-  ##          pStack.setLen pStack.high
-  ##          if length > 0:
-  ##            let matchStr = s.substr(start, start+length-1)
-  ##            case p.nt.name
-  ##            of "Value":
-  ##              try:
-  ##                valStack.add matchStr.parseFloat
-  ##                echo valStack
-  ##              except ValueError:
-  ##                discard
-  ##            of "Sum", "Product":
-  ##              try:
-  ##                let val = matchStr.parseFloat
-  ##              except ValueError:
-  ##                if valStack.len > 1 and opStack.len > 0:
-  ##                  valStack[^2] = case opStack[^1]
-  ##                  of '+': valStack[^2] + valStack[^1]
-  ##                  of '-': valStack[^2] - valStack[^1]
-  ##                  of '*': valStack[^2] * valStack[^1]
-  ##                  else: valStack[^2] / valStack[^1]
-  ##                  valStack.setLen valStack.high
-  ##                  echo valStack
-  ##                  opStack.setLen opStack.high
-  ##                  echo opStack
-  ##      pkChar:
-  ##        leave:
-  ##          if length == 1 and "Value" != pStack[^1]:
-  ##            let matchChar = s[start]
-  ##            opStack.add matchChar
-  ##            echo opStack
+  ##   var
+  ##     pStack: seq[string] = @[]
+  ##     valStack: seq[float] = @[]
+  ##     opStack = ""
+  ##   let
+  ##     parseArithExpr = pegAst.eventParser:
+  ##       pkNonTerminal:
+  ##         enter:
+  ##           pStack.add p.nt.name
+  ##         leave:
+  ##           pStack.setLen pStack.high
+  ##           if length > 0:
+  ##             let matchStr = s.substr(start, start+length-1)
+  ##             case p.nt.name
+  ##             of "Value":
+  ##               try:
+  ##                 valStack.add matchStr.parseFloat
+  ##                 echo valStack
+  ##               except ValueError:
+  ##                 discard
+  ##             of "Sum", "Product":
+  ##               try:
+  ##                 let val = matchStr.parseFloat
+  ##               except ValueError:
+  ##                 if valStack.len > 1 and opStack.len > 0:
+  ##                   valStack[^2] = case opStack[^1]
+  ##                   of '+': valStack[^2] + valStack[^1]
+  ##                   of '-': valStack[^2] - valStack[^1]
+  ##                   of '*': valStack[^2] * valStack[^1]
+  ##                   else: valStack[^2] / valStack[^1]
+  ##                   valStack.setLen valStack.high
+  ##                   echo valStack
+  ##                   opStack.setLen opStack.high
+  ##                   echo opStack
+  ##       pkChar:
+  ##         leave:
+  ##           if length == 1 and "Value" != pStack[^1]:
+  ##             let matchChar = s[start]
+  ##             opStack.add matchChar
+  ##             echo opStack
   ##
-  ##  let pLen = parseArithExpr(txt)
+  ##   let pLen = parseArithExpr(txt)
+  ##   ```
   ##
   ## The *handlers* parameter consists of code blocks for *PegKinds*,
   ## which define the grammar elements of interest. Each block can contain
@@ -1041,10 +1044,10 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
       # by *mkHandlerTplts*.
       template mkDoEnter(hdPostf, s, pegNode, start) =
         when declared(`enter hdPostf`):
-          `enter hdPostf`(s, pegNode, start):
+          `enter hdPostf`(s, pegNode, start)
         else:
           discard
-      let hdPostf = ident(substr(strVal(pegKind), 2))
+      let hdPostf = ident(substr($pegKind, 2))
       getAst(mkDoEnter(hdPostf, s, pegNode, start))
 
     macro leave(pegKind, s, pegNode, start, length: untyped): untyped =
@@ -1052,10 +1055,10 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) =
       # a grammar element of kind *pegKind*.
       template mkDoLeave(hdPostf, s, pegNode, start, length) =
         when declared(`leave hdPostf`):
-          `leave hdPostf`(s, pegNode, start, length):
+          `leave hdPostf`(s, pegNode, start, length)
         else:
           discard
-      let hdPostf = ident(substr(strVal(pegKind), 2))
+      let hdPostf = ident(substr($pegKind, 2))
       getAst(mkDoLeave(hdPostf, s, pegNode, start, length))
 
     matchOrParse(parseIt)
@@ -1172,7 +1175,7 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string =
 func findAll*(s: string, pattern: Peg, start = 0): seq[string] {.
   rtl, extern: "npegs$1".} =
   ## returns all matching *substrings* of `s` that match `pattern`.
-  ## If it does not match, @[] is returned.
+  ## If it does not match, `@[]` is returned.
   result = @[]
   for it in findAll(s, pattern, start): result.add it
 
@@ -1180,8 +1183,7 @@ template `=~`*(s: string, pattern: Peg): bool =
   ## This calls ``match`` with an implicit declared ``matches`` array that
   ## can be used in the scope of the ``=~`` call:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   if line =~ peg"\s* {\w+} \s* '=' \s* {\w+}":
   ##     # matches a key=value pair:
   ##     echo("Key: ", matches[0])
@@ -1193,10 +1195,10 @@ template `=~`*(s: string, pattern: Peg): bool =
   ##     echo("comment: ", matches[0])
   ##   else:
   ##     echo("syntax error")
-  ##
+  ##   ```
   bind MaxSubpatterns
   when not declaredInScope(matches):
-    var matches {.inject.}: array[0..MaxSubpatterns-1, string]
+    var matches {.inject.} = default(array[0..MaxSubpatterns-1, string])
   match(s, pattern, matches)
 
 # ------------------------- more string handling ------------------------------
@@ -1229,14 +1231,15 @@ func replacef*(s: string, sub: Peg, by: string): string {.
   ## 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
+  ##   ```nim
   ##   "var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2")
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   "var1<-keykey; val2<-key2key2"
+  ##   ```
   result = ""
   var i = 0
   var caps: array[0..MaxSubpatterns-1, string]
@@ -1304,8 +1307,7 @@ func replace*(s: string, sub: Peg, cb: proc(
   ## The callback proc receives the index of the current match (starting with 0),
   ## the count of captures and an open array with the captures of each match. Examples:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   func handleMatches*(m: int, n: int, c: openArray[string]): string =
   ##     result = ""
   ##     if m > 0:
@@ -1317,12 +1319,13 @@ func replace*(s: string, sub: Peg, cb: proc(
   ##
   ##   let s = "Var1=key1;var2=Key2;   VAR3"
   ##   echo s.replace(peg"{\ident}('='{\ident})* ';'* \s*", handleMatches)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   "var1: 'key1', var2: 'Key2', var3: ''"
+  ##   ```
   result = ""
   var i = 0
   var caps: array[0..MaxSubpatterns-1, string]
@@ -1360,18 +1363,19 @@ iterator split*(s: string, sep: Peg): string =
   ## Substrings are separated by the PEG `sep`.
   ## Examples:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("00232this02939is39an22example111", peg"\d+"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "this"
   ##   "is"
   ##   "an"
   ##   "example"
-  ##
+  ##   ```
   var c: Captures
   var
     first = 0
@@ -1823,9 +1827,7 @@ type
     skip: Peg
 
 func pegError(p: PegParser, msg: string, line = -1, col = -1) =
-  var e: ref EInvalidPeg
-  new(e)
-  e.msg = errorStr(p, msg, line, col)
+  var e = (ref EInvalidPeg)(msg: errorStr(p, msg, line, col))
   raise e
 
 func getTok(p: var PegParser) =
@@ -1909,7 +1911,8 @@ func primary(p: var PegParser): Peg =
       getTok(p)
     elif not arrowIsNextTok(p):
       var nt = getNonTerminal(p, p.tok.literal)
-      incl(nt.flags, ntUsed)
+      {.cast(noSideEffect).}:
+        incl(nt.flags, ntUsed)
       result = nonterminal(nt).token(p)
       getTok(p)
     else:
@@ -2002,12 +2005,14 @@ func parseRule(p: var PegParser): NonTerminal =
     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)
+    {.cast(noSideEffect).}:
+      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
+    {.cast(noSideEffect).}:
+      result.rule = parseExpr(p)
+      incl(result.flags, ntDeclared) # NOW inlining may be attempted
   else:
     pegError(p, "rule expected, but found: " & p.tok.literal)
 
@@ -2058,9 +2063,9 @@ func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg =
 
 func peg*(pattern: string): Peg =
   ## constructs a Peg object from the `pattern`. The short name has been
-  ## chosen to encourage its use as a raw string modifier::
+  ## chosen to encourage its use as a raw string modifier:
   ##
-  ##   peg"{\ident} \s* '=' \s* {.*}"
+  ##     peg"{\ident} \s* '=' \s* {.*}"
   result = parsePeg(pattern, "pattern")
 
 func escapePeg*(s: string): string =
diff --git a/lib/pure/prelude.nim b/lib/pure/prelude.nim
index 3c98d9d57..9428f29eb 100644
--- a/lib/pure/prelude.nim
+++ b/lib/pure/prelude.nim
@@ -14,7 +14,7 @@ when defined(nimdoc) and isMainModule:
     runnableExamples:
       include std/prelude
         # same as:
-        # import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt]
+        # import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat]
       let x = 1
       assert "foo $# $#" % [$x, "bar"] == "foo 1 bar"
       assert toSeq(1..3) == @[1, 2, 3]
diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim
deleted file mode 100644
index 49e46496e..000000000
--- a/lib/pure/punycode.nim
+++ /dev/null
@@ -1,209 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2016 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Implements a representation of Unicode with the limited
-## ASCII character subset.
-
-import strutils
-import unicode
-
-# issue #3045
-
-const
-  Base = 36
-  TMin = 1
-  TMax = 26
-  Skew = 38
-  Damp = 700
-  InitialBias = 72
-  InitialN = 128
-  Delimiter = '-'
-
-type
-  PunyError* = object of ValueError
-
-func decodeDigit(x: char): int {.raises: [PunyError].} =
-  if '0' <= x and x <= '9':
-    result = ord(x) - (ord('0') - 26)
-  elif 'A' <= x and x <= 'Z':
-    result = ord(x) - ord('A')
-  elif 'a' <= x and x <= 'z':
-    result = ord(x) - ord('a')
-  else:
-    raise newException(PunyError, "Bad input")
-
-func encodeDigit(digit: int): Rune {.raises: [PunyError].} =
-  if 0 <= digit and digit < 26:
-    result = Rune(digit + ord('a'))
-  elif 26 <= digit and digit < 36:
-    result = Rune(digit + (ord('0') - 26))
-  else:
-    raise newException(PunyError, "internal error in punycode encoding")
-
-func isBasic(c: char): bool = ord(c) < 0x80
-func isBasic(r: Rune): bool = int(r) < 0x80
-
-func adapt(delta, numPoints: int, first: bool): int =
-  var d = if first: delta div Damp else: delta div 2
-  d += d div numPoints
-  var k = 0
-  while d > ((Base-TMin)*TMax) div 2:
-    d = d div (Base - TMin)
-    k += Base
-  result = k + (Base - TMin + 1) * d div (d + Skew)
-
-func encode*(prefix, s: string): string {.raises: [PunyError].} =
-  ## Encode a string that may contain Unicode.
-  ## Prepend `prefix` to the result
-  result = prefix
-  var (d, n, bias) = (0, InitialN, InitialBias)
-  var (b, remaining) = (0, 0)
-  for r in s.runes:
-    if r.isBasic:
-      # basic Ascii character
-      inc b
-      result.add($r)
-    else:
-      # special character
-      inc remaining
-
-  var h = b
-  if b > 0:
-    result.add(Delimiter) # we have some Ascii chars
-  while remaining != 0:
-    var m: int = high(int32)
-    for r in s.runes:
-      if m > int(r) and int(r) >= n:
-        m = int(r)
-    d += (m - n) * (h + 1)
-    if d < 0:
-      raise newException(PunyError, "invalid label " & s)
-    n = m
-    for r in s.runes:
-      if int(r) < n:
-        inc d
-        if d < 0:
-          raise newException(PunyError, "invalid label " & s)
-        continue
-      if int(r) > n:
-        continue
-      var q = d
-      var k = Base
-      while true:
-        var t = k - bias
-        if t < TMin:
-          t = TMin
-        elif t > TMax:
-          t = TMax
-        if q < t:
-          break
-        result.add($encodeDigit(t + (q - t) mod (Base - t)))
-        q = (q - t) div (Base - t)
-        k += Base
-      result.add($encodeDigit(q))
-      bias = adapt(d, h + 1, h == b)
-      d = 0
-      inc h
-      dec remaining
-    inc d
-    inc n
-
-func encode*(s: string): string {.raises: [PunyError].} =
-  ## Encode a string that may contain Unicode. Prefix is empty.
-  result = encode("", s)
-
-func decode*(encoded: string): string {.raises: [PunyError].} =
-  ## Decode a Punycode-encoded string
-  var
-    n = InitialN
-    i = 0
-    bias = InitialBias
-  var d = rfind(encoded, Delimiter)
-  var output: seq[Rune]
-
-  if d > 0:
-    # found Delimiter
-    for j in 0..<d:
-      var c = encoded[j] # char
-      if not c.isBasic:
-        raise newException(PunyError, "Encoded contains a non-basic char")
-      output.add(Rune(c)) # add the character
-    inc d
-  else:
-    d = 0 # set to first index
-
-  while (d < len(encoded)):
-    var oldi = i
-    var w = 1
-    var k = Base
-    while true:
-      if d == len(encoded):
-        raise newException(PunyError, "Bad input: " & encoded)
-      var c = encoded[d]; inc d
-      var digit = int(decodeDigit(c))
-      if digit > (high(int32) - i) div w:
-        raise newException(PunyError, "Too large a value: " & $digit)
-      i += digit * w
-      var t: int
-      if k <= bias:
-        t = TMin
-      elif k >= bias + TMax:
-        t = TMax
-      else:
-        t = k - bias
-      if digit < t:
-        break
-      w *= Base - t
-      k += Base
-    bias = adapt(i - oldi, len(output) + 1, oldi == 0)
-
-    if i div (len(output) + 1) > high(int32) - n:
-      raise newException(PunyError, "Value too large")
-
-    n += i div (len(output) + 1)
-    i = i mod (len(output) + 1)
-    insert(output, Rune(n), i)
-    inc i
-
-  result = $output
-
-runnableExamples:
-  static:
-    block:
-      doAssert encode("") == ""
-      doAssert encode("a") == "a-"
-      doAssert encode("A") == "A-"
-      doAssert encode("3") == "3-"
-      doAssert encode("-") == "--"
-      doAssert encode("--") == "---"
-      doAssert encode("abc") == "abc-"
-      doAssert encode("London") == "London-"
-      doAssert encode("Lloyd-Atkinson") == "Lloyd-Atkinson-"
-      doAssert encode("This has spaces") == "This has spaces-"
-      doAssert encode("ü") == "tda"
-      doAssert encode("München") == "Mnchen-3ya"
-      doAssert encode("Mnchen-3ya") == "Mnchen-3ya-"
-      doAssert encode("München-Ost") == "Mnchen-Ost-9db"
-      doAssert encode("Bahnhof München-Ost") == "Bahnhof Mnchen-Ost-u6b"
-    block:
-      doAssert decode("") == ""
-      doAssert decode("a-") ==  "a"
-      doAssert decode("A-") == "A"
-      doAssert decode("3-") == "3"
-      doAssert decode("--") == "-"
-      doAssert decode("---") == "--"
-      doAssert decode("abc-") == "abc"
-      doAssert decode("London-") == "London"
-      doAssert decode("Lloyd-Atkinson-") == "Lloyd-Atkinson"
-      doAssert decode("This has spaces-") == "This has spaces"
-      doAssert decode("tda") == "ü"
-      doAssert decode("Mnchen-3ya") == "München"
-      doAssert decode("Mnchen-3ya-") == "Mnchen-3ya"
-      doAssert decode("Mnchen-Ost-9db") == "München-Ost"
-      doAssert decode("Bahnhof Mnchen-Ost-u6b") == "Bahnhof München-Ost"
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index de93f468f..3ec77d37e 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -66,29 +66,41 @@ runnableExamples:
 ## See also
 ## ========
 ## * `std/sysrand module <sysrand.html>`_ for a cryptographically secure pseudorandom number generator
-## * `mersenne module <mersenne.html>`_ for the Mersenne Twister random number generator
 ## * `math module <math.html>`_ for basic math routines
 ## * `stats module <stats.html>`_ for statistical analysis
 ## * `list of cryptographic and hashing modules <lib.html#pure-libraries-hashing>`_
 ##   in the standard library
 
-import algorithm, math
-import std/private/since
+import std/[algorithm, math]
+import std/private/[since, jsutils]
 
 when defined(nimPreviewSlimSystem):
-  import std/assertions
+  import std/[assertions]
 
 include system/inclrtl
 {.push debugger: off.}
+template whenHasBigInt64(yes64, no64): untyped =
+  when defined(js):
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        yes64
+      else:
+        no64
+    else:
+      no64
+  else:
+    yes64
 
-when defined(js):
-  type Ui = uint32
 
-  const randMax = 4_294_967_295u32
-else:
+whenHasBigInt64:
   type Ui = uint64
 
   const randMax = 18_446_744_073_709_551_615u64
+do:
+  type Ui = uint32
+
+  const randMax = 4_294_967_295u32
+
 
 type
   Rand* = object ## State of a random number generator.
@@ -106,17 +118,17 @@ type
                  ## generator are **not** thread-safe!
     a0, a1: Ui
 
-when defined(js):
-  var state = Rand(
-    a0: 0x69B4C98Cu32,
-    a1: 0xFED1DD30u32) # global for backwards compatibility
-else:
+whenHasBigInt64:
   const DefaultRandSeed = Rand(
     a0: 0x69B4C98CB8530805u64,
     a1: 0xFED1DD3004688D67CAu64)
 
   # racy for multi-threading but good enough for now:
   var state = DefaultRandSeed # global for backwards compatibility
+do:
+  var state = Rand(
+    a0: 0x69B4C98Cu32,
+    a1: 0xFED1DD30u32) # global for backwards compatibility
 
 func isValid(r: Rand): bool {.inline.} =
   ## Check whether state of `r` is valid.
@@ -209,10 +221,10 @@ proc skipRandomNumbers*(s: var Rand) =
     doAssert vals == [501737, 497901, 500683, 500157]
 
 
-  when defined(js):
-    const helper = [0xbeac0467u32, 0xd86b048bu32]
-  else:
+  whenHasBigInt64:
     const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
+  do:
+    const helper = [0xbeac0467u32, 0xd86b048bu32]
   var
     s0 = Ui 0
     s1 = Ui 0
@@ -232,11 +244,14 @@ proc rand[T: uint | uint64](r: var Rand; max: T): T =
     let max = uint64(max)
     when T.high.uint64 == uint64.high:
       if max == uint64.high: return T(next(r))
+    var iters = 0
     while true:
       let x = next(r)
       # avoid `mod` bias
-      if x <= randMax - (randMax mod max):
+      if x <= randMax - (randMax mod max) or iters > 20:
         return T(x mod (max + 1))
+      else:
+        inc iters
 
 proc rand*(r: var Rand; max: Natural): int {.benign.} =
   ## Returns a random integer in the range `0..max` using the given state.
@@ -292,7 +307,13 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
 
   let x = next(r)
   when defined(js):
-    result = (float(x) / float(high(uint32))) * max
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        result = (float(x) / float(high(uint64))) * max
+      else:
+        result = (float(x) / float(high(uint32))) * max
+    else:
+      result = (float(x) / float(high(uint32))) * max
   else:
     let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
     result = (cast[float](u) - 1.0) * max
@@ -338,9 +359,9 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T =
   when T is SomeFloat:
     result = rand(r, x.b - x.a) + x.a
   else: # Integers and Enum types
-    when defined(js):
+    whenJsNoBigInt64:
       result = cast[T](rand(r, cast[uint](x.b) - cast[uint](x.a)) + cast[uint](x.a))
-    else:
+    do:
       result = cast[T](rand(r, cast[uint64](x.b) - cast[uint64](x.a)) + cast[uint64](x.a))
 
 proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T =
@@ -379,14 +400,11 @@ proc rand*[T: Ordinal](r: var Rand; t: typedesc[T]): T {.since: (1, 7, 1).} =
   when T is range or T is enum:
     result = rand(r, low(T)..high(T))
   elif T is bool:
-    when defined(js):
-      result = (r.next or 0) < 0
-    else:
-      result = cast[int64](r.next) < 0
+    result = r.next < randMax div 2
   else:
-    when defined(js):
+    whenJsNoBigInt64:
       result = cast[T](r.next shr (sizeof(uint)*8 - sizeof(T)*8))
-    else:
+    do:
       result = cast[T](r.next shr (sizeof(uint64)*8 - sizeof(T)*8))
 
 proc rand*[T: Ordinal](t: typedesc[T]): T =
@@ -671,7 +689,7 @@ when not defined(standalone):
       import std/[hashes, os, sysrand, monotimes]
 
       when compileOption("threads"):
-        import locks
+        import std/locks
         var baseSeedLock: Lock
         baseSeedLock.initLock
 
diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim
index aba0f4ce4..5f806bd70 100644
--- a/lib/pure/rationals.nim
+++ b/lib/pure/rationals.nim
@@ -21,7 +21,7 @@ runnableExamples:
   doAssert r1 * r2 == -3 // 8
   doAssert r1 / r2 == -2 // 3
 
-import math, hashes
+import std/[math, hashes]
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
@@ -40,16 +40,16 @@ func reduce*[T: SomeInteger](x: var Rational[T]) =
     reduce(r)
     doAssert r.num == 1
     doAssert r.den == 2
-
+  if x.den == 0:
+    raise newException(DivByZeroDefect, "division by zero")
   let common = gcd(x.num, x.den)
   if x.den > 0:
     x.num = x.num div common
     x.den = x.den div common
-  elif x.den < 0:
-    x.num = -x.num div common
-    x.den = -x.den div common
-  else:
-    raise newException(DivByZeroDefect, "division by zero")
+  when T isnot SomeUnsignedInt:
+    if x.den < 0:
+      x.num = -x.num div common
+      x.den = -x.den div common
 
 func initRational*[T: SomeInteger](num, den: T): Rational[T] =
   ## Creates a new rational number with numerator `num` and denominator `den`.
@@ -318,3 +318,23 @@ func hash*[T](x: Rational[T]): Hash =
   h = h !& hash(copy.num)
   h = h !& hash(copy.den)
   result = !$h
+
+func `^`*[T: SomeInteger](x: Rational[T], y: T): Rational[T] =
+  ## Computes `x` to the power of `y`.
+  ##
+  ## The exponent `y` must be an integer. Negative exponents are supported
+  ## but floating point exponents are not.
+  runnableExamples:
+    doAssert (-3 // 5) ^ 0 == (1 // 1)
+    doAssert (-3 // 5) ^ 1 == (-3 // 5)
+    doAssert (-3 // 5) ^ 2 == (9 // 25)
+    doAssert (-3 // 5) ^ -2 == (25 // 9)
+
+  if y >= 0:
+    result.num = x.num ^ y
+    result.den = x.den ^ y
+  else:
+    result.num = x.den ^ -y
+    result.den = x.num ^ -y
+  # Note that all powers of reduced rationals are already reduced,
+  # so we don't need to call reduce() here
diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim
index 99fbe1429..ffa0128dc 100644
--- a/lib/pure/reservedmem.nim
+++ b/lib/pure/reservedmem.nim
@@ -9,7 +9,7 @@
 
 ## :Authors: Zahary Karadjov
 ##
-## This module provides utilities for reserving a portions of the
+## This module provides utilities for reserving portions of the
 ## address space of a program without consuming physical memory.
 ## It can be used to implement a dynamically resizable buffer that
 ## is guaranteed to remain in the same memory location. The buffer
@@ -18,7 +18,10 @@
 ##
 ## Unstable API.
 
-from os import raiseOSError, osLastError
+from std/oserrors import raiseOSError, osLastError
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
 
 template distance*(lhs, rhs: pointer): int =
   cast[int](rhs) - cast[int](lhs)
@@ -41,7 +44,7 @@ type
     mem: ReservedMem
 
 when defined(windows):
-  import winlean
+  import std/winlean
   import std/private/win_getsysteminfo
 
   proc getAllocationGranularity: uint =
@@ -65,7 +68,7 @@ when defined(windows):
       raiseOSError(osLastError())
 
 else:
-  import posix
+  import std/posix
 
   let allocationGranularity = sysconf(SC_PAGESIZE)
 
diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim
index b973fd222..8750aca87 100644
--- a/lib/pure/ropes.nim
+++ b/lib/pure/ropes.nim
@@ -17,7 +17,7 @@
 ## runtime efficiency.
 
 include system/inclrtl
-import streams
+import std/streams
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, formatfloat, assertions]
diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim
index b0eac2299..65b059e86 100644
--- a/lib/pure/segfaults.nim
+++ b/lib/pure/segfaults.nim
@@ -26,7 +26,7 @@ se.msg = "Could not access value because it is nil."
 when defined(windows):
   include "../system/ansi_c"
 
-  import winlean
+  import std/winlean
 
   const
     EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005'i32)
@@ -65,7 +65,7 @@ when defined(windows):
     c_signal(SIGSEGV, segfaultHandler)
 
 else:
-  import posix
+  import std/posix
 
   var sa: Sigaction
 
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index ab7e104fc..ac180e2bd 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -27,7 +27,8 @@
 ##
 ## TODO: `/dev/poll`, `event ports` and filesystem events.
 
-import os, nativesockets
+import std/nativesockets
+import std/oserrors
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -36,7 +37,7 @@ const hasThreadSupport = compileOption("threads") and defined(threadsafe)
 
 const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or
                                 defined(netbsd) or defined(openbsd) or
-                                defined(dragonfly) or
+                                defined(dragonfly) or defined(nuttx) or
                                 (defined(linux) and not defined(android) and not defined(emscripten))
   ## This constant is used to determine whether the destination platform is
   ## fully supported by `ioselectors` module.
@@ -204,12 +205,11 @@ when defined(nimdoc):
     ## to `value`. This `value` can be modified in the scope of
     ## the `withData` call.
     ##
-    ## .. code-block:: nim
-    ##
+    ##   ```nim
     ##   s.withData(fd, value) do:
     ##     # block is executed only if `fd` registered in selector `s`
     ##     value.uid = 1000
-    ##
+    ##   ```
 
   template withData*[T](s: Selector[T], fd: SocketHandle|int, value,
                         body1, body2: untyped) =
@@ -217,15 +217,14 @@ when defined(nimdoc):
     ## to `value`. This `value` can be modified in the scope of
     ## the `withData` call.
     ##
-    ## .. code-block:: nim
-    ##
+    ##   ```nim
     ##   s.withData(fd, value) do:
     ##     # block is executed only if `fd` registered in selector `s`.
     ##     value.uid = 1000
     ##   do:
     ##     # block is executed if `fd` not registered in selector `s`.
     ##     raise
-    ##
+    ##   ```
 
   proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
     ## Determines whether selector contains a file descriptor.
@@ -236,9 +235,9 @@ when defined(nimdoc):
     ## For *poll* and *select* selectors `-1` is returned.
 
 else:
-  import strutils
+  import std/strutils
   when hasThreadSupport:
-    import locks
+    import std/locks
 
     type
       SharedArray[T] = UncheckedArray[T]
@@ -246,8 +245,8 @@ else:
     proc allocSharedArray[T](nsize: int): ptr SharedArray[T] =
       result = cast[ptr SharedArray[T]](allocShared0(sizeof(T) * nsize))
 
-    proc reallocSharedArray[T](sa: ptr SharedArray[T], nsize: int): ptr SharedArray[T] =
-      result = cast[ptr SharedArray[T]](reallocShared(sa, sizeof(T) * nsize))
+    proc reallocSharedArray[T](sa: ptr SharedArray[T], oldsize, nsize: int): ptr SharedArray[T] =
+      result = cast[ptr SharedArray[T]](reallocShared0(sa, oldsize * sizeof(T), sizeof(T) * nsize))
 
     proc deallocSharedArray[T](sa: ptr SharedArray[T]) =
       deallocShared(cast[pointer](sa))
@@ -289,7 +288,7 @@ else:
     setBlocking(fd.SocketHandle, false)
 
   when not defined(windows):
-    import posix
+    import std/posix
 
     template setKey(s, pident, pevents, pparam, pdata: untyped) =
       var skey = addr(s.fds[pident])
@@ -327,7 +326,7 @@ else:
     doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout)
 
   when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or
-       defined(solaris) or defined(zephyr) or defined(freertos):
+       defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(haiku):
     template maxDescriptors*(): int =
       ## Returns the maximum number of active file descriptors for the current
       ## process. This involves a system call. For now `maxDescriptors` is
@@ -343,7 +342,18 @@ else:
           res = int(fdLim.rlim_cur) - 1
         res
 
-  when defined(linux) and not defined(emscripten):
+  when defined(nimIoselector):
+    when nimIoselector == "epoll":
+      include ioselects/ioselectors_epoll
+    elif nimIoselector == "kqueue":
+      include ioselects/ioselectors_kqueue
+    elif nimIoselector == "poll":
+      include ioselects/ioselectors_poll
+    elif nimIoselector == "select":
+      include ioselects/ioselectors_select
+    else:
+      {.fatal: "Unknown nimIoselector specified by define.".}
+  elif defined(linux) and not defined(emscripten):
     include ioselects/ioselectors_epoll
   elif bsdPlatform:
     include ioselects/ioselectors_kqueue
@@ -359,5 +369,7 @@ else:
     include ioselects/ioselectors_select
   elif defined(zephyr):
     include ioselects/ioselectors_poll
+  elif defined(nuttx):
+    include ioselects/ioselectors_epoll
   else:
     include ioselects/ioselectors_poll
diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim
deleted file mode 100644
index f5196ce1e..000000000
--- a/lib/pure/smtp.nim
+++ /dev/null
@@ -1,358 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Dominik Picheta
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## This module implements the SMTP client protocol as specified by RFC 5321,
-## this can be used to send mail to any SMTP Server.
-##
-## This module also implements the protocol used to format messages,
-## as specified by RFC 2822.
-##
-## Example gmail use:
-##
-##
-## .. code-block:: Nim
-##   var msg = createMessage("Hello from Nim's SMTP",
-##                           "Hello!.\n Is this awesome or what?",
-##                           @["foo@gmail.com"])
-##   let smtpConn = newSmtp(useSsl = true, debug=true)
-##   smtpConn.connect("smtp.gmail.com", Port 465)
-##   smtpConn.auth("username", "password")
-##   smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
-##
-##
-## Example for startTls use:
-##
-##
-## .. code-block:: Nim
-##   var msg = createMessage("Hello from Nim's SMTP",
-##                           "Hello!.\n Is this awesome or what?",
-##                           @["foo@gmail.com"])
-##   let smtpConn = newSmtp(debug=true)
-##   smtpConn.connect("smtp.mailtrap.io", Port 2525)
-##   smtpConn.startTls()
-##   smtpConn.auth("username", "password")
-##   smtpConn.sendmail("username@gmail.com", @["foo@gmail.com"], $msg)
-##
-##
-## For SSL support this module relies on OpenSSL. If you want to
-## enable SSL, compile with `-d:ssl`.
-
-import net, strutils, strtabs, base64, os, strutils
-import asyncnet, asyncdispatch
-
-export Port
-
-type
-  Message* = object
-    msgTo: seq[string]
-    msgCc: seq[string]
-    msgSubject: string
-    msgOtherHeaders: StringTableRef
-    msgBody: string
-
-  ReplyError* = object of IOError
-
-  SmtpBase[SocketType] = ref object
-    sock: SocketType
-    address: string
-    debug: bool
-
-  Smtp* = SmtpBase[Socket]
-  AsyncSmtp* = SmtpBase[AsyncSocket]
-
-proc containsNewline(xs: seq[string]): bool =
-  for x in xs:
-    if x.contains({'\c', '\L'}):
-      return true
-
-proc debugSend*(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} =
-  ## Sends `cmd` on the socket connected to the SMTP server.
-  ##
-  ## If the `smtp` object was created with `debug` enabled,
-  ## debugSend will invoke `echo("C:" & cmd)` before sending.
-  ##
-  ## This is a lower level proc and not something that you typically
-  ## would need to call when using this module. One exception to
-  ## this is if you are implementing any
-  ## `SMTP extensions<https://en.wikipedia.org/wiki/Extended_SMTP>`_.
-
-  if smtp.debug:
-    echo("C:" & cmd)
-  await smtp.sock.send(cmd)
-
-proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[string] {.multisync.} =
-  ## Receives a line of data from the socket connected to the
-  ## SMTP server.
-  ##
-  ## If the `smtp` object was created with `debug` enabled,
-  ## debugRecv will invoke `echo("S:" & result.string)` after
-  ## the data is received.
-  ##
-  ## This is a lower level proc and not something that you typically
-  ## would need to call when using this module. One exception to
-  ## this is if you are implementing any
-  ## `SMTP extensions<https://en.wikipedia.org/wiki/Extended_SMTP>`_.
-  ##
-  ## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_.
-  result = await smtp.sock.recvLine()
-  if smtp.debug:
-    echo("S:" & result)
-
-proc quitExcpt(smtp: Smtp, msg: string) =
-  smtp.debugSend("QUIT")
-  raise newException(ReplyError, msg)
-
-const compiledWithSsl = defined(ssl)
-
-when not defined(ssl):
-  let defaultSSLContext: SslContext = nil
-else:
-  var defaultSSLContext {.threadvar.}: SslContext
-
-  proc getSSLContext(): SslContext =
-    if defaultSSLContext == nil:
-      defaultSSLContext = newContext(verifyMode = CVerifyNone)
-    result = defaultSSLContext
-
-proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string],
-                otherHeaders: openArray[tuple[name, value: string]]): Message =
-  ## Creates a new MIME compliant message.
-  ##
-  ## You need to make sure that `mSubject`, `mTo` and `mCc` don't contain
-  ## any newline characters. Failing to do so will raise `AssertionDefect`.
-  doAssert(not mSubject.contains({'\c', '\L'}),
-           "'mSubject' shouldn't contain any newline characters")
-  doAssert(not (mTo.containsNewline() or mCc.containsNewline()),
-           "'mTo' and 'mCc' shouldn't contain any newline characters")
-
-  result.msgTo = mTo
-  result.msgCc = mCc
-  result.msgSubject = mSubject
-  result.msgBody = mBody
-  result.msgOtherHeaders = newStringTable()
-  for n, v in items(otherHeaders):
-    result.msgOtherHeaders[n] = v
-
-proc createMessage*(mSubject, mBody: string, mTo,
-                    mCc: seq[string] = @[]): Message =
-  ## Alternate version of the above.
-  ##
-  ## You need to make sure that `mSubject`, `mTo` and `mCc` don't contain
-  ## any newline characters. Failing to do so will raise `AssertionDefect`.
-  doAssert(not mSubject.contains({'\c', '\L'}),
-           "'mSubject' shouldn't contain any newline characters")
-  doAssert(not (mTo.containsNewline() or mCc.containsNewline()),
-           "'mTo' and 'mCc' shouldn't contain any newline characters")
-  result.msgTo = mTo
-  result.msgCc = mCc
-  result.msgSubject = mSubject
-  result.msgBody = mBody
-  result.msgOtherHeaders = newStringTable()
-
-proc `$`*(msg: Message): string =
-  ## stringify for `Message`.
-  result = ""
-  if msg.msgTo.len() > 0:
-    result = "TO: " & msg.msgTo.join(", ") & "\c\L"
-  if msg.msgCc.len() > 0:
-    result.add("CC: " & msg.msgCc.join(", ") & "\c\L")
-  # TODO: Folding? i.e when a line is too long, shorten it...
-  result.add("Subject: " & msg.msgSubject & "\c\L")
-  for key, value in pairs(msg.msgOtherHeaders):
-    result.add(key & ": " & value & "\c\L")
-
-  result.add("\c\L")
-  result.add(msg.msgBody)
-
-proc newSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): Smtp =
-  ## Creates a new `Smtp` instance.
-  new result
-  result.debug = debug
-  result.sock = newSocket()
-  if useSsl:
-    when compiledWithSsl:
-      if sslContext == nil:
-        getSSLContext().wrapSocket(result.sock)
-      else:
-        sslContext.wrapSocket(result.sock)
-    else:
-      {.error: "SMTP module compiled without SSL support".}
-
-proc newAsyncSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): AsyncSmtp =
-  ## Creates a new `AsyncSmtp` instance.
-  new result
-  result.debug = debug
-  result.sock = newAsyncSocket()
-  if useSsl:
-    when compiledWithSsl:
-      if sslContext == nil:
-        getSSLContext().wrapSocket(result.sock)
-      else:
-        sslContext.wrapSocket(result.sock)
-    else:
-      {.error: "SMTP module compiled without SSL support".}
-
-proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] =
-  var retFuture = newFuture[void]()
-  var sendFut = smtp.debugSend("QUIT")
-  sendFut.callback =
-    proc () =
-      retFuture.fail(newException(ReplyError, msg))
-  return retFuture
-
-proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} =
-  ## Calls `debugRecv<#debugRecv,AsyncSmtp>`_ and checks that the received
-  ## data starts with `reply`. If the received data does not start
-  ## with `reply`, then a `QUIT` command will be sent to the SMTP
-  ## server and a `ReplyError` exception will be raised.
-  ##
-  ## This is a lower level proc and not something that you typically
-  ## would need to call when using this module. One exception to
-  ## this is if you are implementing any
-  ## `SMTP extensions<https://en.wikipedia.org/wiki/Extended_SMTP>`_.
-  var line = await smtp.debugRecv()
-  if not line.startsWith(reply):
-    await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line)
-
-proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} =
-  # Sends the HELO request
-  await smtp.debugSend("HELO " & smtp.address & "\c\L")
-  await smtp.checkReply("250")
-
-proc recvEhlo(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} =
-  ## Skips "250-" lines, read until "250 " found.
-  ## Return `true` if server supports `EHLO`, false otherwise.
-  while true:
-    var line = await smtp.sock.recvLine()
-    if smtp.debug:
-      echo("S:" & line)
-    if line.startsWith("250-"): continue
-    elif line.startsWith("250 "): return true # last line
-    else: return false
-
-proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} =
-  ## Sends EHLO request.
-  await smtp.debugSend("EHLO " & smtp.address & "\c\L")
-  return await smtp.recvEhlo()
-
-proc connect*(smtp: Smtp | AsyncSmtp,
-              address: string, port: Port) {.multisync.} =
-  ## Establishes a connection with a SMTP server.
-  ## May fail with ReplyError or with a socket error.
-  smtp.address = address
-  await smtp.sock.connect(address, port)
-  await smtp.checkReply("220")
-  let speaksEsmtp = await smtp.ehlo()
-  if not speaksEsmtp:
-    await smtp.helo()
-
-proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} =
-  ## Put the SMTP connection in TLS (Transport Layer Security) mode.
-  ## May fail with ReplyError
-  await smtp.debugSend("STARTTLS\c\L")
-  await smtp.checkReply("220")
-  when compiledWithSsl:
-    if sslContext == nil:
-      getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient)
-    else:
-      sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient)
-    let speaksEsmtp = await smtp.ehlo()
-    if not speaksEsmtp:
-      await smtp.helo()
-  else:
-    {.error: "SMTP module compiled without SSL support".}
-
-proc auth*(smtp: Smtp | AsyncSmtp, username, password: string) {.multisync.} =
-  ## Sends an AUTH command to the server to login as the `username`
-  ## using `password`.
-  ## May fail with ReplyError.
-
-  await smtp.debugSend("AUTH LOGIN\c\L")
-  await smtp.checkReply("334") # TODO: Check whether it's asking for the "Username:"
-                               # i.e "334 VXNlcm5hbWU6"
-  await smtp.debugSend(encode(username) & "\c\L")
-  await smtp.checkReply("334") # TODO: Same as above, only "Password:" (I think?)
-
-  await smtp.debugSend(encode(password) & "\c\L")
-  await smtp.checkReply("235") # Check whether the authentication was successful.
-
-proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string,
-               toAddrs: seq[string], msg: string) {.multisync.} =
-  ## Sends `msg` from `fromAddr` to the addresses specified in `toAddrs`.
-  ## Messages may be formed using `createMessage` by converting the
-  ## Message into a string.
-  ##
-  ## You need to make sure that `fromAddr` and `toAddrs` don't contain
-  ## any newline characters. Failing to do so will raise `AssertionDefect`.
-  doAssert(not (toAddrs.containsNewline() or fromAddr.contains({'\c', '\L'})),
-           "'toAddrs' and 'fromAddr' shouldn't contain any newline characters")
-
-  await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L")
-  await smtp.checkReply("250")
-  for address in items(toAddrs):
-    await smtp.debugSend("RCPT TO:<" & address & ">\c\L")
-    await smtp.checkReply("250")
-
-  # Send the message
-  await smtp.debugSend("DATA " & "\c\L")
-  await smtp.checkReply("354")
-  await smtp.sock.send(msg & "\c\L")
-  await smtp.debugSend(".\c\L")
-  await smtp.checkReply("250")
-
-proc close*(smtp: Smtp | AsyncSmtp) {.multisync.} =
-  ## Disconnects from the SMTP server and closes the socket.
-  await smtp.debugSend("QUIT\c\L")
-  smtp.sock.close()
-
-when not defined(testing) and isMainModule:
-  # To test with a real SMTP service, create a smtp.ini file, e.g.:
-  # username = ""
-  # password = ""
-  # smtphost = "smtp.gmail.com"
-  # port = 465
-  # use_tls = true
-  # sender = ""
-  # recipient = ""
-
-  import parsecfg
-
-  proc `[]`(c: Config, key: string): string = c.getSectionValue("", key)
-
-  let
-    conf = loadConfig("smtp.ini")
-    msg = createMessage("Hello from Nim's SMTP!",
-      "Hello!\n Is this awesome or what?", @[conf["recipient"]])
-
-  assert conf["smtphost"] != ""
-
-  proc async_test() {.async.} =
-    let client = newAsyncSmtp(
-      conf["use_tls"].parseBool,
-      debug = true
-    )
-    await client.connect(conf["smtphost"], conf["port"].parseInt.Port)
-    await client.auth(conf["username"], conf["password"])
-    await client.sendMail(conf["sender"], @[conf["recipient"]], $msg)
-    await client.close()
-    echo "async email sent"
-
-  proc sync_test() =
-    var smtpConn = newSmtp(
-      conf["use_tls"].parseBool,
-      debug = true
-    )
-    smtpConn.connect(conf["smtphost"], conf["port"].parseInt.Port)
-    smtpConn.auth(conf["username"], conf["password"])
-    smtpConn.sendMail(conf["sender"], @[conf["recipient"]], $msg)
-    smtpConn.close()
-    echo "sync email sent"
-
-  waitFor async_test()
-  sync_test()
diff --git a/lib/pure/smtp.nim.cfg b/lib/pure/smtp.nim.cfg
deleted file mode 100644
index 521e21de4..000000000
--- a/lib/pure/smtp.nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
--d:ssl
diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim
index c40eadf04..d60cd22eb 100644
--- a/lib/pure/ssl_certs.nim
+++ b/lib/pure/ssl_certs.nim
@@ -10,7 +10,7 @@
 ## The default locations can be overridden using the SSL_CERT_FILE and
 ## SSL_CERT_DIR environment variables.
 
-import os, strutils
+import std/[os, strutils]
 
 # FWIW look for files before scanning entire dirs.
 
@@ -88,7 +88,7 @@ when defined(haiku):
   proc find_paths_etc(architecture: cstring, baseDirectory: cint,
                       subPath: cstring, flags: uint32,
                       paths: var ptr UncheckedArray[cstring],
-                      pathCount: var csize): int32
+                      pathCount: var csize_t): int32
                      {.importc, header: "<FindDirectory.h>".}
   proc free(p: pointer) {.importc, header: "<stdlib.h>".}
 
@@ -137,7 +137,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string =
     else:
       var
         paths: ptr UncheckedArray[cstring]
-        size: csize
+        size: csize_t
       let err = find_paths_etc(
         nil, B_FIND_PATH_DATA_DIRECTORY, "ssl/CARootCertificates.pem",
         B_FIND_PATH_EXISTING_ONLY, paths, size
@@ -150,7 +150,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string =
 # Certificates management on windows
 # when defined(windows) or defined(nimdoc):
 #
-#   import openssl
+#   import std/openssl
 #
 #   type
 #     PCCertContext {.final, pure.} = pointer
diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim
index 7f797529d..6a4fd8f01 100644
--- a/lib/pure/stats.nim
+++ b/lib/pure/stats.nim
@@ -53,7 +53,7 @@ runnableExamples:
   doAssert statistics.kurtosis() ~= -1.0
   doAssert statistics.kurtosisS() ~= -0.7000000000000008
 
-from math import FloatClass, sqrt, pow, round
+from std/math import FloatClass, sqrt, pow, round
 
 when defined(nimPreviewSlimSystem):
   import std/[assertions, formatfloat]
diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim
index 50b3085d2..56f49d7b1 100644
--- a/lib/pure/streams.nim
+++ b/lib/pure/streams.nim
@@ -15,6 +15,11 @@
 ## Other modules may provide other implementations for this standard
 ## stream interface.
 ##
+## .. warning:: Due to the use of `pointer`, the `readData`, `peekData` and
+## `writeData` interfaces are not available on the compile-time VM, and must
+## be cast from a `ptr string` on the JS backend. However, `readDataStr` is
+## available generally in place of `readData`.
+##
 ## Basic usage
 ## ===========
 ##
@@ -27,67 +32,67 @@
 ## StringStream example
 ## --------------------
 ##
-## .. code-block:: Nim
-##
-##  import std/streams
+##   ```Nim
+##   import std/streams
 ##
-##  var strm = newStringStream("""The first line
-##  the second line
-##  the third line""")
+##   var strm = newStringStream("""The first line
+##   the second line
+##   the third line""")
 ##
-##  var line = ""
+##   var line = ""
 ##
-##  while strm.readLine(line):
-##    echo line
+##   while strm.readLine(line):
+##     echo line
 ##
-##  # Output:
-##  # The first line
-##  # the second line
-##  # the third line
+##   # Output:
+##   # The first line
+##   # the second line
+##   # the third line
 ##
-##  strm.close()
+##   strm.close()
+##   ```
 ##
 ## FileStream example
 ## ------------------
 ##
 ## Write file stream example:
 ##
-## .. code-block:: Nim
+##   ```Nim
+##   import std/streams
 ##
-##  import std/streams
+##   var strm = newFileStream("somefile.txt", fmWrite)
+##   var line = ""
 ##
-##  var strm = newFileStream("somefile.txt", fmWrite)
-##  var line = ""
+##   if not isNil(strm):
+##     strm.writeLine("The first line")
+##     strm.writeLine("the second line")
+##     strm.writeLine("the third line")
+##     strm.close()
 ##
-##  if not isNil(strm):
-##    strm.writeLine("The first line")
-##    strm.writeLine("the second line")
-##    strm.writeLine("the third line")
-##    strm.close()
-##
-##  # Output (somefile.txt):
-##  # The first line
-##  # the second line
-##  # the third line
+##   # Output (somefile.txt):
+##   # The first line
+##   # the second line
+##   # the third line
+##   ```
 ##
 ## Read file stream example:
 ##
-## .. code-block:: Nim
-##
-##  import std/streams
+##   ```Nim
+##   import std/streams
 ##
-##  var strm = newFileStream("somefile.txt", fmRead)
-##  var line = ""
+##   var strm = newFileStream("somefile.txt", fmRead)
+##   var line = ""
 ##
-##  if not isNil(strm):
-##    while strm.readLine(line):
-##      echo line
-##    strm.close()
+##   if not isNil(strm):
+##     while strm.readLine(line):
+##       echo line
+##     strm.close()
 ##
-##  # Output:
-##  # The first line
-##  # the second line
-##  # the third line
+##   # Output:
+##   # The first line
+##   # the second line
+##   # the third line
+##   ```
 ##
 ## See also
 ## ========
@@ -98,6 +103,7 @@ import std/private/since
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
+  export FileMode
 
 proc newEIO(msg: string): owned(ref IOError) =
   new(result)
@@ -114,7 +120,7 @@ type
     ## * That these fields here shouldn't be used directly.
     ##   They are accessible so that a stream implementation can override them.
     closeImpl*: proc (s: Stream)
-      {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.}
+      {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.}
     atEndImpl*: proc (s: Stream): bool
       {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.}
     setPositionImpl*: proc (s: Stream, pos: int)
@@ -173,10 +179,20 @@ proc close*(s: Stream) =
   ## See also:
   ## * `flush proc <#flush,Stream>`_
   runnableExamples:
-    var strm = newStringStream("The first line\nthe second line\nthe third line")
-    ## do something...
-    strm.close()
-  if not isNil(s.closeImpl): s.closeImpl(s)
+    block:
+      let strm = newStringStream("The first line\nthe second line\nthe third line")
+      ## do something...
+      strm.close()
+      
+    block:
+      let strm = newFileStream("amissingfile.txt")
+      # deferring works even if newFileStream fails
+      defer: strm.close()
+      if not isNil(strm):
+        ## do something...
+
+  if not isNil(s) and not isNil(s.closeImpl):
+    s.closeImpl(s)
 
 proc atEnd*(s: Stream): bool =
   ## Checks if more data can be read from `s`. Returns ``true`` if all data has
@@ -337,9 +353,9 @@ proc write*[T](s: Stream, x: T) =
   ## **Note:** Not available for JS backend. Use `write(Stream, string)
   ## <#write,Stream,string>`_ for now.
   ##
-  ## .. code-block:: Nim
-  ##
-  ##     s.writeData(s, unsafeAddr(x), sizeof(x))
+  ##   ```Nim
+  ##   s.writeData(s, unsafeAddr(x), sizeof(x))
+  ##   ```
   runnableExamples:
     var strm = newStringStream("")
     strm.write("abcde")
@@ -927,10 +943,14 @@ proc peekFloat64*(s: Stream): float64 =
 
 proc readStrPrivate(s: Stream, length: int, str: var string) =
   if length > len(str): setLen(str, length)
-  when defined(js):
-    let L = readData(s, addr(str), length)
+  var L: int
+  when nimvm:
+    L = readDataStr(s, str, 0..length-1)
   else:
-    let L = readData(s, cstring(str), length)
+    when defined(js):
+      L = readData(s, addr(str), length)
+    else:
+      L = readData(s, cstring(str), length)
   if L != len(str): setLen(str, L)
 
 proc readStr*(s: Stream, length: int, str: var string) {.since: (1, 3).} =
@@ -1471,7 +1491,7 @@ when false:
     # do not import windows as this increases compile times:
     discard
   else:
-    import posix
+    import std/posix
 
     proc hsSetPosition(s: FileHandleStream, pos: int) =
       discard lseek(s.handle, pos, SEEK_SET)
@@ -1519,7 +1539,7 @@ when false:
       of fmReadWrite: flags = O_RDWR or int(O_CREAT)
       of fmReadWriteExisting: flags = O_RDWR
       of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND
-      static: doAssert false # handle bug #17888
+      static: raiseAssert "unreachable" # handle bug #17888
       var handle = open(filename, flags)
       if handle < 0: raise newEOS("posix.open() call failed")
     result = newFileHandleStream(handle)
diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim
index a6c1901d2..99752a9ab 100644
--- a/lib/pure/streamwrapper.nim
+++ b/lib/pure/streamwrapper.nim
@@ -11,7 +11,7 @@
 ##
 ## **Since** version 1.2.
 
-import deques, streams
+import std/[deques, streams]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -91,14 +91,14 @@ proc newPipeOutStream*[T](s: sink (ref T)): owned PipeOutStream[T] =
   ## when setPosition/getPosition is called or write operation is performed.
   ##
   ## Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   import std/[osproc, streamwrapper]
   ##   var
   ##     p = startProcess(exePath)
   ##     outStream = p.outputStream().newPipeOutStream()
   ##   echo outStream.peekChar
   ##   p.close()
+  ##   ```
 
   assert s.readDataImpl != nil
 
diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim
index fe3cfdab0..7d093ebb3 100644
--- a/lib/pure/strformat.nim
+++ b/lib/pure/strformat.nim
@@ -133,13 +133,14 @@ runnableExamples:
 
 An expression like `&"{key} is {value:arg} {{z}}"` is transformed into:
 
-.. code-block:: nim
+  ```nim
   var temp = newStringOfCap(educatedCapGuess)
   temp.formatValue(key, "")
   temp.add(" is ")
   temp.formatValue(value, arg)
   temp.add(" {z}")
   temp
+  ```
 
 Parts of the string that are enclosed in the curly braces are interpreted
 as Nim code. To escape a `{` or `}`, double it.
@@ -171,9 +172,9 @@ For strings and numeric types the optional argument is a so-called
 
 # Standard format specifiers for strings, integers and floats
 
-The general form of a standard format specifier is::
+The general form of a standard format specifier is:
 
-  [[fill]align][sign][#][0][minimumwidth][.precision][type]
+    [[fill]align][sign][#][0][minimumwidth][.precision][type]
 
 The square brackets `[]` indicate an optional element.
 
@@ -263,6 +264,12 @@ The available floating point presentation types are:
                          exponent notation.
 `G`                      General format. Same as `g` except it switches to `E`
                          if the number gets to large.
+`i`                      Complex General format. This is only supported for
+                         complex numbers, which it prints using the mathematical
+                         (RE+IMj) format. The real and imaginary parts are printed
+                         using the general format `g` by default, but it is
+                         possible to combine this format with one of the other
+                         formats (e.g `jf`).
 (None)                   Similar to `g`, except that it prints at least one
                          digit after the decimal point.
 =================        ====================================================
@@ -272,13 +279,14 @@ The available floating point presentation types are:
 Because of the well defined order how templates and macros are
 expanded, strformat cannot expand template arguments:
 
-.. code-block:: nim
+  ```nim
   template myTemplate(arg: untyped): untyped =
     echo "arg is: ", arg
     echo &"--- {arg} ---"
 
   let x = "abc"
   myTemplate(x)
+  ```
 
 First the template `myTemplate` is expanded, where every identifier
 `arg` is substituted with its argument. The `arg` inside the
@@ -289,12 +297,13 @@ identifier that cannot be resolved anymore.
 
 The workaround for this is to bind the template argument to a new local variable.
 
-.. code-block:: nim
+  ```nim
   template myTemplate(arg: untyped): untyped =
     block:
       let arg1 {.inject.} = arg
       echo "arg is: ", arg1
       echo &"--- {arg1} ---"
+  ```
 
 The use of `{.inject.}` here is necessary again because of template
 expansion order and hygienic templates. But since we generally want to
@@ -313,8 +322,8 @@ help with readability, since there is only so much you can cram into
 single letter DSLs.
 ]##
 
-import macros, parseutils, unicode
-import strutils except format
+import std/[macros, parseutils, unicode]
+import std/strutils except format
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -423,9 +432,9 @@ proc formatInt(n: SomeNumber; radix: int; spec: StandardFormatSpecifier): string
 proc parseStandardFormatSpecifier*(s: string; start = 0;
                                    ignoreUnknownSuffix = false): StandardFormatSpecifier =
   ## An exported helper proc that parses the "standard format specifiers",
-  ## as specified by the grammar::
+  ## as specified by the grammar:
   ##
-  ##   [[fill]align][sign][#][0][minimumwidth][.precision][type]
+  ##     [[fill]align][sign][#][0][minimumwidth][.precision][type]
   ##
   ## This is only of interest if you want to write a custom `format` proc that
   ## should support the standard format specifiers. If `ignoreUnknownSuffix` is true,
@@ -472,50 +481,48 @@ proc parseStandardFormatSpecifier*(s: string; start = 0;
     raise newException(ValueError,
       "invalid format string, cannot parse: " & s[i..^1])
 
+proc toRadix(typ: char): int =
+  case typ
+  of 'x', 'X': 16
+  of 'd', '\0': 10
+  of 'o': 8
+  of 'b': 2
+  else:
+    raise newException(ValueError,
+      "invalid type in format string for number, expected one " &
+      " of 'x', 'X', 'b', 'd', 'o' but got: " & typ)
+
 proc formatValue*[T: SomeInteger](result: var string; value: T;
-                                  specifier: string) =
+                                  specifier: static string) =
   ## Standard format implementation for `SomeInteger`. It makes little
   ## sense to call this directly, but it is required to exist
   ## by the `&` macro.
-  if specifier.len == 0:
+  when specifier.len == 0:
     result.add $value
-    return
-  let spec = parseStandardFormatSpecifier(specifier)
-  var radix = 10
-  case spec.typ
-  of 'x', 'X': radix = 16
-  of 'd', '\0': discard
-  of 'b': radix = 2
-  of 'o': radix = 8
   else:
-    raise newException(ValueError,
-      "invalid type in format string for number, expected one " &
-      " of 'x', 'X', 'b', 'd', 'o' but got: " & spec.typ)
-  result.add formatInt(value, radix, spec)
+    const
+      spec = parseStandardFormatSpecifier(specifier)
+      radix = toRadix(spec.typ)
 
-proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
-  ## Standard format implementation for `SomeFloat`. It makes little
+    result.add formatInt(value, radix, spec)
+
+proc formatValue*[T: SomeInteger](result: var string; value: T;
+                                  specifier: string) =
+  ## Standard format implementation for `SomeInteger`. It makes little
   ## sense to call this directly, but it is required to exist
   ## by the `&` macro.
   if specifier.len == 0:
     result.add $value
-    return
-  let spec = parseStandardFormatSpecifier(specifier)
-
-  var fmode = ffDefault
-  case spec.typ
-  of 'e', 'E':
-    fmode = ffScientific
-  of 'f', 'F':
-    fmode = ffDecimal
-  of 'g', 'G':
-    fmode = ffDefault
-  of '\0': discard
   else:
-    raise newException(ValueError,
-      "invalid type in format string for number, expected one " &
-      " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & spec.typ)
+    let
+      spec = parseStandardFormatSpecifier(specifier)
+      radix = toRadix(spec.typ)
 
+    result.add formatInt(value, radix, spec)
+
+proc formatFloat(
+    result: var string, value: SomeFloat, fmode: FloatFormatMode,
+    spec: StandardFormatSpecifier) =
   var f = formatBiggestFloat(value, fmode, spec.precision)
   var sign = false
   if value >= 0.0:
@@ -550,23 +557,83 @@ proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
   else:
     result.add res
 
+proc toFloatFormatMode(typ: char): FloatFormatMode =
+  case typ
+  of 'e', 'E': ffScientific
+  of 'f', 'F': ffDecimal
+  of 'g', 'G': ffDefault
+  of '\0': ffDefault
+  else:
+    raise newException(ValueError,
+      "invalid type in format string for number, expected one " &
+      " of 'e', 'E', 'f', 'F', 'g', 'G' but got: " & typ)
+
+proc formatValue*(result: var string; value: SomeFloat; specifier: static string) =
+  ## Standard format implementation for `SomeFloat`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  when specifier.len == 0:
+    result.add $value
+  else:
+    const
+      spec = parseStandardFormatSpecifier(specifier)
+      fmode = toFloatFormatMode(spec.typ)
+
+    formatFloat(result, value, fmode, spec)
+
+proc formatValue*(result: var string; value: SomeFloat; specifier: string) =
+  ## Standard format implementation for `SomeFloat`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  if specifier.len == 0:
+    result.add $value
+  else:
+    let
+      spec = parseStandardFormatSpecifier(specifier)
+      fmode = toFloatFormatMode(spec.typ)
+
+    formatFloat(result, value, fmode, spec)
+
+proc formatValue*(result: var string; value: string; specifier: static string) =
+  ## Standard format implementation for `string`. It makes little
+  ## sense to call this directly, but it is required to exist
+  ## by the `&` macro.
+  const spec = parseStandardFormatSpecifier(specifier)
+  var value =
+    when spec.typ in {'s', '\0'}: value
+    else: static:
+      raise newException(ValueError,
+        "invalid type in format string for string, expected 's', but got " &
+        spec.typ)
+  when spec.precision != -1:
+    if spec.precision < runeLen(value):
+      const precision = cast[Natural](spec.precision)
+      setLen(value, Natural(runeOffset(value, precision)))
+
+  result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
+
 proc formatValue*(result: var string; value: string; specifier: string) =
   ## Standard format implementation for `string`. It makes little
   ## sense to call this directly, but it is required to exist
   ## by the `&` macro.
   let spec = parseStandardFormatSpecifier(specifier)
-  var value = value
-  case spec.typ
-  of 's', '\0': discard
-  else:
-    raise newException(ValueError,
-      "invalid type in format string for string, expected 's', but got " &
-      spec.typ)
+  var value =
+    if spec.typ in {'s', '\0'}: value
+    else:
+      raise newException(ValueError,
+        "invalid type in format string for string, expected 's', but got " &
+        spec.typ)
   if spec.precision != -1:
     if spec.precision < runeLen(value):
-      setLen(value, runeOffset(value, spec.precision))
+      let precision = cast[Natural](spec.precision)
+      setLen(value, Natural(runeOffset(value, precision)))
+
   result.add alignString(value, spec.minimumWidth, spec.align, spec.fill)
 
+proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: static string) =
+  mixin `$`
+  formatValue(result, $value, specifier)
+
 proc formatValue[T: not SomeInteger](result: var string; value: T; specifier: string) =
   mixin `$`
   formatValue(result, $value, specifier)
@@ -577,7 +644,8 @@ template formatValue(result: var string; value: char; specifier: string) =
 template formatValue(result: var string; value: cstring; specifier: string) =
   result.add value
 
-proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
+proc strformatImpl(f: string; openChar, closeChar: char,
+                   lineInfoNode: NimNode = nil): NimNode =
   template missingCloseChar =
     error("invalid format string: missing closing character '" & closeChar & "'")
 
@@ -585,7 +653,7 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
     error "openChar and closeChar must not be ':'"
   var i = 0
   let res = genSym(nskVar, "fmtRes")
-  result = newNimNode(nnkStmtListExpr)
+  result = newNimNode(nnkStmtListExpr, lineInfoNode)
   # XXX: https://github.com/nim-lang/Nim/issues/8405
   # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils
   # module are not accessible at compile-time:
@@ -644,6 +712,7 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
           x = parseExpr(subexpr)
         except ValueError as e:
           error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg])
+        x.copyLineInfo(lineInfoNode)
         let formatSym = bindSym("formatValue", brOpen)
         var options = ""
         if f[i] == ':':
@@ -661,18 +730,29 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode =
         strlit.add closeChar
         inc i, 2
       else:
-        doAssert false, "invalid format string: '$1' instead of '$1$1'" % $closeChar
-        inc i
+        raiseAssert "invalid format string: '$1' instead of '$1$1'" % $closeChar
     else:
       strlit.add f[i]
       inc i
   if strlit.len > 0:
     result.add newCall(bindSym"add", res, newLit(strlit))
   result.add res
+  # workaround for #20381
+  var blockExpr = newNimNode(nnkBlockExpr, lineInfoNode)
+  blockExpr.add(newEmptyNode())
+  blockExpr.add(result)
+  result = blockExpr
   when defined(debugFmtDsl):
     echo repr result
 
-macro fmt*(pattern: static string; openChar: static char, closeChar: static char): string =
+macro fmt(pattern: static string; openChar: static char, closeChar: static char, lineInfoNode: untyped): string =
+  ## version of `fmt` with dummy untyped param for line info
+  strformatImpl(pattern, openChar, closeChar, lineInfoNode)
+
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template fmt*(pattern: static string; openChar: static char, closeChar: static char): string {.callsite.} =
   ## Interpolates `pattern` using symbols in scope.
   runnableExamples:
     let x = 7
@@ -689,13 +769,13 @@ macro fmt*(pattern: static string; openChar: static char, closeChar: static char
     assert "<x>".fmt('<', '>') == "7"
     assert "<<<x>>>".fmt('<', '>') == "<7>"
     assert "`x`".fmt('`', '`') == "7"
-  strformatImpl(pattern, openChar, closeChar)
+  fmt(pattern, openChar, closeChar, dummyForLineInfo)
 
-template fmt*(pattern: static string): untyped =
+template fmt*(pattern: static string): untyped {.callsite.} =
   ## Alias for `fmt(pattern, '{', '}')`.
-  fmt(pattern, '{', '}')
+  fmt(pattern, '{', '}', dummyForLineInfo)
 
-macro `&`*(pattern: string{lit}): string =
+template `&`*(pattern: string{lit}): string {.callsite.} =
   ## `&pattern` is the same as `pattern.fmt`.
   ## For a specification of the `&` macro, see the module level documentation.
   # pending bug #18275, bug #18278, use `pattern: static string`
@@ -707,4 +787,4 @@ macro `&`*(pattern: string{lit}): string =
     assert &"{x}\n" == "7\n" # regular string literal
     assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead
     assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal
-  strformatImpl(pattern.strVal, '{', '}')
+  fmt(pattern, '{', '}', dummyForLineInfo)
diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim
index c8cd839be..a3e539e7e 100644
--- a/lib/pure/strmisc.nim
+++ b/lib/pure/strmisc.nim
@@ -27,20 +27,17 @@ func expandTabs*(s: string, tabSize: int = 8): string =
     doAssert expandTabs("a\tb\n\txy\t", 3) == "a  b\n   xy "
 
   result = newStringOfCap(s.len + s.len shr 2)
-  var pos = 0
 
   template addSpaces(n) =
-    for j in 0 ..< n:
+    for _ in 1..n:
       result.add(' ')
-      pos += 1
+    pos += n
 
-  for i in 0 ..< len(s):
-    let c = s[i]
+  var pos = 0
+  let denominator = if tabSize > 0: tabSize else: 1
+  for c in s:
     if c == '\t':
-      let
-        denominator = if tabSize > 0: tabSize else: 1
-        numSpaces = tabSize - pos mod denominator
-
+      let numSpaces = tabSize - pos mod denominator
       addSpaces(numSpaces)
     else:
       result.add(c)
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index 8a1ea125f..16ef9e642 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -12,7 +12,7 @@ This module contains a `scanf`:idx: macro that can be used for extracting
 substrings from an input string. This is often easier than regular expressions.
 Some examples as an appetizer:
 
-.. code-block:: nim
+  ```nim
   # check if input string matches a triple of integers:
   const input = "(1,2,4)"
   var x, y, z: int
@@ -26,6 +26,7 @@ Some examples as an appetizer:
   var myfloat: float
   if scanf(input, "$i-$i-$i $w$s$f", year, month, day, identifier, myfloat):
     echo "yes, we have a match!"
+  ```
 
 As can be seen from the examples, strings are matched verbatim except for
 substrings starting with ``$``. These constructions are available:
@@ -83,8 +84,7 @@ matches optional tokens without any result binding.
 In this example, we define a helper proc ``someSep`` that skips some separators
 which we then use in our scanf pattern to help us in the matching process:
 
-.. code-block:: nim
-
+  ```nim
   proc someSep(input: string; start: int; seps: set[char] = {':','-','.'}): int =
     # Note: The parameters and return value must match to what ``scanf`` requires
     result = 0
@@ -92,11 +92,11 @@ which we then use in our scanf pattern to help us in the matching process:
 
   if scanf(input, "$w$[someSep]$w", key, value):
     ...
+  ```
 
-It also possible to pass arguments to a user definable matcher:
-
-.. code-block:: nim
+It is also possible to pass arguments to a user definable matcher:
 
+  ```nim
   proc ndigits(input: string; intVal: var int; start: int; n: int): int =
     # matches exactly ``n`` digits. Matchers need to return 0 if nothing
     # matched or otherwise the number of processed chars.
@@ -115,6 +115,7 @@ It also possible to pass arguments to a user definable matcher:
   var year, month, day: int
   if scanf("2013-01-03", "${ndigits(4)}-${ndigits(2)}-${ndigits(2)}$.", year, month, day):
     ...
+  ```
 
 
 The scanp macro
@@ -145,8 +146,7 @@ not implemented.
 
 Simple example that parses the ``/etc/passwd`` file line by line:
 
-.. code-block:: nim
-
+  ```nim
   const
     etc_passwd = """root:x:0:0:root:/root:/bin/bash
   daemon:x:1:1:daemon:/usr/sbin:/bin/sh
@@ -165,16 +165,16 @@ Simple example that parses the ``/etc/passwd`` file line by line:
         result.add entry
       else:
         break
+  ```
 
 The ``scanp`` maps the grammar code into Nim code that performs the parsing.
 The parsing is performed with the help of 3 helper templates that that can be
 implemented for a custom type.
 
 These templates need to be named ``atom`` and ``nxt``. ``atom`` should be
-overloaded to handle both single characters and sets of character.
-
-.. code-block:: nim
+overloaded to handle both `char` and `set[char]`.
 
+  ```nim
   import std/streams
 
   template atom(input: Stream; idx: int; c: char): bool =
@@ -190,11 +190,11 @@ overloaded to handle both single characters and sets of character.
 
   if scanp(content, idx, +( ~{'\L', '\0'} -> entry.add(peekChar($input))), '\L'):
     result.add entry
+  ```
 
 Calling ordinary Nim procs inside the macro is possible:
 
-.. code-block:: nim
-
+  ```nim
   proc digits(s: string; intVal: var int; start: int): int =
     var x = 0
     while result+start < s.len and s[result+start] in {'0'..'9'} and s[result+start] != ':':
@@ -220,12 +220,12 @@ Calling ordinary Nim procs inside the macro is possible:
           result.add login & " " & homedir
       else:
         break
+  ```
 
 When used for matching, keep in mind that likewise scanf, no backtracking
 is performed.
 
-.. code-block:: nim
-
+  ```nim
   proc skipUntil(s: string; until: string; unless = '\0'; start: int): int =
     # Skips all characters until the string `until` is found. Returns 0
     # if the char `unless` is found first or the end is reached.
@@ -256,12 +256,12 @@ is performed.
 
   for r in collectLinks(body):
     echo r
+  ```
 
 In this example both macros are combined seamlessly in order to maximise
 efficiency and perform different checks.
 
-.. code-block:: nim
-
+  ```nim
   iterator parseIps*(soup: string): string =
     ## ipv4 only!
     const digits = {'0'..'9'}
@@ -279,11 +279,11 @@ efficiency and perform different checks.
           yield buf
       buf.setLen(0) # need to clear `buf` each time, cause it might contain garbage
       idx.inc
-
+  ```
 ]##
 
 
-import macros, parseutils
+import std/[macros, parseutils]
 import std/private/since
 
 when defined(nimPreviewSlimSystem):
@@ -472,7 +472,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
 
 macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}=
   ## Works identically as scanf, but instead of predeclaring variables it returns a tuple.
-  ## Tuple is started with a bool which indicates if the scan was successful 
+  ## Tuple is started with a bool which indicates if the scan was successful
   ## followed by the requested data.
   ## If using a user defined matcher, provide the types in order they appear after pattern:
   ## `line.scanTuple("${yourMatcher()}", int)`
diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim
index aa2886cfa..4b07aca5a 100644
--- a/lib/pure/strtabs.nim
+++ b/lib/pure/strtabs.nim
@@ -51,7 +51,7 @@ runnableExamples:
 import std/private/since
 
 import
-  hashes, strutils
+  std/[hashes, strutils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -61,7 +61,7 @@ when defined(js) or defined(nimscript) or defined(Standalone):
   {.pragma: rtlFunc.}
 else:
   {.pragma: rtlFunc, rtl.}
-  import os
+  import std/envvars
 
 include "system/inclrtl"
 
@@ -261,10 +261,7 @@ proc newStringTable*(mode: StringTableMode): owned(StringTableRef) {.
   ## See also:
   ## * `newStringTable(keyValuePairs) proc
   ##   <#newStringTable,varargs[tuple[string,string]],StringTableMode>`_
-  new(result)
-  result.mode = mode
-  result.counter = 0
-  newSeq(result.data, startSize)
+  result = StringTableRef(mode: mode, counter: 0, data: newSeq[KeyValuePair](startSize))
 
 proc newStringTable*(keyValuePairs: varargs[string],
                      mode: StringTableMode): owned(StringTableRef) {.
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 3ae953a55..81be7db17 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -70,16 +70,16 @@ runnableExamples:
 ##   easier substring extraction than regular expressions
 
 
-import parseutils
-from math import pow, floor, log10
-from algorithm import fill, reverse
+import std/parseutils
+from std/math import pow, floor, log10
+from std/algorithm import fill, reverse
 import std/enumutils
 
-from unicode import toLower, toUpper
+from std/unicode import toLower, toUpper
 export toLower, toUpper
 
 include "system/inclrtl"
-import std/private/since
+import std/private/[since, jsutils]
 from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl,
     startsWithImpl, endsWithImpl
 
@@ -129,11 +129,11 @@ const
     ## Not very useful by its own, you can use it to create *inverted* sets to
     ## make the `find func<#find,string,set[char],Natural,int>`_
     ## find **invalid** characters in strings. Example:
-    ##
-    ## .. code-block:: nim
+    ##   ```nim
     ##   let invalid = AllChars - Digits
     ##   doAssert "01234".find(invalid) == -1
     ##   doAssert "01A34".find(invalid) == 2
+    ##   ```
 
 func isAlphaAscii*(c: char): bool {.rtl, extern: "nsuIsAlphaAsciiChar".} =
   ## Checks whether or not character `c` is alphabetical.
@@ -334,9 +334,9 @@ func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} =
 func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} =
   ## Compares two strings in a case insensitive manner. Returns:
   ##
-  ## | 0 if a == b
-  ## | < 0 if a < b
-  ## | > 0 if a > b
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
   runnableExamples:
     doAssert cmpIgnoreCase("FooBar", "foobar") == 0
     doAssert cmpIgnoreCase("bar", "Foo") < 0
@@ -354,9 +354,9 @@ func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} =
   ##
   ## Returns:
   ##
-  ## | 0 if a == b
-  ## | < 0 if a < b
-  ## | > 0 if a > b
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
   runnableExamples:
     doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
     doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
@@ -366,11 +366,14 @@ func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} =
 # --------- Private templates for different split separators -----------
 
 func substrEq(s: string, pos: int, substr: string): bool =
-  var i = 0
+  # Always returns false for empty `substr`
   var length = substr.len
-  while i < length and pos+i < s.len and s[pos+i] == substr[i]:
-    inc i
-  return i == length
+  if length > 0:
+    var i = 0
+    while i < length and pos+i < s.len and s[pos+i] == substr[i]:
+      inc i
+    i == length
+  else: false
 
 template stringHasSep(s: string, index: int, seps: set[char]): bool =
   s[index] in seps
@@ -420,14 +423,12 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string =
   ##
   ## Substrings are separated by the character `sep`.
   ## The code:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split(";;this;is;an;;example;;;", ';'):
   ##     writeLine(stdout, word)
-  ##
+  ##   ```
   ## Results in:
-  ##
-  ## .. code-block::
+  ##   ```
   ##   ""
   ##   ""
   ##   "this"
@@ -438,6 +439,7 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string =
   ##   ""
   ##   ""
   ##   ""
+  ##   ```
   ##
   ## See also:
   ## * `rsplit iterator<#rsplit.i,string,char,int>`_
@@ -452,41 +454,49 @@ iterator split*(s: string, seps: set[char] = Whitespace,
   ##
   ## Substrings are separated by a substring containing only `seps`.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("this\lis an\texample"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## ...generates this output:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "this"
   ##   "is"
   ##   "an"
   ##   "example"
+  ##   ```
   ##
   ## And the following code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("this:is;an$example", {';', ':', '$'}):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## ...produces the same output as the first example. The code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   let date = "2012-11-20T22:08:08.398990"
   ##   let separators = {' ', '-', ':', 'T'}
   ##   for number in split(date, separators):
   ##     writeLine(stdout, number)
+  ##   ```
   ##
   ## ...results in:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "2012"
   ##   "11"
   ##   "20"
   ##   "22"
   ##   "08"
   ##   "08.398990"
+  ##   ```
+  ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
   ## See also:
   ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
@@ -501,23 +511,30 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string =
   ## Substrings are separated by the string `sep`.
   ## The code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in split("thisDATAisDATAcorrupted", "DATA"):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block::
+  ##   ```
   ##   "this"
   ##   "is"
   ##   "corrupted"
+  ##   ```
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
   ## See also:
   ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `split func<#split,string,string,int>`_
-  splitCommon(s, sep, maxsplit, sep.len)
+  let sepLen = if sep.len == 0: 1 # prevents infinite loop
+    else: sep.len
+  splitCommon(s, sep, maxsplit, sepLen)
 
 
 template rsplitCommon(s, sep, maxsplit, sepLen) =
@@ -548,17 +565,19 @@ iterator rsplit*(s: string, sep: char,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,char,int>`_ except in reverse order.
+  ## <#split.i,string,char,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for piece in "foo:bar".rsplit(':'):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "bar"
   ##   "foo"
+  ##   ```
   ##
   ## Substrings are separated from the right by the char `sep`.
   ##
@@ -573,20 +592,25 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace,
                  maxsplit: int = -1): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,char,int>`_ except in reverse order.
+  ## <#split.i,string,char,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for piece in "foo bar".rsplit(WhiteSpace):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "bar"
   ##   "foo"
+  ##   ```
   ##
   ## Substrings are separated from the right by the set of chars `seps`
   ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
   ## See also:
   ## * `split iterator<#split.i,string,set[char],int>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
@@ -598,44 +622,52 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
                  keepSeparators: bool = false): string =
   ## Splits the string `s` into substrings from the right using a
   ## string separator. Works exactly the same as `split iterator
-  ## <#split.i,string,string,int>`_ except in reverse order.
+  ## <#split.i,string,string,int>`_ except in **reverse** order.
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for piece in "foothebar".rsplit("the"):
   ##     echo piece
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```
   ##   "bar"
   ##   "foo"
+  ##   ```
   ##
   ## Substrings are separated from the right by the string `sep`
   ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
   ## See also:
   ## * `split iterator<#split.i,string,string,int>`_
   ## * `splitLines iterator<#splitLines.i,string>`_
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
   ## * `rsplit func<#rsplit,string,string,int>`_
-  rsplitCommon(s, sep, maxsplit, sep.len)
+  let sepLen = if sep.len == 0: 1 # prevents infinite loop
+    else: sep.len
+  rsplitCommon(s, sep, maxsplit, sepLen)
 
 iterator splitLines*(s: string, keepEol = false): string =
   ## Splits the string `s` into its containing lines.
   ##
   ## Every `character literal <manual.html#lexical-analysis-character-literals>`_
   ## newline combination (CR, LF, CR-LF) is supported. The result strings
-  ## contain no trailing end of line characters unless parameter `keepEol`
+  ## contain no trailing end of line characters unless the parameter `keepEol`
   ## is set to `true`.
   ##
   ## Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for line in splitLines("\nthis\nis\nan\n\nexample\n"):
   ##     writeLine(stdout, line)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   ""
   ##   "this"
   ##   "is"
@@ -643,6 +675,7 @@ iterator splitLines*(s: string, keepEol = false): string =
   ##   ""
   ##   "example"
   ##   ""
+  ##   ```
   ##
   ## See also:
   ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
@@ -675,16 +708,17 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
   ##
   ## The following code:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   let s = "  foo \t bar  baz  "
   ##   for ms in [-1, 1, 2, 3]:
   ##     echo "------ maxsplit = ", ms, ":"
   ##     for item in s.splitWhitespace(maxsplit=ms):
   ##       echo '"', item, '"'
+  ##   ```
   ##
   ## ...results in:
   ##
-  ## .. code-block::
+  ##   ```
   ##   ------ maxsplit = -1:
   ##   "foo"
   ##   "bar"
@@ -700,6 +734,7 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
   ##   "foo"
   ##   "bar"
   ##   "baz"
+  ##   ```
   ##
   ## See also:
   ## * `splitLines iterator<#splitLines.i,string>`_
@@ -728,6 +763,9 @@ func split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[
   ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
   ## documentation), but is a func that returns a sequence of substrings.
   ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
   ## See also:
   ## * `split iterator <#split.i,string,set[char],int>`_
   ## * `rsplit func<#rsplit,string,set[char],int>`_
@@ -736,6 +774,7 @@ func split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[
   runnableExamples:
     doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
     doAssert "".split({' '}) == @[""]
+    doAssert "empty seps return unsplit s".split({}) == @["empty seps return unsplit s"]
   accResult(split(s, seps, maxsplit))
 
 func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
@@ -745,6 +784,9 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
   ## Substrings are separated by the string `sep`. This is a wrapper around the
   ## `split iterator <#split.i,string,string,int>`_.
   ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
+  ##
   ## See also:
   ## * `split iterator <#split.i,string,string,int>`_
   ## * `rsplit func<#rsplit,string,string,int>`_
@@ -757,14 +799,13 @@ func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
     doAssert "a  largely    spaced sentence".split(" ") == @["a", "", "largely",
         "", "", "", "spaced", "sentence"]
     doAssert "a  largely    spaced sentence".split(" ", maxsplit = 1) == @["a", " largely    spaced sentence"]
-  doAssert(sep.len > 0)
-
+    doAssert "empty sep returns unsplit s".split("") == @["empty sep returns unsplit s"]
   accResult(split(s, sep, maxsplit))
 
 func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
     extern: "nsuRSplitChar".} =
   ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a func
-  ## that returns a sequence of substrings.
+  ## that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -772,13 +813,15 @@ func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl,
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
+  ##   ```
   ##
   ## See also:
   ## * `rsplit iterator <#rsplit.i,string,char,int>`_
@@ -792,7 +835,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
              maxsplit: int = -1): seq[string]
              {.rtl, extern: "nsuRSplitCharSet".} =
   ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
-  ## func that returns a sequence of substrings.
+  ## func that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -800,13 +843,18 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
+  ##   ```
+  ##
+  ##  .. note:: Empty separator set results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
   ## See also:
   ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
@@ -819,7 +867,7 @@ func rsplit*(s: string, seps: set[char] = Whitespace,
 func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
     extern: "nsuRSplitString".} =
   ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a func
-  ## that returns a sequence of substrings.
+  ## that returns a sequence of substrings in original order.
   ##
   ## A possible common use case for `rsplit` is path manipulation,
   ## particularly on systems that don't use a common delimiter.
@@ -827,13 +875,18 @@ func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
   ## For example, if a system had `#` as a delimiter, you could
   ## do the following to get the tail of the path:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   var tailSplit = rsplit("Root#Object#Method#Index", "#", maxsplit=1)
+  ##   ```
   ##
   ## Results in `tailSplit` containing:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   @["Root#Object#Method", "Index"]
+  ##   ```
+  ##
+  ##  .. note:: Empty separator string results in returning an original string,
+  ##   following the interpretation "split by no element".
   ##
   ## See also:
   ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
@@ -849,6 +902,7 @@ func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl,
     doAssert "".rsplit("Elon Musk") == @[""]
     doAssert "a  largely    spaced sentence".rsplit(" ") == @["a", "",
         "largely", "", "", "", "spaced", "sentence"]
+    doAssert "empty sep returns unsplit s".rsplit("") == @["empty sep returns unsplit s"]
   accResult(rsplit(s, sep, maxsplit))
   result.reverse()
 
@@ -944,14 +998,26 @@ func toHex*[T: SomeInteger](x: T, len: Positive): string =
     doAssert b.toHex(4) == "1001"
     doAssert toHex(62, 3) == "03E"
     doAssert toHex(-8, 6) == "FFFFF8"
-  toHexImpl(cast[BiggestUInt](x), len, x < 0)
+  whenJsNoBigInt64:
+    toHexImpl(cast[BiggestUInt](x), len, x < 0)
+  do:
+    when T is SomeSignedInt:
+      toHexImpl(cast[BiggestUInt](BiggestInt(x)), len, x < 0)
+    else:
+      toHexImpl(BiggestUInt(x), len, x < 0)
 
 func toHex*[T: SomeInteger](x: T): string =
   ## Shortcut for `toHex(x, T.sizeof * 2)`
   runnableExamples:
     doAssert toHex(1984'i64) == "00000000000007C0"
     doAssert toHex(1984'i16) == "07C0"
-  toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0)
+  whenJsNoBigInt64:
+    toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0)
+  do:
+    when T is SomeSignedInt:
+      toHexImpl(cast[BiggestUInt](BiggestInt(x)), 2*sizeof(T), x < 0)
+    else:
+      toHexImpl(BiggestUInt(x), 2*sizeof(T), x < 0)
 
 func toHex*(s: string): string {.rtl.} =
   ## Converts a bytes string to its hexadecimal representation.
@@ -1241,7 +1307,7 @@ func parseEnum*[T: enum](s: string): T =
   ## type contains multiple fields with the same string value.
   ##
   ## Raises `ValueError` for an invalid value in `s`. The comparison is
-  ## done in a style insensitive way.
+  ## done in a style insensitive way (first letter is still case-sensitive).
   runnableExamples:
     type
       MyEnum = enum
@@ -1261,7 +1327,7 @@ func parseEnum*[T: enum](s: string, default: T): T =
   ## type contains multiple fields with the same string value.
   ##
   ## Uses `default` for an invalid value in `s`. The comparison is done in a
-  ## style insensitive way.
+  ## style insensitive way (first letter is still case-sensitive).
   runnableExamples:
     type
       MyEnum = enum
@@ -1648,7 +1714,7 @@ func removePrefix*(s: var string, chars: set[char] = Newlines) {.rtl,
 
   var start = 0
   while start < s.len and s[start] in chars: start += 1
-  if start > 0: s.delete(0, start - 1)
+  if start > 0: s.delete(0..start - 1)
 
 func removePrefix*(s: var string, c: char) {.rtl,
     extern: "nsuRemovePrefixChar".} =
@@ -1675,8 +1741,8 @@ func removePrefix*(s: var string, prefix: string) {.rtl,
     var answers = "yesyes"
     answers.removePrefix("yes")
     doAssert answers == "yes"
-  if s.startsWith(prefix):
-    s.delete(0, prefix.len - 1)
+  if s.startsWith(prefix) and prefix.len > 0:
+    s.delete(0..prefix.len - 1)
 
 func removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl,
     extern: "nsuRemoveSuffixCharSet".} =
@@ -1742,8 +1808,9 @@ func addSep*(dest: var string, sep = ", ", startLen: Natural = 0) {.inline.} =
   ##
   ## A shorthand for:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   if dest.len > startLen: add(dest, sep)
+  ##   ```
   ##
   ## This is often useful for generating some code where the items need to
   ## be *separated* by `sep`. `sep` is only added if `dest` is longer than
@@ -1808,7 +1875,7 @@ func join*(a: openArray[string], sep: string = ""): string {.rtl,
   else:
     result = ""
 
-func join*[T: not string](a: openArray[T], sep: string = ""): string =
+proc join*[T: not string](a: openArray[T], sep: string = ""): string =
   ## Converts all elements in the container `a` to strings using `$`,
   ## and concatenates them with `sep`.
   runnableExamples:
@@ -1882,9 +1949,6 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): int {.
 when not (defined(js) or defined(nimdoc) or defined(nimscript)):
   func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
                 importc: "memchr", header: "<string.h>".}
-  func c_strstr(haystack, needle: cstring): cstring {.
-    importc: "strstr", header: "<string.h>".}
-
   const hasCStringBuiltin = true
 else:
   const hasCStringBuiltin = false
@@ -1917,7 +1981,7 @@ func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl,
       if length > 0:
         let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length))
         if not found.isNil:
-          return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
+          return cast[int](found) -% cast[int](s.cstring)
     else:
       findImpl()
 
@@ -1939,6 +2003,14 @@ func find*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.
     if s[i] in chars:
       return i
 
+when defined(linux):
+  proc memmem(haystack: pointer, haystacklen: csize_t,
+              needle: pointer, needlelen: csize_t): pointer {.importc, header: """#define _GNU_SOURCE
+#include <string.h>""".}
+elif defined(bsd) or (defined(macosx) and not defined(ios)):
+  proc memmem(haystack: pointer, haystacklen: csize_t,
+              needle: pointer, needlelen: csize_t): pointer {.importc, header: "#include <string.h>".}
+
 func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
     extern: "nsuFindStr".} =
   ## Searches for `sub` in `s` inside range `start..last` (both ends included).
@@ -1960,11 +2032,12 @@ func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl,
   when nimvm:
     useSkipTable()
   else:
-    when hasCStringBuiltin:
-      if last < 0 and start < s.len:
-        let found = c_strstr(s[start].unsafeAddr, sub)
+    when declared(memmem):
+      let subLen = sub.len
+      if last < 0 and start < s.len and subLen != 0:
+        let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen))
         result = if not found.isNil:
-            cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
+            cast[int](found) -% cast[int](s.cstring)
           else:
             -1
       else:
@@ -2208,17 +2281,31 @@ func replaceWord*(s, sub: string, by = ""): string {.rtl,
     add result, substr(s, i)
 
 func 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.
+  ## Same as `replace<#replace,string,string,string>`_, but specialized for
+  ## doing multiple replacements in a single pass through the input string.
   ##
-  ## `multiReplace` performs all replacements in a single pass, this means it
-  ## can be used to swap the occurrences of "a" and "b", for instance.
+  ## `multiReplace` scans the input string from left to right and replaces the
+  ## matching substrings in the same order as passed in the argument list.
+  ##
+  ## The implications of the order of scanning the string and matching the
+  ## replacements:
+  ##   - In case of multiple matches at a given position, the earliest
+  ##     replacement is applied.
+  ##   - Overlaps are not handled. After performing a replacement, the scan
+  ##     continues from the character after the matched substring. If the
+  ##     resulting string then contains a possible match starting in a newly
+  ##     placed substring, the additional replacement is not performed.
   ##
   ## If the resulting string is not longer than the original input string,
   ## only a single memory allocation is required.
   ##
-  ## The order of the replacements does matter. Earlier replacements are
-  ## preferred over later replacements in the argument list.
+  runnableExamples:
+    # Swapping occurrences of 'a' and 'b':
+    doAssert multireplace("abba", [("a", "b"), ("b", "a")]) == "baab"
+
+    # The second replacement ("ab") is matched and performed first, the scan then
+    # continues from 'c', so the "bc" replacement is never matched and thus skipped.
+    doAssert multireplace("abc", [("bc", "x"), ("ab", "_b")]) == "_bc"
   result = newStringOfCap(s.len)
   var i = 0
   var fastChk: set[char] = {}
@@ -2367,8 +2454,8 @@ func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} =
 
 # floating point formatting:
 when not defined(js):
-  func c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
-                                     importc: "sprintf", varargs.}
+  func c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>",
+                                     importc: "snprintf", varargs.}
 
 type
   FloatFormatMode* = enum
@@ -2395,60 +2482,63 @@ func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
     doAssert x.formatBiggestFloat() == "123.4560000000000"
     doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560"
     doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02"
-  when defined(js):
-    var precision = precision
-    if precision == -1:
-      # use the same default precision as c_sprintf
-      precision = 6
-    var res: cstring
-    case format
-    of ffDefault:
-      {.emit: "`res` = `f`.toString();".}
-    of ffDecimal:
-      {.emit: "`res` = `f`.toFixed(`precision`);".}
-    of ffScientific:
-      {.emit: "`res` = `f`.toExponential(`precision`);".}
-    result = $res
-    if 1.0 / f == -Inf:
-      # JavaScript removes the "-" from negative Zero, add it back here
-      result = "-" & $res
-    for i in 0 ..< result.len:
-      # Depending on the locale either dot or comma is produced,
-      # but nothing else is possible:
-      if result[i] in {'.', ','}: result[i] = decimalSep
+  when nimvm:
+    discard "implemented in the vmops"
   else:
-    const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
-    var
-      frmtstr {.noinit.}: array[0..5, char]
-      buf {.noinit.}: array[0..2500, char]
-      L: cint
-    frmtstr[0] = '%'
-    if precision >= 0:
-      frmtstr[1] = '#'
-      frmtstr[2] = '.'
-      frmtstr[3] = '*'
-      frmtstr[4] = floatFormatToChar[format]
-      frmtstr[5] = '\0'
-      L = c_sprintf(addr buf, addr frmtstr, precision, f)
+    when defined(js):
+      var precision = precision
+      if precision == -1:
+        # use the same default precision as c_snprintf
+        precision = 6
+      var res: cstring
+      case format
+      of ffDefault:
+        {.emit: "`res` = `f`.toString();".}
+      of ffDecimal:
+        {.emit: "`res` = `f`.toFixed(`precision`);".}
+      of ffScientific:
+        {.emit: "`res` = `f`.toExponential(`precision`);".}
+      result = $res
+      if 1.0 / f == -Inf:
+        # JavaScript removes the "-" from negative Zero, add it back here
+        result = "-" & $res
+      for i in 0 ..< result.len:
+        # Depending on the locale either dot or comma is produced,
+        # but nothing else is possible:
+        if result[i] in {'.', ','}: result[i] = decimalSep
     else:
-      frmtstr[1] = floatFormatToChar[format]
-      frmtstr[2] = '\0'
-      L = c_sprintf(addr buf, addr frmtstr, f)
-    result = newString(L)
-    for i in 0 ..< L:
-      # Depending on the locale either dot or comma is produced,
-      # but nothing else is possible:
-      if buf[i] in {'.', ','}: result[i] = decimalSep
-      else: result[i] = buf[i]
-    when defined(windows):
-      # VS pre 2015 violates the C standard: "The exponent always contains at
-      # least two digits, and only as many more digits as necessary to
-      # represent the exponent." [C11 §7.21.6.1]
-      # The following post-processing fixes this behavior.
-      if result.len > 4 and result[^4] == '+' and result[^3] == '0':
-        result[^3] = result[^2]
-        result[^2] = result[^1]
-        result.setLen(result.len - 1)
+      const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
+      var
+        frmtstr {.noinit.}: array[0..5, char]
+        buf {.noinit.}: array[0..2500, char]
+        L: cint
+      frmtstr[0] = '%'
+      if precision >= 0:
+        frmtstr[1] = '#'
+        frmtstr[2] = '.'
+        frmtstr[3] = '*'
+        frmtstr[4] = floatFormatToChar[format]
+        frmtstr[5] = '\0'
+        L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), precision, f)
+      else:
+        frmtstr[1] = floatFormatToChar[format]
+        frmtstr[2] = '\0'
+        L = c_snprintf(cast[cstring](addr buf), csize_t(2501), cast[cstring](addr frmtstr), f)
+      result = newString(L)
+      for i in 0 ..< L:
+        # Depending on the locale either dot or comma is produced,
+        # but nothing else is possible:
+        if buf[i] in {'.', ','}: result[i] = decimalSep
+        else: result[i] = buf[i]
+      when defined(windows):
+        # VS pre 2015 violates the C standard: "The exponent always contains at
+        # least two digits, and only as many more digits as necessary to
+        # represent the exponent." [C11 §7.21.6.1]
+        # The following post-processing fixes this behavior.
+        if result.len > 4 and result[^4] == '+' and result[^3] == '0':
+          result[^3] = result[^2]
+          result[^2] = result[^1]
+          result.setLen(result.len - 1)
 
 func formatFloat*(f: float, format: FloatFormatMode = ffDefault,
                   precision: range[-1..32] = 16; decimalSep = '.'): string {.
@@ -2488,7 +2578,8 @@ func trimZeros*(x: var string; decimalSep = '.') =
     var pos = last
     while pos >= 0 and x[pos] == '0': dec(pos)
     if pos > sPos: inc(pos)
-    x.delete(pos, last)
+    if last >= pos:
+      x.delete(pos..last)
 
 type
   BinaryPrefixMode* = enum ## The different names for binary prefixes.
@@ -2580,13 +2671,13 @@ func formatEng*(f: BiggestFloat,
   ## decimal point or (if `trim` is true) the maximum number of digits to be
   ## shown.
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##    formatEng(0, 2, trim=false) == "0.00"
   ##    formatEng(0, 2) == "0"
   ##    formatEng(0.053, 0) == "53e-3"
   ##    formatEng(52731234, 2) == "52.73e6"
   ##    formatEng(-52731234, 2) == "-52.73e6"
+  ##    ```
   ##
   ## If `siPrefix` is set to true, the number will be displayed with the SI
   ## prefix corresponding to the exponent. For example 4100 will be displayed
@@ -2601,8 +2692,7 @@ func formatEng*(f: BiggestFloat,
   ## different to appending the unit to the result as the location of the space
   ## is altered depending on whether there is an exponent.
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##    formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
   ##    formatEng(4.1, siPrefix=true, unit="V") == "4.1 V"
   ##    formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
@@ -2612,6 +2702,7 @@ func formatEng*(f: BiggestFloat,
   ##    formatEng(4100) == "4.1e3"
   ##    formatEng(4100, unit="V") == "4.1e3 V"
   ##    formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " # Space with useUnitSpace=true
+  ##    ```
   ##
   ## `decimalSep` is used as the decimal separator.
   ##
@@ -2775,13 +2866,15 @@ func `%`*(formatstr: string, a: openArray[string]): string {.rtl,
   ##
   ## This is best explained by an example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$1 eats $2." % ["The cat", "fish"]
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "The cat eats fish."
+  ##   ```
   ##
   ## The substitution variables (the thing after the `$`) are enumerated
   ## from 1 to `a.len`.
@@ -2789,21 +2882,24 @@ func `%`*(formatstr: string, a: openArray[string]): string {.rtl,
   ## The notation `$#` can be used to refer to the next substitution
   ## variable:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$# eats $#." % ["The cat", "fish"]
+  ##   ```
   ##
   ## Substitution variables can also be words (that is
   ## `[A-Za-z_]+[A-Za-z0-9_]*`) in which case the arguments in `a` with even
   ## indices are keys and with odd indices are the corresponding values.
   ## An example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "$animal eats $food." % ["animal", "The cat", "food", "fish"]
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   "The cat eats fish."
+  ##   ```
   ##
   ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
   ## raised if an ill-formed format string has been passed to the `%` operator.
@@ -2901,13 +2997,14 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
   ## Substrings are separated by a substring containing only `seps`.
   ## Example:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   for word in tokenize("  this is an  example  "):
   ##     writeLine(stdout, word)
+  ##   ```
   ##
   ## Results in:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   ("  ", true)
   ##   ("this", false)
   ##   (" ", true)
@@ -2917,6 +3014,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
   ##   ("  ", true)
   ##   ("example", false)
   ##   ("  ", true)
+  ##   ```
   var i = 0
   while true:
     var j = i
diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim
index ff2a3bb58..90ba20c13 100644
--- a/lib/pure/sugar.nim
+++ b/lib/pure/sugar.nim
@@ -11,7 +11,7 @@
 ## macro system.
 
 import std/private/since
-import macros
+import std/macros
 
 proc checkPragma(ex, prag: var NimNode) =
   since (1, 3):
@@ -203,7 +203,7 @@ proc freshIdentNodes(ast: NimNode): NimNode =
   # see also https://github.com/nim-lang/Nim/pull/8531#issuecomment-410436458
   proc inspect(node: NimNode): NimNode =
     case node.kind:
-    of nnkIdent, nnkSym:
+    of nnkIdent, nnkSym, nnkOpenSymChoice, nnkClosedSymChoice, nnkOpenSym:
       result = ident($node)
     of nnkEmpty, nnkLiterals:
       result = node
@@ -231,9 +231,19 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).}
   let locals = if locals.len == 1 and locals[0].kind == nnkBracket: locals[0]
                else: locals
   for arg in locals:
-    if arg.strVal == "result":
-      error("The variable name cannot be `result`!", arg)
-    params.add(newIdentDefs(ident(arg.strVal), freshIdentNodes getTypeInst arg))
+    proc getIdent(n: NimNode): NimNode =
+      case n.kind
+      of nnkIdent, nnkSym:
+        let nStr = n.strVal
+        if nStr == "result":
+          error("The variable name cannot be `result`!", n)
+        result = ident(nStr)
+      of nnkHiddenDeref: result = n[0].getIdent()
+      else:
+        error("The argument to be captured `" & n.repr & "` is not a pure identifier. " &
+          "It is an unsupported `" & $n.kind & "` node.", n)
+    let argName = getIdent(arg)
+    params.add(newIdentDefs(argName, freshIdentNodes getTypeInst arg))
   result = newNimNode(nnkCall)
   result.add(newProc(newEmptyNode(), params, body, nnkLambda))
   for arg in locals: result.add(arg)
@@ -335,9 +345,10 @@ proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} =
   let res = genSym(nskVar, "collectResult")
   var bracketExpr: NimNode
   if init != nil:
-    expectKind init, {nnkCall, nnkIdent, nnkSym}
+    expectKind init, {nnkCall, nnkIdent, nnkSym, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}
     bracketExpr = newTree(nnkBracketExpr,
-      if init.kind == nnkCall: freshIdentNodes(init[0]) else: freshIdentNodes(init))
+      if init.kind in {nnkCall, nnkClosedSymChoice, nnkOpenSymChoice, nnkOpenSym}:
+        freshIdentNodes(init[0]) else: freshIdentNodes(init))
   else:
     bracketExpr = newTree(nnkBracketExpr)
   let (resBody, keyType, valueType) = trans(body, res, bracketExpr)
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index 571c9b13c..53b3d61da 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -58,13 +58,13 @@ runnableExamples("-r:off"):
 
   stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text")
 
-import macros
-import strformat
-from strutils import toLowerAscii, `%`
-import colors
+import std/macros
+import std/strformat
+from std/strutils import toLowerAscii, `%`, parseInt
+import std/colors
 
 when defined(windows):
-  import winlean
+  import std/winlean
 
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions]
@@ -96,10 +96,11 @@ const
   fgPrefix = "\e[38;2;"
   bgPrefix = "\e[48;2;"
   ansiResetCode* = "\e[0m"
+  getPos = "\e[6n"
   stylePrefix = "\e["
 
 when defined(windows):
-  import winlean, os
+  import std/[winlean, os]
 
   const
     DUPLICATE_SAME_ACCESS = 2
@@ -220,6 +221,9 @@ when defined(windows):
       raiseOSError(osLastError())
     return (int(c.dwCursorPosition.x), int(c.dwCursorPosition.y))
 
+  proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError, OSError].} =
+    return getCursorPos(getStdHandle(STD_OUTPUT_HANDLE))
+
   proc setCursorPos(h: Handle, x, y: int) =
     var c: COORD
     c.x = int16(x)
@@ -253,7 +257,7 @@ when defined(windows):
     if f == stderr: term.hStderr else: term.hStdout
 
 else:
-  import termios, posix, os, parseutils
+  import std/[termios, posix, os, parseutils]
 
   proc setRaw(fd: FileHandle, time: cint = TCSAFLUSH) =
     var mode: Termios
@@ -267,6 +271,48 @@ else:
     mode.c_cc[VTIME] = 0.cuchar
     discard fd.tcSetAttr(time, addr mode)
 
+  proc getCursorPos*(): tuple [x, y: int] {.raises: [ValueError, IOError].} =
+    ## Returns cursor position (x, y)
+    ## writes to stdout and expects the terminal to respond via stdin
+    var
+      xStr = ""
+      yStr = ""
+      ch: char
+      ct: int
+      readX = false
+
+    # use raw mode to ask terminal for cursor position
+    let fd = getFileHandle(stdin)
+    var oldMode: Termios
+    discard fd.tcGetAttr(addr oldMode)
+    fd.setRaw()
+    stdout.write(getPos)
+    flushFile(stdout)
+
+    try:
+      # parse response format: [yyy;xxxR
+      while true:
+        let n = readBuffer(stdin, addr ch, 1)
+        if n == 0 or ch == 'R':
+          if xStr == "" or yStr == "":
+            raise newException(ValueError, "Got character position message that was missing data")
+          break
+        ct += 1
+        if ct > 16:
+          raise newException(ValueError, "Got unterminated character position message from terminal")
+        if ch == ';':
+          readX = true
+        elif ch in {'0'..'9'}:
+          if readX:
+            xStr.add(ch)
+          else:
+            yStr.add(ch)
+    finally:
+      # restore previous terminal mode
+      discard fd.tcSetAttr(TCSADRAIN, addr oldMode)
+
+    return (parseInt(xStr), parseInt(yStr))
+
   proc terminalWidthIoctl*(fds: openArray[int]): int =
     ## Returns terminal width from first fd that supports the ioctl.
 
@@ -291,25 +337,57 @@ else:
     ## Returns some reasonable terminal width from either standard file
     ## descriptors, controlling terminal, environment variables or tradition.
 
-    var w = terminalWidthIoctl([0, 1, 2]) #Try standard file descriptors
+    # POSIX environment variable takes precendence.
+    # _COLUMNS_: This variable shall represent a decimal integer >0 used
+    # to indicate the user's preferred width in column positions for
+    # the terminal screen or window. If this variable is unset or null,
+    # the implementation determines the number of columns, appropriate
+    # for the terminal or window, in an unspecified manner.
+    # When COLUMNS is set, any terminal-width information implied by TERM
+    # is overridden. Users and conforming applications should not set COLUMNS
+    # unless they wish to override the system selection and produce output
+    # unrelated to the terminal characteristics.
+    # See POSIX Base Definitions Section 8.1 Environment Variable Definition
+
+    var w: int
+    var s = getEnv("COLUMNS") # Try standard env var
+    if len(s) > 0 and parseSaturatedNatural(s, w) > 0 and w > 0:
+      return w
+    w = terminalWidthIoctl([0, 1, 2]) # Try standard file descriptors
     if w > 0: return w
-    var cterm = newString(L_ctermid) #Try controlling tty
+    var cterm = newString(L_ctermid) # Try controlling tty
     var fd = open(ctermid(cstring(cterm)), O_RDONLY)
     if fd != -1:
       w = terminalWidthIoctl([int(fd)])
     discard close(fd)
     if w > 0: return w
-    var s = getEnv("COLUMNS") #Try standard env var
-    if len(s) > 0 and parseInt(s, w) > 0 and w > 0:
-      return w
-    return 80 #Finally default to venerable value
+    return 80 # Finally default to venerable value
 
   proc terminalHeight*(): int =
     ## Returns some reasonable terminal height from either standard file
     ## descriptors, controlling terminal, environment variables or tradition.
     ## Zero is returned if the height could not be determined.
 
-    var h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors
+    # POSIX environment variable takes precendence.
+    # _LINES_: This variable shall represent a decimal integer >0 used
+    # to indicate the user's preferred number of lines on a page or
+    # the vertical screen or window size in lines. A line in this case
+    # is a vertical measure large enough to hold the tallest character
+    # in the character set being displayed. If this variable is unset or null,
+    # the implementation determines the number of lines, appropriate
+    # for the terminal or window (size, terminal baud rate, and so on),
+    # in an unspecified manner.
+    # When LINES is set, any terminal-height information implied by TERM
+    # is overridden. Users and conforming applications should not set LINES
+    # unless they wish to override the system selection and produce output
+    # unrelated to the terminal characteristics.
+    # See POSIX Base Definitions Section 8.1 Environment Variable Definition
+
+    var h: int
+    var s = getEnv("LINES") # Try standard env var
+    if len(s) > 0 and parseSaturatedNatural(s, h) > 0 and h > 0:
+      return h
+    h = terminalHeightIoctl([0, 1, 2]) # Try standard file descriptors
     if h > 0: return h
     var cterm = newString(L_ctermid) # Try controlling tty
     var fd = open(ctermid(cstring(cterm)), O_RDONLY)
@@ -317,9 +395,6 @@ else:
       h = terminalHeightIoctl([int(fd)])
     discard close(fd)
     if h > 0: return h
-    var s = getEnv("LINES") # Try standard env var
-    if len(s) > 0 and parseInt(s, h) > 0 and h > 0:
-      return h
     return 0 # Could not determine height
 
 proc terminalSize*(): tuple[w, h: int] =
@@ -644,9 +719,9 @@ proc setForegroundColor*(f: File, fg: ForegroundColor, bright = false) =
       0, # fg8Bit not supported, see `enableTrueColors` instead.
       0] # unused
     if fg == fgDefault:
-      discard setConsoleTextAttribute(h, toU16(old or defaultForegroundColor))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultForegroundColor)))
     else:
-      discard setConsoleTextAttribute(h, toU16(old or lookup[fg]))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[fg])))
   else:
     gFG = ord(fg)
     if bright: inc(gFG, 60)
@@ -673,9 +748,9 @@ proc setBackgroundColor*(f: File, bg: BackgroundColor, bright = false) =
       0, # bg8Bit not supported, see `enableTrueColors` instead.
       0] # unused
     if bg == bgDefault:
-      discard setConsoleTextAttribute(h, toU16(old or defaultBackgroundColor))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](defaultBackgroundColor)))
     else:
-      discard setConsoleTextAttribute(h, toU16(old or lookup[bg]))
+      discard setConsoleTextAttribute(h, cast[int16](cast[uint16](old) or cast[uint16](lookup[bg])))
   else:
     gBG = ord(bg)
     if bright: inc(gBG, 60)
@@ -847,7 +922,7 @@ when defined(windows):
     stdout.write "\n"
 
 else:
-  import termios
+  import std/termios
 
   proc readPasswordFromStdin*(prompt: string, password: var string):
                             bool {.tags: [ReadIOEffect, WriteIOEffect].} =
@@ -903,7 +978,7 @@ proc isTrueColorSupported*(): bool =
   return getTerminal().trueColorIsSupported
 
 when defined(windows):
-  import os
+  import std/os
 
 proc enableTrueColors*() =
   ## Enables true color.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index 80be55884..e59153455 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -20,7 +20,7 @@
   Examples
   ========
 
-  .. code-block:: nim
+    ```nim
     import std/[times, os]
     # Simple benchmarking
     let time = cpuTime()
@@ -37,6 +37,7 @@
     # Arithmetic using TimeInterval
     echo "One year from now      : ", now() + 1.years
     echo "One month from now     : ", now() + 1.months
+    ```
 
   Parsing and Formatting Dates
   ============================
@@ -44,10 +45,10 @@
   The `DateTime` type can be parsed and formatted using the different
   `parse` and `format` procedures.
 
-  .. code-block:: nim
-
+    ```nim
     let dt = parse("2000-01-01", "yyyy-MM-dd")
     echo dt.format("yyyy-MM-dd")
+    ```
 
   The different format patterns that are supported are documented below.
 
@@ -62,6 +63,8 @@
                                                                                                   | `Monday -> Mon`
   `dddd`       Full string for the day of the week.                                               | `Saturday -> Saturday`
                                                                                                   | `Monday -> Monday`
+  `GG`         The last two digits of the Iso Week-Year                                           | `30/12/2012 -> 13`
+  `GGGG`       The Iso week-calendar year padded to four digits                                   | `30/12/2012 -> 2013`
   `h`          The hours in one digit if possible. Ranging from 1-12.                             | `5pm -> 5`
                                                                                                   | `2am -> 2`
   `hh`         The hours in two digits always. If the hour is one digit, 0 is prepended.          | `5pm -> 05`
@@ -104,6 +107,10 @@
                                                                                                   | `24 AD -> 24`
                                                                                                   | `24 BC -> -23`
                                                                                                   | `12345 AD -> 12345`
+  `V`          The Iso Week-Number as one or two digits                                           | `3/2/2012 -> 5`
+                                                                                                  | `1/4/2012 -> 13`
+  `VV`         The Iso Week-Number as two digits always. 0 is prepended if one digit.             | `3/2/2012 -> 05`
+                                                                                                  | `1/4/2012 -> 13`
   `z`          Displays the timezone offset from UTC.                                             | `UTC+7 -> +7`
                                                                                                   | `UTC-5 -> -5`
   `zz`         Same as above but with leading 0.                                                  | `UTC+7 -> +07`
@@ -124,9 +131,10 @@
   ===========  =================================================================================  ==============================================
 
   Other strings can be inserted by putting them in `''`. For example
-  `hh'->'mm` will give `01->56`.  The following characters can be
-  inserted without quoting them: `:` `-` `(` `)` `/` `[` `]`
-  `,`. A literal `'` can be specified with `''`.
+  `hh'->'mm` will give `01->56`.  In addition to spaces,
+  the following characters can be inserted without quoting them:
+  `:` `-` `,` `.` `(` `)` `/` `[` `]`.
+  A literal `'` can be specified with `''`.
 
   However you don't need to necessarily separate format patterns, as an
   unambiguous format string like `yyyyMMddhhmmss` is also valid (although
@@ -196,7 +204,7 @@
   * `monotimes module <monotimes.html>`_
 ]##
 
-import strutils, math, options
+import std/[strutils, math, options]
 
 import std/private/since
 include "system/inclrtl"
@@ -206,7 +214,7 @@ when defined(nimPreviewSlimSystem):
 
 
 when defined(js):
-  import jscore
+  import std/jscore
 
   # This is really bad, but overflow checks are broken badly for
   # ints on the JS backend. See #6752.
@@ -230,7 +238,7 @@ when defined(js):
   {.pop.}
 
 elif defined(posix):
-  import posix
+  import std/posix
 
   type CTime = posix.Time
 
@@ -239,7 +247,7 @@ elif defined(posix):
       {.importc: "gettimeofday", header: "<sys/time.h>", sideEffect.}
 
 elif defined(windows):
-  import winlean, std/time_t
+  import std/winlean, std/time_t
 
   type
     CTime = time_t.Time
@@ -408,6 +416,16 @@ const unitWeights: array[FixedTimeUnit, int64] = [
   7 * secondsInDay * 1e9.int64,
 ]
 
+when (NimMajor, NimMinor) >= (1, 4):
+  # Newer versions of Nim don't track defects
+  {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError].}
+  {.pragma: parseRaises, raises: [TimeParseError].}
+else:
+  # Still track when using older versions
+  {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError, Defect].}
+  {.pragma: parseRaises, raises: [TimeParseError, Defect].}
+
+
 #
 # Helper procs
 #
@@ -527,7 +545,7 @@ proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay
   assertValidDate monthday, month, year
   # 1970-01-01 is a Thursday, we adjust to the previous Monday
   let days = toEpochDay(monthday, month, year) - 3
-  let weeks = floorDiv(days, 7)
+  let weeks = floorDiv(days, 7'i64)
   let wd = days - weeks * 7
   # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc.
   # so we must correct for the WeekDay type.
@@ -592,8 +610,8 @@ proc stringifyUnit(value: int | int64, unit: TimeUnit): string =
   ## Stringify time unit with it's name, lowercased
   let strUnit = $unit
   result = ""
-  result.add($value)
-  result.add(" ")
+  result.addInt value
+  result.add ' '
   if abs(value) != 1:
     result.add(strUnit.toLowerAscii())
   else:
@@ -636,11 +654,10 @@ template eqImpl(a: Duration|Time, b: Duration|Time): bool =
 
 const DurationZero* = Duration() ## \
   ## Zero value for durations. Useful for comparisons.
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   doAssert initDuration(seconds = 1) > DurationZero
   ##   doAssert initDuration(seconds = 0) == DurationZero
+  ##   ```
 
 proc initDuration*(nanoseconds, microseconds, milliseconds,
                    seconds, minutes, hours, days, weeks: int64 = 0): Duration =
@@ -953,27 +970,33 @@ proc toWinTime*(t: Time): int64 =
   ## since `1601-01-01T00:00:00Z`).
   result = t.seconds * rateDiff + epochDiff + t.nanosecond div 100
 
+proc getTimeImpl(typ: typedesc[Time]): Time =
+  discard "implemented in the vm"
+
 proc getTime*(): Time {.tags: [TimeEffect], benign.} =
   ## Gets the current time as a `Time` with up to nanosecond resolution.
-  when defined(js):
-    let millis = newDate().getTime()
-    let seconds = convert(Milliseconds, Seconds, millis)
-    let nanos = convert(Milliseconds, Nanoseconds,
-      millis mod convert(Seconds, Milliseconds, 1).int)
-    result = initTime(seconds, nanos)
-  elif defined(macosx):
-    var a {.noinit.}: Timeval
-    gettimeofday(a)
-    result = initTime(a.tv_sec.int64,
-                      convert(Microseconds, Nanoseconds, a.tv_usec.int))
-  elif defined(posix):
-    var ts {.noinit.}: Timespec
-    discard clock_gettime(CLOCK_REALTIME, ts)
-    result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
-  elif defined(windows):
-    var f {.noinit.}: FILETIME
-    getSystemTimeAsFileTime(f)
-    result = fromWinTime(rdFileTime(f))
+  when nimvm:
+    result = getTimeImpl(Time)
+  else:
+    when defined(js):
+      let millis = newDate().getTime()
+      let seconds = convert(Milliseconds, Seconds, millis)
+      let nanos = convert(Milliseconds, Nanoseconds,
+        millis mod convert(Seconds, Milliseconds, 1).int)
+      result = initTime(seconds, nanos)
+    elif defined(macosx):
+      var a {.noinit.}: Timeval
+      gettimeofday(a)
+      result = initTime(a.tv_sec.int64,
+                        convert(Microseconds, Nanoseconds, a.tv_usec.int))
+    elif defined(posix):
+      var ts {.noinit.}: Timespec
+      discard clock_gettime(CLOCK_REALTIME, ts)
+      result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+    elif defined(windows):
+      var f {.noinit.}: FILETIME
+      getSystemTimeAsFileTime(f)
+      result = fromWinTime(rdFileTime(f))
 
 proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} =
   ## Computes the duration between two points in time.
@@ -1018,7 +1041,7 @@ proc high*(typ: typedesc[Time]): Time =
   initTime(high(int64), high(NanosecondRange))
 
 proc low*(typ: typedesc[Time]): Time =
-  initTime(low(int64), 0)
+  initTime(0, 0)
 
 #
 # DateTime & Timezone
@@ -1476,20 +1499,42 @@ proc `-=`*(a: var DateTime, b: Duration) =
   a = a - b
 
 proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
-  ## Gets the current local date as a string of the format `YYYY-MM-DD`.
+  ## Gets the current local date as a string of the format `YYYY-MM-dd`.
   runnableExamples:
     echo getDateStr(now() - 1.months)
   assertDateTimeInitialized dt
-  result = $dt.year & '-' & intToStr(dt.monthZero, 2) &
-    '-' & intToStr(dt.monthday, 2)
+  result = newStringOfCap(10)  # len("YYYY-MM-dd") == 10
+  result.addInt dt.year
+  result.add '-'
+  result.add intToStr(dt.monthZero, 2)
+  result.add '-'
+  result.add intToStr(dt.monthday, 2)
 
 proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
   ## Gets the current local clock time as a string of the format `HH:mm:ss`.
   runnableExamples:
     echo getClockStr(now() - 1.hours)
   assertDateTimeInitialized dt
-  result = intToStr(dt.hour, 2) & ':' & intToStr(dt.minute, 2) &
-    ':' & intToStr(dt.second, 2)
+  result = newStringOfCap(8)  # len("HH:mm:ss") == 8
+  result.add intToStr(dt.hour, 2)
+  result.add ':'
+  result.add intToStr(dt.minute, 2)
+  result.add ':'
+  result.add intToStr(dt.second, 2)
+
+
+#
+# Iso week forward declarations
+#
+
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   nanosecond: NanosecondRange,
+                   zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).}
+
+proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
+                   hour: HourRange, minute: MinuteRange, second: SecondRange,
+                   zone: Timezone = local()): DateTime {.gcsafe, raises: [], tags: [], since: (1, 5).}
 
 #
 # TimeFormat
@@ -1521,6 +1566,9 @@ type
     year: Option[int]
     month: Option[int]
     monthday: Option[int]
+    isoyear: Option[int]
+    yearweek: Option[int]
+    weekday: Option[WeekDay]
     utcOffset: Option[int]
 
     # '0' as default for these work fine
@@ -1535,6 +1583,7 @@ type
 
   FormatPattern {.pure.} = enum
     d, dd, ddd, dddd
+    GG, GGGG
     h, hh, H, HH
     m, mm, M, MM, MMM, MMMM
     s, ss
@@ -1544,6 +1593,7 @@ type
     YYYY
     uuuu
     UUUU
+    V, VV
     z, zz, zzz, zzzz
     ZZZ, ZZZZ
     g
@@ -1582,7 +1632,7 @@ const
         "Sunday"],
   )
 
-  FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ','}
+  FormatLiterals = {' ', '-', '/', ':', '(', ')', '[', ']', ',', '.'}
 
 proc `$`*(f: TimeFormat): string =
   ## Returns the format string that was used to construct `f`.
@@ -1672,6 +1722,8 @@ proc stringToPattern(str: string): FormatPattern =
   of "dd": result = dd
   of "ddd": result = ddd
   of "dddd": result = dddd
+  of "GG": result = GG
+  of "GGGG": result = GGGG
   of "h": result = h
   of "hh": result = hh
   of "H": result = H
@@ -1694,6 +1746,8 @@ proc stringToPattern(str: string): FormatPattern =
   of "YYYY": result = YYYY
   of "uuuu": result = uuuu
   of "UUUU": result = UUUU
+  of "V": result = V
+  of "VV": result = VV
   of "z": result = z
   of "zz": result = zz
   of "zzz": result = zzz
@@ -1743,6 +1797,10 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
     result.add loc.ddd[dt.weekday]
   of dddd:
     result.add loc.dddd[dt.weekday]
+  of GG:
+    result.add (dt.getIsoWeekAndYear.isoyear.int mod 100).intToStr(2)
+  of GGGG:
+    result.add $dt.getIsoWeekAndYear.isoyear
   of h:
     result.add(
       if dt.hour == 0: "12"
@@ -1806,6 +1864,10 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string,
       result.add '+' & $year
   of UUUU:
     result.add $dt.year
+  of V:
+    result.add $dt.getIsoWeekAndYear.isoweek
+  of VV:
+    result.add dt.getIsoWeekAndYear.isoweek.intToStr(2)
   of z, zz, zzz, zzzz, ZZZ, ZZZZ:
     if dt.timezone != nil and dt.timezone.name == "Etc/UTC":
       result.add 'Z'
@@ -1860,18 +1922,30 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     result = monthday in MonthdayRange
   of ddd:
     result = false
-    for v in loc.ddd:
+    for d, v in loc.ddd:
       if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        parsed.weekday = some(d.WeekDay)
         result = true
         i.inc v.len
         break
   of dddd:
     result = false
-    for v in loc.dddd:
+    for d, v in loc.dddd:
       if input.substr(i, i+v.len-1).cmpIgnoreCase(v) == 0:
+        parsed.weekday = some(d.WeekDay)
         result = true
         i.inc v.len
         break
+  of GG:
+    # Assumes current century
+    var isoyear = takeInt(2..2)
+    var thisCen = now().year div 100
+    parsed.isoyear = some(thisCen*100 + isoyear)
+    result = isoyear > 0
+  of GGGG:
+    let isoyear = takeInt(1..high(int))
+    parsed.isoyear = some(isoyear)
+    result = isoyear > 0
   of h, H:
     parsed.hour = takeInt(1..2)
     result = parsed.hour in HourRange
@@ -1962,6 +2036,14 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
     parsed.year = some(year)
   of UUUU:
     parsed.year = some(takeInt(1..high(int), allowSign = true))
+  of V:
+    let yearweek = takeInt(1..2)
+    parsed.yearweek = some(yearweek)
+    result = yearweek in IsoWeekRange
+  of VV:
+    let yearweek = takeInt(2..2)
+    parsed.yearweek = some(yearweek)
+    result = yearweek in IsoWeekRange
   of z, zz, zzz, zzzz, ZZZ, ZZZZ:
     case input[i]
     of '+', '-':
@@ -2008,7 +2090,7 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int,
       i.inc 2
     else:
       result = false
-  of Lit: doAssert false, "Can't happen"
+  of Lit: raiseAssert "Can't happen"
 
 proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
                 input: string): DateTime =
@@ -2063,6 +2145,38 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat,
     result = (dateTime(year, month, monthday, hour, minute, second, nanosecond, utc()).toTime +
       initDuration(seconds = p.utcOffset.get())).inZone(zone)
 
+proc toDateTimeByWeek(p: ParsedTime, zone: Timezone, f: TimeFormat,
+                   input: string): DateTime =
+  var isoyear = p.isoyear.get(0)
+  var yearweek = p.yearweek.get(1)
+  var weekday = p.weekday.get(dMon)
+
+  if p.amPm != apUnknown:
+    raiseParseException(f, input, "Parsing iso weekyear dates does not support am/pm")
+
+  if p.year.isSome:
+    raiseParseException(f, input, "Use iso-year GG or GGGG as year with iso week number")
+
+  if p.month.isSome:
+    raiseParseException(f, input, "Use either iso week number V or VV or month")
+
+  if p.monthday.isSome:
+    raiseParseException(f, input, "Use weekday ddd or dddd as day with with iso week number")
+
+  if p.isoyear.isNone:
+    raiseParseException(f, input, "Need iso-year with week number")
+
+  let hour = p.hour
+  let minute = p.minute
+  let second = p.second
+  let nanosecond = p.nanosecond
+
+  if p.utcOffset.isNone:
+    result = initDateTime(weekday, yearweek.IsoWeekRange, isoyear.IsoYear, hour, minute, second, nanosecond, zone)
+  else:
+    result = (initDateTime(weekday, yearweek.IsoWeekRange, isoyear.IsoYear, hour, minute, second, nanosecond, zone).toTime +
+      initDuration(seconds = p.utcOffset.get())).inZone(zone)
+
 proc format*(dt: DateTime, f: TimeFormat,
     loc: DateTimeLocale = DefaultLocale): string {.raises: [].} =
   ## Format `dt` using the format specified by `f`.
@@ -2103,7 +2217,7 @@ proc format*(dt: DateTime, f: static[string]): string {.raises: [].} =
   const f2 = initTimeFormat(f)
   result = dt.format(f2)
 
-proc formatValue*(result: var string; value: DateTime, specifier: string) =
+proc formatValue*(result: var string; value: DateTime | Time, specifier: string) =
   ## adapter for strformat. Not intended to be called directly.
   result.add format(value,
     if specifier.len == 0: "yyyy-MM-dd'T'HH:mm:sszzz" else: specifier)
@@ -2127,13 +2241,8 @@ proc format*(time: Time, f: static[string], zone: Timezone = local()): string
   const f2 = initTimeFormat(f)
   result = time.inZone(zone).format(f2)
 
-template formatValue*(result: var string; value: Time, specifier: string) =
-  ## adapter for `strformat`. Not intended to be called directly.
-  result.add format(value, specifier)
-
 proc parse*(input: string, f: TimeFormat, zone: Timezone = local(),
-    loc: DateTimeLocale = DefaultLocale): DateTime
-    {.raises: [TimeParseError, Defect].} =
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
   ## Parses `input` as a `DateTime` using the format specified by `f`.
   ## If no UTC offset was parsed, then `input` is assumed to be specified in
   ## the `zone` timezone. If a UTC offset was parsed, the result will be
@@ -2173,11 +2282,15 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local(),
     raiseParseException(f, input,
                             "Parsing ended but there was still patterns remaining")
 
-  result = toDateTime(parsed, zone, f, input)
+  if parsed.yearweek.isSome:
+    result = toDateTimeByWeek(parsed, zone, f, input)
+  elif parsed.isoyear.isSome:
+    raiseParseException(f, input, "Iso year GG or GGGG require iso week V or VV")
+  else:
+    result = toDateTime(parsed, zone, f, input)
 
 proc parse*(input, f: string, tz: Timezone = local(),
-    loc: DateTimeLocale = DefaultLocale): DateTime
-    {.raises: [TimeParseError, TimeFormatParseError, Defect].} =
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseFormatRaises.} =
   ## Shorthand for constructing a `TimeFormat` and using it to parse
   ## `input` as a `DateTime`.
   ##
@@ -2190,14 +2303,12 @@ proc parse*(input, f: string, tz: Timezone = local(),
   result = input.parse(dtFormat, tz, loc = loc)
 
 proc parse*(input: string, f: static[string], zone: Timezone = local(),
-    loc: DateTimeLocale = DefaultLocale):
-  DateTime {.raises: [TimeParseError, Defect].} =
+    loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} =
   ## Overload that validates `f` at compile time.
   const f2 = initTimeFormat(f)
   result = input.parse(f2, zone, loc = loc)
 
-proc parseTime*(input, f: string, zone: Timezone): Time
-    {.raises: [TimeParseError, TimeFormatParseError, Defect].} =
+proc parseTime*(input, f: string, zone: Timezone): Time {.parseFormatRaises.} =
   ## Shorthand for constructing a `TimeFormat` and using it to parse
   ## `input` as a `DateTime`, then converting it a `Time`.
   ##
@@ -2209,7 +2320,7 @@ proc parseTime*(input, f: string, zone: Timezone): Time
   parse(input, f, zone).toTime()
 
 proc parseTime*(input: string, f: static[string], zone: Timezone): Time
-    {.raises: [TimeParseError, Defect].} =
+    {.parseRaises.} =
   ## Overload that validates `format` at compile time.
   const f2 = initTimeFormat(f)
   result = input.parse(f2, zone).toTime()
@@ -2638,13 +2749,13 @@ proc `-=`*(t: var Time, b: TimeInterval) =
   t = t - b
 
 #
-# Day of year
+# Iso week
 #
 
 proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
                    hour: HourRange, minute: MinuteRange, second: SecondRange,
                    nanosecond: NanosecondRange,
-                   zone: Timezone = local()): DateTime {.since: (1, 5).} =
+                   zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} =
   ## Create a new `DateTime <#DateTime>`_ from a weekday and an ISO 8601 week number and year
   ## in the specified timezone.
   ##
@@ -2656,12 +2767,12 @@ proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
     assert initDateTime(2, mJan, 2021, 00, 00, 00) == initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00)
 
   # source https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm
-  let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00).weekday.int - 4
+  let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00, zone).weekday.int - 4
   initDateTime(1, mJan, isoyear.int, hour, minute, second, nanosecond, zone) + initTimeInterval(days=d)
 
 proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear,
                    hour: HourRange, minute: MinuteRange, second: SecondRange,
-                   zone: Timezone = local()): DateTime {.since: (1, 5).} =
+                   zone: Timezone = local()): DateTime {.raises: [], tags: [], since: (1, 5).} =
   initDateTime(weekday, isoweek, isoyear, hour, minute, second, 0, zone)
 
 #
@@ -2677,7 +2788,9 @@ proc epochTime*(): float {.tags: [TimeEffect].} =
   ##
   ## .. warning:: Unsuitable for benchmarking (but still better than `now`),
   ##    use `monotimes.getMonoTime` or `cpuTime` instead, depending on the use case.
-  when defined(macosx):
+  when defined(js):
+    result = newDate().getTime() / 1000
+  elif defined(macosx):
     var a {.noinit.}: Timeval
     gettimeofday(a)
     result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat(
@@ -2694,8 +2807,6 @@ proc epochTime*(): float {.tags: [TimeEffect].} =
     var secs = i64 div rateDiff
     var subsecs = i64 mod rateDiff
     result = toFloat(int(secs)) + toFloat(int(subsecs)) * 0.0000001
-  elif defined(js):
-    result = newDate().getTime() / 1000
   else:
     {.error: "unknown OS".}
 
diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim
index c20f9e645..78af84fdd 100644
--- a/lib/pure/typetraits.nim
+++ b/lib/pure/typetraits.nim
@@ -96,6 +96,23 @@ proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".}
   ##
   ## Other languages name a type like these `blob`:idx:.
 
+proc hasDefaultValue*(t: typedesc): bool {.magic: "TypeTrait".} =
+  ## Returns true if `t` has a valid default value.
+  runnableExamples:
+    {.experimental: "strictNotNil".}
+    type
+      NilableObject = ref object
+        a: int
+      Object = NilableObject not nil
+      RequiresInit[T] = object
+        a {.requiresInit.}: T
+
+    assert hasDefaultValue(NilableObject)
+    assert not hasDefaultValue(Object)
+    assert hasDefaultValue(string)
+    assert not hasDefaultValue(var string)
+    assert not hasDefaultValue(RequiresInit[int])
+
 proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} =
   ## Returns true for named tuples, false for any other type.
   runnableExamples:
@@ -113,6 +130,40 @@ template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc =
     assert (var s = "abc"; s[0].addr).typeof.pointerBase is char
   T
 
+proc rangeBase*(T: typedesc[range]): typedesc {.magic: "TypeTrait".} =
+  ## Returns the base type for range types, or the type itself otherwise.
+  ##
+  ## **See also:**
+  ## * `rangeBase template <#rangeBase.t,T>`_
+  runnableExamples:
+    type MyRange = range[0..5]
+    type MyEnum = enum a, b, c
+    type MyEnumRange = range[b..c]
+    doAssert rangeBase(MyRange) is int
+    doAssert rangeBase(MyEnumRange) is MyEnum
+    doAssert rangeBase(range['a'..'z']) is char
+
+template rangeBase*[T: range](a: T): untyped =
+  ## Overload of `rangeBase <#rangeBase,typedesc,static[bool]>`_ for values.
+  runnableExamples:
+    type MyRange = range[0..5]
+    type MyEnum = enum a, b, c
+    type MyEnumRange = range[b..c]
+    let x = MyRange(3)
+    doAssert rangeBase(x) is int
+    doAssert $typeof(rangeBase(x)) == "int"
+    doAssert rangeBase(x) == 3
+    let y: set[MyEnumRange] = {c}
+    for e in y:
+      doAssert rangeBase(e) is MyEnum
+      doAssert $typeof(rangeBase(e)) == "MyEnum"
+      doAssert rangeBase(e) == c
+    let z: seq[range['a'..'z']] = @['c']
+    doAssert rangeBase(z[0]) is char
+    doAssert $typeof(rangeBase(z[0])) == "char"
+    doAssert rangeBase(z[0]) == 'c'
+  rangeBase(typeof(T))(a)
+
 proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} =
   ## Returns the base type for distinct types, or the type itself otherwise.
   ## If `recursive` is false, only the immediate distinct base will be returned.
@@ -187,7 +238,7 @@ since (1, 3, 5):
 
     typeof(block: (for ai in a: ai))
 
-import macros
+import std/macros
 
 macro enumLen*(T: typedesc[enum]): int =
   ## Returns the number of items in the enum `T`.
@@ -225,7 +276,7 @@ macro genericParamsImpl(T: typedesc): untyped =
         case ai.typeKind
         of ntyTypeDesc:
           ret = ai
-        of ntyStatic: doAssert false
+        of ntyStatic: raiseAssert "unreachable"
         else:
           # getType from a resolved symbol might return a typedesc symbol.
           # If so, use it directly instead of wrapping it in StaticParam.
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index 660be7814..8cbe117bb 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -21,6 +21,16 @@
 ## * `encodings module <encodings.html>`_
 
 include "system/inclrtl"
+import std/strbasics
+template toOa(s: string): auto = s.toOpenArray(0, s.high)
+
+proc substr(s: openArray[char] , first, last: int): string =
+  # Copied substr from system
+  let first = max(first, 0)
+  let L = max(min(last, high(s)) - first + 1, 0)
+  result = newString(L)
+  for i in 0 .. L-1:
+    result[i] = s[i+first]
 
 type
   RuneImpl = int32 # underlying type of Rune
@@ -32,7 +42,7 @@ type
 
 template ones(n: untyped): untyped = ((1 shl n)-1)
 
-proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
+proc runeLen*(s: openArray[char]): int {.rtl, extern: "nuc$1".} =
   ## Returns the number of runes of the string ``s``.
   runnableExamples:
     let a = "añyóng"
@@ -51,7 +61,7 @@ proc runeLen*(s: string): int {.rtl, extern: "nuc$1".} =
     else: inc i
     inc(result)
 
-proc runeLenAt*(s: string, i: Natural): int =
+proc runeLenAt*(s: openArray[char], i: Natural): int =
   ## Returns the number of bytes the rune starting at ``s[i]`` takes.
   ##
   ## See also:
@@ -71,7 +81,7 @@ proc runeLenAt*(s: string, i: Natural): int =
 
 const replRune = Rune(0xFFFD)
 
-template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) =
+template fastRuneAt*(s: openArray[char] or string, i: int, result: untyped, doInc = true) =
   ## Returns the rune ``s[i]`` in ``result``.
   ##
   ## If ``doInc == true`` (default), ``i`` is incremented by the number
@@ -149,7 +159,7 @@ template fastRuneAt*(s: string, i: int, result: untyped, doInc = true) =
     result = Rune(uint(s[i]))
     when doInc: inc(i)
 
-proc runeAt*(s: string, i: Natural): Rune =
+proc runeAt*(s: openArray[char], i: Natural): Rune =
   ## Returns the rune in ``s`` at **byte index** ``i``.
   ##
   ## See also:
@@ -163,7 +173,7 @@ proc runeAt*(s: string, i: Natural): Rune =
     doAssert a.runeAt(3) == "y".runeAt(0)
   fastRuneAt(s, i, result, false)
 
-proc validateUtf8*(s: string): int =
+proc validateUtf8*(s: openArray[char]): int =
   ## Returns the position of the invalid byte in ``s`` if the string ``s`` does
   ## not hold valid UTF-8 data. Otherwise ``-1`` is returned.
   ##
@@ -300,7 +310,7 @@ proc `$`*(runes: seq[Rune]): string =
   for rune in runes:
     result.add rune
 
-proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int =
+proc runeOffset*(s: openArray[char], pos: Natural, start: Natural = 0): int =
   ## Returns the byte position of rune
   ## at position ``pos`` in ``s`` with an optional start byte position.
   ## Returns the special value -1 if it runs out of the string.
@@ -327,7 +337,7 @@ proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int =
     inc i
   return o
 
-proc runeReverseOffset*(s: string, rev: Positive): (int, int) =
+proc runeReverseOffset*(s: openArray[char], rev: Positive): (int, int) =
   ## Returns a tuple with the byte offset of the
   ## rune at position ``rev`` in ``s``, counting
   ## from the end (starting with 1) and the total
@@ -355,7 +365,7 @@ proc runeReverseOffset*(s: string, rev: Positive): (int, int) =
     dec a
   result = if a > 0: (-a, rev.int-a) else: (x, -a+rev.int)
 
-proc runeAtPos*(s: string, pos: int): Rune =
+proc runeAtPos*(s: openArray[char], pos: int): Rune =
   ## Returns the rune at position ``pos``.
   ##
   ## **Beware:** This can lead to unoptimized code and slow execution!
@@ -368,7 +378,7 @@ proc runeAtPos*(s: string, pos: int): Rune =
   ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
   fastRuneAt(s, runeOffset(s, pos), result, false)
 
-proc runeStrAtPos*(s: string, pos: Natural): string =
+proc runeStrAtPos*(s: openArray[char], pos: Natural): string =
   ## Returns the rune at position ``pos`` as UTF8 String.
   ##
   ## **Beware:** This can lead to unoptimized code and slow execution!
@@ -380,9 +390,9 @@ proc runeStrAtPos*(s: string, pos: Natural): string =
   ## * `runeAtPos proc <#runeAtPos,string,int>`_
   ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
   let o = runeOffset(s, pos)
-  s[o .. (o+runeLenAt(s, o)-1)]
+  substr(s.toOpenArray(o,  (o+runeLenAt(s, o)-1)))
 
-proc runeSubStr*(s: string, pos: int, len: int = int.high): string =
+proc runeSubStr*(s: openArray[char], pos: int, len: int = int.high): string =
   ## Returns the UTF-8 substring starting at code point ``pos``
   ## with ``len`` code points.
   ##
@@ -401,7 +411,7 @@ proc runeSubStr*(s: string, pos: int, len: int = int.high): string =
   if pos < 0:
     let (o, rl) = runeReverseOffset(s, -pos)
     if len >= rl:
-      result = s.substr(o, s.len-1)
+      result = s.substr(o, s.high)
     elif len < 0:
       let e = rl + len
       if e < 0:
@@ -454,7 +464,7 @@ proc `==`*(a, b: Rune): bool =
 
 include "includes/unicode_ranges"
 
-proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int =
+proc binarySearch(c: RuneImpl, tab: openArray[int32], len, stride: int): int =
   var n = len
   var t = 0
   while n > 1:
@@ -626,7 +636,7 @@ template runeCheck(s, runeProc) =
     fastRuneAt(s, i, rune, doInc = true)
     result = runeProc(rune) and result
 
-proc isAlpha*(s: string): bool {.noSideEffect,
+proc isAlpha*(s: openArray[char]): bool {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
   ## Returns true if ``s`` contains all alphabetic runes.
   runnableExamples:
@@ -634,7 +644,7 @@ proc isAlpha*(s: string): bool {.noSideEffect,
     doAssert a.isAlpha
   runeCheck(s, isAlpha)
 
-proc isSpace*(s: string): bool {.noSideEffect,
+proc isSpace*(s: openArray[char]): bool {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
   ## Returns true if ``s`` contains all whitespace runes.
   runnableExamples:
@@ -655,21 +665,21 @@ template convertRune(s, runeProc) =
     rune = runeProc(rune)
     fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
-proc toUpper*(s: string): string {.noSideEffect,
+proc toUpper*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
   ## Converts ``s`` into upper-case runes.
   runnableExamples:
     doAssert toUpper("abγ") == "ABΓ"
   convertRune(s, toUpper)
 
-proc toLower*(s: string): string {.noSideEffect,
+proc toLower*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1Str".} =
   ## Converts ``s`` into lower-case runes.
   runnableExamples:
     doAssert toLower("ABΓ") == "abγ"
   convertRune(s, toLower)
 
-proc swapCase*(s: string): string {.noSideEffect,
+proc swapCase*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1".} =
   ## Swaps the case of runes in ``s``.
   ##
@@ -691,7 +701,7 @@ proc swapCase*(s: string): string {.noSideEffect,
       rune = rune.toUpper()
     fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
-proc capitalize*(s: string): string {.noSideEffect,
+proc capitalize*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1".} =
   ## Converts the first character of ``s`` into an upper-case rune.
   runnableExamples:
@@ -703,12 +713,12 @@ proc capitalize*(s: string): string {.noSideEffect,
     rune: Rune
     i = 0
   fastRuneAt(s, i, rune, doInc = true)
-  result = $toUpper(rune) & substr(s, i)
+  result = $toUpper(rune) & substr(s.toOpenArray(i, s.high))
 
 when not defined(nimHasEffectsOf):
   {.pragma: effectsOf.}
 
-proc translate*(s: string, replacements: proc(key: string): string): string {.
+proc translate*(s: openArray[char], replacements: proc(key: string): string): string {.
   rtl, extern: "nuc$1", effectsOf: replacements.} =
   ## Translates words in a string using the ``replacements`` proc to substitute
   ## words inside ``s`` with their replacements.
@@ -743,7 +753,7 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
     if whiteSpace and inWord:
       # If we've reached the end of a word
-      let word = s[wordStart ..< lastIndex]
+      let word = substr(s.toOpenArray(wordStart, lastIndex - 1))
       result.add(replacements(word))
       result.add($rune)
       inWord = false
@@ -758,10 +768,10 @@ proc translate*(s: string, replacements: proc(key: string): string): string {.
 
   if wordStart < len(s) and inWord:
     # Get the trailing word at the end
-    let word = s[wordStart .. ^1]
+    let word = substr(s.toOpenArray(wordStart,  s.high))
     result.add(replacements(word))
 
-proc title*(s: string): string {.noSideEffect,
+proc title*(s: openArray[char]): string {.noSideEffect,
   rtl, extern: "nuc$1".} =
   ## Converts ``s`` to a unicode title.
   ##
@@ -787,7 +797,7 @@ proc title*(s: string): string {.noSideEffect,
     fastToUTF8Copy(rune, result, resultIndex, doInc = true)
 
 
-iterator runes*(s: string): Rune =
+iterator runes*(s: openArray[char]): Rune =
   ## Iterates over any rune of the string ``s`` returning runes.
   var
     i = 0
@@ -796,7 +806,7 @@ iterator runes*(s: string): Rune =
     fastRuneAt(s, i, result, true)
     yield result
 
-iterator utf8*(s: string): string =
+iterator utf8*(s: openArray[char]): string =
   ## Iterates over any rune of the string ``s`` returning utf8 values.
   ##
   ## See also:
@@ -807,10 +817,10 @@ iterator utf8*(s: string): string =
   var o = 0
   while o < s.len:
     let n = runeLenAt(s, o)
-    yield s[o .. (o+n-1)]
+    yield substr(s.toOpenArray(o, (o+n-1)))
     o += n
 
-proc toRunes*(s: string): seq[Rune] =
+proc toRunes*(s: openArray[char]): seq[Rune] =
   ## Obtains a sequence containing the Runes in ``s``.
   ##
   ## See also:
@@ -823,12 +833,12 @@ proc toRunes*(s: string): seq[Rune] =
   for r in s.runes:
     result.add(r)
 
-proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} =
+proc cmpRunesIgnoreCase*(a, b: openArray[char]): int {.rtl, extern: "nuc$1".} =
   ## Compares two UTF-8 strings and ignores the case. Returns:
   ##
-  ## | 0 if a == b
-  ## | < 0 if a < b
-  ## | > 0 if a > b
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
   var i = 0
   var j = 0
   var ar, br: Rune
@@ -836,11 +846,16 @@ proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} =
     # slow path:
     fastRuneAt(a, i, ar)
     fastRuneAt(b, j, br)
-    result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
+    when sizeof(int) < 4:
+      const lo = low(int).int32
+      const hi = high(int).int32
+      result = clamp(RuneImpl(toLower(ar)) - RuneImpl(toLower(br)), lo, hi).int
+    else:
+      result = RuneImpl(toLower(ar)) - RuneImpl(toLower(br))
     if result != 0: return
   result = a.len - b.len
 
-proc reversed*(s: string): string =
+proc reversed*(s: openArray[char]): string =
   ## Returns the reverse of ``s``, interpreting it as runes.
   ##
   ## Unicode combining characters are correctly interpreted as well.
@@ -875,9 +890,9 @@ proc reversed*(s: string): string =
 
   reverseUntil(len(s))
 
-proc graphemeLen*(s: string; i: Natural): Natural =
+proc graphemeLen*(s: openArray[char]; i: Natural): Natural =
   ## The number of bytes belonging to byte index ``s[i]``,
-  ## including following combining code unit.
+  ## including following combining code units.
   runnableExamples:
     let a = "añyóng"
     doAssert a.graphemeLen(1) == 2 ## ñ
@@ -894,7 +909,7 @@ proc graphemeLen*(s: string; i: Natural): Natural =
       if not isCombining(r2): break
       result = j-i
 
-proc lastRune*(s: string; last: int): (Rune, int) =
+proc lastRune*(s: openArray[char]; last: int): (Rune, int) =
   ## Length of the last rune in ``s[0..last]``. Returns the rune and its length
   ## in bytes.
   if s[last] <= chr(127):
@@ -923,12 +938,12 @@ proc size*(r: Rune): int {.noSideEffect.} =
   else: result = 1
 
 # --------- Private templates for different split separators -----------
-proc stringHasSep(s: string, index: int, seps: openArray[Rune]): bool =
+proc stringHasSep(s: openArray[char], index: int, seps: openArray[Rune]): bool =
   var rune: Rune
   fastRuneAt(s, index, rune, false)
   return seps.contains(rune)
 
-proc stringHasSep(s: string, index: int, sep: Rune): bool =
+proc stringHasSep(s: openArray[char], index: int, sep: Rune): bool =
   var rune: Rune
   fastRuneAt(s, index, rune, false)
   return sep == rune
@@ -946,12 +961,12 @@ template splitCommon(s, sep, maxsplit: untyped) =
       while last < sLen and not stringHasSep(s, last, sep):
         inc(last, runeLenAt(s, last))
       if splits == 0: last = sLen
-      yield s[first .. (last - 1)]
+      yield substr(s.toOpenArray(first, (last - 1)))
       if splits == 0: break
       dec(splits)
       inc(last, if last < sLen: runeLenAt(s, last) else: 1)
 
-iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces,
+iterator split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces,
   maxsplit: int = -1): string =
   ## Splits the unicode string ``s`` into substrings using a group of separators.
   ##
@@ -977,7 +992,7 @@ iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces,
 
   splitCommon(s, seps, maxsplit)
 
-iterator splitWhitespace*(s: string): string =
+iterator splitWhitespace*(s: openArray[char]): string =
   ## Splits a unicode string at whitespace runes.
   splitCommon(s, unicodeSpaces, -1)
 
@@ -985,13 +1000,13 @@ template accResult(iter: untyped) =
   result = @[]
   for x in iter: add(result, x)
 
-proc splitWhitespace*(s: string): seq[string] {.noSideEffect,
+proc splitWhitespace*(s: openArray[char]): seq[string] {.noSideEffect,
   rtl, extern: "ncuSplitWhitespace".} =
   ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
   ## iterator, but is a proc that returns a sequence of substrings.
   accResult(splitWhitespace(s))
 
-iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
+iterator split*(s: openArray[char], sep: Rune, maxsplit: int = -1): string =
   ## Splits the unicode string ``s`` into substrings using a single separator.
   ## Substrings are separated by the rune ``sep``.
   runnableExamples:
@@ -1002,19 +1017,19 @@ iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
 
   splitCommon(s, sep, maxsplit)
 
-proc split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1):
+proc split*(s: openArray[char], seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1):
     seq[string] {.noSideEffect, rtl, extern: "nucSplitRunes".} =
   ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_,
   ## but is a proc that returns a sequence of substrings.
   accResult(split(s, seps, maxsplit))
 
-proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect,
+proc split*(s: openArray[char], sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect,
   rtl, extern: "nucSplitRune".} =
   ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc
   ## that returns a sequence of substrings.
   accResult(split(s, sep, maxsplit))
 
-proc strip*(s: string, leading = true, trailing = true,
+proc strip*(s: openArray[char], leading = true, trailing = true,
             runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect,
             rtl, extern: "nucStrip".} =
   ## Strips leading or trailing ``runes`` from ``s`` and returns
@@ -1069,7 +1084,7 @@ proc strip*(s: string, leading = true, trailing = true,
   let newLen = eI - sI + 1
   result = newStringOfCap(newLen)
   if newLen > 0:
-    result.add s[sI .. eI]
+    result.add substr(s.toOpenArray(sI, eI))
 
 proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
   rtl, extern: "nucRepeatRune".} =
@@ -1085,7 +1100,7 @@ proc repeat*(c: Rune, count: Natural): string {.noSideEffect,
   for i in 0 ..< count:
     result.add s
 
-proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
+proc align*(s: openArray[char], count: Natural, padding = ' '.Rune): string {.
   noSideEffect, rtl, extern: "nucAlignString".} =
   ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length
   ## of ``count``.
@@ -1110,9 +1125,9 @@ proc align*(s: string, count: Natural, padding = ' '.Rune): string {.
     for i in 0 ..< spaces: result.add padStr
     result.add s
   else:
-    result = s
+    result = s.substr
 
-proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.
+proc alignLeft*(s: openArray[char], count: Natural, padding = ' '.Rune): string {.
     noSideEffect.} =
   ## Left-aligns a unicode string ``s`` with ``padding``, so that it has a
   ## rune-length of ``count``.
@@ -1136,4 +1151,365 @@ proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.
     for i in sLen ..< count:
       result.add padStr
   else:
-    result = s
+    result = s.substr
+
+
+proc runeLen*(s: string): int {.inline.} =
+  ## Returns the number of runes of the string ``s``.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLen == 6
+    ## note: a.len == 8
+  runeLen(toOa(s))
+
+proc runeLenAt*(s: string, i: Natural): int {.inline.} =
+  ## Returns the number of bytes the rune starting at ``s[i]`` takes.
+  ##
+  ## See also:
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeLenAt(0) == 1
+    doAssert a.runeLenAt(1) == 2
+  runeLenAt(toOa(s), i)
+
+proc runeAt*(s: string, i: Natural): Rune {.inline.} =
+  ## Returns the rune in ``s`` at **byte index** ``i``.
+  ##
+  ## See also:
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeAt(1) == "ñ".runeAt(0)
+    doAssert a.runeAt(2) == "ñ".runeAt(1)
+    doAssert a.runeAt(3) == "y".runeAt(0)
+  fastRuneAt(s, i, result, false)
+
+proc validateUtf8*(s: string): int {.inline.} =
+  ## Returns the position of the invalid byte in ``s`` if the string ``s`` does
+  ## not hold valid UTF-8 data. Otherwise ``-1`` is returned.
+  ##
+  ## See also:
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
+  validateUtf8(toOa(s))
+
+proc runeOffset*(s: string, pos: Natural, start: Natural = 0): int {.inline.} =
+  ## Returns the byte position of rune
+  ## at position ``pos`` in ``s`` with an optional start byte position.
+  ## Returns the special value -1 if it runs out of the string.
+  ##
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeReverseOffset proc <#runeReverseOffset,string,Positive>`_
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.runeOffset(1) == 1
+    doAssert a.runeOffset(3) == 4
+    doAssert a.runeOffset(4) == 6
+  runeOffset(toOa(s), pos, start)
+
+proc runeReverseOffset*(s: string, rev: Positive): (int, int) {.inline.} =
+  ## Returns a tuple with the byte offset of the
+  ## rune at position ``rev`` in ``s``, counting
+  ## from the end (starting with 1) and the total
+  ## number of runes in the string.
+  ##
+  ## Returns a negative value for offset if there are too few runes in
+  ## the string to satisfy the request.
+  ##
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeOffset proc <#runeOffset,string,Natural,Natural>`_
+  runeReverseOffset(toOa(s), rev)
+
+proc runeAtPos*(s: string, pos: int): Rune {.inline.} =
+  ## Returns the rune at position ``pos``.
+  ##
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeStrAtPos proc <#runeStrAtPos,string,Natural>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  fastRuneAt(toOa(s), runeOffset(s, pos), result, false)
+
+proc runeStrAtPos*(s: string, pos: Natural): string {.inline.} =
+  ## Returns the rune at position ``pos`` as UTF8 String.
+  ##
+  ## **Beware:** This can lead to unoptimized code and slow execution!
+  ## Most problems can be solved more efficiently by using an iterator
+  ## or conversion to a seq of Rune.
+  ##
+  ## See also:
+  ## * `runeAt proc <#runeAt,string,Natural>`_
+  ## * `runeAtPos proc <#runeAtPos,string,int>`_
+  ## * `fastRuneAt template <#fastRuneAt.t,string,int,untyped>`_
+  let o = runeOffset(s, pos)
+  substr(s.toOpenArray(o, (o+runeLenAt(s, o)-1)))
+
+proc runeSubStr*(s: string, pos: int, len: int = int.high): string {.inline.} =
+  ## Returns the UTF-8 substring starting at code point ``pos``
+  ## with ``len`` code points.
+  ##
+  ## If ``pos`` or ``len`` is negative they count from
+  ## the end of the string. If ``len`` is not given it means the longest
+  ## possible string.
+  runnableExamples:
+    let s = "Hänsel  ««: 10,00€"
+    doAssert(runeSubStr(s, 0, 2) == "Hä")
+    doAssert(runeSubStr(s, 10, 1) == ":")
+    doAssert(runeSubStr(s, -6) == "10,00€")
+    doAssert(runeSubStr(s, 10) == ": 10,00€")
+    doAssert(runeSubStr(s, 12, 5) == "10,00")
+    doAssert(runeSubStr(s, -6, 3) == "10,")
+  runeSubStr(toOa(s), pos, len)
+
+
+proc isAlpha*(s: string): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` contains all alphabetic runes.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.isAlpha
+  isAlpha(toOa(s))
+
+proc isSpace*(s: string): bool {.noSideEffect, inline.} =
+  ## Returns true if ``s`` contains all whitespace runes.
+  runnableExamples:
+    let a = "\t\l \v\r\f"
+    doAssert a.isSpace
+  isSpace(toOa(s))
+
+
+proc toUpper*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` into upper-case runes.
+  runnableExamples:
+    doAssert toUpper("abγ") == "ABΓ"
+  toUpper(toOa(s))
+
+proc toLower*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` into lower-case runes.
+  runnableExamples:
+    doAssert toLower("ABΓ") == "abγ"
+  toLower(toOa(s))
+
+proc swapCase*(s: string): string {.noSideEffect, inline.} =
+  ## Swaps the case of runes in ``s``.
+  ##
+  ## Returns a new string such that the cases of all runes
+  ## are swapped if possible.
+  runnableExamples:
+    doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA"
+  swapCase(toOa(s))
+
+proc capitalize*(s: string): string {.noSideEffect.} =
+  ## Converts the first character of ``s`` into an upper-case rune.
+  runnableExamples:
+    doAssert capitalize("βeta") == "Βeta"
+  capitalize(toOa(s))
+
+
+proc translate*(s: string, replacements: proc(key: string): string): string {.effectsOf: replacements, inline.} =
+  ## Translates words in a string using the ``replacements`` proc to substitute
+  ## words inside ``s`` with their replacements.
+  ##
+  ## ``replacements`` is any proc that takes a word and returns
+  ## a new word to fill it's place.
+  runnableExamples:
+    proc wordToNumber(s: string): string =
+      case s
+      of "one": "1"
+      of "two": "2"
+      else: s
+    let a = "one two three four"
+    doAssert a.translate(wordToNumber) == "1 2 three four"
+  translate(toOa(s), replacements)
+
+proc title*(s: string): string {.noSideEffect, inline.} =
+  ## Converts ``s`` to a unicode title.
+  ##
+  ## Returns a new string such that the first character
+  ## in each word inside ``s`` is capitalized.
+  runnableExamples:
+    doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
+  title(toOa(s))
+
+
+iterator runes*(s: string): Rune =
+  ## Iterates over any rune of the string ``s`` returning runes.
+  for rune in runes(toOa(s)):
+    yield rune
+
+iterator utf8*(s: string): string =
+  ## Iterates over any rune of the string ``s`` returning utf8 values.
+  ##
+  ## See also:
+  ## * `validateUtf8 proc <#validateUtf8,string>`_
+  ## * `toUTF8 proc <#toUTF8,Rune>`_
+  ## * `$ proc <#$,Rune>`_ alias for `toUTF8`
+  ## * `fastToUTF8Copy template <#fastToUTF8Copy.t,Rune,string,int>`_
+  for str in utf8(toOa(s)):
+    yield str
+
+proc toRunes*(s: string): seq[Rune] {.inline.} =
+  ## Obtains a sequence containing the Runes in ``s``.
+  ##
+  ## See also:
+  ## * `$ proc <#$,Rune>`_ for a reverse operation
+  runnableExamples:
+    let a = toRunes("aáä")
+    doAssert a == @["a".runeAt(0), "á".runeAt(0), "ä".runeAt(0)]
+  toRunes(toOa(s))
+
+proc cmpRunesIgnoreCase*(a, b: string): int {.inline.} =
+  ## Compares two UTF-8 strings and ignores the case. Returns:
+  ##
+  ## | `0` if a == b
+  ## | `< 0` if a < b
+  ## | `> 0` if a > b
+  cmpRunesIgnoreCase(a.toOa(), b.toOa())
+
+proc reversed*(s: string): string {.inline.} =
+  ## Returns the reverse of ``s``, interpreting it as runes.
+  ##
+  ## Unicode combining characters are correctly interpreted as well.
+  runnableExamples:
+    assert reversed("Reverse this!") == "!siht esreveR"
+    assert reversed("先秦兩漢") == "漢兩秦先"
+    assert reversed("as⃝df̅") == "f̅ds⃝a"
+    assert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞"
+  reversed(toOa(s))
+
+proc graphemeLen*(s: string; i: Natural): Natural {.inline.} =
+  ## The number of bytes belonging to byte index ``s[i]``,
+  ## including following combining code unit.
+  runnableExamples:
+    let a = "añyóng"
+    doAssert a.graphemeLen(1) == 2 ## ñ
+    doAssert a.graphemeLen(2) == 1
+    doAssert a.graphemeLen(4) == 2 ## ó
+  graphemeLen(toOa(s), i)
+
+proc lastRune*(s: string; last: int): (Rune, int) {.inline.} =
+  ## Length of the last rune in ``s[0..last]``. Returns the rune and its length
+  ## in bytes.
+  lastRune(toOa(s), last)
+
+iterator split*(s: string, seps: openArray[Rune] = unicodeSpaces,
+  maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a group of separators.
+  ##
+  ## Substrings are separated by a substring containing only ``seps``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq("hÃllo\lthis\lis an\texample\l是".split) ==
+      @["hÃllo", "this", "is", "an", "example", "是"]
+
+    # And the following code splits the same string using a sequence of Runes.
+    assert toSeq(split("añyóng:hÃllo;是$example", ";:$".toRunes)) ==
+      @["añyóng", "hÃllo", "是", "example"]
+
+    # example with a `Rune` separator and unused one `;`:
+    assert toSeq(split("ab是de:f:", ";:是".toRunes)) == @["ab", "de", "f", ""]
+
+    # Another example that splits a string containing a date.
+    let date = "2012-11-20T22:08:08.398990"
+
+    assert toSeq(split(date, " -:T".toRunes)) ==
+      @["2012", "11", "20", "22", "08", "08.398990"]
+
+  splitCommon(toOa(s), seps, maxsplit)
+
+iterator splitWhitespace*(s: string): string =
+  ## Splits a unicode string at whitespace runes.
+  splitCommon(s.toOa(), unicodeSpaces, -1)
+
+
+proc splitWhitespace*(s: string): seq[string] {.noSideEffect, inline.}=
+  ## The same as the `splitWhitespace <#splitWhitespace.i,string>`_
+  ## iterator, but is a proc that returns a sequence of substrings.
+  accResult(splitWhitespace(toOa(s)))
+
+iterator split*(s: string, sep: Rune, maxsplit: int = -1): string =
+  ## Splits the unicode string ``s`` into substrings using a single separator.
+  ## Substrings are separated by the rune ``sep``.
+  runnableExamples:
+    import std/sequtils
+
+    assert toSeq(split(";;hÃllo;this;is;an;;example;;;是", ";".runeAt(0))) ==
+      @["", "", "hÃllo", "this", "is", "an", "", "example", "", "", "是"]
+
+  splitCommon(toOa(s), sep, maxsplit)
+
+proc split*(s: string, seps: openArray[Rune] = unicodeSpaces, maxsplit: int = -1):
+    seq[string] {.noSideEffect, inline.} =
+  ## The same as the `split iterator <#split.i,string,openArray[Rune],int>`_,
+  ## but is a proc that returns a sequence of substrings.
+  accResult(split(toOa(s), seps, maxsplit))
+
+proc split*(s: string, sep: Rune, maxsplit: int = -1): seq[string] {.noSideEffect, inline.} =
+  ## The same as the `split iterator <#split.i,string,Rune,int>`_, but is a proc
+  ## that returns a sequence of substrings.
+  accResult(split(toOa(s), sep, maxsplit))
+
+proc strip*(s: string, leading = true, trailing = true,
+            runes: openArray[Rune] = unicodeSpaces): string {.noSideEffect, inline.} =
+  ## Strips leading or trailing ``runes`` from ``s`` and returns
+  ## the resulting string.
+  ##
+  ## If ``leading`` is true (default), leading ``runes`` are stripped.
+  ## If ``trailing`` is true (default), trailing ``runes`` are stripped.
+  ## If both are false, the string is returned unchanged.
+  runnableExamples:
+    let a = "\táñyóng   "
+    doAssert a.strip == "áñyóng"
+    doAssert a.strip(leading = false) == "\táñyóng"
+    doAssert a.strip(trailing = false) == "áñyóng   "
+  strip(toOa(s), leading, trailing, runes)
+
+
+proc align*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} =
+  ## Aligns a unicode string ``s`` with ``padding``, so that it has a rune-length
+  ## of ``count``.
+  ##
+  ## ``padding`` characters (by default spaces) are added before ``s`` resulting in
+  ## right alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
+  ## returned unchanged. If you need to left align a string use the `alignLeft
+  ## proc <#alignLeft,string,Natural>`_.
+  runnableExamples:
+    assert align("abc", 4) == " abc"
+    assert align("a", 0) == "a"
+    assert align("1232", 6) == "  1232"
+    assert align("1232", 6, '#'.Rune) == "##1232"
+    assert align("Åge", 5) == "  Åge"
+    assert align("×", 4, '_'.Rune) == "___×"
+  align(toOa(s), count, padding)
+
+proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {.noSideEffect, inline.} =
+  ## Left-aligns a unicode string ``s`` with ``padding``, so that it has a
+  ## rune-length of ``count``.
+  ##
+  ## ``padding`` characters (by default spaces) are added after ``s`` resulting in
+  ## left alignment. If ``s.runelen >= count``, no spaces are added and ``s`` is
+  ## returned unchanged. If you need to right align a string use the `align
+  ## proc <#align,string,Natural>`_.
+  runnableExamples:
+    assert alignLeft("abc", 4) == "abc "
+    assert alignLeft("a", 0) == "a"
+    assert alignLeft("1232", 6) == "1232  "
+    assert alignLeft("1232", 6, '#'.Rune) == "1232##"
+    assert alignLeft("Åge", 5) == "Åge  "
+    assert alignLeft("×", 4, '_'.Rune) == "×___"
+  alignLeft(toOa(s), count, padding)
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 593bc1ca4..cfb762258 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -34,9 +34,9 @@
 ##
 ## Specify the test name as a command line argument.
 ##
-## .. code::
-##
+##   ```cmd
 ##   nim c -r test "my test name" "another test"
+##   ```
 ##
 ## Multiple arguments can be used.
 ##
@@ -45,9 +45,9 @@
 ##
 ## Specify the suite name delimited by `"::"`.
 ##
-## .. code::
-##
+##   ```cmd
 ##   nim c -r test "my test name::"
+##   ```
 ##
 ## Selecting tests by pattern
 ## ==========================
@@ -58,19 +58,18 @@
 ##
 ## Tests matching **any** of the arguments are executed.
 ##
-## .. code::
-##
+##   ```cmd
 ##   nim c -r test fast_suite::mytest1 fast_suite::mytest2
 ##   nim c -r test "fast_suite::mytest*"
 ##   nim c -r test "auth*::" "crypto::hashing*"
 ##   # Run suites starting with 'bug #' and standalone tests starting with '#'
 ##   nim c -r test 'bug #*::' '::#*'
+##   ```
 ##
 ## Examples
 ## ========
 ##
-## .. code:: nim
-##
+##   ```nim
 ##   suite "description for this stuff":
 ##     echo "suite setup: run once before the tests"
 ##
@@ -96,6 +95,7 @@
 ##         discard v[4]
 ##
 ##     echo "suite teardown: run once after the tests"
+##   ```
 ##
 ## Limitations/Bugs
 ## ================
@@ -111,15 +111,15 @@ import std/exitprocs
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-import macros, strutils, streams, times, sets, sequtils
+import std/[macros, strutils, streams, times, sets, sequtils]
 
 when declared(stdout):
-  import os
+  import std/os
 
 const useTerminal = not defined(js)
 
 when useTerminal:
-  import terminal
+  import std/terminal
 
 type
   TestStatus* = enum ## The status of a test when it is done.
@@ -235,7 +235,7 @@ proc colorOutput(): bool =
     else: result = false
   of "on": result = true
   of "off": result = false
-  else: doAssert false, $color
+  else: raiseAssert $color
 
   when declared(stdout):
     if existsEnv("NIMTEST_COLOR"):
@@ -433,8 +433,6 @@ proc matchFilter(suiteName, testName, filter: string): bool =
   return glob(suiteName, suiteAndTestFilters[0]) and
          glob(testName, suiteAndTestFilters[1])
 
-when defined(testing): export matchFilter
-
 proc shouldRun(currentSuiteName, testName: string): bool =
   ## Check if a test should be run by matching suiteName and testName against
   ## test filters.
@@ -475,27 +473,26 @@ template suite*(name, body) {.dirty.} =
   ## common fixture (``setup``, ``teardown``). The fixture is executed
   ## for EACH test.
   ##
-  ## .. code-block:: nim
-  ##  suite "test suite for addition":
-  ##    setup:
-  ##      let result = 4
+  ##   ```nim
+  ##   suite "test suite for addition":
+  ##     setup:
+  ##       let result = 4
   ##
-  ##    test "2 + 2 = 4":
-  ##      check(2+2 == result)
+  ##     test "2 + 2 = 4":
+  ##       check(2+2 == result)
   ##
-  ##    test "(2 + -2) != 4":
-  ##      check(2 + -2 != result)
+  ##     test "(2 + -2) != 4":
+  ##       check(2 + -2 != result)
   ##
-  ##    # No teardown needed
+  ##     # No teardown needed
+  ##   ```
   ##
   ## The suite will run the individual test cases in the order in which
   ## they were listed. With default global settings the above code prints:
   ##
-  ## .. code-block::
-  ##
-  ##  [Suite] test suite for addition
-  ##    [OK] 2 + 2 = 4
-  ##    [OK] (2 + -2) != 4
+  ##     [Suite] test suite for addition
+  ##       [OK] 2 + 2 = 4
+  ##       [OK] (2 + -2) != 4
   bind formatters, ensureInitialized, suiteEnded
 
   block:
@@ -521,20 +518,24 @@ proc exceptionTypeName(e: ref Exception): string {.inline.} =
   if e == nil: "<foreign exception>"
   else: $e.name
 
+when not declared(setProgramResult):
+  {.warning: "setProgramResult not available on platform, unittest will not" &
+    " give failing exit code on test failure".}
+  template setProgramResult(a: int) =
+    discard
+
 template test*(name, body) {.dirty.} =
   ## Define a single test case identified by `name`.
   ##
-  ## .. code-block:: nim
-  ##
-  ##  test "roses are red":
-  ##    let roses = "red"
-  ##    check(roses == "red")
+  ##   ```nim
+  ##   test "roses are red":
+  ##     let roses = "red"
+  ##     check(roses == "red")
+  ##   ```
   ##
   ## The above code outputs:
   ##
-  ## .. code-block::
-  ##
-  ##  [OK] roses are red
+  ##     [OK] roses are red
   bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName, setProgramResult
 
   ensureInitialized()
@@ -546,11 +547,14 @@ template test*(name, body) {.dirty.} =
     for formatter in formatters:
       formatter.testStarted(name)
 
+    {.push warning[BareExcept]:off.}
     try:
       when declared(testSetupIMPLFlag): testSetupIMPL()
       when declared(testTeardownIMPLFlag):
         defer: testTeardownIMPL()
+      {.push warning[BareExcept]:on.}
       body
+      {.pop.}
 
     except:
       let e = getCurrentException()
@@ -572,16 +576,17 @@ template test*(name, body) {.dirty.} =
       )
       testEnded(testResult)
       checkpoints = @[]
+    {.pop.}
 
 proc checkpoint*(msg: string) =
   ## Set a checkpoint identified by `msg`. Upon test failure all
   ## checkpoints encountered so far are printed out. Example:
   ##
-  ## .. code-block:: nim
-  ##
-  ##  checkpoint("Checkpoint A")
-  ##  check((42, "the Answer to life and everything") == (1, "a"))
-  ##  checkpoint("Checkpoint B")
+  ##   ```nim
+  ##   checkpoint("Checkpoint A")
+  ##   check((42, "the Answer to life and everything") == (1, "a"))
+  ##   checkpoint("Checkpoint B")
+  ##   ```
   ##
   ## outputs "Checkpoint A" once it fails.
   checkpoints.add(msg)
@@ -593,11 +598,11 @@ template fail* =
   ## failed (change exit code and test status). This template is useful
   ## for debugging, but is otherwise mostly used internally. Example:
   ##
-  ## .. code-block:: nim
-  ##
-  ##  checkpoint("Checkpoint A")
-  ##  complicatedProcInThread()
-  ##  fail()
+  ##   ```nim
+  ##   checkpoint("Checkpoint A")
+  ##   complicatedProcInThread()
+  ##   fail()
+  ##   ```
   ##
   ## outputs "Checkpoint A" before quitting.
   bind ensureInitialized, setProgramResult
@@ -625,11 +630,10 @@ template skip* =
   ## for reasons depending on outer environment,
   ## or certain application logic conditions or configurations.
   ## The test code is still executed.
-  ##
-  ## .. code-block:: nim
-  ##
-  ##  if not isGLContextCreated():
-  ##    skip()
+  ##   ```nim
+  ##   if not isGLContextCreated():
+  ##     skip()
+  ##   ```
   bind checkpoints
 
   testStatusIMPL = TestStatus.SKIPPED
@@ -759,15 +763,20 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
       defectiveRobot()
 
   template expectBody(errorTypes, lineInfoLit, body): NimNode {.dirty.} =
+    {.push warning[BareExcept]:off.}
     try:
+      {.push warning[BareExcept]:on.}
       body
+      {.pop.}
       checkpoint(lineInfoLit & ": Expect Failed, no exception was thrown.")
       fail()
     except errorTypes:
       discard
     except:
-      checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.")
+      let err = getCurrentException()
+      checkpoint(lineInfoLit & ": Expect Failed, " & $err.name & " was thrown.")
       fail()
+    {.pop.}
 
   var errorTypes = newNimNode(nnkBracket)
   for exp in exceptions:
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index 50b1b9445..725d5bbd9 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -14,6 +14,8 @@
 ## as a locator, a name, or both. The term "Uniform Resource Locator"
 ## (URL) refers to the subset of URIs.
 ##
+## .. warning:: URI parsers in this module do not perform security validation.
+##
 ## # Basic usage
 
 
@@ -32,11 +34,11 @@ runnableExamples:
 
 ## ## Data URI Base64
 runnableExamples:
-  doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ="
-  doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt"
+  assert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ="
+  assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt"
 
 
-import strutils, parseutils, base64
+import std/[strutils, parseutils, base64]
 import std/private/[since, decode_helpers]
 
 when defined(nimPreviewSlimSystem):
@@ -50,7 +52,7 @@ type
     scheme*, username*, password*: string
     hostname*, port*, path*, query*, anchor*: string
     opaque*: bool
-    isIpv6: bool # not expose it for compatibility.
+    isIpv6*: bool
 
   UriParseError* = object of ValueError
 
@@ -494,42 +496,61 @@ func `$`*(u: Uri): string =
   ## Returns the string representation of the specified URI object.
   runnableExamples:
     assert $parseUri("https://nim-lang.org") == "https://nim-lang.org"
-  result = ""
-  if u.scheme.len > 0:
-    result.add(u.scheme)
-    if u.opaque:
-      result.add(":")
-    else:
-      result.add("://")
-  if u.username.len > 0:
-    result.add(u.username)
-    if u.password.len > 0:
-      result.add(":")
-      result.add(u.password)
-    result.add("@")
+  # Get the len of all the parts.
+  let schemeLen = u.scheme.len
+  let usernameLen = u.username.len
+  let passwordLen = u.password.len
+  let hostnameLen = u.hostname.len
+  let portLen = u.port.len
+  let pathLen = u.path.len
+  let queryLen = u.query.len
+  let anchorLen = u.anchor.len
+  # Prepare a string that fits all the parts and all punctuation chars.
+  # 12 is the max len required by all possible punctuation chars.
+  result = newStringOfCap(
+    schemeLen + usernameLen + passwordLen + hostnameLen + portLen + pathLen + queryLen + anchorLen + 12
+  )
+  # Insert to result.
+  if schemeLen > 0:
+    result.add u.scheme
+    result.add ':'
+    if not u.opaque:
+      result.add '/'
+      result.add '/'
+  if usernameLen > 0:
+    result.add u.username
+    if passwordLen > 0:
+      result.add ':'
+      result.add u.password
+    result.add '@'
   if u.hostname.endsWith('/'):
     if u.isIpv6:
-      result.add("[" & u.hostname[0 .. ^2] & "]")
+      result.add '['
+      result.add u.hostname[0 .. ^2]
+      result.add ']'
     else:
-      result.add(u.hostname[0 .. ^2])
+      result.add u.hostname[0 .. ^2]
   else:
     if u.isIpv6:
-      result.add("[" & u.hostname & "]")
+      result.add '['
+      result.add u.hostname
+      result.add ']'
     else:
-      result.add(u.hostname)
-  if u.port.len > 0:
-    result.add(":")
-    result.add(u.port)
-  if u.path.len > 0:
-    if u.hostname.len > 0 and u.path[0] != '/':
-      result.add('/')
-    result.add(u.path)
-  if u.query.len > 0:
-    result.add("?")
-    result.add(u.query)
-  if u.anchor.len > 0:
-    result.add("#")
-    result.add(u.anchor)
+      result.add u.hostname
+  if portLen > 0:
+    result.add ':'
+    result.add u.port
+  if pathLen > 0:
+    if hostnameLen > 0 and u.path[0] != '/':
+      result.add '/'
+    result.add u.path
+  if queryLen > 0:
+    result.add '?'
+    result.add u.query
+  if anchorLen > 0:
+    result.add '#'
+    result.add u.anchor
+
 
 proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3).} =
   ## Convenience proc for `base64.encode` returns a standard Base64 Data URI (RFC-2397)
@@ -540,4 +561,12 @@ proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3)
   ## * https://en.wikipedia.org/wiki/Data_URI_scheme
   runnableExamples: static: assert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt"
   assert encoding.len > 0 and mime.len > 0 # Must *not* be URL-Safe, see RFC-2397
-  result = "data:" & mime & ";charset=" & encoding & ";base64," & base64.encode(data)
+  let base64encoded: string = base64.encode(data)
+  # ("data:".len + ";charset=".len + ";base64,".len) == 22
+  result = newStringOfCap(22 + mime.len + encoding.len + base64encoded.len)
+  result.add "data:"
+  result.add mime
+  result.add ";charset="
+  result.add encoding
+  result.add ";base64,"
+  result.add base64encoded
diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim
index f30d899df..a38247c7d 100644
--- a/lib/pure/volatile.nim
+++ b/lib/pure/volatile.nim
@@ -10,20 +10,18 @@
 ## This module contains code for generating volatile loads and stores,
 ## which are useful in embedded and systems programming.
 
-template volatileLoad*[T](src: ptr T): T =
+proc volatileLoad*[T](src: ptr T): T {.inline, noinit.} =
   ## Generates a volatile load of the value stored in the container `src`.
   ## Note that this only effects code generation on `C` like backends.
   when nimvm:
-    src[]
+    result = src[]
   else:
     when defined(js):
-      src[]
+      result = src[]
     else:
-      var res: T
-      {.emit: [res, " = (*(", typeof(src[]), " volatile*)", src, ");"].}
-      res
+      {.emit: [result, " = (*(", typeof(src[]), " volatile*)", src, ");"].}
 
-template volatileStore*[T](dest: ptr T, val: T) =
+proc volatileStore*[T](dest: ptr T, val: T) {.inline.} =
   ## Generates a volatile store into the container `dest` of the value
   ## `val`. Note that this only effects code generation on `C` like
   ## backends.
diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim
index 6785fa66e..2c1e4e37c 100644
--- a/lib/pure/xmlparser.nim
+++ b/lib/pure/xmlparser.nim
@@ -9,7 +9,7 @@
 
 ## This module parses an XML document and creates its XML tree representation.
 
-import streams, parsexml, strtabs, xmltree
+import std/[streams, parsexml, strtabs, xmltree]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -151,7 +151,7 @@ proc loadXml*(path: string, options: set[XmlParseOption] = {reportComments}): Xm
 
 when isMainModule:
   when not defined(testing):
-    import os
+    import std/os
 
     var errors: seq[string] = @[]
     var x = loadXml(paramStr(1), errors)
diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim
index 72645ef96..5c0cbc5e4 100644
--- a/lib/pure/xmltree.nim
+++ b/lib/pure/xmltree.nim
@@ -31,7 +31,7 @@ runnableExamples:
 ## * `htmlgen module <htmlgen.html>`_ for html code generator
 
 import std/private/since
-import macros, strtabs, strutils
+import std/[macros, strtabs, strutils, sequtils]
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -70,6 +70,14 @@ const
   xmlHeader* = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
     ## Header to use for complete XML output.
 
+template expect(node: XmlNode, kind: set[XmlNodeKind]) =
+  ## Check the node's kind is within a set of values
+  assert node.k in kind, "Got " & $node.k
+
+template expect(node: XmlNode, kind: XmlNodeKind) =
+  ## Check the node's kind equals a value
+  assert node.k == kind, "Got " & $node.k
+
 proc newXmlNode(kind: XmlNodeKind): XmlNode =
   ## Creates a new ``XmlNode``.
   result = XmlNode(k: kind)
@@ -182,7 +190,7 @@ proc text*(n: XmlNode): lent string {.inline.} =
     assert $c == "<!-- my comment -->"
     assert c.text == "my comment"
 
-  assert n.k in {xnText, xnComment, xnCData, xnEntity}
+  n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity}
   result = n.fText
 
 proc `text=`*(n: XmlNode, text: sink string) {.inline.} =
@@ -200,7 +208,7 @@ proc `text=`*(n: XmlNode, text: sink string) {.inline.} =
     e.text = "a new entity text"
     assert $e == "&a new entity text;"
 
-  assert n.k in {xnText, xnComment, xnCData, xnEntity}
+  n.expect {xnText, xnVerbatimText, xnComment, xnCData, xnEntity}
   n.fText = text
 
 proc tag*(n: XmlNode): lent string {.inline.} =
@@ -221,7 +229,7 @@ proc tag*(n: XmlNode): lent string {.inline.} =
 </firstTag>"""
     assert a.tag == "firstTag"
 
-  assert n.k == xnElement
+  n.expect xnElement
   result = n.fTag
 
 proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} =
@@ -244,7 +252,7 @@ proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} =
   <childTag />
 </newTag>"""
 
-  assert n.k == xnElement
+  n.expect xnElement
   n.fTag = tag
 
 proc rawText*(n: XmlNode): string {.inline.} =
@@ -298,26 +306,60 @@ proc innerText*(n: XmlNode): string =
 
 proc add*(father, son: XmlNode) {.inline.} =
   ## Adds the child `son` to `father`.
+  ## `father` must be of `xnElement` type
   ##
   ## See also:
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
   ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
   ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
   runnableExamples:
     var f = newElement("myTag")
     f.add newText("my text")
     f.add newElement("sonTag")
     f.add newEntity("my entity")
     assert $f == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+  father.expect xnElement
   add(father.s, son)
 
+proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} =
+  ## Adds the children `sons` to `father`.
+  ## `father` must be of `xnElement` type
+  ##
+  ## See also:
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add(@[newText("my text"), newElement("sonTag"), newEntity("my entity")])
+    assert $f == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+  father.expect xnElement
+  add(father.s, sons)
+
+
 proc insert*(father, son: XmlNode, index: int) {.inline.} =
   ## Inserts the child `son` to a given position in `father`.
   ##
-  ## `father` and `son` must be of `xnElement` kind.
+  ## `father` must be of `xnElement` kind.
   ##
   ## See also:
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
   ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
   ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
   runnableExamples:
     var f = newElement("myTag")
     f.add newElement("first")
@@ -327,18 +369,52 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} =
   <first />
 </myTag>"""
 
-  assert father.k == xnElement and son.k == xnElement
+  father.expect xnElement
   if len(father.s) > index:
     insert(father.s, son, index)
   else:
     insert(father.s, son, len(father.s))
 
+proc insert*(father: XmlNode, sons: openArray[XmlNode], index: int) {.inline.} =
+  ## Inserts the children openArray[`sons`] to a given position in `father`.
+  ##
+  ## `father` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("third")], 0)
+    assert $f == """<myTag>
+  <second />
+  <third />
+  <first />
+</myTag>"""
+
+  father.expect xnElement
+  if len(father.s) > index:
+    insert(father.s, sons, index)
+  else:
+    insert(father.s, sons, len(father.s))
+
 proc delete*(n: XmlNode, i: Natural) =
   ## Deletes the `i`'th child of `n`.
   ##
   ## See also:
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
   ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
   ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
   runnableExamples:
     var f = newElement("myTag")
     f.add newElement("first")
@@ -348,9 +424,88 @@ proc delete*(n: XmlNode, i: Natural) =
   <first />
 </myTag>"""
 
-  assert n.k == xnElement
+  n.expect xnElement
   n.s.delete(i)
 
+proc delete*(n: XmlNode, slice: Slice[int]) =
+  ## Deletes the items `n[slice]` of `n`.
+  ##
+  ## See also:
+  ## * `delete proc <#delete.XmlNode,int>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("third")], 0)
+    f.delete(0..1)
+    assert $f == """<myTag>
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(slice)
+
+proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) =
+  ## Replaces the `i`'th child of `n` with `replacement` openArray.
+  ##
+  ## `n` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert(newElement("second"), 0)
+    f.replace(0, @[newElement("third"), newElement("fourth")])
+    assert $f == """<myTag>
+  <third />
+  <fourth />
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(i)
+  n.s.insert(replacement, i)
+
+proc replace*(n: XmlNode, slice: Slice[int], replacement: openArray[XmlNode]) =
+  ## Deletes the items `n[slice]` of `n`.
+  ##
+  ## `n` must be of `xnElement` kind.
+  ##
+  ## See also:
+  ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_
+  ## * `add proc <#add,XmlNode,XmlNode>`_
+  ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_
+  ## * `delete proc <#delete,XmlNode,Natural>`_
+  ## * `delete proc <#delete.XmlNode,Slice[int]>`_
+  ## * `insert proc <#insert,XmlNode,XmlNode,int>`_
+  ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_
+  runnableExamples:
+    var f = newElement("myTag")
+    f.add newElement("first")
+    f.insert([newElement("second"), newElement("fifth")], 0)
+    f.replace(0..1, @[newElement("third"), newElement("fourth")])
+    assert $f == """<myTag>
+  <third />
+  <fourth />
+  <first />
+</myTag>"""
+
+  n.expect xnElement
+  n.s.delete(slice)
+  n.s.insert(replacement, slice.a)
+
 proc len*(n: XmlNode): int {.inline.} =
   ## Returns the number of `n`'s children.
   runnableExamples:
@@ -378,12 +533,12 @@ proc `[]`*(n: XmlNode, i: int): XmlNode {.inline.} =
     assert $f[1] == "<first />"
     assert $f[0] == "<second />"
 
-  assert n.k == xnElement
+  n.expect xnElement
   result = n.s[i]
 
 proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} =
   ## Returns the `i`'th child of `n` so that it can be modified.
-  assert n.k == xnElement
+  n.expect xnElement
   result = n.s[i]
 
 proc clear*(n: var XmlNode) =
@@ -435,12 +590,12 @@ iterator items*(n: XmlNode): XmlNode {.inline.} =
     # <!-- this is comment -->
     # <secondTag>&some entity;<![CDATA[some cdata]]></secondTag>
 
-  assert n.k == xnElement
+  n.expect xnElement
   for i in 0 .. n.len-1: yield n[i]
 
 iterator mitems*(n: var XmlNode): var XmlNode {.inline.} =
   ## Iterates over all direct children of `n` so that they can be modified.
-  assert n.k == xnElement
+  n.expect xnElement
   for i in 0 .. n.len-1: yield n[i]
 
 proc toXmlAttributes*(keyValuePairs: varargs[tuple[key,
@@ -472,7 +627,7 @@ proc attrs*(n: XmlNode): XmlAttributes {.inline.} =
     j.attrs = att
     assert j.attrs == att
 
-  assert n.k == xnElement
+  n.expect xnElement
   result = n.fAttr
 
 proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} =
@@ -489,7 +644,7 @@ proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} =
     j.attrs = att
     assert j.attrs == att
 
-  assert n.k == xnElement
+  n.expect xnElement
   n.fAttr = attr
 
 proc attrsLen*(n: XmlNode): int {.inline.} =
@@ -506,7 +661,7 @@ proc attrsLen*(n: XmlNode): int {.inline.} =
     j.attrs = att
     assert j.attrsLen == 2
 
-  assert n.k == xnElement
+  n.expect xnElement
   if not isNil(n.fAttr): result = len(n.fAttr)
 
 proc attr*(n: XmlNode, name: string): string =
@@ -524,7 +679,7 @@ proc attr*(n: XmlNode, name: string): string =
     assert j.attr("key1") == "first value"
     assert j.attr("key2") == "second value"
 
-  assert n.kind == xnElement
+  n.expect xnElement
   if n.attrs == nil: return ""
   return n.attrs.getOrDefault(name)
 
@@ -576,23 +731,11 @@ proc addIndent(result: var string, indent: int, addNewLines: bool) =
   for i in 1 .. indent:
     result.add(' ')
 
-proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
-          addNewLines = true) =
-  ## Adds the textual representation of `n` to string `result`.
-  runnableExamples:
-    var
-      a = newElement("firstTag")
-      b = newText("my text")
-      c = newComment("my comment")
-      s = ""
-    s.add(c)
-    s.add(a)
-    s.add(b)
-    assert s == "<!-- my comment --><firstTag />my text"
-
+proc addImpl(result: var string, n: XmlNode, indent = 0, indWidth = 2,
+          addNewLines = true, lastNodeIsText = false) =
   proc noWhitespace(n: XmlNode): bool =
     for i in 0 ..< n.len:
-      if n[i].kind in {xnText, xnEntity}: return true
+      if n[i].kind in {xnText, xnVerbatimText, xnEntity}: return true
 
   proc addEscapedAttr(result: var string, s: string) =
     # `addEscaped` alternative with less escaped characters.
@@ -609,7 +752,7 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
 
   case n.k
   of xnElement:
-    if indent > 0:
+    if indent > 0 and not lastNodeIsText:
       result.addIndent(indent, addNewLines)
 
     let
@@ -638,8 +781,10 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
                    else:
                      indent+indWidth
     result.add('>')
+    var lastNodeIsText = false
     for i in 0 ..< n.len:
-      result.add(n[i], indentNext, indWidth, addNewLines)
+      result.addImpl(n[i], indentNext, indWidth, addNewLines, lastNodeIsText)
+      lastNodeIsText = (n[i].kind == xnText) or (n[i].kind == xnVerbatimText)
 
     if not n.noWhitespace():
       result.addIndent(indent, addNewLines)
@@ -664,6 +809,21 @@ proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
     result.add(n.fText)
     result.add(';')
 
+proc add*(result: var string, n: XmlNode, indent = 0, indWidth = 2,
+          addNewLines = true) {.inline.} =
+  ## Adds the textual representation of `n` to string `result`.
+  runnableExamples:
+    var
+      a = newElement("firstTag")
+      b = newText("my text")
+      c = newComment("my comment")
+      s = ""
+    s.add(c)
+    s.add(a)
+    s.add(b)
+    assert s == "<!-- my comment --><firstTag />my text"
+  result.addImpl(n, indent, indWidth, addNewLines)
+
 proc `$`*(n: XmlNode): string =
   ## Converts `n` into its string representation.
   ##
@@ -682,7 +842,7 @@ proc child*(n: XmlNode, name: string): XmlNode =
     f.add newElement("thirdSon")
     assert $(f.child("secondSon")) == "<secondSon />"
 
-  assert n.kind == xnElement
+  n.expect xnElement
   for i in items(n):
     if i.kind == xnElement:
       if i.tag == name:
@@ -718,7 +878,7 @@ proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode],
     a.findAll("BAD", s, caseInsensitive = true)
     assert $s == "@[<bad>c text</bad>, <BAD>d text</BAD>]"
 
-  assert n.k == xnElement
+  n.expect xnElement
   for child in n.items():
     if child.k != xnElement:
       continue
@@ -775,11 +935,12 @@ proc xmlConstructor(a: NimNode): NimNode =
 macro `<>`*(x: untyped): untyped =
   ## Constructor macro for XML. Example usage:
   ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   <>a(href="http://nim-lang.org", newText("Nim rules."))
+  ##   ```
   ##
-  ## Produces an XML tree for::
+  ## Produces an XML tree for:
   ##
-  ##  <a href="http://nim-lang.org">Nim rules.</a>
+  ##     <a href="http://nim-lang.org">Nim rules.</a>
   ##
   result = xmlConstructor(x)
diff --git a/lib/std/appdirs.nim b/lib/std/appdirs.nim
new file mode 100644
index 000000000..963451efe
--- /dev/null
+++ b/lib/std/appdirs.nim
@@ -0,0 +1,94 @@
+## This module implements helpers for determining special directories used by apps.
+
+## .. importdoc:: paths.nim
+
+from std/private/osappdirs import nil
+import std/paths
+import std/envvars
+
+proc getHomeDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the `expandTilde proc`_
+  ## for the convenience of processing paths coming from user configuration files.
+  ##
+  ## See also:
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  result = Path(osappdirs.getHomeDir())
+
+proc getDataDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the data directory of the current user for applications.
+  ## 
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.local/share" or "~/Library/Application Support" on macOS).
+  ## 
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  result = Path(osappdirs.getDataDir())
+
+proc getConfigDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the config directory of the current user for applications.
+  ##
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.config/").
+  ##
+  ## An OS-dependent trailing slash is always present at the end of the
+  ## returned string: `\\` on Windows and `/` on all other OSs.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  result = Path(osappdirs.getConfigDir())
+
+proc getCacheDir*(): Path {.inline.} =
+  ## Returns the cache directory of the current user for applications.
+  ##
+  ## This makes use of the following environment variables:
+  ##
+  ## * On Windows: `getEnv("LOCALAPPDATA")`
+  ##
+  ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")`
+  ##
+  ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")`
+  ##
+  ## **See also:**
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getConfigDir proc`_
+  # follows https://crates.io/crates/platform-dirs
+  result = Path(osappdirs.getCacheDir())
+
+proc getCacheDir*(app: Path): Path {.inline.} =
+  ## Returns the cache directory for an application `app`.
+  ##
+  ## * On Windows, this uses: `getCacheDir() / app / "cache"`
+  ## * On other platforms, this uses: `getCacheDir() / app`
+  result = Path(osappdirs.getCacheDir(app.string))
+
+proc getTempDir*(): Path {.inline, tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the temporary directory of the current user for applications to
+  ## save temporary files in.
+  ##
+  ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw).
+  ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order.
+  ## On all platforms, `/tmp` will be returned if the procs fails.
+  ##
+  ## You can override this implementation
+  ## by adding `-d:tempDir=mytempname` to your compiler invocation.
+  ##
+  ## .. Note:: This proc does not check whether the returned path exists.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  result = Path(osappdirs.getTempDir())
diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim
index 0bc4653f2..56c37d205 100644
--- a/lib/std/assertions.nim
+++ b/lib/std/assertions.nim
@@ -7,11 +7,12 @@
 #    distribution, for details about the copyright.
 #
 
-## This module implements assertion handling.
-
-when not declared(sysFatal):
+when not defined(nimPreviewSlimSystem) and not declared(sysFatal):
+  include "system/rawquits"
   include "system/fatal"
 
+## This module implements assertion handling.
+
 import std/private/miscdollars
 # ---------------------------------------------------------------------------
 # helpers
@@ -26,22 +27,18 @@ proc `$`(info: InstantiationInfo): string =
 
 # ---------------------------------------------------------------------------
 
-when not defined(nimHasSinkInference):
-  {.pragma: nosinks.}
 
 proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} =
   ## Raises an `AssertionDefect` with `msg`.
-  sysFatal(AssertionDefect, msg)
+  when defined(nimPreviewSlimSystem):
+    raise newException(AssertionDefect, msg)
+  else:
+    sysFatal(AssertionDefect, msg)
 
 proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} =
   ## Raises an `AssertionDefect` with `msg`, but this is hidden
   ## from the effect system. Called when an assertion failed.
-  # trick the compiler to not list `AssertionDefect` when called
-  # by `assert`.
-  # xxx simplify this pending bootstrap >= 1.4.0, after which cast not needed
-  # anymore since `Defect` can't be raised.
-  type Hide = proc (msg: string) {.noinline, raises: [], noSideEffect, tags: [].}
-  cast[Hide](raiseAssert)(msg)
+  raiseAssert(msg)
 
 template assertImpl(cond: bool, msg: string, expr: string, enabled: static[bool]) =
   when enabled:
@@ -101,6 +98,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
   const begin = "expected raising '" & astToStr(exception) & "', instead"
   const msgEnd = " by: " & astToStr(code)
   template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd)
+  {.push warning[BareExcept]:off.}
   when Exception is exception:
     try:
       if true:
@@ -119,5 +117,6 @@ template doAssertRaises*(exception: typedesc, code: untyped) =
       mixin `$` # alternatively, we could define $cstring in this module
       raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd)
     except: raisedForeign()
+  {.pop.}
   if wrong:
     raiseAssert(begin & " nothing was raised" & msgEnd)
diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim
new file mode 100644
index 000000000..0ba4619e5
--- /dev/null
+++ b/lib/std/cmdline.nim
@@ -0,0 +1,313 @@
+#

+#

+#            Nim's Runtime Library

+#        (c) Copyright 2022 Andreas Rumpf

+#

+#    See the file "copying.txt", included in this

+#    distribution, for details about the copyright.

+#

+

+## This module contains system facilities for reading command

+## line parameters.

+

+## **See also:**

+## * `parseopt module <parseopt.html>`_ for command-line parser beyond

+##   `parseCmdLine proc`_

+

+

+include system/inclrtl

+

+when defined(nimPreviewSlimSystem):

+  import std/widestrs

+  

+when defined(nodejs):

+  from std/private/oscommon import ReadDirEffect

+

+

+const weirdTarget = defined(nimscript) or defined(js)

+

+

+when weirdTarget:

+  discard

+elif defined(windows):

+  import std/winlean

+elif defined(posix):

+  import std/posix

+else:

+  {.error: "The cmdline module has not been implemented for the target platform.".}

+

+

+# Needed by windows in order to obtain the command line for targets

+# other than command line targets

+when defined(windows) and not weirdTarget:

+  template getCommandLine*(): untyped = getCommandLineW()

+

+

+proc parseCmdLine*(c: string): seq[string] {.

+  noSideEffect, rtl, extern: "nos$1".} =

+  ## Splits a `command line`:idx: into several components.

+  ##

+  ## **Note**: This proc is only occasionally useful, better use the

+  ## `parseopt module <parseopt.html>`_.

+  ##

+  ## On Windows, it uses the `following parsing rules

+  ## <http://msdn.microsoft.com/en-us/library/17w5ykft.aspx>`_:

+  ##

+  ## * Arguments are delimited by white space, which is either a space or a tab.

+  ## * The caret character (^) is not recognized as an escape character or

+  ##   delimiter. The character is handled completely by the command-line parser

+  ##   in the operating system before being passed to the argv array in the

+  ##   program.

+  ## * A string surrounded by double quotation marks ("string") is interpreted

+  ##   as a single argument, regardless of white space contained within. A

+  ##   quoted string can be embedded in an argument.

+  ## * A double quotation mark preceded by a backslash (\") is interpreted as a

+  ##   literal double quotation mark character (").

+  ## * Backslashes are interpreted literally, unless they immediately precede

+  ##   a double quotation mark.

+  ## * If an even number of backslashes is followed by a double quotation mark,

+  ##   one backslash is placed in the argv array for every pair of backslashes,

+  ##   and the double quotation mark is interpreted as a string delimiter.

+  ## * If an odd number of backslashes is followed by a double quotation mark,

+  ##   one backslash is placed in the argv array for every pair of backslashes,

+  ##   and the double quotation mark is "escaped" by the remaining backslash,

+  ##   causing a literal double quotation mark (") to be placed in argv.

+  ##

+  ## On Posix systems, it uses the following parsing rules:

+  ## Components are separated by whitespace unless the whitespace

+  ## occurs within ``"`` or ``'`` quotes.

+  ##

+  ## See also:

+  ## * `parseopt module <parseopt.html>`_

+  ## * `paramCount proc`_

+  ## * `paramStr proc`_

+  ## * `commandLineParams proc`_

+

+  result = @[]

+  var i = 0

+  var a = ""

+  while true:

+    setLen(a, 0)

+    # eat all delimiting whitespace

+    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:

+      var inQuote = false

+      while i < c.len:

+        case c[i]

+        of '\\':

+          var j = i

+          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

+            else:

+              a.add('"')

+              i = j+1

+          else:

+            a.add(c[i])

+            inc(i)

+        of '"':

+          inc(i)

+          if not inQuote: inQuote = true

+          elif i < c.len and c[i] == '"':

+            a.add(c[i])

+            inc(i)

+          else:

+            inQuote = false

+            break

+        of ' ', '\t':

+          if not inQuote: break

+          a.add(c[i])

+          inc(i)

+        else:

+          a.add(c[i])

+          inc(i)

+    else:

+      case c[i]

+      of '\'', '\"':

+        var delim = c[i]

+        inc(i) # skip ' or "

+        while i < c.len and c[i] != delim:

+          add a, c[i]

+          inc(i)

+        if i < c.len: inc(i)

+      else:

+        while i < c.len and c[i] > ' ':

+          add(a, c[i])

+          inc(i)

+    add(result, move a)

+

+when defined(nimdoc):

+  # Common forward declaration docstring block for parameter retrieval procs.

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    ## Returns the number of `command line arguments`:idx: given to the

+    ## application.

+    ##

+    ## Unlike `argc`:idx: in C, if your binary was called without parameters this

+    ## will return zero.

+    ## You can query each individual parameter with `paramStr proc`_

+    ## or retrieve all of them in one go with `commandLineParams proc`_.

+    ##

+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on

+    ## Posix this proc is not defined.

+    ## Test for availability using `declared() <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramStr proc`_

+    ## * `commandLineParams proc`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(paramCount):

+    ##     # Use paramCount() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    ## Returns the `i`-th `command line argument`:idx: given to the application.

+    ##

+    ## `i` should be in the range `1..paramCount()`, the `IndexDefect`

+    ## exception will be raised for invalid values. Instead of iterating

+    ## over `paramCount()`_ with this proc you can

+    ## call the convenience `commandLineParams()`_.

+    ##

+    ## Similarly to `argv`:idx: in C,

+    ## it is possible to call `paramStr(0)` but this will return OS specific

+    ## contents (usually the name of the invoked executable). You should avoid

+    ## this and call `getAppFilename() <os.html#getAppFilename>`_ instead.

+    ##

+    ## **Availability**: When generating a dynamic library (see `--app:lib`) on

+    ## Posix this proc is not defined.

+    ## Test for availability using `declared() <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramCount proc`_

+    ## * `commandLineParams proc`_

+    ## * `getAppFilename proc <os.html#getAppFilename>`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(paramStr):

+    ##     # Use paramStr() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+

+elif defined(nimscript): discard

+elif defined(nodejs):

+  type Argv = object of JsRoot

+  let argv {.importjs: "process.argv".} : Argv

+  proc len(argv: Argv): int {.importjs: "#.length".}

+  proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".}

+

+  proc paramCount*(): int {.tags: [ReadDirEffect].} =

+    result = argv.len - 2

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    let i = i + 1

+    if i < argv.len and i >= 0:

+      result = $argv[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2))

+elif defined(windows):

+  # Since we support GUI applications with Nim, we sometimes generate

+  # a WinMain entry proc. But a WinMain proc has no access to the parsed

+  # command line arguments. The way to get them differs. Thus we parse them

+  # ourselves. This has the additional benefit that the program's behaviour

+  # is always the same -- independent of the used C compiler.

+  var

+    ownArgv {.threadvar.}: seq[string]

+    ownParsedArgv {.threadvar.}: bool

+

+  proc paramCount*(): int {.rtl, extern: "nos$1", tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if not ownParsedArgv:

+      ownArgv = parseCmdLine($getCommandLine())

+      ownParsedArgv = true

+    result = ownArgv.len-1

+

+  proc paramStr*(i: int): string {.rtl, extern: "nos$1",

+    tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if not ownParsedArgv:

+      ownArgv = parseCmdLine($getCommandLine())

+      ownParsedArgv = true

+    if i < ownArgv.len and i >= 0:

+      result = ownArgv[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i, ownArgv.len-1))

+

+elif defined(genode):

+  proc paramStr*(i: int): string =

+    raise newException(OSError, "paramStr is not implemented on Genode")

+

+  proc paramCount*(): int =

+    raise newException(OSError, "paramCount is not implemented on Genode")

+elif weirdTarget or (defined(posix) and appType == "lib"):

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    raise newException(OSError, "paramStr is not implemented on current platform")

+

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    raise newException(OSError, "paramCount is not implemented on current platform")

+elif not defined(createNimRtl) and

+  not(defined(posix) and appType == "lib"):

+  # On Posix, there is no portable way to get the command line from a DLL.

+  var

+    cmdCount {.importc: "cmdCount".}: cint

+    cmdLine {.importc: "cmdLine".}: cstringArray

+

+  proc paramStr*(i: int): string {.tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    if i < cmdCount and i >= 0:

+      result = $cmdLine[i]

+    else:

+      raise newException(IndexDefect, formatErrorIndexBound(i, cmdCount-1))

+

+  proc paramCount*(): int {.tags: [ReadIOEffect].} =

+    # Docstring in nimdoc block.

+    result = cmdCount-1

+

+when declared(paramCount) or defined(nimdoc):

+  proc commandLineParams*(): seq[string] =

+    ## Convenience proc which returns the command line parameters.

+    ##

+    ## This returns **only** the parameters. If you want to get the application

+    ## executable filename, call `getAppFilename() <os.html#getAppFilename>`_.

+    ##

+    ## **Availability**: On Posix there is no portable way to get the command

+    ## line from a DLL and thus the proc isn't defined in this environment. You

+    ## can test for its availability with `declared()

+    ## <system.html#declared,untyped>`_.

+    ##

+    ## See also:

+    ## * `parseopt module <parseopt.html>`_

+    ## * `parseCmdLine proc`_

+    ## * `paramCount proc`_

+    ## * `paramStr proc`_

+    ## * `getAppFilename proc <os.html#getAppFilename>`_

+    ##

+    ## **Examples:**

+    ##

+    ##   ```nim

+    ##   when declared(commandLineParams):

+    ##     # Use commandLineParams() here

+    ##   else:

+    ##     # Do something else!

+    ##   ```

+    result = @[]

+    for i in 1..paramCount():

+      result.add(paramStr(i))

+else:

+  proc commandLineParams*(): seq[string] {.error:

+  "commandLineParams() unsupported by dynamic libraries".} =

+    discard

diff --git a/lib/std/decls.nim b/lib/std/decls.nim
index 7b907f5e1..bb7ec3593 100644
--- a/lib/std/decls.nim
+++ b/lib/std/decls.nim
@@ -1,16 +1,15 @@
-# see `semLowerLetVarCustomPragma` for compiler support that enables these
-# lowerings
+## This module implements syntax sugar for some declarations.
 
-import macros
+import std/macros
 
 macro byaddr*(sect) =
   ## Allows a syntax for l-value references, being an exact analog to
   ## `auto& a = ex;` in C++.
   ## 
-  ## Warning: This makes use of 2 experimental features, namely nullary
-  ## templates instantiated as symbols and variable macro pragmas.
-  ## For this reason, its behavior is not stable. The current implementation
-  ## allows redefinition, but this is not an intended consequence.
+  ## .. warning:: This makes use of 2 experimental features, namely nullary
+  ##   templates instantiated as symbols and variable macro pragmas.
+  ##   For this reason, its behavior is not stable. The current implementation
+  ##   allows redefinition, but this is not an intended consequence.
   runnableExamples:
     var s = @[10, 11, 12]
     var a {.byaddr.} = s[0]
diff --git a/lib/std/dirs.nim b/lib/std/dirs.nim
new file mode 100644
index 000000000..380d6d08f
--- /dev/null
+++ b/lib/std/dirs.nim
@@ -0,0 +1,135 @@
+## This module implements directory handling.
+
+from std/paths import Path, ReadDirEffect, WriteDirEffect
+
+from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir,
+                               moveDir, walkDir, setCurrentDir,
+                               walkDirRec, PathComponent
+
+export PathComponent
+
+proc dirExists*(dir: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  result = dirExists(dir.string)
+
+proc createDir*(dir: Path) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Creates the `directory`:idx: `dir`.
+  ##
+  ## The directory may contain several subdirectories that do not exist yet.
+  ## The full path is created. If this fails, `OSError` is raised.
+  ##
+  ## It does **not** fail if the directory already exists because for
+  ## most usages this does not indicate an error.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `moveDir proc`_
+  createDir(dir.string)
+
+proc existsOrCreateDir*(dir: Path): bool {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
+  ##
+  ## Does not create parent directories (raises `OSError` if parent directories do not exist).
+  ## Returns `true` if the directory already exists, and `false` otherwise.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  result = existsOrCreateDir(dir.string)
+
+proc removeDir*(dir: Path, checkDir = false
+                ) {.inline, tags: [WriteDirEffect, ReadDirEffect].} =
+  ## Removes the directory `dir` including all subdirectories and files
+  ## in `dir` (recursively).
+  ##
+  ## If this fails, `OSError` is raised. This does not fail if the directory never
+  ## existed in the first place, unless `checkDir` = true.
+  ##
+  ## See also:
+  ## * `removeFile proc <files.html#removeFile>`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  removeDir(dir.string, checkDir)
+
+proc moveDir*(source, dest: Path) {.inline, tags: [ReadIOEffect, WriteIOEffect].} =
+  ## Moves a directory from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` contains symlinks, they themself are
+  ## moved, not their target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `moveFile proc <files.html#moveFile>`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  moveDir(source.string, dest.string)
+
+iterator walkDir*(dir: Path; relative = false, checkDir = false,
+                 skipSpecial = false):
+    tuple[kind: PathComponent, path: Path] {.tags: [ReadDirEffect].} =
+  ## Walks over the directory `dir` and yields for each directory or file in
+  ## `dir`. The component type and full path for each item are returned.
+  ##
+  ## Walking is not recursive.
+  ## * If `relative` is true (default: false)
+  ##   the resulting path is shortened to be relative to ``dir``,
+  ##   otherwise the full path is returned.
+  ## * If `checkDir` is true, `OSError` is raised when `dir`
+  ##   doesn't exist.
+  ## * If `skipSpecial` is true, then (besides all directories) only *regular*
+  ##   files (**without** special "file" objects like FIFOs, device files,
+  ##   etc) will be yielded on Unix.
+  for (k, p) in walkDir(dir.string, relative, checkDir, skipSpecial):
+    yield (k, Path(p))
+
+iterator walkDirRec*(dir: Path,
+                     yieldFilter = {pcFile}, followFilter = {pcDir},
+                     relative = false, checkDir = false, skipSpecial = false):
+                    Path {.tags: [ReadDirEffect].} =
+  ## Recursively walks over the directory `dir` and yields for each file
+  ## or directory in `dir`.
+  ##
+  ## Options `relative`, `checkdir`, `skipSpecial` are explained in
+  ## [walkDir iterator] description.
+  ##
+  ## .. warning:: Modifying the directory structure while the iterator
+  ##   is traversing may result in undefined behavior!
+  ##
+  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
+  ##
+  ## =====================   =============================================
+  ## yieldFilter             meaning
+  ## =====================   =============================================
+  ## ``pcFile``              yield real files (default)
+  ## ``pcLinkToFile``        yield symbolic links to files
+  ## ``pcDir``               yield real directories
+  ## ``pcLinkToDir``         yield symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ## =====================   =============================================
+  ## followFilter            meaning
+  ## =====================   =============================================
+  ## ``pcDir``               follow real directories (default)
+  ## ``pcLinkToDir``         follow symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ##
+  ## See also:
+  ## * `walkDir iterator`_
+  for p in walkDirRec(dir.string, yieldFilter, followFilter, relative,
+                      checkDir, skipSpecial):
+    yield Path(p)
+
+proc setCurrentDir*(newDir: Path) {.inline, tags: [].} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getCurrentDir proc <paths.html#getCurrentDir>`_
+  osdirs.setCurrentDir(newDir.string)
diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim
index 9f29c5c05..40c0017ae 100644
--- a/lib/std/editdistance.nim
+++ b/lib/std/editdistance.nim
@@ -10,7 +10,7 @@
 ## This module implements an algorithm to compute the
 ## `edit distance`:idx: between two Unicode strings.
 
-import unicode
+import std/unicode
 
 proc editDistance*(a, b: string): int {.noSideEffect.} =
   ## Returns the **unicode-rune** edit distance between `a` and `b`.
@@ -18,7 +18,7 @@ proc editDistance*(a, b: string): int {.noSideEffect.} =
   ## This uses the `Levenshtein`:idx: distance algorithm with only a linear
   ## memory overhead.
   runnableExamples: static: doAssert editdistance("Kitten", "Bitten") == 1
-  if len(a) > len(b):
+  if runeLen(a) > runeLen(b):
     # make `b` the longer string
     return editDistance(b, a)
   # strip common prefix
diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim
index fb057a669..3d1b4ffd3 100644
--- a/lib/std/effecttraits.nim
+++ b/lib/std/effecttraits.nim
@@ -14,7 +14,7 @@
 ## One can test for the existence of this standard module
 ## via `defined(nimHasEffectTraitsModule)`.
 
-import macros
+import std/macros
 
 proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
 proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim"
diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim
index a8f0e1ba7..beb65ed30 100644
--- a/lib/std/enumerate.nim
+++ b/lib/std/enumerate.nim
@@ -11,7 +11,7 @@
 ## macro system.
 
 import std/private/since
-import macros
+import std/macros
 
 
 macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} =
@@ -21,7 +21,7 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} =
   ## The default starting count `0` can be manually overridden if needed.
   runnableExamples:
     let a = [10, 20, 30]
-    var b: seq[(int, int)]
+    var b: seq[(int, int)] = @[]
     for i, x in enumerate(a):
       b.add((i, x))
     assert b == @[(0, 10), (1, 20), (2, 30)]
diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim
index 9d4ff1bcf..9c338817d 100644
--- a/lib/std/enumutils.nim
+++ b/lib/std/enumutils.nim
@@ -7,8 +7,8 @@
 #    distribution, for details about the copyright.
 #
 
-import macros
-from typetraits import OrdinalEnum, HoleyEnum
+import std/macros
+from std/typetraits import OrdinalEnum, HoleyEnum
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -21,32 +21,34 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
   # Generates a case stmt, which assigns the correct enum field given
   # a normalized string comparison to the `argSym` input.
   # string normalization is done using passed normalizer.
-  # NOTE: for an enum with fields Foo, Bar, ... we cannot generate
-  # `of "Foo".nimIdentNormalize: Foo`.
-  # This will fail, if the enum is not defined at top level (e.g. in a block).
-  # Thus we check for the field value of the (possible holed enum) and convert
-  # the integer value to the generic argument `typ`.
   let typ = typ.getTypeInst[1]
-  let impl = typ.getImpl[2]
+  let typSym = typ.getTypeImpl.getTypeInst # skip aliases etc to get type sym
+  let impl = typSym.getImpl[2]
   expectKind impl, nnkEnumTy
   let normalizerNode = quote: `normalizer`
   expectKind normalizerNode, nnkSym
   result = nnkCaseStmt.newTree(newCall(normalizerNode, argSym))
   # stores all processed field strings to give error msg for ambiguous enums
   var foundFields: seq[string] = @[]
+  var fVal = ""
   var fStr = "" # string of current field
   var fNum = BiggestInt(0) # int value of current field
   for f in impl:
     case f.kind
     of nnkEmpty: continue # skip first node of `enumTy`
-    of nnkSym, nnkIdent: fStr = f.strVal
+    of nnkSym, nnkIdent:
+      fVal = f.strVal
+      fStr = fVal
     of nnkAccQuoted:
-      fStr = ""
+      fVal = ""
       for ch in f:
-        fStr.add ch.strVal
+        fVal.add ch.strVal
+      fStr = fVal
     of nnkEnumFieldDef:
+      fVal = f[0].strVal
       case f[1].kind
-      of nnkStrLit: fStr = f[1].strVal
+      of nnkStrLit:
+        fStr = f[1].strVal
       of nnkTupleConstr:
         fStr = f[1][1].strVal
         fNum = f[1][0].intVal
@@ -64,7 +66,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
     if fNum >= userMin and fNum <= userMax:
       fStr = normalizer(fStr)
       if fStr notin foundFields:
-        result.add nnkOfBranch.newTree(newLit fStr,  nnkCall.newTree(typ, newLit fNum))
+        result.add nnkOfBranch.newTree(newLit fStr, newDotExpr(typ, ident fVal))
         foundFields.add fStr
       else:
         error("Ambiguous enums cannot be parsed, field " & $fStr &
@@ -80,7 +82,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed,
     result.add nnkElse.newTree(default)
 
 macro enumFullRange(a: typed): untyped =
-  newNimNode(nnkCurly).add(a.getType[1][1..^1])
+  newNimNode(nnkBracket).add(a.getType[1][1..^1])
 
 macro enumNames(a: typed): untyped =
   # this could be exported too; in particular this could be useful for enum with holes.
@@ -112,9 +114,9 @@ const invalidSlot = uint8.high
 
 proc genLookup[T: typedesc[HoleyEnum]](_: T): auto =
   const n = span(T)
-  var ret: array[n, uint8]
   var i = 0
   assert n <= invalidSlot.int
+  var ret {.noinit.}: array[n, uint8]
   for ai in mitems(ret): ai = invalidSlot
   for ai in items(T):
     ret[ai.ord - T.low.ord] = uint8(i)
@@ -172,6 +174,9 @@ template symbolRank*[T: enum](a: T): int =
   when T is Ordinal: ord(a) - T.low.ord.static
   else: symbolRankImpl(a)
 
+proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".}
+  # skip one level of range; return the base type of a range type
+
 func symbolName*[T: enum](a: T): string =
   ## Returns the symbol name of an enum.
   ##
@@ -190,5 +195,8 @@ func symbolName*[T: enum](a: T): string =
       c1 = 4
       c2 = 20
     assert c1.symbolName == "c1"
-  const names = enumNames(T)
+  when T is range:
+    const names = enumNames(rangeBase T)
+  else:
+    const names = enumNames(T)
   names[a.symbolRank]
diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim
index d7706c17d..a955077ea 100644
--- a/lib/std/envvars.nim
+++ b/lib/std/envvars.nim
@@ -8,7 +8,7 @@
 #
 
 
-## The `std/envvars` module implements environment variables handling.
+## The `std/envvars` module implements environment variable handling.
 import std/oserrors
 
 type
@@ -60,10 +60,16 @@ when not defined(nimscript):
     when defined(windows):
       proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "<stdlib.h>".}
       from std/private/win_setenv import setEnvImpl
-      import winlean
-      proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv",
+      import std/winlean
+      when defined(nimPreviewSlimSystem):
+        import std/widestrs
+
+      type wchar_t {.importc: "wchar_t", header: "<stdlib.h>".} = int16
+      proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv",
           header: "<stdlib.h>".}
-      proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString)
+      proc getEnvImpl(env: cstring): WideCString =
+        let r: WideCString = env.newWideCString
+        cast[WideCString](c_wgetenv(cast[ptr wchar_t](r)))
     else:
       proc c_getenv(env: cstring): cstring {.
         importc: "getenv", header: "<stdlib.h>".}
@@ -88,8 +94,10 @@ when not defined(nimscript):
         assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist"
 
       let env = getEnvImpl(key)
-      if env == nil: return default
-      result = $env
+      if env == nil:
+        result = default
+      else:
+        result = $env
 
     proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} =
       ## Checks whether the environment variable named `key` exists.
@@ -103,7 +111,7 @@ when not defined(nimscript):
       runnableExamples:
         assert not existsEnv("unknownEnv")
 
-      return getEnvImpl(key) != nil
+      result = getEnvImpl(key) != nil
 
     proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} =
       ## Sets the value of the `environment variable`:idx: named `key` to `val`.
@@ -134,7 +142,7 @@ when not defined(nimscript):
       ## * `envPairs iterator`_
       template bail = raiseOSError(osLastError(), key)
       when defined(windows):
-        #[ 
+        #[
         # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160
         > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string
         note that nil is not legal
@@ -171,20 +179,17 @@ when not defined(nimscript):
 
     iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} =
       when defined(windows):
-        block:
-          template impl(get_fun, typ, size, zero, free_fun) =
-            let env = get_fun()
-            var e = env
-            if e == nil: break
-            while true:
-              let eend = strEnd(e)
-              let kv = $e
-              let p = find(kv, '=')
-              yield (substr(kv, 0, p-1), substr(kv, p+1))
-              e = cast[typ](cast[ByteAddress](eend)+size)
-              if typeof(zero)(eend[1]) == zero: break
-            discard free_fun(env)
-          impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW)
+        let env = getEnvironmentStringsW()
+        var e = env
+        if e != nil:
+          while true:
+            let eend = strEnd(e)
+            let kv = $e
+            let p = find(kv, '=')
+            yield (substr(kv, 0, p-1), substr(kv, p+1))
+            e = cast[WideCString](cast[ByteAddress](eend)+2)
+            if int(eend[1]) == 0: break
+          discard freeEnvironmentStringsW(env)
       else:
         var i = 0
         when defined(macosx) and not defined(ios) and not defined(emscripten):
diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim
index c6537f7f8..f26368f42 100644
--- a/lib/std/exitprocs.nim
+++ b/lib/std/exitprocs.nim
@@ -7,7 +7,11 @@
 #    distribution, for details about the copyright.
 #
 
-import locks
+## This module allows adding hooks to program exit.
+
+import std/locks
+when defined(js) and not defined(nodejs):
+  import std/assertions
 
 type
   FunKind = enum kClosure, kNoconv # extend as needed
@@ -18,20 +22,20 @@ type
 
 var
   gFunsLock: Lock
-  gFuns: seq[Fun]
+  gFuns {.cursor.}: seq[Fun] #Intentionally use the cursor to break up the lifetime trace and make it compatible with JS.
 
 initLock(gFunsLock)
 
 when defined(js):
   proc addAtExit(quitProc: proc() {.noconv.}) =
     when defined(nodejs):
-      asm """
+      {.emit: """
         process.on('exit', `quitProc`);
-      """
+      """.}
     elif defined(js):
-      asm """
+      {.emit: """
         window.onbeforeunload = `quitProc`;
-      """
+      """.}
 else:
   proc addAtExit(quitProc: proc() {.noconv.}) {.
     importc: "atexit", header: "<stdlib.h>".}
@@ -43,6 +47,7 @@ proc callClosures() {.noconv.} =
       case fun.kind
       of kClosure: fun.fun1()
       of kNoconv: fun.fun2()
+    gFuns.setLen(0)
 
 template fun() =
   if gFuns.len == 0:
@@ -64,24 +69,19 @@ proc addExitProc*(cl: proc() {.noconv.}) =
     fun()
     gFuns.add Fun(kind: kNoconv, fun2: cl)
 
-when not defined(nimscript):
+when not defined(nimscript) and (not defined(js) or defined(nodejs)):
   proc getProgramResult*(): int =
     when defined(js) and defined(nodejs):
-      asm """
+      {.emit: """
 `result` = process.exitCode;
-"""
-    elif not defined(js):
-      result = programResult
+""".}
     else:
-      doAssert false
+      result = programResult
 
   proc setProgramResult*(a: int) =
-    # pending https://github.com/nim-lang/Nim/issues/14674
     when defined(js) and defined(nodejs):
-      asm """
+      {.emit: """
 process.exitCode = `a`;
-"""
-    elif not defined(js):
-      programResult = a
+""".}
     else:
-      doAssert false
+      programResult = a
diff --git a/lib/std/files.nim b/lib/std/files.nim
new file mode 100644
index 000000000..c4e0491c9
--- /dev/null
+++ b/lib/std/files.nim
@@ -0,0 +1,46 @@
+## This module implements file handling.
+##
+## **See also:**
+## * `paths module <paths.html>`_ for path manipulation
+
+from std/paths import Path, ReadDirEffect, WriteDirEffect
+
+from std/private/osfiles import fileExists, removeFile,
+                                moveFile
+
+
+proc fileExists*(filename: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  result = fileExists(filename.string)
+
+proc removeFile*(file: Path) {.inline, tags: [WriteDirEffect].} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, `OSError` is raised. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `removeDir proc <dirs.html#removeDir>`_
+  ## * `moveFile proc`_
+  removeFile(file.string)
+
+proc moveFile*(source, dest: Path) {.inline,
+    tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect].} =
+  ## Moves a file from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` is a symlink, it is itself moved,
+  ## not its target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ## If `dest` already exists, it will be overwritten.
+  ##
+  ## Can be used to `rename files`:idx:.
+  ##
+  ## See also:
+  ## * `moveDir proc <dirs.html#moveDir>`_
+  ## * `removeFile proc`_
+  moveFile(source.string, dest.string)
diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim
index 6f2383760..9258245f6 100644
--- a/lib/std/formatfloat.nim
+++ b/lib/std/formatfloat.nim
@@ -7,10 +7,10 @@
 #    distribution, for details about the copyright.
 #
 
+## This module implements formatting floats as strings.
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
-else:
-  {.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".}
 
 proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "<string.h>", discardable.}
 
@@ -28,15 +28,15 @@ proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat)
   ##
   ## returns the amount of bytes written to `buf` not counting the
   ## terminating '\0' character.
-  result = toChars(buf, value, forceTrailingDotZero=true)
+  result = toChars(buf, value, forceTrailingDotZero=true).int
   buf[result] = '\0'
 
 proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int =
-  result = float32ToChars(buf, value, forceTrailingDotZero=true)
+  result = float32ToChars(buf, value, forceTrailingDotZero=true).int
   buf[result] = '\0'
 
-proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
-                                    importc: "sprintf", varargs, noSideEffect.}
+proc c_snprintf(buf: cstring, n: csize_t, frmt: cstring): cint {.header: "<stdio.h>",
+                                    importc: "snprintf", varargs, noSideEffect.}
 
 proc writeToBuffer(buf: var array[65, char]; value: cstring) =
   var i = 0
@@ -49,7 +49,7 @@ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat):
   ##
   ## returns the amount of bytes written to `buf` not counting the
   ## terminating '\0' character.
-  var n: int = c_sprintf(addr buf, "%.16g", value)
+  var n = c_snprintf(cast[cstring](addr buf), 65, "%.16g", value).int
   var hasDot = false
   for i in 0..n-1:
     if buf[i] == ',':
@@ -86,36 +86,37 @@ proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32
 
 proc addFloatRoundtrip*(result: var string; x: float | float32) =
   when nimvm:
-    doAssert false
+    raiseAssert "unreachable"
   else:
     var buffer {.noinit.}: array[65, char]
     let n = writeFloatToBufferRoundtrip(buffer, x)
-    result.addCstringN(cstring(buffer[0].addr), n)
+    result.addCstringN(cast[cstring](buffer[0].addr), n)
 
 proc addFloatSprintf*(result: var string; x: float) =
   when nimvm:
-    doAssert false
+    raiseAssert "unreachable"
   else:
     var buffer {.noinit.}: array[65, char]
     let n = writeFloatToBufferSprintf(buffer, x)
-    result.addCstringN(cstring(buffer[0].addr), n)
-
-proc nimFloatToString(a: float): cstring =
-  ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
-  # print `-0.0` properly
-  asm """
-    function nimOnlyDigitsOrMinus(n) {
-      return n.toString().match(/^-?\d+$/);
-    }
-    if (Number.isSafeInteger(`a`))
-      `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0"
-    else {
-      `result` = `a`+""
-      if(nimOnlyDigitsOrMinus(`result`)){
-        `result` = `a`+".0"
+    result.addCstringN(cast[cstring](buffer[0].addr), n)
+
+when defined(js):
+  proc nimFloatToString(a: float): cstring =
+    ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2
+    # print `-0.0` properly
+    {.emit: """
+      function nimOnlyDigitsOrMinus(n) {
+        return n.toString().match(/^-?\d+$/);
+      }
+      if (Number.isSafeInteger(`a`))
+        `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0";
+      else {
+        `result` = `a`+"";
+        if(nimOnlyDigitsOrMinus(`result`)){
+          `result` = `a`+".0";
+        }
       }
-    }
-  """
+    """.}
 
 proc addFloat*(result: var string; x: float | float32) {.inline.} =
   ## Converts float to its string representation and appends it to `result`.
diff --git a/lib/std/genasts.nim b/lib/std/genasts.nim
index c5f51e5d9..d0f07c527 100644
--- a/lib/std/genasts.nim
+++ b/lib/std/genasts.nim
@@ -1,4 +1,6 @@
-import macros
+## This module implements AST generation using captured variables for macros.
+
+import std/macros
 
 type GenAstOpt* = enum
   kDirtyTemplate,
@@ -22,7 +24,7 @@ macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untype
       result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]):
         # each local symbol we access must be explicitly captured
         if not cond:
-          doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
+          raiseAssert "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs]
     let a = 3
     check2 a*2 == a+3
     if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4'
diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim
index 7d6ac6092..b03e00651 100644
--- a/lib/std/isolation.nim
+++ b/lib/std/isolation.nim
@@ -15,7 +15,7 @@
 ##
 
 type
-  Isolated*[T] = object ## Isolated data can only be moved, not copied.
+  Isolated*[T] {.sendable.} = object ## Isolated data can only be moved, not copied.
     value: T
 
 proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.}
@@ -38,9 +38,9 @@ func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} =
 
 func unsafeIsolate*[T](value: sink T): Isolated[T] =
   ## Creates an isolated subgraph from the expression `value`.
-  ## 
+  ##
   ## .. warning:: The proc doesn't check whether `value` is isolated.
-  ## 
+  ##
   Isolated[T](value: value)
 
 func extract*[T](src: var Isolated[T]): T =
diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim
index 04578fc87..4e996ea7b 100644
--- a/lib/std/jsbigints.nim
+++ b/lib/std/jsbigints.nim
@@ -14,7 +14,7 @@ func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} =
   runnableExamples:
     doAssert big(1234567890) == big"1234567890"
     doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big
-  when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
 
 func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} =
   ## Constructor for `JsBigInt`.
@@ -28,11 +28,11 @@ func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} =
     doAssert 0xdeadbeaf'big == 0xdeadbeaf.big
     doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big
     doAssert not compiles(static(12'big))
-  when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
 
 func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} =
   ## Alias for `'big`
-  when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard
+  when nimvm: raiseAssert "JsBigInt can not be used at compile-time nor static context" else: discard
 
 func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} =
   ## Converts from `JsBigInt` to `cstring` representation.
@@ -64,10 +64,10 @@ func wrapToUint*(this: JsBigInt; bits: Natural): JsBigInt {.importjs:
   runnableExamples:
     doAssert (big("3") + big("2") ** big("66")).wrapToUint(66) == big("3")
 
-func toNumber*(this: JsBigInt): BiggestInt {.importjs: "Number(#)".} =
+func toNumber*(this: JsBigInt): int {.importjs: "Number(#)".} =
   ## Does not do any bounds check and may or may not return an inexact representation.
   runnableExamples:
-    doAssert toNumber(big"2147483647") == 2147483647.BiggestInt
+    doAssert toNumber(big"2147483647") == 2147483647.int
 
 func `+`*(x, y: JsBigInt): JsBigInt {.importjs: "(# $1 #)".} =
   runnableExamples:
@@ -110,7 +110,7 @@ func `==`*(x, y: JsBigInt): bool {.importjs: "(# == #)".} =
     doAssert big"42" == big"42"
 
 func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
-  # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812
+  # (#) needed due to unary minus
   runnableExamples:
     doAssert big"2" ** big"64" == big"18446744073709551616"
     doAssert big"-2" ** big"3" == big"-8"
@@ -120,8 +120,6 @@ func `**`*(x, y: JsBigInt): JsBigInt {.importjs: "((#) $1 #)".} =
     try: discard big"2" ** big"-1" # raises foreign `RangeError`
     except: ok = true
     doAssert ok
-  # pending https://github.com/nim-lang/Nim/pull/15940, simplify to:
-  # doAssertRaises: discard big"2" ** big"-1" # raises foreign `RangeError`
 
 func `and`*(x, y: JsBigInt): JsBigInt {.importjs: "(# & #)".} =
   runnableExamples:
diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim
index 7fe154325..219594619 100644
--- a/lib/std/jsfetch.nim
+++ b/lib/std/jsfetch.nim
@@ -2,7 +2,8 @@
 when not defined(js):
   {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".}
 
-import std/[asyncjs, jsheaders, jsformdata]
+import std/[asyncjs, jsformdata, jsheaders]
+export jsformdata, jsheaders
 from std/httpcore import HttpMethod
 from std/jsffi import JsObject
 
@@ -84,9 +85,9 @@ proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolic
     "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #, headers: #}".}
   ## .. warning:: Unsafe `newfetchOptions`.
 
-func newfetchOptions*(metod: HttpMethod; body: cstring;
-    mode: FetchModes; credentials: FetchCredentials; cache: FetchCaches; referrerPolicy: FetchReferrerPolicies;
-    keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring,
+func newfetchOptions*(metod = HttpGet; body: cstring = nil;
+    mode = fmCors; credentials = fcSameOrigin; cache = fchDefault; referrerPolicy = frpNoReferrerWhenDowngrade;
+    keepalive = false; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring,
     headers: Headers = newHeaders()): FetchOptions =
   ## Constructor for `FetchOptions`.
   result = FetchOptions(
@@ -116,7 +117,7 @@ func `$`*(self: Request | Response | FetchOptions): string = $toCstring(self)
 
 
 runnableExamples("-r:off"):
-  import std/[asyncjs, jsconsole, jsheaders, jsformdata]
+  import std/[asyncjs, jsconsole, jsformdata, jsheaders]
   from std/httpcore import HttpMethod
   from std/jsffi import JsObject
   from std/sugar import `=>`
diff --git a/lib/std/jsformdata.nim b/lib/std/jsformdata.nim
index 120f8742d..61dcc39a3 100644
--- a/lib/std/jsformdata.nim
+++ b/lib/std/jsformdata.nim
@@ -2,17 +2,21 @@
 when not defined(js):
   {.fatal: "Module jsformdata is designed to be used with the JavaScript backend.".}
 
+from std/dom import Blob
+
 type FormData* = ref object of JsRoot ## FormData API.
 
 func newFormData*(): FormData {.importjs: "new FormData()".}
 
-func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring) {.importjs: "#.append(#, #)".}
+func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.append(#, #)".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
-  ## Duplicate keys are allowed and order is preserved.
+  ##
+  ## .. hint:: Duplicate keys are allowed and order is preserved.
 
-func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring, filename: cstring) {.importjs: "#.append(#, #, #)".}
+func add*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.append(#, #, #)".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/append
-  ## Duplicate keys are allowed and order is preserved.
+  ##
+  ## .. hint:: Duplicate keys are allowed and order is preserved.
 
 func delete*(self: FormData; name: cstring) {.importjs: "#.$1(#)".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/delete
@@ -34,10 +38,10 @@ func values*(self: FormData): seq[cstring] {.importjs: "Array.from(#.$1())".}
 func pairs*(self: FormData): seq[tuple[key, val: cstring]] {.importjs: "Array.from(#.entries())".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/entries
 
-func put*(self: FormData; name, value, filename: cstring) {.importjs: "#.set(#, #, #)".}
+func put*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob; filename: cstring) {.importjs: "#.set(#, #, #)".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set
 
-func `[]=`*(self: FormData; name, value: cstring) {.importjs: "#.set(#, #)".}
+func `[]=`*(self: FormData; name: cstring; value: SomeNumber | bool | cstring | Blob) {.importjs: "#.set(#, #)".}
   ## https://developer.mozilla.org/en-US/docs/Web/API/FormData/set
 
 func `[]`*(self: FormData; name: cstring): cstring {.importjs: "#.get(#)".}
diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim
index 722ea49b5..2d28748ce 100644
--- a/lib/std/jsonutils.nim
+++ b/lib/std/jsonutils.nim
@@ -1,5 +1,5 @@
 ##[
-This module implements a hookable (de)serialization for arbitrary types.
+This module implements a hookable (de)serialization for arbitrary types using JSON.
 Design goal: avoid importing modules where a custom serialization is needed;
 see strtabs.fromJsonHook,toJsonHook for an example.
 ]##
@@ -16,7 +16,7 @@ runnableExamples:
   assert 0.0.toJson.kind == JFloat
   assert Inf.toJson.kind == JString
 
-import json, strutils, tables, sets, strtabs, options
+import std/[json, strutils, tables, sets, strtabs, options, strformat]
 
 #[
 Future directions:
@@ -30,29 +30,15 @@ add a way to customize serialization, for e.g.:
   objects.
 ]#
 
-import macros
-from enumutils import symbolName
-from typetraits import OrdinalEnum
+import std/macros
+from std/enumutils import symbolName
+from std/typetraits import OrdinalEnum, tupleLen
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-when not defined(nimFixedForwardGeneric):
-  # xxx remove pending csources_v1 update >= 1.2.0
-  proc to[T](node: JsonNode, t: typedesc[T]): T =
-    when T is string: node.getStr
-    elif T is bool: node.getBool
-    else: static: doAssert false, $T # support as needed (only needed during bootstrap)
-  proc isNamedTuple(T: typedesc): bool = # old implementation
-    when T isnot tuple: result = false
-    else:
-      var t: T
-      for name, _ in t.fieldPairs:
-        when name == "Field0": return compiles(t.Field0)
-        else: return true
-      return false
-else:
-  proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
+
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
 
 type
   Joptions* = object # xxx rename FromJsonOptions
@@ -92,19 +78,24 @@ macro getDiscriminants(a: typedesc): seq[string] =
   let sym = a[1]
   let t = sym.getTypeImpl
   let t2 = t[2]
-  doAssert t2.kind == nnkRecList
-  result = newTree(nnkBracket)
-  for ti in t2:
-    if ti.kind == nnkRecCase:
-      let key = ti[0][0]
-      let typ = ti[0][1]
-      result.add newLit key.strVal
-  if result.len > 0:
+  case t2.kind
+  of nnkEmpty: # allow empty objects
     result = quote do:
-      @`result`
+        seq[string].default
+  of nnkRecList:
+    result = newTree(nnkBracket)
+    for ti in t2:
+      if ti.kind == nnkRecCase:
+        let key = ti[0][0]
+        result.add newLit key.strVal
+    if result.len > 0:
+      result = quote do:
+        @`result`
+    else:
+      result = quote do:
+        seq[string].default
   else:
-    result = quote do:
-      seq[string].default
+    raiseAssert "unexpected kind: " & $t2.kind
 
 macro initCaseObject(T: typedesc, fun: untyped): untyped =
   ## does the minimum to construct a valid case object, only initializing
@@ -118,7 +109,7 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped =
   case t.kind
   of nnkObjectTy: t2 = t[2]
   of nnkRefTy: t2 = t[0].getTypeImpl[2]
-  else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too
+  else: raiseAssert $t.kind # xxx `nnkPtrTy` could be handled too
   doAssert t2.kind == nnkRecList
   result = newTree(nnkObjConstr)
   result.add sym
@@ -168,7 +159,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) =
           if discKeys.len == 0 or hasField(oldObj, key):
             val = accessField(oldObj, key)
       else:
-        checkJson false, $($T, key, json)
+        checkJson false, "key '$1' for $2 not in $3" % [key, $T, json.pretty()]
     else:
       if json.hasKey key:
         numMatched.inc
@@ -187,7 +178,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) =
     else:
       json.len == num and num == numMatched
 
-  checkJson ok, $(json.len, num, numMatched, $T, json)
+  checkJson ok, "There were $1 keys (expecting $2) for $3 with $4" % [$json.len, $num, $T, json.pretty()]
 
 proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions())
 
@@ -220,28 +211,24 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
   adding "json path" leading to `b` can be added in future work.
   ]#
   checkJson b != nil, $($T, b)
-  when compiles(fromJsonHook(a, b)): fromJsonHook(a, b)
+  when compiles(fromJsonHook(a, b, opt)): fromJsonHook(a, b, opt)
+  elif compiles(fromJsonHook(a, b)): fromJsonHook(a, b)
   elif T is bool: a = to(b,T)
   elif T is enum:
     case b.kind
     of JInt: a = T(b.getBiggestInt())
     of JString: a = parseEnum[T](b.getStr())
-    else: checkJson false, $($T, " ", b)
+    else: checkJson false, fmt"Expecting int/string for {$T} got {b.pretty()}"
   elif T is uint|uint64: a = T(to(b, uint64))
   elif T is Ordinal: a = cast[T](to(b, int))
   elif T is pointer: a = cast[pointer](to(b, int))
-  elif T is distinct:
-    when nimvm:
-      # bug, potentially related to https://github.com/nim-lang/Nim/issues/12282
-      a = T(jsonTo(b, distinctBase(T)))
-    else:
-      a.distinctBase.fromJson(b)
+  elif T is distinct: a.distinctBase.fromJson(b)
   elif T is string|SomeNumber: a = to(b,T)
   elif T is cstring:
     case b.kind
     of JNull: a = nil
     of JString: a = b.str
-    else: checkJson false, $($T, " ", b)
+    else: checkJson false, fmt"Expecting null/string for {$T} got {b.pretty()}"
   elif T is JsonNode: a = b
   elif T is ref | ptr:
     if b.kind == JNull: a = nil
@@ -249,7 +236,7 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
       a = T()
       fromJson(a[], b, opt)
   elif T is array:
-    checkJson a.len == b.len, $(a.len, b.len, $T)
+    checkJson a.len == b.len, fmt"Json array size doesn't match for {$T}"
     var i = 0
     for ai in mitems(a):
       fromJson(ai, b[i], opt)
@@ -285,14 +272,24 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) =
       fromJsonFields(a, nil, b, seq[string].default, opt)
     else:
       checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull
+
+      when compiles(tupleLen(T)):
+        let tupleSize = tupleLen(T)
+      else:
+        # Tuple len isn't in csources_v1 so using tupleLen would fail.
+        # Else branch basically never runs (tupleLen added in 1.1 and jsonutils in 1.4), but here for consistency
+        var tupleSize = 0
+        for val in fields(a):
+          tupleSize.inc
+
+      checkJson b.len == tupleSize, fmt"Json doesn't match expected length of {tupleSize}, got {b.pretty()}"
       var i = 0
       for val in fields(a):
         fromJson(val, b[i], opt)
         i.inc
-      checkJson b.len == i, $(b.len, i, $T, b) # could customize
   else:
     # checkJson not appropriate here
-    static: doAssert false, "not yet implemented: " & $T
+    static: raiseAssert "not yet implemented: " & $T
 
 proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T =
   ## reverse of `toJson`
@@ -305,7 +302,8 @@ proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode =
   ## .. note:: With `-d:nimPreviewJsonutilsHoleyEnum`, `toJson` now can 
   ##    serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings.
   ##    It is expected that this behavior becomes the new default in upcoming versions.
-  when compiles(toJsonHook(a)): result = toJsonHook(a)
+  when compiles(toJsonHook(a, opt)): result = toJsonHook(a, opt)
+  elif compiles(toJsonHook(a)): result = toJsonHook(a)
   elif T is object | tuple:
     when T is object or isNamedTuple(T):
       result = newJObject()
@@ -348,7 +346,7 @@ proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode =
   else: result = %a
 
 proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V]),
-                         jsonNode: JsonNode) =
+                         jsonNode: JsonNode, opt = Joptions()) =
   ## Enables `fromJson` for `Table` and `OrderedTable` types.
   ##
   ## See also:
@@ -366,14 +364,13 @@ proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V
           "type is `" & $jsonNode.kind & "`."
   clear(t)
   for k, v in jsonNode:
-    t[k] = jsonTo(v, V)
+    t[k] = jsonTo(v, V, opt)
 
-proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode =
+proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V]), opt = initToJsonOptions()): JsonNode =
   ## Enables `toJson` for `Table` and `OrderedTable` types.
   ##
   ## See also:
   ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_
-  # pending PR #9217 use: toSeq(a) instead of `collect` in `runnableExamples`.
   runnableExamples:
     import std/[tables, json, sugar]
     let foo = (
@@ -388,9 +385,9 @@ proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V])): J
   result = newJObject()
   for k, v in pairs(t):
     # not sure if $k has overhead for string
-    result[(when K is string: k else: $k)] = toJson(v)
+    result[(when K is string: k else: $k)] = toJson(v, opt)
 
-proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) =
+proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode, opt = Joptions()) =
   ## Enables `fromJson` for `HashSet` and `OrderedSet` types.
   ##
   ## See also:
@@ -408,9 +405,9 @@ proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) =
           "type is `" & $jsonNode.kind & "`."
   clear(s)
   for v in jsonNode:
-    incl(s, jsonTo(v, A))
+    incl(s, jsonTo(v, A, opt))
 
-proc toJsonHook*[A](s: SomeSet[A]): JsonNode =
+proc toJsonHook*[A](s: SomeSet[A], opt = initToJsonOptions()): JsonNode =
   ## Enables `toJson` for `HashSet` and `OrderedSet` types.
   ##
   ## See also:
@@ -422,9 +419,9 @@ proc toJsonHook*[A](s: SomeSet[A]): JsonNode =
 
   result = newJArray()
   for k in s:
-    add(result, toJson(k))
+    add(result, toJson(k, opt))
 
-proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode) =
+proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode, opt = Joptions()) =
   ## Enables `fromJson` for `Option` types.
   ##
   ## See also:
@@ -438,11 +435,11 @@ proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode) =
     assert isNone(opt)
 
   if jsonNode.kind != JNull:
-    self = some(jsonTo(jsonNode, T))
+    self = some(jsonTo(jsonNode, T, opt))
   else:
     self = none[T]()
 
-proc toJsonHook*[T](self: Option[T]): JsonNode =
+proc toJsonHook*[T](self: Option[T], opt = initToJsonOptions()): JsonNode =
   ## Enables `toJson` for `Option` types.
   ##
   ## See also:
@@ -455,7 +452,7 @@ proc toJsonHook*[T](self: Option[T]): JsonNode =
     assert $toJson(optNone) == "null"
 
   if isSome(self):
-    toJson(get(self))
+    toJson(get(self), opt)
   else:
     newJNull()
 
diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim
index 5c67a5d4c..bf6dc776b 100644
--- a/lib/std/monotimes.nim
+++ b/lib/std/monotimes.nim
@@ -36,7 +36,7 @@ See also
 * `times module <times.html>`_
 ]##
 
-import times
+import std/times
 
 type
   MonoTime* = object ## Represents a monotonic timestamp.
@@ -74,7 +74,7 @@ when defined(js):
   {.pop.}
 
 elif defined(posix) and not defined(osx):
-  import posix
+  import std/posix
 
 when defined(zephyr):
   proc k_uptime_ticks(): int64 {.importc: "k_uptime_ticks", header: "<kernel.h>".}
diff --git a/lib/std/objectdollar.nim b/lib/std/objectdollar.nim
index f413bbc46..86ce9afc8 100644
--- a/lib/std/objectdollar.nim
+++ b/lib/std/objectdollar.nim
@@ -1,3 +1,5 @@
+## This module implements a generic `$` operator to convert objects to strings.
+
 import std/private/miscdollars
 
 proc `$`*[T: object](x: T): string =
diff --git a/lib/std/oserrors.nim b/lib/std/oserrors.nim
index 9c2649eab..7b11c5e8e 100644
--- a/lib/std/oserrors.nim
+++ b/lib/std/oserrors.nim
@@ -15,7 +15,9 @@ type
 
 when not defined(nimscript):
   when defined(windows):
-    import winlean
+    import std/winlean
+    when defined(nimPreviewSlimSystem):
+      import std/widestrs
   else:
     var errno {.importc, header: "<errno.h>".}: cint
 
@@ -73,17 +75,14 @@ proc newOSError*(
   ## See also:
   ## * `osErrorMsg proc`_
   ## * `osLastError proc`_
-  var e: owned(ref OSError); new(e)
-  e.errorCode = errorCode.int32
-  e.msg = osErrorMsg(errorCode)
+  result = (ref OSError)(errorCode: errorCode.int32, msg: osErrorMsg(errorCode))
   if additionalInfo.len > 0:
-    if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n'
-    e.msg.add "Additional info: "
-    e.msg.add additionalInfo
+    if result.msg.len > 0 and result.msg[^1] != '\n': result.msg.add '\n'
+    result.msg.add "Additional info: "
+    result.msg.add additionalInfo
       # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs.
-  if e.msg == "":
-    e.msg = "unknown OS error"
-  return e
+  if result.msg == "":
+    result.msg = "unknown OS error"
 
 proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} =
   ## Raises an `OSError exception <system.html#OSError>`_.
diff --git a/lib/std/outparams.nim b/lib/std/outparams.nim
new file mode 100644
index 000000000..a471fbaa7
--- /dev/null
+++ b/lib/std/outparams.nim
@@ -0,0 +1,38 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2022 Nim contributors
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## `outParamsAt` macro for easy writing code that works with both 2.0 and 1.x.
+
+import std/macros
+
+macro outParamsAt*(positions: static openArray[int]; n: untyped): untyped =
+  ## Use this macro to annotate `out` parameters in a portable way.
+  runnableExamples:
+    proc p(x: var int) {.outParamsAt: [1].} =
+      discard "x is really an 'out int' if the Nim compiler supports 'out' parameters"
+
+  result = n
+  when defined(nimHasOutParams):
+    var p = n.params
+    for po in positions:
+      p[po][^2].expectKind nnkVarTy
+      p[po][^2] = newTree(nnkOutTy, p[po][^2][0])
+
+when isMainModule:
+  {.experimental: "strictDefs".}
+
+  proc main(x: var int) {.outParamsAt: [1].} =
+    x = 3
+
+  proc us =
+    var x: int
+    main x
+    echo x
+
+  us()
diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim
index 1e2892658..3320558f2 100644
--- a/lib/std/packedsets.nim
+++ b/lib/std/packedsets.nim
@@ -12,17 +12,12 @@
 ##
 ## Supports any Ordinal type.
 ##
-## .. note:: Currently the assignment operator `=` for `PackedSet[A]`
-##   performs some rather meaningless shallow copy. Since Nim currently does
-##   not allow the assignment operator to be overloaded, use the `assign proc
-##   <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy.
-##
 ## See also
 ## ========
 ## * `sets module <sets.html>`_ for more general hash sets
 
 import std/private/since
-import hashes
+import std/hashes
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -114,7 +109,6 @@ proc intSetPut[A](t: var PackedSet[A], key: int): Trunk =
   t.data[h] = result
 
 proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} =
-  var ret: Trunk
   var t = intSetPut(s, key shr TrunkShift)
   var u = key and TrunkMask
   t.bits[u shr IntShift] = t.bits[u shr IntShift] or
@@ -204,6 +198,7 @@ proc contains*[A](s: PackedSet[A], key: A): bool =
     assert B notin letters
 
   if s.elems <= s.a.len:
+    result = false
     for i in 0..<s.elems:
       if s.a[i] == ord(key): return true
   else:
@@ -412,18 +407,9 @@ proc isNil*[A](x: PackedSet[A]): bool {.inline.} =
 
   x.head.isNil and x.elems == 0
 
-proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) =
+proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) =
   ## Copies `src` to `dest`.
   ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_.
-  runnableExamples:
-    var
-      a = initPackedSet[int]()
-      b = initPackedSet[int]()
-    b.incl(5)
-    b.incl(7)
-    a.assign(b)
-    assert len(a) == 2
-
   if src.elems <= src.a.len:
     dest.data = @[]
     dest.max = 0
@@ -452,6 +438,19 @@ proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) =
       dest.data[h] = n
       it = it.next
 
+proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) {.inline, deprecated.} =
+  ## Copies `src` to `dest`.
+  ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_.
+  runnableExamples:
+    var
+      a = initPackedSet[int]()
+      b = initPackedSet[int]()
+    b.incl(5)
+    b.incl(7)
+    a.assign(b)
+    assert len(a) == 2
+  `=copy`(dest, src)
+
 proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] =
   ## Returns the union of the sets `s1` and `s2`.
   ##
diff --git a/lib/std/paths.nim b/lib/std/paths.nim
new file mode 100644
index 000000000..664dedd31
--- /dev/null
+++ b/lib/std/paths.nim
@@ -0,0 +1,302 @@
+## This module implements path handling.
+##
+## **See also:**
+## * `files module <files.html>`_ for file access
+
+import std/private/osseps
+export osseps
+
+import std/envvars
+import std/private/osappdirs
+
+import std/[pathnorm, hashes, sugar, strutils]
+
+from std/private/ospaths2 import  joinPath, splitPath,
+                                  ReadDirEffect, WriteDirEffect,
+                                  isAbsolute, relativePath,
+                                  normalizePathEnd, isRelativeTo, parentDir,
+                                  tailDir, isRootDir, parentDirs, `/../`,
+                                  extractFilename, lastPathPart,
+                                  changeFileExt, addFileExt, cmpPaths, splitFile,
+                                  unixToNativePath, absolutePath, normalizeExe,
+                                  normalizePath
+export ReadDirEffect, WriteDirEffect
+
+type
+  Path* = distinct string
+
+func hash*(x: Path): Hash =
+  let x = x.string.dup(normalizePath)
+  if FileSystemCaseSensitive:
+    result = x.hash
+  else:
+    result = x.toLowerAscii.hash
+
+template `$`*(x: Path): string =
+  string(x)
+
+func `==`*(x, y: Path): bool {.inline.} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively.
+  result = cmpPaths(x.string, y.string) == 0
+
+template endsWith(a: string, b: set[char]): bool =
+  a.len > 0 and a[^1] in b
+
+func add(x: var string, tail: string) =
+  var state = 0
+  let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and x.endsWith({DirSep, AltSep})
+  normalizePathEnd(x, trailingSep=false)
+  addNormalizePath(tail, x, state, DirSep)
+  normalizePathEnd(x, trailingSep=trailingSep)
+
+func add*(x: var Path, y: Path) {.borrow.}
+
+func `/`*(head, tail: Path): Path {.inline.} =
+  ## Joins two directory names to one.
+  ##
+  ## returns normalized path concatenation of `head` and `tail`, preserving
+  ## whether or not `tail` has a trailing slash (or, if tail if empty, whether
+  ## head has one).
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  Path(joinPath(head.string, tail.string))
+
+func splitPath*(path: Path): tuple[head, tail: Path] {.inline.} =
+  ## Splits a directory into `(head, tail)` tuple, so that
+  ## ``head / tail == path`` (except for edge cases like "/usr").
+  ##
+  ## See also:
+  ## * `add proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `relativePath proc`_
+  let res = splitPath(path.string)
+  result = (Path(res.head), Path(res.tail))
+
+func splitFile*(path: Path): tuple[dir, name: Path, ext: string] {.inline.} =
+  ## Splits a filename into `(dir, name, extension)` tuple.
+  ##
+  ## `dir` does not end in DirSep unless it's `/`.
+  ## `extension` includes the leading dot.
+  ##
+  ## If `path` has no extension, `ext` is the empty string.
+  ## If `path` has no directory component, `dir` is the empty string.
+  ## If `path` has no filename component, `name` and `ext` are empty strings.
+  ##
+  ## See also:
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  let res = splitFile(path.string)
+  result = (Path(res.dir), Path(res.name), res.ext)
+
+func isAbsolute*(path: Path): bool {.inline, raises: [].} =
+  ## Checks whether a given `path` is absolute.
+  ##
+  ## On Windows, network paths are considered absolute too.
+  result = isAbsolute(path.string)
+
+proc relativePath*(path, base: Path, sep = DirSep): Path {.inline.} =
+  ## Converts `path` to a path relative to `base`.
+  ##
+  ## The `sep` (default: DirSep) is used for the path normalizations,
+  ## this can be useful to ensure the relative path only contains `'/'`
+  ## so that it can be used for URL constructions.
+  ##
+  ## On Windows, if a root of `path` and a root of `base` are different,
+  ## returns `path` as is because it is impossible to make a relative path.
+  ## That means an absolute path can be returned.
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  ## * `tailDir proc`_
+  result = Path(relativePath(path.string, base.string, sep))
+
+proc isRelativeTo*(path: Path, base: Path): bool {.inline.} =
+  ## Returns true if `path` is relative to `base`.
+  result = isRelativeTo(path.string, base.string)
+
+
+func parentDir*(path: Path): Path {.inline.} =
+  ## Returns the parent directory of `path`.
+  ##
+  ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end
+  ## in a dir separator, but also takes care of path normalizations.
+  ## The remainder can be obtained with `lastPathPart(path) proc`_.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `tailDir proc`_
+  ## * `parentDirs iterator`_
+  result = Path(parentDir(path.string))
+
+func tailDir*(path: Path): Path {.inline.} =
+  ## Returns the tail part of `path`.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  result = Path(tailDir(path.string))
+
+func isRootDir*(path: Path): bool {.inline.} =
+  ## Checks whether a given `path` is a root directory.
+  result = isRootDir(path.string)
+
+iterator parentDirs*(path: Path, fromRoot=false, inclusive=true): Path =
+  ## Walks over all parent directories of a given `path`.
+  ##
+  ## If `fromRoot` is true (default: false), the traversal will start from
+  ## the file system root directory.
+  ## If `inclusive` is true (default), the original argument will be included
+  ## in the traversal.
+  ##
+  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
+  ## only the directories appearing in the relative path.
+  ##
+  ## See also:
+  ## * `parentDir proc`_
+  ##
+  for p in parentDirs(path.string, fromRoot, inclusive):
+    yield Path(p)
+
+func `/../`*(head, tail: Path): Path {.inline.} =
+  ## The same as ``parentDir(head) / tail``, unless there is no parent
+  ## directory. Then ``head / tail`` is performed instead.
+  ##
+  ## See also:
+  ## * `/ proc`_
+  ## * `parentDir proc`_
+  Path(`/../`(head.string, tail.string))
+
+func extractFilename*(path: Path): Path {.inline.} =
+  ## Extracts the filename of a given `path`.
+  ##
+  ## This is the same as ``name & ext`` from `splitFile(path) proc`_.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  result = Path(extractFilename(path.string))
+
+func lastPathPart*(path: Path): Path {.inline.} =
+  ## Like `extractFilename proc`_, but ignores
+  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  result = Path(lastPathPart(path.string))
+
+func changeFileExt*(filename: Path, ext: string): Path {.inline.} =
+  ## Changes the file extension to `ext`.
+  ##
+  ## If the `filename` has no extension, `ext` will be added.
+  ## If `ext` == "" then any extension is removed.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character. (Although I know
+  ## of none such beast.)
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `addFileExt proc`_
+  result = Path(changeFileExt(filename.string, ext))
+
+func addFileExt*(filename: Path, ext: string): Path {.inline.} =
+  ## Adds the file extension `ext` to `filename`, unless
+  ## `filename` already has an extension.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character.
+  ## (Although I know of none such beast.)
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  result = Path(addFileExt(filename.string, ext))
+
+func unixToNativePath*(path: Path, drive=Path("")): Path {.inline.} =
+  ## Converts an UNIX-like path to a native one.
+  ##
+  ## On an UNIX system this does nothing. Else it converts
+  ## `'/'`, `'.'`, `'..'` to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+  result = Path(unixToNativePath(path.string, drive.string))
+
+proc getCurrentDir*(): Path {.inline, tags: [].} =
+  ## Returns the `current working directory`:idx: i.e. where the built
+  ## binary is run.
+  ##
+  ## So the path returned by this proc is determined at run time.
+  ##
+  ## See also:
+  ## * `getHomeDir proc <appdirs.html#getHomeDir>`_
+  ## * `getConfigDir proc <appdirs.html#getConfigDir>`_
+  ## * `getTempDir proc <appdirs.html#getTempDir>`_
+  ## * `setCurrentDir proc <dirs.html#setCurrentDir>`_
+  ## * `currentSourcePath template <system.html#currentSourcePath.t>`_
+  ## * `getProjectPath proc <macros.html#getProjectPath>`_
+  result = Path(ospaths2.getCurrentDir())
+
+proc normalizeExe*(file: var Path) {.borrow.}
+
+proc normalizePath*(path: var Path) {.borrow.}
+
+proc normalizePathEnd*(path: var Path, trailingSep = false) {.borrow.}
+
+proc absolutePath*(path: Path, root = getCurrentDir()): Path =
+  ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
+  ## default: current directory).
+  ## If `path` is absolute, return it, ignoring `root`.
+  ##
+  ## See also:
+  ## * `normalizePath proc`_
+  result = Path(absolutePath(path.string, root.string))
+
+proc expandTildeImpl(path: string): string {.
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  if len(path) == 0 or path[0] != '~':
+    result = path
+  elif len(path) == 1:
+    result = getHomeDir()
+  elif (path[1] in {DirSep, AltSep}):
+    result = joinPath(getHomeDir(), path.substr(2))
+  else:
+    # TODO: handle `~bob` and `~bob/` which means home of bob
+    result = path
+
+proc expandTilde*(path: Path): Path {.inline,
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
+  ## ``~`` with `getHomeDir() <appdirs.html#getHomeDir>`_ (otherwise returns ``path`` unmodified).
+  ##
+  ## Windows: this is still supported despite the Windows platform not having this
+  ## convention; also, both ``~/`` and ``~\`` are handled.
+  runnableExamples:
+    import std/appdirs
+    assert expandTilde(Path("~") / Path("appname.cfg")) == getHomeDir() / Path("appname.cfg")
+    assert expandTilde(Path("~/foo/bar")) == getHomeDir() / Path("foo/bar")
+    assert expandTilde(Path("/foo/bar")) == Path("/foo/bar")
+  result = Path(expandTildeImpl(path.string))
diff --git a/lib/std/private/dbutils.nim b/lib/std/private/dbutils.nim
deleted file mode 100644
index 0ae3b3702..000000000
--- a/lib/std/private/dbutils.nim
+++ /dev/null
@@ -1,15 +0,0 @@
-import db_common
-
-
-template dbFormatImpl*(formatstr: SqlQuery, dbQuote: proc (s: string): string, args: varargs[string]): string =
-  var res = ""
-  var a = 0
-  for c in items(string(formatstr)):
-    if c == '?':
-      if a == args.len:
-        dbError("""The number of "?" given exceeds the number of parameters present in the query.""")
-      add(res, dbQuote(args[a]))
-      inc(a)
-    else:
-      add(res, c)
-  res
diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim
index 588bcaec0..f2d0d25cb 100644
--- a/lib/std/private/digitsutils.nim
+++ b/lib/std/private/digitsutils.nim
@@ -19,7 +19,7 @@ const
 
 # Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c
 # Generates:
-# .. code-block:: nim
+#   ```nim
 #   var res = ""
 #   for i in 0 .. 99:
 #     if i < 10:
@@ -27,14 +27,15 @@ const
 #     else:
 #       res.add $i
 #   doAssert res == digits100
+#   ```
 
 proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} =
   buf[pos] = digits100[2 * digits]
   buf[pos+1] = digits100[2 * digits + 1]
   #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char)))
 
-proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} =
-  return trailingZeros100[digits]
+proc trailingZeros2Digits*(digits: uint32): int {.inline.} =
+  trailingZeros100[digits]
 
 when defined(js):
   proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".}
@@ -63,14 +64,14 @@ func addIntImpl(result: var string, x: uint64) {.inline.} =
   while num >= nbatch:
     let originNum = num
     num = num div nbatch
-    let index = (originNum - num * nbatch) shl 1
+    let index = int16((originNum - num * nbatch) shl 1)
     tmp[next] = digits100[index + 1]
     tmp[next - 1] = digits100[index]
     dec(next, 2)
 
   # process last 1-2 digits
   if num < 10:
-    tmp[next] = chr(ord('0') + num)
+    tmp[next] = chr(ord('0') + num.uint8)
   else:
     let index = num * 2
     tmp[next] = digits100[index + 1]
@@ -101,9 +102,7 @@ proc addInt*(result: var string; x: int64) {.enforceNoRaises.} =
         num = cast[uint64](x)
       else:
         num = uint64(-x)
-      let base = result.len
-      setLen(result, base + 1)
-      result[base] = '-'
+      result.add '-'
     else:
       num = uint64(x)
     addInt(result, num)
diff --git a/lib/std/private/dragonbox.nim b/lib/std/private/dragonbox.nim
index 23adff385..85ffea84a 100644
--- a/lib/std/private/dragonbox.nim
+++ b/lib/std/private/dragonbox.nim
@@ -75,10 +75,10 @@ const
 const
   signMask*: BitsType = not (not BitsType(0) shr 1)
 
-proc constructDouble*(bits: BitsType): Double {.constructor.} =
+proc constructDouble*(bits: BitsType): Double  =
   result.bits = bits
 
-proc constructDouble*(value: ValueType): Double {.constructor.} =
+proc constructDouble*(value: ValueType): Double  =
   result.bits = cast[typeof(result.bits)](value)
 
 proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} =
@@ -1052,7 +1052,7 @@ when false:
   proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.}
   proc memmove(a, b: cstring; L: int) {.importc, nodecl.}
 
-proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int32 {.inline.} =
+proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int {.inline.} =
   dragonbox_Assert(digits >= 1)
   dragonbox_Assert(digits <= 99999999'u32)
   let q: uint32 = digits div 10000
@@ -1070,12 +1070,12 @@ proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: u
     utoa2Digits(buf, pos + 6, rL)
     return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0)
 
-proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int32 {.inline.} =
+proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int {.inline.} =
   var pos = pos
   var output64 = output64
-  var tz: int32 = 0
+  var tz = 0
   ##  number of trailing zeros removed.
-  var nd: int32 = 0
+  var nd = 0
   ##  number of decimal digits processed.
   ##  At most 17 digits remaining
   if output64 >= 100000000'u64:
@@ -1146,7 +1146,7 @@ proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64:
     buf[pos] = chr(ord('0') + q)
   return tz
 
-proc decimalLength*(v: uint64): int32 {.inline.} =
+proc decimalLength*(v: uint64): int {.inline.} =
   dragonbox_Assert(v >= 1)
   dragonbox_Assert(v <= 99999999999999999'u64)
   if cast[uint32](v shr 32) != 0:
@@ -1166,48 +1166,48 @@ proc decimalLength*(v: uint64): int32 {.inline.} =
       return 11
     return 10
   let v32: uint32 = cast[uint32](v)
-  if v32 >= 1000000000'u:
+  if v32 >= 1000000000'u32:
     return 10
-  if v32 >= 100000000'u:
+  if v32 >= 100000000'u32:
     return 9
-  if v32 >= 10000000'u:
+  if v32 >= 10000000'u32:
     return 8
-  if v32 >= 1000000'u:
+  if v32 >= 1000000'u32:
     return 7
-  if v32 >= 100000'u:
+  if v32 >= 100000'u32:
     return 6
-  if v32 >= 10000'u:
+  if v32 >= 10000'u32:
     return 5
-  if v32 >= 1000'u:
+  if v32 >= 1000'u32:
     return 4
-  if v32 >= 100'u:
+  if v32 >= 100'u32:
     return 3
-  if v32 >= 10'u:
+  if v32 >= 10'u32:
     return 2
   return 1
 
-proc formatDigits*(buffer: var openArray[char]; pos: int; digits: uint64; decimalExponent: int32;
+proc formatDigits*[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint64; decimalExponent: int;
                   forceTrailingDotZero = false): int {.inline.} =
   const
-    minFixedDecimalPoint: int32 = -6
+    minFixedDecimalPoint = -6
   const
-    maxFixedDecimalPoint: int32 = 17
-  var pos = pos
+    maxFixedDecimalPoint = 17
+  var pos:int = pos.int
   assert(minFixedDecimalPoint <= -1, "internal error")
   assert(maxFixedDecimalPoint >= 17, "internal error")
   dragonbox_Assert(digits >= 1)
   dragonbox_Assert(digits <= 99999999999999999'u64)
   dragonbox_Assert(decimalExponent >= -999)
   dragonbox_Assert(decimalExponent <= 999)
-  var numDigits: int32 = decimalLength(digits)
-  let decimalPoint: int32 = numDigits + decimalExponent
+  var numDigits = decimalLength(digits)
+  let decimalPoint = numDigits + decimalExponent
   let useFixed: bool = minFixedDecimalPoint <= decimalPoint and
       decimalPoint <= maxFixedDecimalPoint
   ## Prepare the buffer.
   for i in 0..<32: buffer[pos+i] = '0'
   assert(minFixedDecimalPoint >= -30, "internal error")
   assert(maxFixedDecimalPoint <= 32, "internal error")
-  var decimalDigitsPosition: int32
+  var decimalDigitsPosition: int
   if useFixed:
     if decimalPoint <= 0:
       ##  0.[000]digits
@@ -1220,7 +1220,7 @@ proc formatDigits*(buffer: var openArray[char]; pos: int; digits: uint64; decima
     ##  dE+123 or d.igitsE+123
     decimalDigitsPosition = 1
   var digitsEnd = pos + int(decimalDigitsPosition + numDigits)
-  let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
+  let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
   dec(digitsEnd, tz)
   dec(numDigits, tz)
   ##   decimal_exponent += tz; // => decimal_point unchanged.
@@ -1258,7 +1258,7 @@ proc formatDigits*(buffer: var openArray[char]; pos: int; digits: uint64; decima
       ##  d.igitsE+123
       buffer[pos+1] = '.'
       pos = digitsEnd
-    let scientificExponent: int32 = decimalPoint - 1
+    let scientificExponent: int = decimalPoint - 1
     ##       SF_ASSERT(scientific_exponent != 0);
     buffer[pos] = 'e'
     buffer[pos+1] = if scientificExponent < 0: '-' else: '+'
@@ -1291,7 +1291,7 @@ proc toChars*(buffer: var openArray[char]; v: float; forceTrailingDotZero = fals
     if exponent != 0 or significand != 0:
       ##  != 0
       let dec = toDecimal64(significand, exponent)
-      return formatDigits(buffer, pos, dec.significand, dec.exponent,
+      return formatDigits(buffer, pos, dec.significand, dec.exponent.int,
                          forceTrailingDotZero)
     else:
       buffer[pos] = '0'
diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim
index 5bcd9e377..6dc9c8f3b 100644
--- a/lib/std/private/gitutils.nim
+++ b/lib/std/private/gitutils.nim
@@ -4,7 +4,10 @@ internal API for now, API subject to change
 
 # xxx move other git utilities here; candidate for stdlib.
 
-import std/[os, osproc, strutils, tempfiles]
+import std/[os, paths, osproc, strutils, tempfiles]
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
 
 const commitHead* = "HEAD"
 
@@ -29,15 +32,8 @@ template retryCall*(maxRetry = 3, backoffDuration = 1.0, call: untyped): bool =
   result
 
 proc isGitRepo*(dir: string): bool =
-  ## This command is used to get the relative path to the root of the repository.
-  ## Using this, we can verify whether a folder is a git repository by checking
-  ## whether the command success and if the output is empty.
-  let (output, status) = execCmdEx("git rev-parse --show-cdup", workingDir = dir)
-  # On Windows there will be a trailing newline on success, remove it.
-  # The value of a successful call typically won't have a whitespace (it's
-  # usually a series of ../), so we know that it's safe to unconditionally
-  # remove trailing whitespaces from the result.
-  result = status == 0 and output.strip() == ""
+  ## Avoid calling git since it depends on /bin/sh existing and fails in Nix.
+  return fileExists(dir/".git/HEAD")
 
 proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] =
   ## Returns a human readable diff of files `path1`, `path2`, the exact form of
diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim
index 28a810372..a6d088558 100644
--- a/lib/std/private/globs.nim
+++ b/lib/std/private/globs.nim
@@ -4,12 +4,12 @@ this can eventually be moved to std/os and `walkDirRec` can be implemented in te
 to avoid duplication
 ]##
 
-import os
+import std/os
 when defined(windows):
-  from strutils import replace
+  from std/strutils import replace
 
 when defined(nimPreviewSlimSystem):
-  import std/assertions
+  import std/[assertions, objectdollar]
 
 
 when defined(nimHasEffectsOf):
@@ -60,11 +60,11 @@ proc nativeToUnixPath*(path: string): string =
       result[0] = '/'
       result[1] = path[0]
       if path.len > 2 and path[2] != '\\':
-        doAssert false, "paths like `C:foo` are currently unsupported, path: " & path
+        raiseAssert "paths like `C:foo` are currently unsupported, path: " & path
   when DirSep == '\\':
     result = replace(result, '\\', '/')
 
 when isMainModule:
-  import sugar
+  import std/sugar
   for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", "csources_v1", "csources", "bin"]):
     echo a
diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim
index 836b3512a..5f79eab27 100644
--- a/lib/std/private/jsutils.nim
+++ b/lib/std/private/jsutils.nim
@@ -37,13 +37,13 @@ when defined(js):
       let a = array[2, float64].default
       assert jsConstructorName(a) == "Float64Array"
       assert jsConstructorName(a.toJs) == "Float64Array"
-    asm """`result` = `a`.constructor.name"""
+    {.emit: """`result` = `a`.constructor.name;""".}
 
   proc hasJsBigInt*(): bool =
-    asm """`result` = typeof BigInt != 'undefined'"""
+    {.emit: """`result` = typeof BigInt != 'undefined';""".}
 
   proc hasBigUint64Array*(): bool =
-    asm """`result` = typeof BigUint64Array != 'undefined'"""
+    {.emit: """`result` = typeof BigUint64Array != 'undefined';""".}
 
   proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} =
     runnableExamples:
@@ -79,5 +79,18 @@ when defined(js):
       assert not "123".toJs.isSafeInteger
       assert 123.isSafeInteger
       assert 123.toJs.isSafeInteger
-      assert 9007199254740991.toJs.isSafeInteger
-      assert not 9007199254740992.toJs.isSafeInteger
+      when false:
+        assert 9007199254740991.toJs.isSafeInteger
+        assert not 9007199254740992.toJs.isSafeInteger
+
+template whenJsNoBigInt64*(no64, yes64): untyped =
+  when defined(js):
+    when compiles(compileOption("jsbigint64")):
+      when compileOption("jsbigint64"):
+        yes64
+      else:
+        no64
+    else:
+      no64
+  else:
+    no64
diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim
index 47b788ee9..06fda6fa1 100644
--- a/lib/std/private/miscdollars.nim
+++ b/lib/std/private/miscdollars.nim
@@ -13,21 +13,7 @@ template toLocation*(result: var string, file: string | cstring, line: int, col:
       addInt(result, col)
     result.add ")"
 
-when defined(nimHasIsNamedTuple):
-  proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
-else:
-  # for bootstrap; remove after release 1.2
-  proc isNamedTuple(T: typedesc): bool =
-    # Taken from typetraits.
-    when T isnot tuple: result = false
-    else:
-      var t: T
-      for name, _ in t.fieldPairs:
-        when name == "Field0":
-          return compiles(t.Field0)
-        else:
-          return true
-      return false
+proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
 
 template tupleObjectDollar*[T: tuple | object](result: var string, x: T) =
   result = "("
diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim
new file mode 100644
index 000000000..07a6809bb
--- /dev/null
+++ b/lib/std/private/osappdirs.nim
@@ -0,0 +1,176 @@
+## .. importdoc:: paths.nim, dirs.nim
+
+include system/inclrtl
+import std/envvars
+import std/private/ospaths2
+
+proc getHomeDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the home directory of the current user.
+  ##
+  ## This proc is wrapped by the `expandTilde proc`_
+  ## for the convenience of processing paths coming from user configuration files.
+  ##
+  ## See also:
+  ## * `getDataDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  runnableExamples:
+    import std/os
+    assert getHomeDir() == expandTilde("~")
+
+  when defined(windows): return getEnv("USERPROFILE") & "\\"
+  else: return getEnv("HOME") & "/"
+
+proc getDataDir*(): string {.rtl, extern: "nos$1"
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the data directory of the current user for applications.
+  ## 
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.local/share" or "~/Library/Application Support" on macOS).
+  ## 
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  when defined(windows):
+    result = getEnv("APPDATA")
+  elif defined(macosx):
+    result = getEnv("XDG_DATA_HOME", getEnv("HOME") / "Library" / "Application Support")
+  else:
+    result = getEnv("XDG_DATA_HOME", getEnv("HOME") / ".local" / "share")
+  result.normalizePathEnd(trailingSep = true)
+
+proc getConfigDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the config directory of the current user for applications.
+  ##
+  ## On non-Windows OSs, this proc conforms to the XDG Base Directory
+  ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment
+  ## variable if it is set, otherwise it returns the default configuration
+  ## directory ("~/.config/").
+  ##
+  ## An OS-dependent trailing slash is always present at the end of the
+  ## returned string: `\\` on Windows and `/` on all other OSs.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getDataDir proc`_
+  ## * `getTempDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  when defined(windows):
+    result = getEnv("APPDATA")
+  else:
+    result = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config")
+  result.normalizePathEnd(trailingSep = true)
+
+proc getCacheDir*(): string =
+  ## Returns the cache directory of the current user for applications.
+  ##
+  ## This makes use of the following environment variables:
+  ##
+  ## * On Windows: `getEnv("LOCALAPPDATA")`
+  ##
+  ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")`
+  ##
+  ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")`
+  ##
+  ## **See also:**
+  ## * `getHomeDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getDataDir proc`_
+  # follows https://crates.io/crates/platform-dirs
+  when defined(windows):
+    result = getEnv("LOCALAPPDATA")
+  elif defined(osx):
+    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")
+  else:
+    result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")
+  result.normalizePathEnd(false)
+
+proc getCacheDir*(app: string): string =
+  ## Returns the cache directory for an application `app`.
+  ##
+  ## * On Windows, this uses: `getCacheDir() / app / "cache"`
+  ## * On other platforms, this uses: `getCacheDir() / app`
+  when defined(windows):
+    getCacheDir() / app / "cache"
+  else:
+    getCacheDir() / app
+
+
+when defined(windows):
+  type DWORD = uint32
+
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+
+  proc getTempPath(
+    nBufferLength: DWORD, lpBuffer: WideCString
+  ): DWORD {.stdcall, dynlib: "kernel32.dll", importc: "GetTempPathW".} =
+    ## Retrieves the path of the directory designated for temporary files.
+
+template getEnvImpl(result: var string, tempDirList: openArray[string]) =
+  for dir in tempDirList:
+    if existsEnv(dir):
+      result = getEnv(dir)
+      break
+
+template getTempDirImpl(result: var string) =
+  when defined(windows):
+    getEnvImpl(result, ["TMP", "TEMP", "USERPROFILE"])
+  else:
+    getEnvImpl(result, ["TMPDIR", "TEMP", "TMP", "TEMPDIR"])
+
+proc getTempDir*(): string {.rtl, extern: "nos$1",
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Returns the temporary directory of the current user for applications to
+  ## save temporary files in.
+  ##
+  ## On Windows, it calls [GetTempPath](https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppathw).
+  ## On Posix based platforms, it will check `TMPDIR`, `TEMP`, `TMP` and `TEMPDIR` environment variables in order.
+  ## On all platforms, `/tmp` will be returned if the procs fails.
+  ##
+  ## You can override this implementation
+  ## by adding `-d:tempDir=mytempname` to your compiler invocation.
+  ##
+  ## **Note:** This proc does not check whether the returned path exists.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `expandTilde proc`_
+  ## * `getCurrentDir proc`_
+  ## * `setCurrentDir proc`_
+  const tempDirDefault = "/tmp"
+  when defined(tempDir):
+    const tempDir {.strdefine.}: string = tempDirDefault
+    result = tempDir
+  else:
+    when nimvm:
+      getTempDirImpl(result)
+    else:
+      when defined(windows):
+        let size = getTempPath(0, nil)
+        # If the function fails, the return value is zero.
+        if size > 0:
+          let buffer = newWideCString(size.int)
+          if getTempPath(size, buffer) > 0:
+            result = $buffer
+      elif defined(android): result = "/data/local/tmp"
+      else:
+        getTempDirImpl(result)
+    if result.len == 0:
+      result = tempDirDefault
+  normalizePathEnd(result, trailingSep=true)
diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim
new file mode 100644
index 000000000..c49d52ef2
--- /dev/null
+++ b/lib/std/private/oscommon.nim
@@ -0,0 +1,186 @@
+include system/inclrtl
+
+import std/[oserrors]
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+## .. importdoc:: osdirs.nim, os.nim
+
+const weirdTarget* = defined(nimscript) or defined(js)
+
+
+type
+  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
+                                            ## operation from the directory
+                                            ## structure.
+  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
+                                            ## operation to
+                                            ## the directory structure.
+
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/posix
+  proc c_rename(oldname, newname: cstring): cint {.
+    importc: "rename", header: "<stdio.h>".}
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+when defined(windows) and not weirdTarget:
+  template wrapUnary*(varname, winApiProc, arg: untyped) =
+    var varname = winApiProc(newWideCString(arg))
+
+  template wrapBinary*(varname, winApiProc, arg, arg2: untyped) =
+    var varname = winApiProc(newWideCString(arg), arg2)
+  proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle =
+    result = findFirstFileW(newWideCString(a), b)
+  template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b)
+
+  template getFilename*(f: untyped): untyped =
+    $cast[WideCString](addr(f.cFileName[0]))
+
+  proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} =
+    # Note - takes advantage of null delimiter in the cstring
+    const dot = ord('.')
+    result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
+             f.cFileName[1].int == dot and f.cFileName[2].int == 0)
+
+
+type
+  PathComponent* = enum   ## Enumeration specifying a path component.
+    ##
+    ## See also:
+    ## * `walkDirRec iterator`_
+    ## * `FileInfo object`_
+    pcFile,               ## path refers to a file
+    pcLinkToFile,         ## path refers to a symbolic link to a file
+    pcDir,                ## path refers to a directory
+    pcLinkToDir           ## path refers to a symbolic link to a directory
+
+
+when defined(posix) and not weirdTarget:
+  proc getSymlinkFileKind*(path: string):
+      tuple[pc: PathComponent, isSpecial: bool] =
+    # Helper function.
+    var s: Stat
+    assert(path != "")
+    result = (pcLinkToFile, false)
+    if stat(path, s) == 0'i32:
+      if S_ISDIR(s.st_mode):
+        result = (pcLinkToDir, false)
+      elif not S_ISREG(s.st_mode):
+        result = (pcLinkToFile, true)
+
+proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
+  ## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
+  ##
+  ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
+  ## In case of other errors `OSError` is raised.
+  ## Returns true in case of success.
+  when defined(windows):
+    let s = newWideCString(source)
+    let d = newWideCString(dest)
+    result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
+  else:
+    result = c_rename(source, dest) == 0'i32
+
+  if not result:
+    let err = osLastError()
+    let isAccessDeniedError =
+      when defined(windows):
+        const AccessDeniedError = OSErrorCode(5)
+        isDir and err == AccessDeniedError
+      else:
+        err == EXDEV.OSErrorCode
+    if not isAccessDeniedError:
+      raiseOSError(err, $(source, dest))
+
+when not defined(windows):
+  const maxSymlinkLen* = 1024
+
+proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
+                                          tags: [ReadDirEffect], noNimJs, sideEffect.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  ##
+  ## See also:
+  ## * `dirExists proc`_
+  ## * `symlinkExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, filename)
+    if a != -1'i32:
+      result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
+  else:
+    var res: Stat
+    return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
+
+
+proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
+                                     noNimJs, sideEffect.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  ##
+  ## See also:
+  ## * `fileExists proc`_
+  ## * `symlinkExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, dir)
+    if a != -1'i32:
+      result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
+  else:
+    var res: Stat
+    result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
+
+
+proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
+                                          tags: [ReadDirEffect],
+                                          noWeirdTarget, sideEffect.} =
+  ## Returns true if the symlink `link` exists. Will return true
+  ## regardless of whether the link points to a directory or file.
+  ##
+  ## See also:
+  ## * `fileExists proc`_
+  ## * `dirExists proc`_
+  when defined(windows):
+    wrapUnary(a, getFileAttributesW, link)
+    if a != -1'i32:
+      # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
+      # may also be needed.
+      result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
+  else:
+    var res: Stat
+    result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
+
+when defined(windows) and not weirdTarget:
+  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
+
+    result = createFileW(
+      newWideCString(path), access,
+      FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
+      nil, OPEN_EXISTING, flags, 0
+      )
diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim
new file mode 100644
index 000000000..a44cad7d9
--- /dev/null
+++ b/lib/std/private/osdirs.nim
@@ -0,0 +1,570 @@
+## .. importdoc:: osfiles.nim, appdirs.nim, paths.nim
+
+include system/inclrtl
+import std/oserrors
+
+
+import ospaths2, osfiles
+import oscommon
+export dirExists, PathComponent
+
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/[posix, times]
+
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+# Templates for filtering directories and files
+when defined(windows) and not weirdTarget:
+  template isDir(f: WIN32_FIND_DATA): bool =
+    (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
+  template isFile(f: WIN32_FIND_DATA): bool =
+    not isDir(f)
+else:
+  template isDir(f: string): bool {.dirty.} =
+    dirExists(f)
+  template isFile(f: string): bool {.dirty.} =
+    fileExists(f)
+
+template defaultWalkFilter(item): bool =
+  ## Walk filter used to return true on both
+  ## files and directories
+  true
+
+template walkCommon(pattern: string, filter) =
+  ## Common code for getting the files and directories with the
+  ## specified `pattern`
+  when defined(windows):
+    var
+      f: WIN32_FIND_DATA
+      res: int
+    res = findFirstFile(pattern, f)
+    if res != -1:
+      defer: findClose(res)
+      let dotPos = searchExtPos(pattern)
+      while true:
+        if not skipFindData(f) and filter(f):
+          # Windows bug/gotcha: 't*.nim' matches 'tfoo.nims' -.- so we check
+          # that the file extensions have the same length ...
+          let ff = getFilename(f)
+          let idx = ff.len - pattern.len + dotPos
+          if dotPos < 0 or idx >= ff.len or (idx >= 0 and ff[idx] == '.') or
+              (dotPos >= 0 and dotPos+1 < pattern.len and pattern[dotPos+1] == '*'):
+            yield splitFile(pattern).dir / extractFilename(ff)
+        if findNextFile(res, f) == 0'i32:
+          let errCode = getLastError()
+          if errCode == ERROR_NO_MORE_FILES: break
+          else: raiseOSError(errCode.OSErrorCode)
+  else: # here we use glob
+    var
+      f: Glob
+      res: int
+    f.gl_offs = 0
+    f.gl_pathc = 0
+    f.gl_pathv = nil
+    res = glob(pattern, 0, nil, addr(f))
+    defer: globfree(addr(f))
+    if res == 0:
+      for i in 0.. f.gl_pathc - 1:
+        assert(f.gl_pathv[i] != nil)
+        let path = $f.gl_pathv[i]
+        if filter(path):
+          yield path
+
+iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the files and directories that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too
+    assert "lib/pure/concurrency".unixToNativePath in paths
+    assert "lib/pure/os.nim".unixToNativePath in paths
+  walkCommon(pattern, defaultWalkFilter)
+
+iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the files that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too
+  walkCommon(pattern, isFile)
+
+iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Iterate over all the directories that match the `pattern`.
+  ##
+  ## On POSIX this uses the `glob`:idx: call.
+  ## `pattern` is OS dependent, but at least the `"*.ext"`
+  ## notation is supported.
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDir iterator`_
+  ## * `walkDirRec iterator`_
+  runnableExamples:
+    import std/os
+    import std/sequtils
+    let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too
+    assert "lib/pure/concurrency".unixToNativePath in paths
+  walkCommon(pattern, isDir)
+
+proc staticWalkDir(dir: string; relative: bool): seq[
+                  tuple[kind: PathComponent, path: string]] =
+  discard
+
+iterator walkDir*(dir: string; relative = false, checkDir = false,
+                  skipSpecial = false):
+  tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
+  ## Walks over the directory `dir` and yields for each directory or file in
+  ## `dir`. The component type and full path for each item are returned.
+  ##
+  ## Walking is not recursive.
+  ## * If `relative` is true (default: false)
+  ##   the resulting path is shortened to be relative to ``dir``,
+  ##   otherwise the full path is returned.
+  ## * If `checkDir` is true, `OSError` is raised when `dir`
+  ##   doesn't exist.
+  ## * If `skipSpecial` is true, then (besides all directories) only *regular*
+  ##   files (**without** special "file" objects like FIFOs, device files,
+  ##   etc) will be yielded on Unix.
+  ##
+  ## **Example:**
+  ##
+  ## This directory structure:
+  ##
+  ##     dirA / dirB / fileB1.txt
+  ##          / dirC
+  ##          / fileA1.txt
+  ##          / fileA2.txt
+  ##
+  ## and this code:
+  runnableExamples("-r:off"):
+    import std/[strutils, sugar]
+    # note: order is not guaranteed
+    # this also works at compile time
+    assert collect(for k in walkDir("dirA"): k.path).join(" ") ==
+                          "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt"
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDirRec iterator`_
+
+  when nimvm:
+    for k, v in items(staticWalkDir(dir, relative)):
+      yield (k, v)
+  else:
+    when weirdTarget:
+      for k, v in items(staticWalkDir(dir, relative)):
+        yield (k, v)
+    elif defined(windows):
+      var f: WIN32_FIND_DATA
+      var h = findFirstFile(dir / "*", f)
+      if h == -1:
+        if checkDir:
+          raiseOSError(osLastError(), dir)
+      else:
+        defer: findClose(h)
+        while true:
+          var k = pcFile
+          if not skipFindData(f):
+            if (f.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32:
+              k = pcDir
+            if (f.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32:
+              k = succ(k)
+            let xx = if relative: extractFilename(getFilename(f))
+                     else: dir / extractFilename(getFilename(f))
+            yield (k, xx)
+          if findNextFile(h, f) == 0'i32:
+            let errCode = getLastError()
+            if errCode == ERROR_NO_MORE_FILES: break
+            else: raiseOSError(errCode.OSErrorCode)
+    else:
+      var d = opendir(dir)
+      if d == nil:
+        if checkDir:
+          raiseOSError(osLastError(), dir)
+      else:
+        defer: discard closedir(d)
+        while true:
+          var x = readdir(d)
+          if x == nil: break
+          var y = $cast[cstring](addr x.d_name)
+          if y != "." and y != "..":
+            var s: Stat
+            let path = dir / y
+            if not relative:
+              y = path
+            var k = pcFile
+
+            template resolveSymlink() =
+              var isSpecial: bool
+              (k, isSpecial) = getSymlinkFileKind(path)
+              if skipSpecial and isSpecial: continue
+
+            template kSetGeneric() =  # pure Posix component `k` resolution
+              if lstat(path.cstring, s) < 0'i32: continue  # don't yield
+              elif S_ISDIR(s.st_mode):
+                k = pcDir
+              elif S_ISLNK(s.st_mode):
+                resolveSymlink()
+              elif skipSpecial and not S_ISREG(s.st_mode): continue
+
+            when defined(linux) or defined(macosx) or
+                 defined(bsd) or defined(genode) or defined(nintendoswitch):
+              case x.d_type
+              of DT_DIR: k = pcDir
+              of DT_LNK:
+                resolveSymlink()
+              of DT_UNKNOWN:
+                kSetGeneric()
+              else: # DT_REG or special "files" like FIFOs
+                if skipSpecial and x.d_type != DT_REG: continue
+                else: discard # leave it as pcFile
+            else:  # assuming that field `d_type` is not present
+              kSetGeneric()
+
+            yield (k, y)
+
+iterator walkDirRec*(dir: string,
+                     yieldFilter = {pcFile}, followFilter = {pcDir},
+                     relative = false, checkDir = false, skipSpecial = false):
+                    string {.tags: [ReadDirEffect].} =
+  ## Recursively walks over the directory `dir` and yields for each file
+  ## or directory in `dir`.
+  ##
+  ## Options `relative`, `checkdir`, `skipSpecial` are explained in
+  ## [walkDir iterator] description.
+  ##
+  ## .. warning:: Modifying the directory structure while the iterator
+  ##   is traversing may result in undefined behavior!
+  ##
+  ## Walking is recursive. `followFilter` controls the behaviour of the iterator:
+  ##
+  ## =====================   =============================================
+  ## yieldFilter             meaning
+  ## =====================   =============================================
+  ## ``pcFile``              yield real files (default)
+  ## ``pcLinkToFile``        yield symbolic links to files
+  ## ``pcDir``               yield real directories
+  ## ``pcLinkToDir``         yield symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ## =====================   =============================================
+  ## followFilter            meaning
+  ## =====================   =============================================
+  ## ``pcDir``               follow real directories (default)
+  ## ``pcLinkToDir``         follow symbolic links to directories
+  ## =====================   =============================================
+  ##
+  ##
+  ## See also:
+  ## * `walkPattern iterator`_
+  ## * `walkFiles iterator`_
+  ## * `walkDirs iterator`_
+  ## * `walkDir iterator`_
+
+  var stack = @[""]
+  var checkDir = checkDir
+  while stack.len > 0:
+    let d = stack.pop()
+    for k, p in walkDir(dir / d, relative = true, checkDir = checkDir,
+                        skipSpecial = skipSpecial):
+      let rel = d / p
+      if k in {pcDir, pcLinkToDir} and k in followFilter:
+        stack.add rel
+      if k in yieldFilter:
+        yield if relative: rel else: dir / rel
+    checkDir = false
+      # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong
+      # permissions), it'll abort iteration and there would be no way to
+      # continue iteration.
+      # Future work can provide a way to customize this and do error reporting.
+
+
+proc rawRemoveDir(dir: string) {.noWeirdTarget.} =
+  when defined(windows):
+    wrapUnary(res, removeDirectoryW, dir)
+    let lastError = osLastError()
+    if res == 0'i32 and lastError.int32 != 3'i32 and
+        lastError.int32 != 18'i32 and lastError.int32 != 2'i32:
+      raiseOSError(lastError, dir)
+  else:
+    if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir)
+
+proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [
+  WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} =
+  ## Removes the directory `dir` including all subdirectories and files
+  ## in `dir` (recursively).
+  ##
+  ## If this fails, `OSError` is raised. This does not fail if the directory never
+  ## existed in the first place, unless `checkDir` = true.
+  ##
+  ## See also:
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  for kind, path in walkDir(dir, checkDir = checkDir):
+    case kind
+    of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path)
+    of pcDir: removeDir(path, true)
+      # for subdirectories there is no benefit in `checkDir = false`
+      # (unless perhaps for edge case of concurrent processes also deleting
+      # the same files)
+  rawRemoveDir(dir)
+
+proc rawCreateDir(dir: string): bool {.noWeirdTarget.} =
+  # Try to create one directory (not the whole path).
+  # returns `true` for success, `false` if the path has previously existed
+  #
+  # This is a thin wrapper over mkDir (or alternatives on other systems),
+  # so in case of a pre-existing path we don't check that it is a directory.
+  when defined(solaris):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno in {EEXIST, ENOSYS}:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+  elif defined(haiku):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno == EEXIST or errno == EROFS:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+  elif defined(posix):
+    let res = mkdir(dir, 0o777)
+    if res == 0'i32:
+      result = true
+    elif errno == EEXIST:
+      result = false
+    else:
+      #echo res
+      raiseOSError(osLastError(), dir)
+  else:
+    wrapUnary(res, createDirectoryW, dir)
+
+    if res != 0'i32:
+      result = true
+    elif getLastError() == 183'i32:
+      result = false
+    else:
+      raiseOSError(osLastError(), dir)
+
+proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
+  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
+  ## Checks if a `directory`:idx: `dir` exists, and creates it otherwise.
+  ##
+  ## Does not create parent directories (raises `OSError` if parent directories do not exist).
+  ## Returns `true` if the directory already exists, and `false` otherwise.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `createDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  result = not rawCreateDir(dir)
+  if result:
+    # path already exists - need to check that it is indeed a directory
+    if not dirExists(dir):
+      raise newException(IOError, "Failed to create '" & dir & "'")
+
+proc createDir*(dir: string) {.rtl, extern: "nos$1",
+  tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} =
+  ## Creates the `directory`:idx: `dir`.
+  ##
+  ## The directory may contain several subdirectories that do not exist yet.
+  ## The full path is created. If this fails, `OSError` is raised.
+  ##
+  ## It does **not** fail if the directory already exists because for
+  ## most usages this does not indicate an error.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `moveDir proc`_
+  if dir == "":
+    return
+  var omitNext = isAbsolute(dir)
+  for p in parentDirs(dir, fromRoot=true):
+    if omitNext:
+      omitNext = false
+    else:
+      discard existsOrCreateDir(p)
+
+proc copyDir*(source, dest: string, skipSpecial = false) {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} =
+  ## Copies a directory from `source` to `dest`.
+  ##
+  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
+  ## are skipped.
+  ##
+  ## If `skipSpecial` is true, then (besides all directories) only *regular*
+  ## files (**without** special "file" objects like FIFOs, device files,
+  ## etc) will be copied on Unix.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will copy the attributes from
+  ## `source` into `dest`.
+  ##
+  ## On other platforms created files and directories will inherit the
+  ## default permissions of a newly created file/directory for the user.
+  ## Use `copyDirWithPermissions proc`_
+  ## to preserve attributes recursively on these platforms.
+  ##
+  ## See also:
+  ## * `copyDirWithPermissions proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  ## * `moveDir proc`_
+  createDir(dest)
+  for kind, path in walkDir(source, skipSpecial = skipSpecial):
+    var noSource = splitPath(path).tail
+    if kind == pcDir:
+      copyDir(path, dest / noSource, skipSpecial = skipSpecial)
+    else:
+      copyFile(path, dest / noSource, {cfSymlinkAsIs})
+
+
+proc copyDirWithPermissions*(source, dest: string,
+                             ignorePermissionErrors = true,
+                             skipSpecial = false)
+  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteIOEffect, ReadIOEffect],
+   benign, noWeirdTarget.} =
+  ## Copies a directory from `source` to `dest` preserving file permissions.
+  ##
+  ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks
+  ## are skipped.
+  ##
+  ## If `skipSpecial` is true, then (besides all directories) only *regular*
+  ## files (**without** special "file" objects like FIFOs, device files,
+  ## etc) will be copied on Unix.
+  ##
+  ## If this fails, `OSError` is raised. This is a wrapper proc around
+  ## `copyDir`_ and `copyFileWithPermissions`_ procs
+  ## on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyDir proc`_ since
+  ## that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file or directory
+  ## itself has been copied, which won't happen atomically and could lead to a
+  ## race condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `copyDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `moveDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  createDir(dest)
+  when not defined(windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
+                         false)
+    except:
+      if not ignorePermissionErrors:
+        raise
+  for kind, path in walkDir(source, skipSpecial = skipSpecial):
+    var noSource = splitPath(path).tail
+    if kind == pcDir:
+      copyDirWithPermissions(path, dest / noSource, ignorePermissionErrors, skipSpecial = skipSpecial)
+    else:
+      copyFileWithPermissions(path, dest / noSource, ignorePermissionErrors, {cfSymlinkAsIs})
+
+proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
+  ## Moves a directory from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` contains symlinks, they themself are
+  ## moved, not their target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## See also:
+  ## * `moveFile proc`_
+  ## * `copyDir proc`_
+  ## * `copyDirWithPermissions proc`_
+  ## * `removeDir proc`_
+  ## * `existsOrCreateDir proc`_
+  ## * `createDir proc`_
+  if not tryMoveFSObject(source, dest, isDir = true):
+    # Fallback to copy & del
+    copyDir(source, dest)
+    removeDir(source)
+
+proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getCurrentDir proc`_
+  when defined(windows):
+    if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
+      raiseOSError(osLastError(), newDir)
+  else:
+    if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim
new file mode 100644
index 000000000..37d8eabca
--- /dev/null
+++ b/lib/std/private/osfiles.nim
@@ -0,0 +1,416 @@
+include system/inclrtl
+import std/private/since
+import std/oserrors
+
+import oscommon
+export fileExists
+
+import ospaths2, ossymlinks
+
+## .. importdoc:: osdirs.nim, os.nim
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/winlean
+elif defined(posix):
+  import std/[posix, times]
+
+  proc toTime(ts: Timespec): times.Time {.inline.} =
+    result = initTime(ts.tv_sec.int64, ts.tv_nsec.int)
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+type
+  FilePermission* = enum   ## File access permission, modelled after UNIX.
+    ##
+    ## See also:
+    ## * `getFilePermissions`_
+    ## * `setFilePermissions`_
+    ## * `FileInfo object`_
+    fpUserExec,            ## execute access for the file owner
+    fpUserWrite,           ## write access for the file owner
+    fpUserRead,            ## read access for the file owner
+    fpGroupExec,           ## execute access for the group
+    fpGroupWrite,          ## write access for the group
+    fpGroupRead,           ## read access for the group
+    fpOthersExec,          ## execute access for others
+    fpOthersWrite,         ## write access for others
+    fpOthersRead           ## read access for others
+
+proc getFilePermissions*(filename: string): set[FilePermission] {.
+  rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Retrieves file permissions for `filename`.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is checked, every other
+  ## permission is available in any case.
+  ##
+  ## See also:
+  ## * `setFilePermissions proc`_
+  ## * `FilePermission enum`_
+  when defined(posix):
+    var a: Stat
+    if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename)
+    result = {}
+    if (a.st_mode and S_IRUSR.Mode) != 0.Mode: result.incl(fpUserRead)
+    if (a.st_mode and S_IWUSR.Mode) != 0.Mode: result.incl(fpUserWrite)
+    if (a.st_mode and S_IXUSR.Mode) != 0.Mode: result.incl(fpUserExec)
+
+    if (a.st_mode and S_IRGRP.Mode) != 0.Mode: result.incl(fpGroupRead)
+    if (a.st_mode and S_IWGRP.Mode) != 0.Mode: result.incl(fpGroupWrite)
+    if (a.st_mode and S_IXGRP.Mode) != 0.Mode: result.incl(fpGroupExec)
+
+    if (a.st_mode and S_IROTH.Mode) != 0.Mode: result.incl(fpOthersRead)
+    if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite)
+    if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec)
+  else:
+    wrapUnary(res, getFileAttributesW, filename)
+    if res == -1'i32: raiseOSError(osLastError(), filename)
+    if (res and FILE_ATTRIBUTE_READONLY) != 0'i32:
+      result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead,
+                fpOthersExec, fpOthersRead}
+    else:
+      result = {fpUserExec..fpOthersRead}
+
+proc setFilePermissions*(filename: string, permissions: set[FilePermission],
+                         followSymlinks = true)
+  {.rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect],
+   noWeirdTarget.} =
+  ## Sets the file permissions for `filename`.
+  ##
+  ## If `followSymlinks` set to true (default) and ``filename`` points to a
+  ## symlink, permissions are set to the file symlink points to.
+  ## `followSymlinks` set to false is a noop on Windows and some POSIX
+  ## systems (including Linux) on which `lchmod` is either unavailable or always
+  ## fails, given that symlinks permissions there are not observed.
+  ##
+  ## `OSError` is raised in case of an error.
+  ## On Windows, only the ``readonly`` flag is changed, depending on
+  ## ``fpUserWrite`` permission.
+  ##
+  ## See also:
+  ## * `getFilePermissions proc`_
+  ## * `FilePermission enum`_
+  when defined(posix):
+    var p = 0.Mode
+    if fpUserRead in permissions: p = p or S_IRUSR.Mode
+    if fpUserWrite in permissions: p = p or S_IWUSR.Mode
+    if fpUserExec in permissions: p = p or S_IXUSR.Mode
+
+    if fpGroupRead in permissions: p = p or S_IRGRP.Mode
+    if fpGroupWrite in permissions: p = p or S_IWGRP.Mode
+    if fpGroupExec in permissions: p = p or S_IXGRP.Mode
+
+    if fpOthersRead in permissions: p = p or S_IROTH.Mode
+    if fpOthersWrite in permissions: p = p or S_IWOTH.Mode
+    if fpOthersExec in permissions: p = p or S_IXOTH.Mode
+
+    if not followSymlinks and filename.symlinkExists:
+      when declared(lchmod):
+        if lchmod(filename, cast[Mode](p)) != 0:
+          raiseOSError(osLastError(), $(filename, permissions))
+    else:
+      if chmod(filename, cast[Mode](p)) != 0:
+        raiseOSError(osLastError(), $(filename, permissions))
+  else:
+    wrapUnary(res, getFileAttributesW, filename)
+    if res == -1'i32: raiseOSError(osLastError(), filename)
+    if fpUserWrite in permissions:
+      res = res and not FILE_ATTRIBUTE_READONLY
+    else:
+      res = res or FILE_ATTRIBUTE_READONLY
+    wrapBinary(res2, setFileAttributesW, filename, res)
+    if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions))
+
+
+const hasCCopyfile = defined(osx) and not defined(nimLegacyCopyFile)
+  # xxx instead of `nimLegacyCopyFile`, support something like: `when osxVersion >= (10, 5)`
+
+when hasCCopyfile:
+  # `copyfile` API available since osx 10.5.
+  {.push nodecl, header: "<copyfile.h>".}
+  type
+    copyfile_state_t {.nodecl.} = pointer
+    copyfile_flags_t = cint
+  proc copyfile_state_alloc(): copyfile_state_t
+  proc copyfile_state_free(state: copyfile_state_t): cint
+  proc c_copyfile(src, dst: cstring,  state: copyfile_state_t, flags: copyfile_flags_t): cint {.importc: "copyfile".}
+  when (NimMajor, NimMinor) >= (1, 4):
+    let
+      COPYFILE_DATA {.nodecl.}: copyfile_flags_t
+      COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
+  else:
+    var
+      COPYFILE_DATA {.nodecl.}: copyfile_flags_t
+      COPYFILE_XATTR {.nodecl.}: copyfile_flags_t
+  {.pop.}
+
+type
+  CopyFlag* = enum    ## Copy options.
+    cfSymlinkAsIs,    ## Copy symlinks as symlinks
+    cfSymlinkFollow,  ## Copy the files symlinks point to
+    cfSymlinkIgnore   ## Ignore symlinks
+
+const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore}
+
+proc copyFile*(source, dest: string, options = {cfSymlinkFollow}; bufferSize = 16_384) {.rtl,
+  extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect],
+  noWeirdTarget.} =
+  ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## If this fails, `OSError` is raised.
+  ##
+  ## On the Windows platform this proc will
+  ## copy the source file's attributes into dest.
+  ##
+  ## On other platforms you need
+  ## to use `getFilePermissions`_ and
+  ## `setFilePermissions`_
+  ## procs
+  ## to copy them by hand (or use the convenience `copyFileWithPermissions
+  ## proc`_),
+  ## otherwise `dest` will inherit the default permissions of a newly
+  ## created file for the user.
+  ##
+  ## If `dest` already exists, the file attributes
+  ## will be preserved and the content overwritten.
+  ##
+  ## On OSX, `copyfile` C api will be used (available since OSX 10.5) unless
+  ## `-d:nimLegacyCopyFile` is used.
+  ##
+  ## `copyFile` allows to specify `bufferSize` to improve I/O performance.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyDir proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+
+  doAssert card(copyFlagSymlink * options) == 1, "There should be exactly one cfSymlink* in options"
+  let isSymlink = source.symlinkExists
+  if isSymlink and (cfSymlinkIgnore in options or defined(windows)):
+    return
+  when defined(windows):
+    let s = newWideCString(source)
+    let d = newWideCString(dest)
+    if copyFileW(s, d, 0'i32) == 0'i32:
+      raiseOSError(osLastError(), $(source, dest))
+  else:
+    if isSymlink and cfSymlinkAsIs in options:
+      createSymlink(expandSymlink(source), dest)
+    else:
+      when hasCCopyfile:
+        let state = copyfile_state_alloc()
+        # xxx `COPYFILE_STAT` could be used for one-shot
+        # `copyFileWithPermissions`.
+        let status = c_copyfile(source.cstring, dest.cstring, state,
+                                COPYFILE_DATA)
+        if status != 0:
+          let err = osLastError()
+          discard copyfile_state_free(state)
+          raiseOSError(err, $(source, dest))
+        let status2 = copyfile_state_free(state)
+        if status2 != 0: raiseOSError(osLastError(), $(source, dest))
+      else:
+        # generic version of copyFile which works for any platform:
+        var d, s: File
+        if not open(s, source): raiseOSError(osLastError(), source)
+        if not open(d, dest, fmWrite):
+          close(s)
+          raiseOSError(osLastError(), dest)
+
+        # Hints for kernel-level aggressive sequential low-fragmentation read-aheads:
+        # https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html
+        when defined(linux) or defined(osx):
+          discard posix_fadvise(getFileHandle(d), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL)
+          discard posix_fadvise(getFileHandle(s), 0.cint, 0.cint, POSIX_FADV_SEQUENTIAL)
+
+        var buf = alloc(bufferSize)
+        while true:
+          var bytesread = readBuffer(s, buf, bufferSize)
+          if bytesread > 0:
+            var byteswritten = writeBuffer(d, buf, bytesread)
+            if bytesread != byteswritten:
+              dealloc(buf)
+              close(s)
+              close(d)
+              raiseOSError(osLastError(), dest)
+          if bytesread != bufferSize: break
+        dealloc(buf)
+        close(s)
+        flushFile(d)
+        close(d)
+
+proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}; bufferSize = 16_384)
+  {.noWeirdTarget, since: (1,3,7).} =
+  ## Copies a file `source` into directory `dir`, which must exist.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## `copyFileToDir` allows to specify `bufferSize` to improve I/O performance.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyFile proc`_
+  if dir.len == 0: # treating "" as "." is error prone
+    raise newException(ValueError, "dest is empty")
+  copyFile(source, dir / source.lastPathPart, options, bufferSize)
+
+
+proc copyFileWithPermissions*(source, dest: string,
+                              ignorePermissionErrors = true,
+                              options = {cfSymlinkFollow}) {.noWeirdTarget.} =
+  ## Copies a file from `source` to `dest` preserving file permissions.
+  ##
+  ## On non-Windows OSes, `options` specify the way file is copied; by default,
+  ## if `source` is a symlink, copies the file symlink points to. `options` is
+  ## ignored on Windows: symlinks are skipped.
+  ##
+  ## This is a wrapper proc around `copyFile`_,
+  ## `getFilePermissions`_ and `setFilePermissions`_
+  ## procs on non-Windows platforms.
+  ##
+  ## On Windows this proc is just a wrapper for `copyFile proc`_ since
+  ## that proc already copies attributes.
+  ##
+  ## On non-Windows systems permissions are copied after the file itself has
+  ## been copied, which won't happen atomically and could lead to a race
+  ## condition. If `ignorePermissionErrors` is true (default), errors while
+  ## reading/setting file attributes will be ignored, otherwise will raise
+  ## `OSError`.
+  ##
+  ## See also:
+  ## * `CopyFlag enum`_
+  ## * `copyFile proc`_
+  ## * `copyDir proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+  ## * `copyDirWithPermissions proc`_
+  copyFile(source, dest, options)
+  when not defined(windows):
+    try:
+      setFilePermissions(dest, getFilePermissions(source), followSymlinks =
+                         (cfSymlinkFollow in options))
+    except:
+      if not ignorePermissionErrors:
+        raise
+
+when not declared(ENOENT) and not defined(windows):
+  when defined(nimscript):
+    when not defined(haiku):
+      const ENOENT = cint(2) # 2 on most systems including Solaris
+    else:
+      const ENOENT = cint(-2147459069)
+  else:
+    var ENOENT {.importc, header: "<errno.h>".}: cint
+
+when defined(windows) and not weirdTarget:
+  template deleteFile(file: untyped): untyped  = deleteFileW(file)
+  template setFileAttributes(file, attrs: untyped): untyped =
+    setFileAttributesW(file, attrs)
+
+proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, returns `false`. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeFile proc`_
+  ## * `moveFile proc`_
+  result = true
+  when defined(windows):
+    let f = newWideCString(file)
+    if deleteFile(f) == 0:
+      result = false
+      let err = getLastError()
+      if err == ERROR_FILE_NOT_FOUND or err == ERROR_PATH_NOT_FOUND:
+        result = true
+      elif err == ERROR_ACCESS_DENIED and
+         setFileAttributes(f, FILE_ATTRIBUTE_NORMAL) != 0 and
+         deleteFile(f) != 0:
+        result = true
+  else:
+    if unlink(file) != 0'i32 and errno != ENOENT:
+      result = false
+
+proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} =
+  ## Removes the `file`.
+  ##
+  ## If this fails, `OSError` is raised. This does not fail
+  ## if the file never existed in the first place.
+  ##
+  ## On Windows, ignores the read-only attribute.
+  ##
+  ## See also:
+  ## * `removeDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `tryRemoveFile proc`_
+  ## * `moveFile proc`_
+  if not tryRemoveFile(file):
+    raiseOSError(osLastError(), file)
+
+proc moveFile*(source, dest: string) {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} =
+  ## Moves a file from `source` to `dest`.
+  ##
+  ## Symlinks are not followed: if `source` is a symlink, it is itself moved,
+  ## not its target.
+  ##
+  ## If this fails, `OSError` is raised.
+  ## If `dest` already exists, it will be overwritten.
+  ##
+  ## Can be used to `rename files`:idx:.
+  ##
+  ## See also:
+  ## * `moveDir proc`_
+  ## * `copyFile proc`_
+  ## * `copyFileWithPermissions proc`_
+  ## * `removeFile proc`_
+  ## * `tryRemoveFile proc`_
+
+  if not tryMoveFSObject(source, dest, isDir = false):
+    when defined(windows):
+      raiseAssert "unreachable"
+    else:
+      # Fallback to copy & del
+      copyFileWithPermissions(source, dest, options={cfSymlinkAsIs})
+      try:
+        removeFile(source)
+      except:
+        discard tryRemoveFile(dest)
+        raise
diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim
new file mode 100644
index 000000000..bc69ff725
--- /dev/null
+++ b/lib/std/private/ospaths2.nim
@@ -0,0 +1,1030 @@
+include system/inclrtl
+import std/private/since
+
+import std/[strutils, pathnorm]
+import std/oserrors
+
+import oscommon
+export ReadDirEffect, WriteDirEffect
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+## .. importdoc:: osappdirs.nim, osdirs.nim, osseps.nim, os.nim
+
+const weirdTarget = defined(nimscript) or defined(js)
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/winlean
+elif defined(posix):
+  import std/posix, system/ansi_c
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+
+proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
+
+
+import std/private/osseps
+export osseps
+
+proc absolutePathInternal(path: string): string {.gcsafe.}
+
+proc normalizePathEnd*(path: var string, trailingSep = false) =
+  ## Ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
+  ## ``trailingSep``, and taking care of edge cases: it preservers whether
+  ## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
+  ## not `AltSep`. Trailing `/.` are compressed, see examples.
+  if path.len == 0: return
+  var i = path.len
+  while i >= 1:
+    if path[i-1] in {DirSep, AltSep}: dec(i)
+    elif path[i-1] == '.' and i >= 2 and path[i-2] in {DirSep, AltSep}: dec(i)
+    else: break
+  if trailingSep:
+    # foo// => foo
+    path.setLen(i)
+    # foo => foo/
+    path.add DirSep
+  elif i > 0:
+    # foo// => foo
+    path.setLen(i)
+  else:
+    # // => / (empty case was already taken care of)
+    path = $DirSep
+
+proc normalizePathEnd*(path: string, trailingSep = false): string =
+  ## outplace overload
+  runnableExamples:
+    when defined(posix):
+      assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/"
+      assert normalizePathEnd("lib/./.", trailingSep = false) == "lib"
+      assert normalizePathEnd(".//./.", trailingSep = false) == "."
+      assert normalizePathEnd("", trailingSep = true) == "" # not / !
+      assert normalizePathEnd("/", trailingSep = false) == "/" # not "" !
+  result = path
+  result.normalizePathEnd(trailingSep)
+
+template endsWith(a: string, b: set[char]): bool =
+  a.len > 0 and a[^1] in b
+
+proc joinPathImpl(result: var string, state: var int, tail: string) =
+  let trailingSep = tail.endsWith({DirSep, AltSep}) or tail.len == 0 and result.endsWith({DirSep, AltSep})
+  normalizePathEnd(result, trailingSep=false)
+  addNormalizePath(tail, result, state, DirSep)
+  normalizePathEnd(result, trailingSep=trailingSep)
+
+proc joinPath*(head, tail: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Joins two directory names to one.
+  ##
+  ## returns normalized path concatenation of `head` and `tail`, preserving
+  ## whether or not `tail` has a trailing slash (or, if tail if empty, whether
+  ## head has one).
+  ##
+  ## See also:
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `/ proc`_
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("usr", "lib") == "usr/lib"
+      assert joinPath("usr", "lib/") == "usr/lib/"
+      assert joinPath("usr", "") == "usr"
+      assert joinPath("usr/", "") == "usr/"
+      assert joinPath("", "") == ""
+      assert joinPath("", "lib") == "lib"
+      assert joinPath("", "/lib") == "/lib"
+      assert joinPath("usr/", "/lib") == "usr/lib"
+      assert joinPath("usr/lib", "../bin") == "usr/bin"
+
+  result = newStringOfCap(head.len + tail.len)
+  var state = 0
+  joinPathImpl(result, state, head)
+  joinPathImpl(result, state, tail)
+  when false:
+    if len(head) == 0:
+      result = tail
+    elif head[len(head)-1] in {DirSep, AltSep}:
+      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
+        result = head & substr(tail, 1)
+      else:
+        result = head & tail
+    else:
+      if tail.len > 0 and tail[0] in {DirSep, AltSep}:
+        result = head & tail
+      else:
+        result = head & DirSep & tail
+
+proc joinPath*(parts: varargs[string]): string {.noSideEffect,
+  rtl, extern: "nos$1OpenArray".} =
+  ## The same as `joinPath(head, tail) proc`_,
+  ## but works with any number of directory parts.
+  ##
+  ## You need to pass at least one element or the proc
+  ## will assert in debug builds and crash on release builds.
+  ##
+  ## See also:
+  ## * `joinPath(head, tail) proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `splitPath proc`_
+  runnableExamples:
+    when defined(posix):
+      assert joinPath("a") == "a"
+      assert joinPath("a", "b", "c") == "a/b/c"
+      assert joinPath("usr/lib", "../../var", "log") == "var/log"
+
+  var estimatedLen = 0
+  for p in parts: estimatedLen += p.len
+  result = newStringOfCap(estimatedLen)
+  var state = 0
+  for i in 0..high(parts):
+    joinPathImpl(result, state, parts[i])
+
+proc `/`*(head, tail: string): string {.noSideEffect, inline.} =
+  ## The same as `joinPath(head, tail) proc`_.
+  ##
+  ## See also:
+  ## * `/../ proc`_
+  ## * `joinPath(head, tail) proc`_
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `splitPath proc`_
+  ## * `uri.combine proc <uri.html#combine,Uri,Uri>`_
+  ## * `uri./ proc <uri.html#/,Uri,string>`_
+  runnableExamples:
+    when defined(posix):
+      assert "usr" / "" == "usr"
+      assert "" / "lib" == "lib"
+      assert "" / "/lib" == "/lib"
+      assert "usr/" / "/lib/" == "usr/lib/"
+      assert "usr" / "lib" / "../bin" == "usr/bin"
+
+  result = joinPath(head, tail)
+
+when doslikeFileSystem:
+  import std/private/ntpath
+
+proc splitPath*(path: string): tuple[head, tail: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a directory into `(head, tail)` tuple, so that
+  ## ``head / tail == path`` (except for edge cases like "/usr").
+  ##
+  ## See also:
+  ## * `joinPath(head, tail) proc`_
+  ## * `joinPath(parts: varargs[string]) proc`_
+  ## * `/ proc`_
+  ## * `/../ proc`_
+  ## * `relativePath proc`_
+  runnableExamples:
+    assert splitPath("usr/local/bin") == ("usr/local", "bin")
+    assert splitPath("usr/local/bin/") == ("usr/local/bin", "")
+    assert splitPath("/bin/") == ("/bin", "")
+    when (NimMajor, NimMinor) <= (1, 0):
+      assert splitPath("/bin") == ("", "bin")
+    else:
+      assert splitPath("/bin") == ("/", "bin")
+    assert splitPath("bin") == ("", "bin")
+    assert splitPath("") == ("", "")
+
+  when doslikeFileSystem:
+    let (drive, splitpath) = splitDrive(path)
+    let stop = drive.len
+  else:
+    const stop = 0
+
+  var sepPos = -1
+  for i in countdown(len(path)-1, stop):
+    if path[i] in {DirSep, AltSep}:
+      sepPos = i
+      break
+  if sepPos >= 0:
+    result.head = substr(path, 0,
+      when (NimMajor, NimMinor) <= (1, 0):
+        sepPos-1
+      else:
+        if likely(sepPos >= 1): sepPos-1 else: 0
+    )
+    result.tail = substr(path, sepPos+1)
+  else:
+    when doslikeFileSystem:
+      result.head = drive
+      result.tail = splitpath
+    else:
+      result.head = ""
+      result.tail = path
+
+proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
+  ## Checks whether a given `path` is absolute.
+  ##
+  ## On Windows, network paths are considered absolute too.
+  runnableExamples:
+    assert not "".isAbsolute
+    assert not ".".isAbsolute
+    when defined(posix):
+      assert "/".isAbsolute
+      assert not "a/".isAbsolute
+      assert "/a/".isAbsolute
+
+  if len(path) == 0: return false
+
+  when doslikeFileSystem:
+    var len = len(path)
+    result = (path[0] in {'/', '\\'}) or
+              (len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
+  elif defined(macos):
+    # according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
+    result = path[0] != ':'
+  elif defined(RISCOS):
+    result = path[0] == '$'
+  elif defined(posix):
+    result = path[0] == '/'
+  elif defined(nodejs):
+    {.emit: [result," = require(\"path\").isAbsolute(",path.cstring,");"].}
+  else:
+    raiseAssert "unreachable" # if ever hits here, adapt as needed
+
+when FileSystemCaseSensitive:
+  template `!=?`(a, b: char): bool = a != b
+else:
+  template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
+
+when doslikeFileSystem:
+  proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: [].} =
+    ## An absolute path from the root of the current drive (e.g. "\foo")
+    path.len > 0 and
+    (path[0] == AltSep or
+     (path[0] == DirSep and
+      (path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
+
+  proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} =
+    ## Return true if path1 and path2 have a same root.
+    ##
+    ## Detail of Windows path formats:
+    ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+
+    assert(isAbsolute(path1))
+    assert(isAbsolute(path2))
+
+    if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
+      result = true
+    elif cmpIgnoreCase(splitDrive(path1).drive, splitDrive(path2).drive) == 0:
+      result = true
+    else:
+      result = false
+
+proc relativePath*(path, base: string, sep = DirSep): string {.
+  rtl, extern: "nos$1".} =
+  ## Converts `path` to a path relative to `base`.
+  ##
+  ## The `sep` (default: DirSep_) is used for the path normalizations,
+  ## this can be useful to ensure the relative path only contains `'/'`
+  ## so that it can be used for URL constructions.
+  ##
+  ## On Windows, if a root of `path` and a root of `base` are different,
+  ## returns `path` as is because it is impossible to make a relative path.
+  ## That means an absolute path can be returned.
+  ##
+  ## See also:
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  ## * `tailDir proc`_
+  runnableExamples:
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
+    when not doslikeFileSystem: # On Windows, UNC-paths start with `//`
+      assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
+    assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
+    assert relativePath("", "/users/moo", '/') == ""
+    assert relativePath("foo", ".", '/') == "foo"
+    assert relativePath("foo", "foo", '/') == "."
+
+  if path.len == 0: return ""
+  var base = if base == ".": "" else: base
+  var path = path
+  path.normalizePathAux
+  base.normalizePathAux
+  let a1 = isAbsolute(path)
+  let a2 = isAbsolute(base)
+  if a1 and not a2:
+    base = absolutePathInternal(base)
+  elif a2 and not a1:
+    path = absolutePathInternal(path)
+
+  when doslikeFileSystem:
+    if isAbsolute(path) and isAbsolute(base):
+      if not sameRoot(path, base):
+        return path
+
+  var f = default PathIter
+  var b = default PathIter
+  var ff = (0, -1)
+  var bb = (0, -1) # (int, int)
+  result = newStringOfCap(path.len)
+  # skip the common prefix:
+  while f.hasNext(path) and b.hasNext(base):
+    ff = next(f, path)
+    bb = next(b, base)
+    let diff = ff[1] - ff[0]
+    if diff != bb[1] - bb[0]: break
+    var same = true
+    for i in 0..diff:
+      if path[i + ff[0]] !=? base[i + bb[0]]:
+        same = false
+        break
+    if not same: break
+    ff = (0, -1)
+    bb = (0, -1)
+  #  for i in 0..diff:
+  #    result.add base[i + bb[0]]
+
+  # /foo/bar/xxx/ -- base
+  # /foo/bar/baz  -- path path
+  #   ../baz
+  # every directory that is in 'base', needs to add '..'
+  while true:
+    if bb[1] >= bb[0]:
+      if result.len > 0 and result[^1] != sep:
+        result.add sep
+      result.add ".."
+    if not b.hasNext(base): break
+    bb = b.next(base)
+
+  # add the rest of 'path':
+  while true:
+    if ff[1] >= ff[0]:
+      if result.len > 0 and result[^1] != sep:
+        result.add sep
+      for i in 0..ff[1] - ff[0]:
+        result.add path[i + ff[0]]
+    if not f.hasNext(path): break
+    ff = f.next(path)
+
+  when not defined(nimOldRelativePathBehavior):
+    if result.len == 0: result.add "."
+
+proc isRelativeTo*(path: string, base: string): bool {.since: (1, 1).} =
+  ## Returns true if `path` is relative to `base`.
+  runnableExamples:
+    doAssert isRelativeTo("./foo//bar", "foo")
+    doAssert isRelativeTo("foo/bar", ".")
+    doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim")
+    doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim")
+  let path = path.normalizePath
+  let base = base.normalizePath
+  let ret = relativePath(path, base)
+  result = path.len > 0 and not ret.startsWith ".."
+
+proc parentDirPos(path: string): int =
+  var q = 1
+  if len(path) >= 1 and path[len(path)-1] in {DirSep, AltSep}: q = 2
+  for i in countdown(len(path)-q, 0):
+    if path[i] in {DirSep, AltSep}: return i
+  result = -1
+
+proc parentDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the parent directory of `path`.
+  ##
+  ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end
+  ## in a dir separator, but also takes care of path normalizations.
+  ## The remainder can be obtained with `lastPathPart(path) proc`_.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `tailDir proc`_
+  ## * `parentDirs iterator`_
+  runnableExamples:
+    assert parentDir("") == ""
+    when defined(posix):
+      assert parentDir("/usr/local/bin") == "/usr/local"
+      assert parentDir("foo/bar//") == "foo"
+      assert parentDir("//foo//bar//.") == "/foo"
+      assert parentDir("./foo") == "."
+      assert parentDir("/./foo//./") == "/"
+      assert parentDir("a//./") == "."
+      assert parentDir("a/b/c/..") == "a"
+  result = pathnorm.normalizePath(path)
+  when doslikeFileSystem:
+    let (drive, splitpath) = splitDrive(result)
+    result = splitpath
+  var sepPos = parentDirPos(result)
+  if sepPos >= 0:
+    result = substr(result, 0, sepPos)
+    normalizePathEnd(result)
+  elif result == ".." or result == "." or result.len == 0 or result[^1] in {DirSep, AltSep}:
+    # `.` => `..` and .. => `../..`(etc) would be a sensible alternative
+    # `/` => `/` (as done with splitFile) would be a sensible alternative
+    result = ""
+  else:
+    result = "."
+  when doslikeFileSystem:
+    if result.len == 0:
+      discard
+    elif drive.len > 0 and result.len == 1 and result[0] in {DirSep, AltSep}:
+      result = drive
+    else:
+      result = drive & result
+
+proc tailDir*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Returns the tail part of `path`.
+  ##
+  ## See also:
+  ## * `relativePath proc`_
+  ## * `splitPath proc`_
+  ## * `parentDir proc`_
+  runnableExamples:
+    assert tailDir("/bin") == "bin"
+    assert tailDir("bin") == ""
+    assert tailDir("bin/") == ""
+    assert tailDir("/usr/local/bin") == "usr/local/bin"
+    assert tailDir("//usr//local//bin//") == "usr//local//bin//"
+    assert tailDir("./usr/local/bin") == "usr/local/bin"
+    assert tailDir("usr/local/bin") == "local/bin"
+
+  var i = 0
+  when doslikeFileSystem:
+    let (drive, splitpath) = path.splitDrive
+    if drive != "":
+      return splitpath.strip(chars = {DirSep, AltSep}, trailing = false)
+  while i < len(path):
+    if path[i] in {DirSep, AltSep}:
+      while i < len(path) and path[i] in {DirSep, AltSep}: inc i
+      return substr(path, i)
+    inc i
+  result = ""
+
+proc isRootDir*(path: string): bool {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Checks whether a given `path` is a root directory.
+  runnableExamples:
+    assert isRootDir("")
+    assert isRootDir(".")
+    assert isRootDir("/")
+    assert isRootDir("a")
+    assert not isRootDir("/a")
+    assert not isRootDir("a/b/c")
+
+  when doslikeFileSystem:
+    if splitDrive(path).path == "":
+      return true
+  result = parentDirPos(path) < 0
+
+iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string =
+  ## Walks over all parent directories of a given `path`.
+  ##
+  ## If `fromRoot` is true (default: false), the traversal will start from
+  ## the file system root directory.
+  ## If `inclusive` is true (default), the original argument will be included
+  ## in the traversal.
+  ##
+  ## Relative paths won't be expanded by this iterator. Instead, it will traverse
+  ## only the directories appearing in the relative path.
+  ##
+  ## See also:
+  ## * `parentDir proc`_
+  ##
+  runnableExamples:
+    let g = "a/b/c"
+
+    for p in g.parentDirs:
+      echo p
+      # a/b/c
+      # a/b
+      # a
+
+    for p in g.parentDirs(fromRoot=true):
+      echo p
+      # a/
+      # a/b/
+      # a/b/c
+
+    for p in g.parentDirs(inclusive=false):
+      echo p
+      # a/b
+      # a
+
+  if not fromRoot:
+    var current = path
+    if inclusive: yield path
+    while true:
+      if current.isRootDir: break
+      current = current.parentDir
+      yield current
+  else:
+    when doslikeFileSystem:
+      let start = path.splitDrive.drive.len
+    else:
+      const start = 0
+    for i in countup(start, path.len - 2): # ignore the last /
+      # deal with non-normalized paths such as /foo//bar//baz
+      if path[i] in {DirSep, AltSep} and
+          (i == 0 or path[i-1] notin {DirSep, AltSep}):
+        yield path.substr(0, i)
+
+    if inclusive: yield path
+
+proc `/../`*(head, tail: string): string {.noSideEffect.} =
+  ## The same as ``parentDir(head) / tail``, unless there is no parent
+  ## directory. Then ``head / tail`` is performed instead.
+  ##
+  ## See also:
+  ## * `/ proc`_
+  ## * `parentDir proc`_
+  runnableExamples:
+    when defined(posix):
+      assert "a/b/c" /../ "d/e" == "a/b/d/e"
+      assert "a" /../ "d/e" == "a/d/e"
+
+  when doslikeFileSystem:
+    let (drive, head) = splitDrive(head)
+  let sepPos = parentDirPos(head)
+  if sepPos >= 0:
+    result = substr(head, 0, sepPos-1) / tail
+  else:
+    result = head / tail
+  when doslikeFileSystem:
+    result = drive / result
+
+proc normExt(ext: string): string =
+  if ext == "" or ext[0] == ExtSep: result = ext # no copy needed here
+  else: result = ExtSep & ext
+
+proc searchExtPos*(path: string): int =
+  ## Returns index of the `'.'` char in `path` if it signifies the beginning
+  ## of the file extension. Returns -1 otherwise.
+  ##
+  ## See also:
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert searchExtPos("a/b/c") == -1
+    assert searchExtPos("c.nim") == 1
+    assert searchExtPos("a/b/c.nim") == 5
+    assert searchExtPos("a.b.c.nim") == 5
+    assert searchExtPos(".nim") == -1
+    assert searchExtPos("..nim") == -1
+    assert searchExtPos("a..nim") == 2
+
+  # Unless there is any char that is not `ExtSep` before last `ExtSep` in the file name,
+  # it is not a file extension.
+  const DirSeps = when doslikeFileSystem: {DirSep, AltSep, ':'} else: {DirSep, AltSep}
+  result = -1
+  var i = path.high
+  while i >= 1:
+    if path[i] == ExtSep:
+      break
+    elif path[i] in DirSeps:
+      return -1 # do not skip over path
+    dec i
+
+  for j in countdown(i - 1, 0):
+    if path[j] in DirSeps:
+      return -1
+    elif path[j] != ExtSep:
+      result = i
+      break
+
+proc splitFile*(path: string): tuple[dir, name, ext: string] {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Splits a filename into `(dir, name, extension)` tuple.
+  ##
+  ## `dir` does not end in DirSep_ unless it's `/`.
+  ## `extension` includes the leading dot.
+  ##
+  ## If `path` has no extension, `ext` is the empty string.
+  ## If `path` has no directory component, `dir` is the empty string.
+  ## If `path` has no filename component, `name` and `ext` are empty strings.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    var (dir, name, ext) = splitFile("usr/local/nimc.html")
+    assert dir == "usr/local"
+    assert name == "nimc"
+    assert ext == ".html"
+    (dir, name, ext) = splitFile("/usr/local/os")
+    assert dir == "/usr/local"
+    assert name == "os"
+    assert ext == ""
+    (dir, name, ext) = splitFile("/usr/local/")
+    assert dir == "/usr/local"
+    assert name == ""
+    assert ext == ""
+    (dir, name, ext) = splitFile("/tmp.txt")
+    assert dir == "/"
+    assert name == "tmp"
+    assert ext == ".txt"
+
+  var namePos = 0
+  var dotPos = 0
+  when doslikeFileSystem:
+    let (drive, _) = splitDrive(path)
+    let stop = len(drive)
+    result.dir = drive
+  else:
+    const stop = 0
+  for i in countdown(len(path) - 1, stop):
+    if path[i] in {DirSep, AltSep} or i == 0:
+      if path[i] in {DirSep, AltSep}:
+        result.dir = substr(path, 0, if likely(i >= 1): i - 1 else: 0)
+        namePos = i + 1
+      if dotPos > i:
+        result.name = substr(path, namePos, dotPos - 1)
+        result.ext = substr(path, dotPos)
+      else:
+        result.name = substr(path, namePos)
+      break
+    elif path[i] == ExtSep and i > 0 and i < len(path) - 1 and
+         path[i - 1] notin {DirSep, AltSep} and
+         path[i + 1] != ExtSep and dotPos == 0:
+      dotPos = i
+
+proc extractFilename*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Extracts the filename of a given `path`.
+  ##
+  ## This is the same as ``name & ext`` from `splitFile(path) proc`_.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert extractFilename("foo/bar/") == ""
+    assert extractFilename("foo/bar") == "bar"
+    assert extractFilename("foo/bar.baz") == "bar.baz"
+
+  if path.len == 0 or path[path.len-1] in {DirSep, AltSep}:
+    result = ""
+  else:
+    result = splitPath(path).tail
+
+proc lastPathPart*(path: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Like `extractFilename proc`_, but ignores
+  ## trailing dir separator; aka: `baseName`:idx: in some other languages.
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `changeFileExt proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert lastPathPart("foo/bar/") == "bar"
+    assert lastPathPart("foo/bar") == "bar"
+
+  let path = path.normalizePathEnd(trailingSep = false)
+  result = extractFilename(path)
+
+proc changeFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Changes the file extension to `ext`.
+  ##
+  ## If the `filename` has no extension, `ext` will be added.
+  ## If `ext` == "" then any extension is removed.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character. (Although I know
+  ## of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `addFileExt proc`_
+  runnableExamples:
+    assert changeFileExt("foo.bar", "baz") == "foo.baz"
+    assert changeFileExt("foo.bar", "") == "foo"
+    assert changeFileExt("foo", "baz") == "foo.baz"
+
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = substr(filename, 0, extPos-1) & normExt(ext)
+
+proc addFileExt*(filename, ext: string): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Adds the file extension `ext` to `filename`, unless
+  ## `filename` already has an extension.
+  ##
+  ## `Ext` should be given without the leading `'.'`, because some
+  ## filesystems may use a different character.
+  ## (Although I know of none such beast.)
+  ##
+  ## See also:
+  ## * `searchExtPos proc`_
+  ## * `splitFile proc`_
+  ## * `extractFilename proc`_
+  ## * `lastPathPart proc`_
+  ## * `changeFileExt proc`_
+  runnableExamples:
+    assert addFileExt("foo.bar", "baz") == "foo.bar"
+    assert addFileExt("foo.bar", "") == "foo.bar"
+    assert addFileExt("foo", "baz") == "foo.baz"
+
+  var extPos = searchExtPos(filename)
+  if extPos < 0: result = filename & normExt(ext)
+  else: result = filename
+
+proc cmpPaths*(pathA, pathB: string): int {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively. Returns:
+  ##
+  ## | `0` if pathA == pathB
+  ## | `< 0` if pathA < pathB
+  ## | `> 0` if pathA > pathB
+  runnableExamples:
+    when defined(macosx):
+      assert cmpPaths("foo", "Foo") == 0
+    elif defined(posix):
+      assert cmpPaths("foo", "Foo") > 0
+
+  let a = normalizePath(pathA)
+  let b = normalizePath(pathB)
+  if FileSystemCaseSensitive:
+    result = cmp(a, b)
+  else:
+    when defined(nimscript):
+      result = cmpic(a, b)
+    elif defined(nimdoc): discard
+    else:
+      result = cmpIgnoreCase(a, b)
+
+proc unixToNativePath*(path: string, drive=""): string {.
+  noSideEffect, rtl, extern: "nos$1".} =
+  ## Converts an UNIX-like path to a native one.
+  ##
+  ## On an UNIX system this does nothing. Else it converts
+  ## `'/'`, `'.'`, `'..'` to the appropriate things.
+  ##
+  ## On systems with a concept of "drives", `drive` is used to determine
+  ## which drive label to use during absolute path conversion.
+  ## `drive` defaults to the drive of the current working directory, and is
+  ## ignored on systems that do not have a concept of "drives".
+  when defined(unix):
+    result = path
+  else:
+    if path.len == 0: return ""
+
+    var start: int
+    if path[0] == '/':
+      # an absolute path
+      when doslikeFileSystem:
+        if drive != "":
+          result = drive & ":" & DirSep
+        else:
+          result = $DirSep
+      elif defined(macos):
+        result = "" # must not start with ':'
+      else:
+        result = $DirSep
+      start = 1
+    elif path[0] == '.' and (path.len == 1 or path[1] == '/'):
+      # current directory
+      result = $CurDir
+      start = when doslikeFileSystem: 1 else: 2
+    else:
+      result = ""
+      start = 0
+
+    var i = start
+    while i < len(path): # ../../../ --> ::::
+      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)] == ':':
+            add result, ':'
+          else:
+            add result, ParDir
+        else:
+          add result, ParDir & DirSep
+        inc(i, 3)
+      elif path[i] == '/':
+        add result, DirSep
+        inc(i)
+      else:
+        add result, path[i]
+        inc(i)
+
+
+when not defined(nimscript):
+  proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} =
+    ## Returns the `current working directory`:idx: i.e. where the built
+    ## binary is run.
+    ##
+    ## So the path returned by this proc is determined at run time.
+    ##
+    ## See also:
+    ## * `getHomeDir proc`_
+    ## * `getConfigDir proc`_
+    ## * `getTempDir proc`_
+    ## * `setCurrentDir proc`_
+    ## * `currentSourcePath template <system.html#currentSourcePath.t>`_
+    ## * `getProjectPath proc <macros.html#getProjectPath>`_
+    when defined(nodejs):
+      var ret: cstring
+      {.emit: "`ret` = process.cwd();".}
+      return $ret
+    elif defined(js):
+      raiseAssert "use -d:nodejs to have `getCurrentDir` defined"
+    elif defined(windows):
+      var bufsize = MAX_PATH.int32
+      var res = newWideCString(bufsize)
+      while true:
+        var L = getCurrentDirectoryW(bufsize, res)
+        if L == 0'i32:
+          raiseOSError(osLastError())
+        elif L > bufsize:
+          res = newWideCString(L)
+          bufsize = L
+        else:
+          result = res$L
+          break
+    else:
+      var bufsize = 1024 # should be enough
+      result = newString(bufsize)
+      while true:
+        if getcwd(result.cstring, bufsize) != nil:
+          setLen(result, c_strlen(result.cstring))
+          break
+        else:
+          let err = osLastError()
+          if err.int32 == ERANGE:
+            bufsize = bufsize shl 1
+            doAssert(bufsize >= 0)
+            result = newString(bufsize)
+          else:
+            raiseOSError(osLastError())
+
+proc absolutePath*(path: string, root = getCurrentDir()): string =
+  ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
+  ## default: current directory).
+  ## If `path` is absolute, return it, ignoring `root`.
+  ##
+  ## See also:
+  ## * `normalizedPath proc`_
+  ## * `normalizePath proc`_
+  runnableExamples:
+    assert absolutePath("a") == getCurrentDir() / "a"
+
+  if isAbsolute(path): path
+  else:
+    if not root.isAbsolute:
+      raise newException(ValueError, "The specified root is not absolute: " & root)
+    joinPath(root, path)
+
+proc absolutePathInternal(path: string): string =
+  absolutePath(path, getCurrentDir())
+
+
+proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} =
+  ## Normalize a path.
+  ##
+  ## Consecutive directory separators are collapsed, including an initial double slash.
+  ##
+  ## On relative paths, double dot (`..`) sequences are collapsed if possible.
+  ## On absolute paths they are always collapsed.
+  ##
+  ## .. warning:: URL-encoded and Unicode attempts at directory traversal are not detected.
+  ##   Triple dot is not handled.
+  ##
+  ## See also:
+  ## * `absolutePath proc`_
+  ## * `normalizedPath proc`_ for outplace version
+  ## * `normalizeExe proc`_
+  runnableExamples:
+    when defined(posix):
+      var a = "a///b//..//c///d"
+      a.normalizePath()
+      assert a == "a/c/d"
+
+  path = pathnorm.normalizePath(path)
+  when false:
+    let isAbs = isAbsolute(path)
+    var stack: seq[string] = @[]
+    for p in split(path, {DirSep}):
+      case p
+      of "", ".":
+        continue
+      of "..":
+        if stack.len == 0:
+          if isAbs:
+            discard  # collapse all double dots on absoluta paths
+          else:
+            stack.add(p)
+        elif stack[^1] == "..":
+          stack.add(p)
+        else:
+          discard stack.pop()
+      else:
+        stack.add(p)
+
+    if isAbs:
+      path = DirSep & join(stack, $DirSep)
+    elif stack.len > 0:
+      path = join(stack, $DirSep)
+    else:
+      path = "."
+
+proc normalizePathAux(path: var string) = normalizePath(path)
+
+proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} =
+  ## Returns a normalized path for the current OS.
+  ##
+  ## See also:
+  ## * `absolutePath proc`_
+  ## * `normalizePath proc`_ for the in-place version
+  runnableExamples:
+    when defined(posix):
+      assert normalizedPath("a///b//..//c///d") == "a/c/d"
+  result = pathnorm.normalizePath(path)
+
+proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
+  ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`.
+  runnableExamples:
+    import std/sugar
+    when defined(posix):
+      doAssert "foo".dup(normalizeExe) == "./foo"
+      doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
+    doAssert "".dup(normalizeExe) == ""
+  when defined(posix):
+    if file.len > 0 and DirSep notin file and file != "." and file != "..":
+      file = "./" & file
+
+proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noWeirdTarget.} =
+  ## Returns true if both pathname arguments refer to the same physical
+  ## file or directory.
+  ##
+  ## Raises `OSError` if any of the files does not
+  ## exist or information about it can not be obtained.
+  ##
+  ## This proc will return true if given two alternative hard-linked or
+  ## sym-linked paths to the same file or directory.
+  ##
+  ## See also:
+  ## * `sameFileContent proc`_
+  when defined(windows):
+    var success = true
+    var f1 = openHandle(path1)
+    var f2 = openHandle(path2)
+
+    var lastErr: OSErrorCode
+    if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
+      var fi1, fi2: BY_HANDLE_FILE_INFORMATION
+
+      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
+         getFileInformationByHandle(f2, addr(fi2)) != 0:
+        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
+                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
+                 fi1.nFileIndexLow == fi2.nFileIndexLow
+      else:
+        lastErr = osLastError()
+        success = false
+    else:
+      lastErr = osLastError()
+      success = false
+
+    discard closeHandle(f1)
+    discard closeHandle(f2)
+
+    if not success: raiseOSError(lastErr, $(path1, path2))
+  else:
+    var a, b: Stat
+    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
+      raiseOSError(osLastError(), $(path1, path2))
+    else:
+      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino
diff --git a/lib/pure/includes/osseps.nim b/lib/std/private/osseps.nim
index 1ea587e3c..f2d49d886 100644
--- a/lib/pure/includes/osseps.nim
+++ b/lib/std/private/osseps.nim
@@ -3,6 +3,8 @@
 
 # Improved based on info in 'compiler/platform.nim'
 
+## .. importdoc:: ospaths2.nim
+
 const
   doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS)
 
diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim
new file mode 100644
index 000000000..c1760c42e
--- /dev/null
+++ b/lib/std/private/ossymlinks.nim
@@ -0,0 +1,78 @@
+include system/inclrtl
+import std/oserrors
+
+import oscommon
+export symlinkExists
+
+when defined(nimPreviewSlimSystem):
+  import std/[syncio, assertions, widestrs]
+
+when weirdTarget:
+  discard
+elif defined(windows):
+  import std/[winlean, times]
+elif defined(posix):
+  import std/posix
+else:
+  {.error: "OS module not ported to your operating system!".}
+
+
+when weirdTarget:
+  {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
+else:
+  {.pragma: noWeirdTarget.}
+
+
+when defined(nimscript):
+  # for procs already defined in scriptconfig.nim
+  template noNimJs(body): untyped = discard
+elif defined(js):
+  {.pragma: noNimJs, error: "this proc is not available on the js target".}
+else:
+  {.pragma: noNimJs.}
+
+## .. importdoc:: os.nim
+
+proc createSymlink*(src, dest: string) {.noWeirdTarget.} =
+  ## Create a symbolic link at `dest` which points to the item specified
+  ## by `src`. On most operating systems, will fail if a link already exists.
+  ##
+  ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation
+  ##   of symlinks to root users (administrators) or users with developer mode enabled.
+  ##
+  ## See also:
+  ## * `createHardlink proc`_
+  ## * `expandSymlink proc`_
+
+  when defined(windows):
+    const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2
+    # allows anyone with developer mode on to create a link
+    let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+    var wSrc = newWideCString(src)
+    var wDst = newWideCString(dest)
+    if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0:
+      raiseOSError(osLastError(), $(src, dest))
+  else:
+    if symlink(src, dest) != 0:
+      raiseOSError(osLastError(), $(src, dest))
+
+proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} =
+  ## Returns a string representing the path to which the symbolic link points.
+  ##
+  ## On Windows this is a noop, `symlinkPath` is simply returned.
+  ##
+  ## See also:
+  ## * `createSymlink proc`_
+  when defined(windows) or defined(nintendoswitch):
+    result = symlinkPath
+  else:
+    var bufLen = 1024
+    while true:
+      result = newString(bufLen)
+      let len = readlink(symlinkPath.cstring, result.cstring, bufLen)
+      if len < 0:
+        raiseOSError(osLastError(), symlinkPath)
+      if len < bufLen:
+        result.setLen(len)
+        break
+      bufLen = bufLen shl 1
diff --git a/lib/std/private/schubfach.nim b/lib/std/private/schubfach.nim
index 872317ebf..b8c85d2bc 100644
--- a/lib/std/private/schubfach.nim
+++ b/lib/std/private/schubfach.nim
@@ -39,10 +39,10 @@ const
   exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1)
   signMask: BitsType = not (not BitsType(0) shr 1)
 
-proc constructSingle(bits: BitsType): Single {.constructor.} =
+proc constructSingle(bits: BitsType): Single  =
   result.bits = bits
 
-proc constructSingle(value: ValueType): Single {.constructor.} =
+proc constructSingle(value: ValueType): Single  =
   result.bits = cast[typeof(result.bits)](value)
 
 proc physicalSignificand(this: Single): BitsType {.noSideEffect.} =
@@ -244,12 +244,12 @@ proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal
 ##  ToChars
 ## ==================================================================================================
 
-proc printDecimalDigitsBackwards(buf: var openArray[char]; pos: int; output: uint32): int32 {.inline.} =
+proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int {.inline.} =
   var output = output
   var pos = pos
-  var tz: int32 = 0
+  var tz = 0
   ##  number of trailing zeros removed.
-  var nd: int32 = 0
+  var nd = 0
   ##  number of decimal digits processed.
   ##  At most 9 digits remaining
   if output >= 10000:
@@ -300,7 +300,7 @@ proc printDecimalDigitsBackwards(buf: var openArray[char]; pos: int; output: uin
     buf[pos] = chr(uint32('0') + q)
   return tz
 
-proc decimalLength(v: uint32): int32 {.inline.} =
+proc decimalLength(v: uint32): int {.inline.} =
   sf_Assert(v >= 1)
   sf_Assert(v <= 999999999'u)
   if v >= 100000000'u:
@@ -321,7 +321,7 @@ proc decimalLength(v: uint32): int32 {.inline.} =
     return 2
   return 1
 
-proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimalExponent: int32;
+proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint32; decimalExponent: int;
                   forceTrailingDotZero: bool = false): int {.inline.} =
   const
     minFixedDecimalPoint: int32 = -4
@@ -333,8 +333,8 @@ proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimal
   sf_Assert(digits <= 999999999'u)
   sf_Assert(decimalExponent >= -99)
   sf_Assert(decimalExponent <= 99)
-  var numDigits: int32 = decimalLength(digits)
-  let decimalPoint: int32 = numDigits + decimalExponent
+  var numDigits = decimalLength(digits)
+  let decimalPoint = numDigits + decimalExponent
   let useFixed: bool = minFixedDecimalPoint <= decimalPoint and
       decimalPoint <= maxFixedDecimalPoint
   ##  Prepare the buffer.
@@ -342,7 +342,7 @@ proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimal
   for i in 0..<32: buffer[pos+i] = '0'
   assert(minFixedDecimalPoint >= -30, "internal error")
   assert(maxFixedDecimalPoint <= 32, "internal error")
-  var decimalDigitsPosition: int32
+  var decimalDigitsPosition: int
   if useFixed:
     if decimalPoint <= 0:
       ##  0.[000]digits
@@ -355,7 +355,7 @@ proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimal
     ##  dE+123 or d.igitsE+123
     decimalDigitsPosition = 1
   var digitsEnd = pos + decimalDigitsPosition + numDigits
-  let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
+  let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
   dec(digitsEnd, tz)
   dec(numDigits, tz)
   ##   decimal_exponent += tz; // => decimal_point unchanged.
@@ -386,7 +386,7 @@ proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimal
       ##  d.igitsE+123
       buffer[pos+1] = '.'
       pos = digitsEnd
-    let scientificExponent: int32 = decimalPoint - 1
+    let scientificExponent = decimalPoint - 1
     ##       SF_ASSERT(scientific_exponent != 0);
     buffer[pos] = 'e'
     buffer[pos+1] = if scientificExponent < 0: '-' else: '+'
@@ -412,7 +412,7 @@ proc float32ToChars*(buffer: var openArray[char]; v: float32; forceTrailingDotZe
     if exponent != 0 or significand != 0:
       ##  != 0
       let dec: auto = toDecimal32(significand, exponent)
-      return formatDigits(buffer, pos, dec.digits, dec.exponent, forceTrailingDotZero)
+      return formatDigits(buffer, pos, dec.digits, dec.exponent.int, forceTrailingDotZero)
     else:
       buffer[pos] = '0'
       buffer[pos+1] = '.'
diff --git a/lib/std/private/since.nim b/lib/std/private/since.nim
index 5b22b6391..720120f11 100644
--- a/lib/std/private/since.nim
+++ b/lib/std/private/since.nim
@@ -1,5 +1,5 @@
 ##[
-`since` is used to emulate older versions of nim stdlib with `--useVersion`,
+`since` is used to emulate older versions of nim stdlib,
 see `tuse_version.nim`.
 
 If a symbol `foo` is added in version `(1,3,5)`, use `{.since: (1.3.5).}`, not
@@ -15,19 +15,19 @@ The emulation cannot be 100% faithful and to avoid adding too much complexity,
 template since*(version: (int, int), body: untyped) {.dirty.} =
   ## Evaluates `body` if the ``(NimMajor, NimMinor)`` is greater than
   ## or equal to `version`. Usage:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   proc fun*() {.since: (1, 3).}
   ##   since (1, 3): fun()
+  ##   ```
   when (NimMajor, NimMinor) >= version:
     body
 
 template since*(version: (int, int, int), body: untyped) {.dirty.} =
   ## Evaluates `body` if ``(NimMajor, NimMinor, NimPatch)`` is greater than 
   ## or equal to `version`. Usage:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   proc fun*() {.since: (1, 3, 1).}
   ##   since (1, 3, 1): fun()
+  ##   ```
   when (NimMajor, NimMinor, NimPatch) >= version:
     body
diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim
index 7d42a7cf8..f8c9236a5 100644
--- a/lib/std/private/strimpl.nim
+++ b/lib/std/private/strimpl.nim
@@ -74,3 +74,40 @@ template endsWithImpl*[T: string | cstring](s, suffix: T) =
 
 func cmpNimIdentifier*[T: string | cstring](a, b: T): int =
   cmpIgnoreStyleImpl(a, b, true)
+
+func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
+              importc: "memchr", header: "<string.h>".}
+func c_strstr(haystack, needle: cstring): cstring {.
+  importc: "strstr", header: "<string.h>".}
+
+
+func find*(s: cstring, sub: char, start: Natural = 0, last = 0): int =
+  ## Searches for `sub` in `s` inside the range `start..last` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Use `s[start..last].rfind` for a `start`-origin index.
+  let last = if last == 0: s.high else: last
+  let L = last-start+1
+  if L > 0:
+    let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L))
+    if not found.isNil:
+      return cast[int](found) -% cast[int](s)
+  return -1
+
+func find*(s, sub: cstring, start: Natural = 0, last = 0): int =
+  ## Searches for `sub` in `s` inside the range `start..last` (both ends included).
+  ## If `last` is unspecified, it defaults to `s.high` (the last element).
+  ##
+  ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
+  ## Otherwise the index returned is relative to `s[0]`, not `start`.
+  ## Use `s[start..last].find` for a `start`-origin index.
+  if sub.len > s.len - start: return -1
+  if sub.len == 1: return find(s, sub[0], start, last)
+  if last == 0 and s.len > start:
+    let found = c_strstr(cast[cstring](s[start].unsafeAddr), sub)
+    if not found.isNil:
+      result = cast[int](found) -% cast[int](s)
+    else:
+      result = -1
diff --git a/lib/std/private/syslocks.nim b/lib/std/private/syslocks.nim
new file mode 100644
index 000000000..e19ec2c04
--- /dev/null
+++ b/lib/std/private/syslocks.nim
@@ -0,0 +1,234 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# Low level system locks and condition vars.
+
+{.push stackTrace: off.}
+
+when defined(windows):
+  type
+    Handle = int
+
+    SysLock* {.importc: "CRITICAL_SECTION",
+              header: "<windows.h>", final, pure, byref.} = object # CRITICAL_SECTION in WinApi
+      DebugInfo: pointer
+      LockCount: int32
+      RecursionCount: int32
+      OwningThread: int
+      LockSemaphore: int
+      SpinCount: int
+
+    SysCond* {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>", byref.} = object
+      thePtr {.importc: "Ptr".} : Handle
+
+  proc initSysLock*(L: var SysLock) {.importc: "InitializeCriticalSection",
+                                     header: "<windows.h>".}
+    ## Initializes the lock `L`.
+
+  proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection",
+                                                 header: "<windows.h>".}
+    ## Tries to acquire the lock `L`.
+
+  proc tryAcquireSys*(L: var SysLock): bool {.inline.} =
+    result = tryAcquireSysAux(L) != 0'i32
+
+  proc acquireSys*(L: var SysLock) {.importc: "EnterCriticalSection",
+                                    header: "<windows.h>".}
+    ## Acquires the lock `L`.
+
+  proc releaseSys*(L: var SysLock) {.importc: "LeaveCriticalSection",
+                                    header: "<windows.h>".}
+    ## Releases the lock `L`.
+
+  proc deinitSys*(L: SysLock) {.importc: "DeleteCriticalSection",
+                                   header: "<windows.h>".}
+
+  proc initializeConditionVariable(
+    conditionVariable: var SysCond
+  ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".}
+
+  proc sleepConditionVariableCS(
+    conditionVariable: var SysCond,
+    PCRITICAL_SECTION: var SysLock,
+    dwMilliseconds: int
+  ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".}
+
+
+  proc signalSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
+    dynlib: "kernel32", importc: "WakeConditionVariable".}
+
+  proc broadcastSysCond*(hEvent: var SysCond) {.stdcall, noSideEffect,
+    dynlib: "kernel32", importc: "WakeAllConditionVariable".}
+
+  proc initSysCond*(cond: var SysCond) {.inline.} =
+    initializeConditionVariable(cond)
+  proc deinitSysCond*(cond: SysCond) {.inline.} =
+    discard
+  proc waitSysCond*(cond: var SysCond, lock: var SysLock) =
+    discard sleepConditionVariableCS(cond, lock, -1'i32)
+
+elif defined(genode):
+  const
+    Header = "genode_cpp/syslocks.h"
+  type
+    SysLock* {.importcpp: "Nim::SysLock", pure, final,
+              header: Header.} = object
+    SysCond* {.importcpp: "Nim::SysCond", pure, final,
+              header: Header.} = object
+
+  proc initSysLock*(L: var SysLock) = discard
+  proc deinitSys*(L: SysLock) = discard
+  proc acquireSys*(L: var SysLock) {.noSideEffect, importcpp.}
+  proc tryAcquireSys*(L: var SysLock): bool {.noSideEffect, importcpp.}
+  proc releaseSys*(L: var SysLock) {.noSideEffect, importcpp.}
+
+  proc initSysCond*(L: var SysCond) = discard
+  proc deinitSysCond*(L: SysCond) = discard
+  proc waitSysCond*(cond: var SysCond, lock: var SysLock) {.
+    noSideEffect, importcpp.}
+  proc signalSysCond*(cond: var SysCond) {.
+    noSideEffect, importcpp.}
+  proc broadcastSysCond*(cond: var SysCond) {.
+    noSideEffect, importcpp.}
+
+else:
+  type
+    SysLockObj {.importc: "pthread_mutex_t", pure, final,
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""", byref.} = object
+      when defined(linux) and defined(amd64):
+        abi: array[40 div sizeof(clong), clong]
+
+    SysLockAttr* {.importc: "pthread_mutexattr_t", pure, final
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""".} = object
+      when defined(linux) and defined(amd64):
+        abi: array[4 div sizeof(cint), cint]  # actually a cint
+
+    SysCondObj {.importc: "pthread_cond_t", pure, final,
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""", byref.} = object
+      when defined(linux) and defined(amd64):
+        abi: array[48 div sizeof(clonglong), clonglong]
+
+    SysCondAttr {.importc: "pthread_condattr_t", pure, final
+               header: """#include <sys/types.h>
+                          #include <pthread.h>""".} = object
+      when defined(linux) and defined(amd64):
+        abi: array[4 div sizeof(cint), cint]  # actually a cint
+
+    SysLockType = distinct cint
+
+  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
+    importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysAux(L: SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
+
+  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_lock", header: "<pthread.h>".}
+  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
+    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
+
+  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
+
+  when defined(ios):
+    # iOS will behave badly if sync primitives are moved in memory. In order
+    # to prevent this once and for all, we're doing an extra malloc when
+    # initializing the primitive.
+    type
+      SysLock* = ptr SysLockObj
+      SysCond* = ptr SysCondObj
+
+    when not declared(c_malloc):
+      proc c_malloc(size: csize_t): pointer {.
+        importc: "malloc", header: "<stdlib.h>".}
+      proc c_free(p: pointer) {.
+        importc: "free", header: "<stdlib.h>".}
+
+    proc initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj))))
+      initSysLockAux(L[], attr)
+
+    proc deinitSys*(L: SysLock) =
+      deinitSysAux(L[])
+      c_free(L)
+
+    template acquireSys*(L: var SysLock) =
+      acquireSysAux(L[])
+    template tryAcquireSys*(L: var SysLock): bool =
+      tryAcquireSysAux(L[]) == 0'i32
+    template releaseSys*(L: var SysLock) =
+      releaseSysAux(L[])
+  else:
+    type
+      SysLock* = SysLockObj
+      SysCond* = SysCondObj
+
+    template initSysLock*(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      initSysLockAux(L, attr)
+    template deinitSys*(L: SysLock) =
+      deinitSysAux(L)
+    template acquireSys*(L: var SysLock) =
+      acquireSysAux(L)
+    template tryAcquireSys*(L: var SysLock): bool =
+      tryAcquireSysAux(L) == 0'i32
+    template releaseSys*(L: var SysLock) =
+      releaseSysAux(L)
+
+  # rlocks
+  var SysLockType_Reentrant* {.importc: "PTHREAD_MUTEX_RECURSIVE",
+    header: "<pthread.h>".}: SysLockType
+  proc initSysLockAttr*(a: var SysLockAttr) {.
+    importc: "pthread_mutexattr_init", header: "<pthread.h>", noSideEffect.}
+  proc setSysLockType*(a: var SysLockAttr, t: SysLockType) {.
+    importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
+
+  # locks
+  proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
+    importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysCondAux(cond: SysCondObj) {.noSideEffect,
+    importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+  proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {.
+    importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
+  proc signalSysCondAux(cond: var SysCondObj) {.
+    importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
+  proc broadcastSysCondAux(cond: var SysCondObj) {.
+    importc: "pthread_cond_broadcast", header: "<pthread.h>", noSideEffect.}
+
+  when defined(ios):
+    proc initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
+      cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj))))
+      initSysCondAux(cond[], cond_attr)
+
+    proc deinitSysCond*(cond: SysCond) =
+      deinitSysCondAux(cond[])
+      c_free(cond)
+
+    template waitSysCond*(cond: var SysCond, lock: var SysLock) =
+      discard waitSysCondAux(cond[], lock[])
+    template signalSysCond*(cond: var SysCond) =
+      signalSysCondAux(cond[])
+    template broadcastSysCond*(cond: var SysCond) =
+      broadcastSysCondAux(cond[])
+  else:
+    template initSysCond*(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
+      initSysCondAux(cond, cond_attr)
+    template deinitSysCond*(cond: SysCond) =
+      deinitSysCondAux(cond)
+
+    template waitSysCond*(cond: var SysCond, lock: var SysLock) =
+      discard waitSysCondAux(cond, lock)
+    template signalSysCond*(cond: var SysCond) =
+      signalSysCondAux(cond)
+    template broadcastSysCond*(cond: var SysCond) =
+      broadcastSysCondAux(cond)
+
+{.pop.}
diff --git a/lib/std/private/threadtypes.nim b/lib/std/private/threadtypes.nim
new file mode 100644
index 000000000..a1cdf21dc
--- /dev/null
+++ b/lib/std/private/threadtypes.nim
@@ -0,0 +1,176 @@
+include system/inclrtl
+
+const hasSharedHeap* = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
+
+when defined(windows):
+  type
+    Handle* = int
+    SysThread* = Handle
+    WinThreadProc* = proc (x: pointer): int32 {.stdcall.}
+
+  proc createThread*(lpThreadAttributes: pointer, dwStackSize: int32,
+                     lpStartAddress: WinThreadProc,
+                     lpParameter: pointer,
+                     dwCreationFlags: int32,
+                     lpThreadId: var int32): SysThread {.
+    stdcall, dynlib: "kernel32", importc: "CreateThread".}
+
+  proc winSuspendThread*(hThread: SysThread): int32 {.
+    stdcall, dynlib: "kernel32", importc: "SuspendThread".}
+
+  proc winResumeThread*(hThread: SysThread): int32 {.
+    stdcall, dynlib: "kernel32", importc: "ResumeThread".}
+
+  proc waitForSingleObject*(hHandle: SysThread, dwMilliseconds: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
+
+  proc waitForMultipleObjects*(nCount: int32,
+                              lpHandles: ptr SysThread,
+                              bWaitAll: int32,
+                              dwMilliseconds: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
+
+  proc terminateThread*(hThread: SysThread, dwExitCode: int32): int32 {.
+    stdcall, dynlib: "kernel32", importc: "TerminateThread".}
+
+  proc setThreadAffinityMask*(hThread: SysThread, dwThreadAffinityMask: uint) {.
+    importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
+
+elif defined(genode):
+  const
+    GenodeHeader* = "genode_cpp/threads.h"
+  type
+    SysThread* {.importcpp: "Nim::SysThread",
+                 header: GenodeHeader, final, pure.} = object
+    GenodeThreadProc* = proc (x: pointer) {.noconv.}
+
+  proc initThread*(s: var SysThread,
+                  env: GenodeEnv,
+                  stackSize: culonglong,
+                  entry: GenodeThreadProc,
+                  arg: pointer,
+                  affinity: cuint) {.
+    importcpp: "#.initThread(@)".}
+
+
+else:
+  when not (defined(macosx) or defined(haiku)):
+    {.passl: "-pthread".}
+
+  when not defined(haiku):
+    {.passc: "-pthread".}
+
+  const
+    schedh = "#define _GNU_SOURCE\n#include <sched.h>"
+    pthreadh* = "#define _GNU_SOURCE\n#include <pthread.h>"
+
+  when not declared(Time):
+    when defined(linux):
+      type Time = clong
+    else:
+      type Time = int
+
+  when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
+    type
+      SysThread* {.importc: "pthread_t",
+                  header: "<sys/types.h>" .} = distinct culong
+      Pthread_attr* {.importc: "pthread_attr_t",
+                    header: "<sys/types.h>".} = object
+        abi: array[56 div sizeof(clong), clong]
+  elif defined(openbsd) and defined(amd64):
+    type
+      SysThread* {.importc: "pthread_t", header: "<pthread.h>".} = object
+      Pthread_attr* {.importc: "pthread_attr_t",
+                       header: "<pthread.h>".} = object
+  else:
+    type
+      SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = int
+      Pthread_attr* {.importc: "pthread_attr_t",
+                       header: "<sys/types.h>".} = object
+  type
+    Timespec* {.importc: "struct timespec", header: "<time.h>".} = object
+      tv_sec*: Time
+      tv_nsec*: clong
+
+  proc pthread_attr_init*(a1: var Pthread_attr): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_setstacksize*(a1: var Pthread_attr, a2: int): cint {.
+    importc, header: pthreadh.}
+  proc pthread_attr_destroy*(a1: var Pthread_attr): cint {.
+    importc, header: pthreadh.}
+
+  proc pthread_create*(a1: var SysThread, a2: var Pthread_attr,
+            a3: proc (x: pointer): pointer {.noconv.},
+            a4: pointer): cint {.importc: "pthread_create",
+            header: pthreadh.}
+  proc pthread_join*(a1: SysThread, a2: ptr pointer): cint {.
+    importc, header: pthreadh.}
+
+  proc pthread_cancel*(a1: SysThread): cint {.
+    importc: "pthread_cancel", header: pthreadh.}
+
+  type CpuSet* {.importc: "cpu_set_t", header: schedh.} = object
+     when defined(linux) and defined(amd64):
+       abi: array[1024 div (8 * sizeof(culong)), culong]
+
+  proc cpusetZero*(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
+  proc cpusetIncl*(cpu: cint; s: var CpuSet) {.
+    importc: "CPU_SET", header: schedh.}
+
+  when defined(android):
+    # libc of android doesn't implement pthread_setaffinity_np,
+    # it exposes pthread_gettid_np though, so we can use that in combination
+    # with sched_setaffinity to set the thread affinity.
+    type Pid* {.importc: "pid_t", header: "<sys/types.h>".} = int32 # From posix_other.nim
+
+    proc setAffinityTID*(tid: Pid; setsize: csize_t; s: var CpuSet) {.
+      importc: "sched_setaffinity", header: schedh.}
+
+    proc pthread_gettid_np*(thread: SysThread): Pid {.
+      importc: "pthread_gettid_np", header: pthreadh.}
+
+    proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) =
+      setAffinityTID(pthread_gettid_np(thread), setsize, s)
+  else:
+    proc setAffinity*(thread: SysThread; setsize: csize_t; s: var CpuSet) {.
+      importc: "pthread_setaffinity_np", header: pthreadh.}
+
+
+const
+  emulatedThreadVars* = compileOption("tlsEmulation")
+# we preallocate a fixed size for thread local storage, so that no heap
+# allocations are needed. Currently less than 16K are used on a 64bit machine.
+# We use `float` for proper alignment:
+const nimTlsSize {.intdefine.} = 16000
+type
+  ThreadLocalStorage* = array[0..(nimTlsSize div sizeof(float)), float]
+  PGcThread* = ptr GcThread
+  GcThread* {.pure, inheritable.} = object
+    when emulatedThreadVars:
+      tls*: ThreadLocalStorage
+    else:
+      nil
+    when hasSharedHeap:
+      next*, prev*: PGcThread
+      stackBottom*, stackTop*: pointer
+      stackSize*: int
+    else:
+      nil
+
+const hasAllocStack* = defined(zephyr) # maybe freertos too?
+
+type
+  Thread*[TArg] = object
+    core*: PGcThread
+    sys*: SysThread
+    when TArg is void:
+      dataFn*: proc () {.nimcall, gcsafe.}
+    else:
+      dataFn*: proc (m: TArg) {.nimcall, gcsafe.}
+      data*: TArg
+    when hasAllocStack:
+      rawStack*: pointer
+
+proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.}
diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim
index f0bcbcc74..f853572b5 100644
--- a/lib/std/private/underscored_calls.nim
+++ b/lib/std/private/underscored_calls.nim
@@ -10,7 +10,9 @@
 
 ## This is an internal helper module. Do not use.
 
-import macros
+import std/macros
+
+proc underscoredCalls*(result, calls, arg0: NimNode)
 
 proc underscoredCall(n, arg0: NimNode): NimNode =
   proc underscorePos(n: NimNode): int =
@@ -19,13 +21,19 @@ proc underscoredCall(n, arg0: NimNode): NimNode =
     return 0
 
   if n.kind in nnkCallKinds:
-    result = copyNimNode(n)
-    result.add n[0]
+    if n[0].kind in {nnkIdent, nnkSym} and n[0].eqIdent("with"):
+      expectKind n[1], {nnkIdent, nnkSym}
 
-    let u = underscorePos(n)
-    for i in 1..u-1: result.add n[i]
-    result.add arg0
-    for i in u+1..n.len-1: result.add n[i]
+      result = newStmtList()
+      underscoredCalls(result, n[2 .. ^1].newStmtList, newDotExpr(arg0, n[1]))
+    else:
+      result = copyNimNode(n)
+      result.add n[0]
+
+      let u = underscorePos(n)
+      for i in 1..u-1: result.add n[i]
+      result.add arg0
+      for i in u+1..n.len-1: result.add n[i]
   elif n.kind in {nnkAsgn, nnkExprEqExpr}:
     var field = n[0]
     if n[0].kind == nnkDotExpr and n[0][0].eqIdent("_"):
diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim
index 89bb0421f..66e199dfe 100644
--- a/lib/std/private/win_setenv.nim
+++ b/lib/std/private/win_setenv.nim
@@ -23,6 +23,9 @@ check errno_t vs cint
 
 when not defined(windows): discard
 else:
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
+
   type wchar_t  {.importc: "wchar_t".} = int16
 
   proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
@@ -30,25 +33,25 @@ else:
     # same as winlean.setEnvironmentVariableA
 
   proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
-  proc c_wputenv(envstring: WideCString): cint {.importc: "_wputenv", header: "<stdlib.h>".}
-  proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".}
+  proc c_wputenv(envstring: ptr wchar_t): cint {.importc: "_wputenv", header: "<stdlib.h>".}
+  proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "<stdlib.h>".}
 
   var errno {.importc, header: "<errno.h>".}: cint
   var genviron {.importc: "_environ".}: ptr ptr char
     # xxx `ptr UncheckedArray[WideCString]` did not work
 
-  proc wcstombs(wcstr: ptr char, mbstr: WideCString, count: csize_t): csize_t {.importc, header: "<stdlib.h>".}
+  proc wcstombs(wcstr: ptr char, mbstr: ptr wchar_t, count: csize_t): csize_t {.importc, header: "<stdlib.h>".}
     # xxx cint vs errno_t?
 
   proc setEnvImpl*(name: string, value: string, overwrite: cint): cint =
     const EINVAL = cint(22)
-    let wideName = newWideCString(name)
-    if overwrite == 0 and c_wgetenv(wideName) != nil:
+    let wideName: WideCString = newWideCString(name)
+    if overwrite == 0 and c_wgetenv(cast[ptr wchar_t](wideName)) != nil:
       return 0
 
     if value != "":
-      let envstring = name & "=" & value
-      let e = c_wputenv(newWideCString(envstring))
+      let envstring: WideCString = newWideCString(name & "=" & value)
+      let e = c_wputenv(cast[ptr wchar_t](envstring))
       if e != 0:
         errno = EINVAL
         return -1
@@ -59,19 +62,19 @@ else:
     SetEnvironmentVariableA doesn't update `_environ`,
     so we have to do these terrible things.
     ]#
-    let envstring = name & "=  "
-    if c_wputenv(newWideCString(envstring)) != 0:
+    let envstring: WideCString = newWideCString(name & "=  ")
+    if c_wputenv(cast[ptr wchar_t](envstring)) != 0:
       errno = EINVAL
       return -1
     # Here lies the documentation we blatently ignore to make this work.
-    var s = c_wgetenv(wideName)
+    var s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName)))
     s[0] = Utf16Char('\0')
     #[
     This would result in a double null termination, which normally signifies the
     end of the environment variable list, so we stick a completely empty
     environment variable into the list instead.
     ]#
-    s = c_wgetenv(wideName)
+    s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName)))
     s[1] = Utf16Char('=')
     #[
     If genviron is null, the MBCS environment has not been initialized
@@ -85,15 +88,15 @@ else:
       # in the current codepage. Skip updating MBCS environment in this case.
       # For some reason, second `wcstombs` can find non-convertible characters
       # that the first `wcstombs` cannot.
-      let requiredSizeS = wcstombs(nil, wideName, 0)
+      let requiredSizeS = wcstombs(nil, cast[ptr wchar_t](wideName), 0)
       if requiredSizeS != high(csize_t):
         let requiredSize = requiredSizeS.int
         var buf = newSeq[char](requiredSize + 1)
         let buf2 = buf[0].addr
-        if wcstombs(buf2, wideName, csize_t(requiredSize + 1)) != high(csize_t):
-          var ptrToEnv = c_getenv(buf2)
+        if wcstombs(buf2, cast[ptr wchar_t](wideName), csize_t(requiredSize + 1)) != high(csize_t):
+          var ptrToEnv = c_getenv(cast[cstring](buf2))
           ptrToEnv[0] = '\0'
-          ptrToEnv = c_getenv(buf2)
+          ptrToEnv = c_getenv(cast[cstring](buf2))
           ptrToEnv[1] = '='
 
     # And now, we have to update the outer environment to have a proper empty value.
diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim
index 4664d6dcc..8e7bc6a92 100644
--- a/lib/std/setutils.nim
+++ b/lib/std/setutils.nim
@@ -14,7 +14,7 @@
 ## * `std/packedsets <packedsets.html>`_
 ## * `std/sets <sets.html>`_
 
-import typetraits, macros
+import std/[typetraits, macros]
 
 #[
   type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8
diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim
index 50175024c..213af4229 100644
--- a/lib/std/sha1.nim
+++ b/lib/std/sha1.nim
@@ -26,8 +26,11 @@ runnableExamples("-r:off"):
     b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C")
   assert a == b, "files don't match"
 
-import strutils
-from endians import bigEndian32, bigEndian64
+
+{.deprecated: "use command `nimble install checksums` and import `checksums/sha1` instead".}
+
+import std/strutils
+from std/endians import bigEndian32, bigEndian64
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -281,4 +284,4 @@ proc `==`*(a, b: SecureHash): bool =
 
 proc isValidSha1Hash*(s: string): bool =
   ## Checks if a string is a valid sha1 hash sum.
-  s.len == 40 and allCharsInSet(s, HexDigits)
+  s.len == 40 and allCharsInSet(s, HexDigits)
\ No newline at end of file
diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim
index 5c882858d..45e906795 100644
--- a/lib/std/socketstreams.nim
+++ b/lib/std/socketstreams.nim
@@ -31,39 +31,40 @@
 ## Examples
 ## ========
 ##
-## .. code-block:: Nim
-##  import std/socketstreams
+##   ```Nim
+##   import std/socketstreams
 ##
-##  var
-##    socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
-##    stream = newReadSocketStream(socket)
-##  socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST")
-##  echo stream.readLine() # Will call `recv`
-##  stream.setPosition(0)
-##  echo stream.readLine() # Will return the read line from the buffer
-##  stream.resetStream() # Buffer is now empty, position is 0
-##  echo stream.readLine() # Will call `recv` again
-##  stream.close() # Closes the socket
+##   var
+##     socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+##     stream = newReadSocketStream(socket)
+##   socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST")
+##   echo stream.readLine() # Will call `recv`
+##   stream.setPosition(0)
+##   echo stream.readLine() # Will return the read line from the buffer
+##   stream.resetStream() # Buffer is now empty, position is 0
+##   echo stream.readLine() # Will call `recv` again
+##   stream.close() # Closes the socket
+##   ```
 ##
-## .. code-block:: Nim
+##   ```Nim
+##   import std/socketstreams
 ##
-##  import std/socketstreams
-##
-##  var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
-##  socket.connect("127.0.0.1", Port(12345))
-##  var sendStream = newWriteSocketStream(socket)
-##  sendStream.write "NOM"
-##  sendStream.setPosition(1)
-##  echo sendStream.peekStr(2) # OM
-##  sendStream.write "I"
-##  sendStream.setPosition(0)
-##  echo sendStream.readStr(3) # NIM
-##  echo sendStream.getPosition() # 3
-##  sendStream.flush() # This actually performs the writing to the socket
-##  sendStream.setPosition(1)
-##  sendStream.write "I" # Throws an error as we can't write into an already sent buffer
-
-import net, streams
+##   var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
+##   socket.connect("127.0.0.1", Port(12345))
+##   var sendStream = newWriteSocketStream(socket)
+##   sendStream.write "NOM"
+##   sendStream.setPosition(1)
+##   echo sendStream.peekStr(2) # OM
+##   sendStream.write "I"
+##   sendStream.setPosition(0)
+##   echo sendStream.readStr(3) # NIM
+##   echo sendStream.getPosition() # 3
+##   sendStream.flush() # This actually performs the writing to the socket
+##   sendStream.setPosition(1)
+##   sendStream.write "I" # Throws an error as we can't write into an already sent buffer
+##   ```
+
+import std/[net, streams]
 
 type
   ReadSocketStream* = ref ReadSocketStreamObj
@@ -146,7 +147,7 @@ proc wsFlush(s: Stream) =
   s.lastFlush = s.buf.len
 
 proc rsClose(s: Stream) =
-  {.cast(tags: []).}:
+  {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something?
     var s = ReadSocketStream(s)
     s.data.close()
 
diff --git a/lib/std/staticos.nim b/lib/std/staticos.nim
new file mode 100644
index 000000000..2617c6913
--- /dev/null
+++ b/lib/std/staticos.nim
@@ -0,0 +1,13 @@
+## This module implements path handling like os module but works at only compile-time.
+## This module works even when cross compiling to OS that is not supported by os module.
+
+proc staticFileExists*(filename: string): bool {.compileTime.} =
+  ## Returns true if `filename` exists and is a regular file or symlink.
+  ##
+  ## Directories, device files, named pipes and sockets return false.
+  discard
+
+proc staticDirExists*(dir: string): bool {.compileTime.} =
+  ## Returns true if the directory `dir` exists. If `dir` is a file, false
+  ## is returned. Follows symlinks.
+  discard
diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim
index be1dd7a58..b2c36a4be 100644
--- a/lib/std/strbasics.nim
+++ b/lib/std/strbasics.nim
@@ -23,8 +23,8 @@ proc add*(x: var string, y: openArray[char]) =
   # Use `{.noalias.}` ?
   let n = x.len
   x.setLen n + y.len
-    # pending https://github.com/nim-lang/Nim/issues/14655#issuecomment-643671397
-    # use x.setLen(n + y.len, isInit = false)
+    # pending #19727
+    # setLen unnecessarily zeros memory
   var i = 0
   while i < y.len:
     x[n + i] = y[i]
diff --git a/lib/std/symlinks.nim b/lib/std/symlinks.nim
new file mode 100644
index 000000000..dbe908612
--- /dev/null
+++ b/lib/std/symlinks.nim
@@ -0,0 +1,33 @@
+## This module implements symlink (symbolic link) handling.
+
+## .. importdoc:: os.nim
+
+from std/paths import Path, ReadDirEffect
+
+from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink
+
+proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect], sideEffect.} =
+  ## Returns true if the symlink `link` exists. Will return true
+  ## regardless of whether the link points to a directory or file.
+  result = symlinkExists(link.string)
+
+proc createSymlink*(src, dest: Path) {.inline.} =
+  ## Create a symbolic link at `dest` which points to the item specified
+  ## by `src`. On most operating systems, will fail if a link already exists.
+  ##
+  ## .. warning:: Some OS's (such as Microsoft Windows) restrict the creation
+  ##   of symlinks to root users (administrators) or users with developer mode enabled.
+  ##
+  ## See also:
+  ## * `createHardlink proc`_
+  ## * `expandSymlink proc`_
+  createSymlink(src.string, dest.string)
+
+proc expandSymlink*(symlinkPath: Path): Path {.inline.} =
+  ## Returns a string representing the path to which the symbolic link points.
+  ##
+  ## On Windows this is a noop, `symlinkPath` is simply returned.
+  ##
+  ## See also:
+  ## * `createSymlink proc`_
+  result = Path(expandSymlink(symlinkPath.string))
diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim
index 22e981198..c34a025af 100644
--- a/lib/std/syncio.nim
+++ b/lib/std/syncio.nim
@@ -12,6 +12,8 @@
 include system/inclrtl
 import std/private/since
 import std/formatfloat
+when defined(windows):
+  import std/widestrs
 
 # ----------------- IO Part ------------------------------------------------
 type
@@ -36,8 +38,15 @@ type
                          ## at the end. If the file does not exist, it
                          ## will be created.
 
-  FileHandle* = cint ## type that represents an OS file handle; this is
-                      ## useful for low-level file access
+  FileHandle* = cint ## The type that represents an OS file handle; this is
+                      ## useful for low-level file access.
+
+  FileSeekPos* = enum ## Position relative to which seek should happen.
+                      # The values are ordered so that they match with stdio
+                      # SEEK_SET, SEEK_CUR and SEEK_END respectively.
+    fspSet            ## Seek to absolute value
+    fspCur            ## Seek relative to current position
+    fspEnd            ## Seek relative to end
 
 # text file handling:
 when not defined(nimscript) and not defined(js):
@@ -96,7 +105,7 @@ proc c_feof(f: File): cint {.
   importc: "feof", header: "<stdio.h>".}
 
 when not declared(c_fwrite):
-  proc c_fwrite(buf: pointer, size, n: csize_t, f: File): cint {.
+  proc c_fwrite(buf: pointer, size, n: csize_t, f: File): csize_t {.
     importc: "fwrite", header: "<stdio.h>".}
 
 # C routine that is used here:
@@ -142,21 +151,11 @@ proc c_fprintf(f: File, frmt: cstring): cint {.
 proc c_fputc(c: char, f: File): cint {.
   importc: "fputc", header: "<stdio.h>".}
 
-# When running nim in android app, stdout goes nowhere, so echo gets ignored
-# To redirect echo to the android logcat, use -d:androidNDK
-when defined(androidNDK):
-  const ANDROID_LOG_VERBOSE = 2.cint
-  proc android_log_print(prio: cint, tag: cstring, fmt: cstring): cint
-    {.importc: "__android_log_print", header: "<android/log.h>", varargs, discardable.}
-
-template sysFatal(exc, msg) =
-  raise newException(exc, msg)
-
 proc raiseEIO(msg: string) {.noinline, noreturn.} =
-  sysFatal(IOError, msg)
+  raise newException(IOError, msg)
 
 proc raiseEOF() {.noinline, noreturn.} =
-  sysFatal(EOFError, "EOF reached")
+  raise newException(EOFError, "EOF reached")
 
 proc strerror(errnum: cint): cstring {.importc, header: "<string.h>".}
 
@@ -244,7 +243,7 @@ when defined(windows):
     # machine. We also enable `setConsoleOutputCP(65001)` now by default.
     # But we cannot call printf directly as the string might contain \0.
     # So we have to loop over all the sections separated by potential \0s.
-    var i = c_fprintf(f, "%s", s)
+    var i = int c_fprintf(f, "%s", s)
     while i < s.len:
       if s[i] == '\0':
         let w = c_fputc('\0', f)
@@ -321,7 +320,7 @@ elif defined(windows):
 const
   BufSize = 4000
 
-proc close*(f: File) {.tags: [], gcsafe.} =
+proc close*(f: File) {.tags: [], gcsafe, sideEffect.} =
   ## Closes the file.
   if not f.isNil:
     discard c_fclose(f)
@@ -357,12 +356,12 @@ proc getOsFileHandle*(f: File): FileHandle =
 
 when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(windows):
   proc setInheritable*(f: FileHandle, inheritable: bool): bool =
-    ## control whether a file handle can be inherited by child processes. Returns
+    ## Controls whether a file handle can be inherited by child processes. Returns
     ## `true` on success. This requires the OS file handle, which can be
     ## retrieved via `getOsFileHandle <#getOsFileHandle,File>`_.
     ##
     ## This procedure is not guaranteed to be available for all platforms. Test for
-    ## availability with `declared() <system.html#declared,untyped>`.
+    ## availability with `declared() <system.html#declared,untyped>`_.
     when SupportIoctlInheritCtl:
       result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1
     elif defined(freertos) or defined(zephyr):
@@ -388,7 +387,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
   proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {.
     importc: "memchr", header: "<string.h>".}
 
-  when defined(windows) and not defined(useWinAnsi):
+  when defined(windows):
     proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer,
                      nNumberOfCharsToRead: int32,
                      lpNumberOfCharsRead: ptr int32,
@@ -420,7 +419,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
     if f.isatty:
       const numberOfCharsToRead = 2048
       var numberOfCharsRead = 0'i32
-      var buffer = newWideCString("", numberOfCharsToRead)
+      var buffer = newWideCString(numberOfCharsToRead)
       if readConsole(getOsFileHandle(f), addr(buffer[0]),
         numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0:
         var error = getLastError()
@@ -465,7 +464,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
     while true:
       # fixes #9634; this pattern may need to be abstracted as a template if reused;
       # likely other io procs need this for correctness.
-      fgetsSuccess = c_fgets(addr line[pos], sp.cint, f) != nil
+      fgetsSuccess = c_fgets(cast[cstring](addr line[pos]), sp.cint, f) != nil
       if fgetsSuccess: break
       when not defined(nimscript):
         if errno == EINTR:
@@ -478,15 +477,16 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect],
     let m = c_memchr(addr line[pos], '\L'.ord, cast[csize_t](sp))
     if m != nil:
       # \l found: Could be our own or the one by fgets, in any case, we're done
-      var last = cast[ByteAddress](m) - cast[ByteAddress](addr line[0])
+      var last = cast[int](m) - cast[int](addr line[0])
       if last > 0 and line[last-1] == '\c':
         line.setLen(last-1)
         return last > 1 or fgetsSuccess
-        # We have to distinguish between two possible cases:
+      elif last > 0 and line[last-1] == '\0':
+        # We have to distinguish among three possible cases:
         # \0\l\0 => line ending in a null character.
         # \0\l\l => last line without newline, null was put there by fgets.
-      elif last > 0 and line[last-1] == '\0':
-        if last < pos + sp - 1 and line[last+1] != '\0':
+        #   \0\l => last line without newline, null was put there by fgets.
+        if last >= pos + sp - 1 or line[last+1] != '\0': # bug #21273
           dec last
       line.setLen(last)
       return last > 0 or fgetsSuccess
@@ -571,7 +571,7 @@ proc readAllFile(file: File, len: int64): string =
   result = newString(len)
   let bytes = readBuffer(file, addr(result[0]), len)
   if endOfFile(file):
-    if bytes < len:
+    if bytes.int64 < len:
       result.setLen(bytes)
   else:
     # We read all the bytes but did not reach the EOF
@@ -609,7 +609,7 @@ proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline,
 
 # interface to the C procs:
 
-when defined(windows) and not defined(useWinAnsi):
+when defined(windows):
   when defined(cpp):
     proc wfopen(filename, mode: WideCString): pointer {.
       importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.}
@@ -648,6 +648,9 @@ const
         ""
     else:
       ""
+  RawFormatOpen: array[FileMode, cstring] = [
+    # used for open by FileHandle, which calls `fdopen`
+    cstring("rb"), "wb", "w+b", "r+b", "ab"]
   FormatOpen: array[FileMode, cstring] = [
     cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag,
     "r+b" & NoInheritFlag, "ab" & NoInheritFlag
@@ -715,7 +718,7 @@ proc open*(f: var File, filename: string,
 
     result = true
     f = cast[File](p)
-    if bufSize > 0 and bufSize <= high(cint).int:
+    if bufSize > 0 and bufSize.uint <= high(uint):
       discard c_setvbuf(f, nil, IOFBF, cast[csize_t](bufSize))
     elif bufSize == 0:
       discard c_setvbuf(f, nil, IONBF, 0)
@@ -749,7 +752,7 @@ proc open*(f: var File, filehandle: FileHandle,
         filehandle) else: filehandle
     if not setInheritable(oshandle, false):
       return false
-  f = c_fdopen(filehandle, FormatOpen[mode])
+  f = c_fdopen(filehandle, RawFormatOpen[mode])
   result = f != nil
 
 proc open*(filename: string,
@@ -761,9 +764,9 @@ proc open*(filename: string,
   ##
   ## The file handle associated with the resulting `File` is not inheritable.
   if not open(result, filename, mode, bufSize):
-    sysFatal(IOError, "cannot open: " & filename)
+    raise newException(IOError, "cannot open: " & filename)
 
-proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} =
+proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign, sideEffect.} =
   ## Sets the position of the file pointer that is used for read/write
   ## operations. The file's first byte has the index zero.
   if c_fseek(f, pos, cint(relativeTo)) != 0:
@@ -791,52 +794,6 @@ proc setStdIoUnbuffered*() {.tags: [], benign.} =
   when declared(stdin):
     discard c_setvbuf(stdin, nil, IONBF, 0)
 
-when declared(stdout):
-  when defined(windows) and compileOption("threads"):
-    proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
-
-    const insideRLocksModule = false
-    include "system/syslocks"
-
-
-    var echoLock: SysLock
-    initSysLock echoLock
-    addSysExitProc(proc() {.noconv.} = deinitSys(echoLock))
-
-  const stdOutLock = not defined(windows) and
-                     not defined(android) and
-                     not defined(nintendoswitch) and
-                     not defined(freertos) and
-                     not defined(zephyr) and
-                     hostOS != "any"
-
-  proc echoBinSafe(args: openArray[string]) {.compilerproc.} =
-    when defined(androidNDK):
-      var s = ""
-      for arg in args:
-        s.add arg
-      android_log_print(ANDROID_LOG_VERBOSE, "nim", s)
-    else:
-      # flockfile deadlocks some versions of Android 5.x.x
-      when stdOutLock:
-        proc flockfile(f: File) {.importc, nodecl.}
-        proc funlockfile(f: File) {.importc, nodecl.}
-        flockfile(stdout)
-      when defined(windows) and compileOption("threads"):
-        acquireSys echoLock
-      for s in args:
-        when defined(windows):
-          writeWindows(stdout, s)
-        else:
-          discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout)
-      const linefeed = "\n"
-      discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
-      discard c_fflush(stdout)
-      when stdOutLock:
-        funlockfile(stdout)
-      when defined(windows) and compileOption("threads"):
-        releaseSys echoLock
-
 
 when defined(windows) and not defined(nimscript) and not defined(js):
   # work-around C's sucking abstraction:
@@ -854,14 +811,33 @@ when defined(windows) and not defined(nimscript) and not defined(js):
 
 when defined(windows) and appType == "console" and
     not defined(nimDontSetUtf8CodePage) and not defined(nimscript):
+  import std/exitprocs
+
   proc setConsoleOutputCP(codepage: cuint): int32 {.stdcall, dynlib: "kernel32",
     importc: "SetConsoleOutputCP".}
   proc setConsoleCP(wCodePageID: cuint): int32 {.stdcall, dynlib: "kernel32",
     importc: "SetConsoleCP".}
+  proc getConsoleOutputCP(): cuint {.stdcall, dynlib: "kernel32",
+    importc: "GetConsoleOutputCP".}
+  proc getConsoleCP(): cuint {.stdcall, dynlib: "kernel32",
+    importc: "GetConsoleCP".}
+
+  const Utf8codepage = 65001'u32
+
+  let
+    consoleOutputCP = getConsoleOutputCP()
+    consoleCP = getConsoleCP()
 
-  const Utf8codepage = 65001
-  discard setConsoleOutputCP(Utf8codepage)
-  discard setConsoleCP(Utf8codepage)
+  proc restoreConsoleOutputCP() = discard setConsoleOutputCP(consoleOutputCP)
+  proc restoreConsoleCP() = discard setConsoleCP(consoleCP)
+
+  if consoleOutputCP != Utf8codepage:
+    discard setConsoleOutputCP(Utf8codepage)
+    addExitProc(restoreConsoleOutputCP)
+
+  if consoleCP != Utf8codepage:
+    discard setConsoleCP(Utf8codepage)
+    addExitProc(restoreConsoleCP)
 
 proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} =
   ## Opens a file named `filename` for reading, calls `readAll
@@ -876,7 +852,7 @@ proc readFile*(filename: string): string {.tags: [ReadIOEffect], benign.} =
     finally:
       close(f)
   else:
-    sysFatal(IOError, "cannot open: " & filename)
+    raise newException(IOError, "cannot open: " & filename)
 
 proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
   ## Opens a file named `filename` for writing. Then writes the
@@ -889,7 +865,7 @@ proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} =
     finally:
       close(f)
   else:
-    sysFatal(IOError, "cannot open: " & filename)
+    raise newException(IOError, "cannot open: " & filename)
 
 proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} =
   ## Opens a file named `filename` for writing. Then writes the
@@ -898,7 +874,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} =
   var f: File = nil
   if open(f, filename, fmWrite):
     try:
-      f.writeBuffer(unsafeAddr content[0], content.len)
+      discard f.writeBuffer(unsafeAddr content[0], content.len)
     finally:
       close(f)
   else:
@@ -919,7 +895,7 @@ proc readLines*(filename: string, n: Natural): seq[string] =
     finally:
       close(f)
   else:
-    sysFatal(IOError, "cannot open: " & filename)
+    raise newException(IOError, "cannot open: " & filename)
 
 template readLines*(filename: string): seq[
     string] {.deprecated: "use readLines with two arguments".} =
@@ -960,3 +936,7 @@ iterator lines*(f: File): string {.tags: [ReadIOEffect].} =
         result.lines += 1
   var res = newStringOfCap(80)
   while f.readLine(res): yield res
+
+template `&=`*(f: File, x: typed) =
+  ## An alias for `write`.
+  write(f, x)
diff --git a/lib/system/atomics.nim b/lib/std/sysatomics.nim
index 8d29c287d..2f203b3eb 100644
--- a/lib/system/atomics.nim
+++ b/lib/std/sysatomics.nim
@@ -7,17 +7,22 @@
 #    distribution, for details about the copyright.
 #
 
+when defined(nimPreviewSlimSystem):
+  {.deprecated: "use `std/atomics` instead".}
+
 # Atomic operations for Nim.
 {.push stackTrace:off, profiler:off.}
 
-const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang)
+const
+  hasThreadSupport = compileOption("threads") and not defined(nimscript)
+const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) or defined(nintendoswitch)
 const someVcc = defined(vcc) or defined(clang_cl)
 
 type
   AtomType* = SomeNumber|pointer|ptr|char|bool
     ## Type Class representing valid types for use with atomic procs
 
-when someGcc and hasThreadSupport:
+when someGcc:
   type AtomMemModel* = distinct cint
 
   var ATOMIC_RELAXED* {.importc: "__ATOMIC_RELAXED", nodecl.}: AtomMemModel
@@ -164,16 +169,16 @@ when someGcc and hasThreadSupport:
     ## ignore this parameter.
 
   template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
-elif someVcc and hasThreadSupport:
+elif someVcc:
   type AtomMemModel* = distinct cint
 
   const
-    ATOMIC_RELAXED = 0.AtomMemModel
-    ATOMIC_CONSUME = 1.AtomMemModel
-    ATOMIC_ACQUIRE = 2.AtomMemModel
-    ATOMIC_RELEASE = 3.AtomMemModel
-    ATOMIC_ACQ_REL = 4.AtomMemModel
-    ATOMIC_SEQ_CST = 5.AtomMemModel
+    ATOMIC_RELAXED* = 0.AtomMemModel
+    ATOMIC_CONSUME* = 1.AtomMemModel
+    ATOMIC_ACQUIRE* = 2.AtomMemModel
+    ATOMIC_RELEASE* = 3.AtomMemModel
+    ATOMIC_ACQ_REL* = 4.AtomMemModel
+    ATOMIC_SEQ_CST* = 5.AtomMemModel
 
   proc `==`(x1, x2: AtomMemModel): bool {.borrow.}
 
@@ -181,6 +186,31 @@ elif someVcc and hasThreadSupport:
   proc writeBarrier() {.importc: "_WriteBarrier", header: "<intrin.h>".}
   proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
 
+  when defined(cpp):
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importcpp: "_InterlockedCompareExchange(static_cast<long volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
+    proc interlockedExchange8(location: pointer; desired: int8): int8 {.importcpp: "_InterlockedExchange8(static_cast<NI8 volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange16(location: pointer; desired: int16): int16 {.importcpp: "_InterlockedExchange16(static_cast<NI16 volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange32(location: pointer; desired: int32): int32 {.importcpp: "_InterlockedExchange(static_cast<long volatile *>(#), #)", header: "<intrin.h>".}
+    proc interlockedExchange64(location: pointer; desired: int64): int64 {.importcpp: "_InterlockedExchange64(static_cast<NI64 volatile *>(#), #)", header: "<intrin.h>".}
+  else:
+    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
+      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
+    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
+      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
+    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
+      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
+
+    proc interlockedExchange8(location: pointer; desired: int8): int8 {.importc: "_InterlockedExchange8", header: "<intrin.h>".}
+    proc interlockedExchange16(location: pointer; desired: int16): int16 {.importc: "_InterlockedExchange16", header: "<intrin.h>".}
+    proc interlockedExchange32(location: pointer; desired: int32): int32 {.importc: "_InterlockedExchange", header: "<intrin.h>".}
+    proc interlockedExchange64(location: pointer; desired: int64): int64 {.importc: "_InterlockedExchange64", header: "<intrin.h>".}
+
+
   template barrier(mem: AtomMemModel) =
     when mem == ATOMIC_RELAXED: discard
     elif mem == ATOMIC_CONSUME: readBarrier()
@@ -189,10 +219,28 @@ elif someVcc and hasThreadSupport:
     elif mem == ATOMIC_ACQ_REL: fence()
     elif mem == ATOMIC_SEQ_CST: fence()
 
+  proc atomicStoreN*[T: AtomType](p: ptr T, val: T, mem: static[AtomMemModel]) =
+    barrier(mem)
+    p[] = val
+
   proc atomicLoadN*[T: AtomType](p: ptr T, mem: static[AtomMemModel]): T =
     result = p[]
     barrier(mem)
 
+  proc atomicCompareExchangeN*[T: ptr](p, expected: ptr T, desired: T,
+    weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool =
+    when sizeof(T) == 8:
+      interlockedCompareExchange64(p, cast[int64](desired), cast[int64](expected)) ==
+        cast[int64](expected)
+    elif sizeof(T) == 4:
+      interlockedCompareExchange32(p, cast[int32](desired), cast[int32](expected)) ==
+        cast[int32](expected)
+
+  proc atomicExchangeN*[T: ptr](p: ptr T, val: T, mem: AtomMemModel): T =
+    when sizeof(T) == 8:
+      cast[T](interlockedExchange64(p, cast[int64](val)))
+    elif sizeof(T) == 4:
+      cast[T](interlockedExchange32(p, cast[int32](val)))
   when defined(cpp):
     when sizeof(int) == 8:
       proc addAndFetch*(p: ptr int, val: int): int {.
@@ -215,7 +263,9 @@ else:
     inc(p[], val)
     result = p[]
 
-proc atomicInc*(memLoc: var int, x: int = 1): int =
+
+proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} =
+  ## Atomically increments the integer by some `x`. It returns the new value.
   when someGcc and hasThreadSupport:
     result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST)
   elif someVcc and hasThreadSupport:
@@ -225,7 +275,8 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
     inc(memLoc, x)
     result = memLoc
 
-proc atomicDec*(memLoc: var int, x: int = 1): int =
+proc atomicDec*(memLoc: var int, x: int = 1): int {.inline, discardable, raises: [], tags: [].} =
+  ## Atomically decrements the integer by some `x`. It returns the new value.
   when someGcc and hasThreadSupport:
     when declared(atomicSubFetch):
       result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST)
@@ -239,21 +290,6 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
     result = memLoc
 
 when someVcc:
-  when defined(cpp):
-    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-      {.importcpp: "_InterlockedCompareExchange64(static_cast<NI64 volatile *>(#), #, #)", header: "<intrin.h>".}
-    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-      {.importcpp: "_InterlockedCompareExchange(static_cast<NI volatile *>(#), #, #)", header: "<intrin.h>".}
-    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-      {.importcpp: "_InterlockedCompareExchange8(static_cast<char volatile *>(#), #, #)", header: "<intrin.h>".}
-  else:
-    proc interlockedCompareExchange64(p: pointer; exchange, comparand: int64): int64
-      {.importc: "_InterlockedCompareExchange64", header: "<intrin.h>".}
-    proc interlockedCompareExchange32(p: pointer; exchange, comparand: int32): int32
-      {.importc: "_InterlockedCompareExchange", header: "<intrin.h>".}
-    proc interlockedCompareExchange8(p: pointer; exchange, comparand: byte): byte
-      {.importc: "_InterlockedCompareExchange8", header: "<intrin.h>".}
-
   proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
     when sizeof(T) == 8:
       interlockedCompareExchange64(p, cast[int64](newValue), cast[int64](oldValue)) ==
@@ -327,7 +363,7 @@ elif someGcc or defined(tcc):
 elif defined(icl):
   proc cpuRelax* {.importc: "_mm_pause", header: "xmmintrin.h".}
 elif false:
-  from os import sleep
+  from std/os import sleep
 
   proc cpuRelax* {.inline.} = os.sleep(1)
 
diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim
index ff62c920b..6f2c6b0c1 100644
--- a/lib/std/sysrand.nim
+++ b/lib/std/sysrand.nim
@@ -20,7 +20,7 @@
 ## | :---                 | ----:                 |
 ## | Windows              | `BCryptGenRandom`_    |
 ## | Linux                | `getrandom`_          |
-## | MacOSX               | `getentropy`_         |
+## | MacOSX               | `SecRandomCopyBytes`_ |
 ## | iOS                  | `SecRandomCopyBytes`_ |
 ## | OpenBSD              | `getentropy openbsd`_ |
 ## | FreeBSD              | `getrandom freebsd`_  |
@@ -57,16 +57,16 @@ runnableExamples:
 
 
 when not defined(js):
-  import os
+  import std/oserrors
 
 when defined(posix):
-  import posix
+  import std/posix
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
 const
-  batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr) or (defined(macosx) and not defined(ios))
+  batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr)
   batchSize {.used.} = 256
 
 when batchImplOS:
@@ -168,8 +168,10 @@ elif defined(windows):
     result = randomBytes(addr dest[0], size)
 
 elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten):
-  # TODO using let, pending bootstrap >= 1.4.0
-  var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong
+  when (NimMajor, NimMinor) >= (1, 4):
+    let SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong
+  else:
+    var SYS_getrandom {.importc: "SYS_getrandom", header: "<sys/syscall.h>".}: clong
   const syscallHeader = """#include <unistd.h>
 #include <sys/syscall.h>"""
 
@@ -190,12 +192,11 @@ elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten):
     while result < size:
       let readBytes = syscall(SYS_getrandom, addr dest[result], cint(size - result), 0).int
       if readBytes == 0:
-        doAssert false
+        raiseAssert "unreachable"
       elif readBytes > 0:
         inc(result, readBytes)
       else:
-        if osLastError().int in {EINTR, EAGAIN}:
-          discard
+        if osLastError().cint in [EINTR, EAGAIN]: discard
         else:
           result = -1
           break
@@ -231,8 +232,8 @@ elif defined(freebsd):
   proc getRandomImpl(p: pointer, size: int): int {.inline.} =
     result = getrandom(p, csize_t(size), 0)
 
-elif defined(ios):
-  {.passL: "-framework Security".}
+elif defined(ios) or defined(macosx):
+  {.passl: "-framework Security".}
 
   const errSecSuccess = 0 ## No error.
 
@@ -254,19 +255,6 @@ elif defined(ios):
 
     result = secRandomCopyBytes(nil, csize_t(size), addr dest[0])
 
-elif defined(macosx):
-  const sysrandomHeader = """#include <Availability.h>
-#include <sys/random.h>
-"""
-
-  proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.}
-    # getentropy() fills a buffer with random data, which can be used as input
-    # for process-context pseudorandom generators like arc4random(3).
-    # The maximum buffer size permitted is 256 bytes.
-
-  proc getRandomImpl(p: pointer, size: int): int {.inline.} =
-    result = getentropy(p, csize_t(size)).int
-
 else:
   template urandomImpl(result: var int, dest: var openArray[byte]) =
     let size = dest.len
diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim
index ac35e26bf..7e59747f5 100644
--- a/lib/std/tasks.nim
+++ b/lib/std/tasks.nim
@@ -11,7 +11,6 @@
 ## A `Task` should be only owned by a single Thread, it cannot be shared by threads.
 
 import std/[macros, isolation, typetraits]
-import system/ansi_c
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
@@ -62,24 +61,33 @@ when compileOption("threads"):
 
 type
   Task* = object ## `Task` contains the callback and its arguments.
-    callback: proc (args: pointer) {.nimcall, gcsafe.}
+    callback: proc (args, res: pointer) {.nimcall, gcsafe.}
     args: pointer
     destroy: proc (args: pointer) {.nimcall, gcsafe.}
 
 
 proc `=copy`*(x: var Task, y: Task) {.error.}
 
-proc `=destroy`*(t: var Task) {.inline, gcsafe.} =
-  ## Frees the resources allocated for a `Task`.
-  if t.args != nil:
-    if t.destroy != nil:
-      t.destroy(t.args)
-    c_free(t.args)
-
-proc invoke*(task: Task) {.inline, gcsafe.} =
+const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+when defined(nimAllowNonVarDestructor) and arcLike:
+  proc `=destroy`*(t: Task) {.inline, gcsafe.} =
+    ## Frees the resources allocated for a `Task`.
+    if t.args != nil:
+      if t.destroy != nil:
+        t.destroy(t.args)
+      deallocShared(t.args)
+else:
+  proc `=destroy`*(t: var Task) {.inline, gcsafe.} =
+    ## Frees the resources allocated for a `Task`.
+    if t.args != nil:
+      if t.destroy != nil:
+        t.destroy(t.args)
+      deallocShared(t.args)
+
+proc invoke*(task: Task; res: pointer = nil) {.inline, gcsafe.} =
   ## Invokes the `task`.
   assert task.callback != nil
-  task.callback(task.args)
+  task.callback(task.args, res)
 
 template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) =
   # block:
@@ -102,21 +110,38 @@ template addAllNode(assignParam: NimNode, procParam: NimNode) =
   tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0]))
   scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam)
 
+proc analyseRootSym(s: NimNode): NimNode =
+  result = s
+  while true:
+    case result.kind
+    of nnkBracketExpr, nnkDerefExpr, nnkHiddenDeref,
+        nnkAddr, nnkHiddenAddr,
+        nnkObjDownConv, nnkObjUpConv:
+      result = result[0]
+    of nnkDotExpr, nnkCheckedFieldExpr, nnkHiddenStdConv, nnkHiddenSubConv:
+      result = result[1]
+    else:
+      break
+
 macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task =
   ## Converts the call and its arguments to `Task`.
-  runnableExamples("--gc:orc"):
+  runnableExamples:
     proc hello(a: int) = echo a
 
     let b = toTask hello(13)
     assert b is Task
 
-  doAssert getTypeInst(e).typeKind == ntyVoid
+  let retType = getTypeInst(e)
+  let returnsVoid = retType.typeKind == ntyVoid
+
+  let rootSym = analyseRootSym(e[0])
+  expectKind rootSym, nnkSym
 
   when compileOption("threads"):
-    if not isGcSafe(e[0]):
+    if not isGcSafe(rootSym):
       error("'toTask' takes a GC safe call expression", e)
 
-  if hasClosure(e[0]):
+  if hasClosure(rootSym):
     error("closure call is not allowed", e)
 
   if e.len > 1:
@@ -165,7 +190,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC
         # passing by static parameters
         # so we pass them directly instead of passing by scratchObj
         callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i])
-      of nnkSym, nnkPtrTy:
+      of nnkSym, nnkPtrTy, nnkProcTy, nnkTupleConstr:
         addAllNode(param, e[i])
       of nnkCharLit..nnkNilLit:
         callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i])
@@ -187,40 +212,43 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC
 
 
     let scratchObjPtrType = quote do:
-      cast[ptr `scratchObjType`](c_calloc(csize_t 1, csize_t sizeof(`scratchObjType`)))
-
-    let scratchLetSection = newLetStmt(
-      scratchIdent,
-      scratchObjPtrType
-    )
+      cast[ptr `scratchObjType`](allocShared0(sizeof(`scratchObjType`)))
 
-    let scratchCheck = quote do:
-      if `scratchIdent`.isNil:
-        raise newException(OutOfMemDefect, "Could not allocate memory")
+    let scratchLetSection = newLetStmt(scratchIdent, scratchObjPtrType)
 
     var stmtList = newStmtList()
     stmtList.add(scratchObj)
     stmtList.add(scratchLetSection)
-    stmtList.add(scratchCheck)
     stmtList.add(nnkBlockStmt.newTree(newEmptyNode(), newStmtList(scratchAssignList)))
 
     var functionStmtList = newStmtList()
     let funcCall = newCall(e[0], callNode)
     functionStmtList.add tempAssignList
-    functionStmtList.add funcCall
 
-    let funcName = genSym(nskProc, e[0].strVal)
+    let funcName = genSym(nskProc, rootSym.strVal)
     let destroyName = genSym(nskProc, "destroyScratch")
     let objTemp2 = genSym(ident = "obj")
     let tempNode = quote("@") do:
         `=destroy`(@objTemp2[])
 
+    var funcDecl: NimNode
+    if returnsVoid:
+      funcDecl = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          let `objTemp` = cast[ptr `scratchObjType`](args)
+          `functionStmtList`
+          `funcCall`
+    else:
+      funcDecl = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          let `objTemp` = cast[ptr `scratchObjType`](args)
+          `functionStmtList`
+          cast[ptr `retType`](res)[] = `funcCall`
+
     result = quote do:
       `stmtList`
 
-      proc `funcName`(args: pointer) {.gcsafe, nimcall.} =
-        let `objTemp` = cast[ptr `scratchObjType`](args)
-        `functionStmtList`
+      `funcDecl`
 
       proc `destroyName`(args: pointer) {.gcsafe, nimcall.} =
         let `objTemp2` = cast[ptr `scratchObjType`](args)
@@ -229,18 +257,26 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC
       Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`)
   else:
     let funcCall = newCall(e[0])
-    let funcName = genSym(nskProc, e[0].strVal)
+    let funcName = genSym(nskProc, rootSym.strVal)
 
-    result = quote do:
-      proc `funcName`(args: pointer) {.gcsafe, nimcall.} =
-        `funcCall`
+    if returnsVoid:
+      result = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          `funcCall`
+
+        Task(callback: `funcName`, args: nil)
+    else:
+      result = quote do:
+        proc `funcName`(args, res: pointer) {.gcsafe, nimcall.} =
+          cast[ptr `retType`](res)[] = `funcCall`
+
+        Task(callback: `funcName`, args: nil)
 
-      Task(callback: `funcName`, args: nil)
 
   when defined(nimTasksDebug):
     echo result.repr
 
-runnableExamples("--gc:orc"):
+runnableExamples:
   block:
     var num = 0
     proc hello(a: int) = inc num, a
diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim
index ee42f8a5c..539305bde 100644
--- a/lib/std/tempfiles.nim
+++ b/lib/std/tempfiles.nim
@@ -17,7 +17,7 @@ See also:
 * `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html
 ]#
 
-import os, random
+import std / [os, random]
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -29,7 +29,9 @@ const
 
 
 when defined(windows):
-  import winlean
+  import std/winlean
+  when defined(nimPreviewSlimSystem):
+    import std/widestrs
 
   var O_RDWR {.importc: "_O_RDWR", header: "<fcntl.h>".}: cint
 
@@ -44,7 +46,7 @@ when defined(windows):
   proc close_osfandle(fd: cint): cint {.
     importc: "_close", header: "<io.h>".}
 else:
-  import posix
+  import std/posix
 
   proc c_fdopen(
     filehandle: cint,
diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim
index 7fb6e6d46..de051b135 100644
--- a/lib/std/time_t.nim
+++ b/lib/std/time_t.nim
@@ -14,10 +14,10 @@ when defined(nimdoc):
       ## Wrapper for `time_t`. On posix, this is an alias to `posix.Time`.
 elif defined(windows):
   when defined(i386) and defined(gcc):
-    type Time* {.importc: "time_t", header: "<time.h>".} = distinct int32
+    type Time* {.importc: "time_t", header: "<time.h>".} = distinct clong
   else:
     # newest version of Visual C++ defines time_t to be of 64 bits
     type Time* {.importc: "time_t", header: "<time.h>".} = distinct int64
 elif defined(posix):
-  import posix
+  import std/posix
   export posix.Time
\ No newline at end of file
diff --git a/lib/system/threads.nim b/lib/std/typedthreads.nim
index aaaa33bb7..7b0b81968 100644
--- a/lib/system/threads.nim
+++ b/lib/std/typedthreads.nim
@@ -7,59 +7,97 @@
 #    distribution, for details about the copyright.
 #
 
-## Thread support for Nim.
-##
-## **Note**: This is part of the system module. Do not import it directly.
-## To activate thread support you need to compile
-## with the `--threads:on`:option: command line switch.
-##
-## Nim's memory model for threads is quite different from other common
-## programming languages (C, Pascal): Each thread has its own
-## (garbage collected) heap and sharing of memory is restricted. This helps
-## to prevent race conditions and improves efficiency. See `the manual for
-## details of this memory model <manual.html#threads>`_.
-##
-## Examples
-## ========
-##
-## .. code-block:: Nim
-##
-##  import std/locks
-##
-##  var
-##    thr: array[0..4, Thread[tuple[a,b: int]]]
-##    L: Lock
-##
-##  proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
-##    for i in interval.a..interval.b:
-##      acquire(L) # lock stdout
-##      echo i
-##      release(L)
-##
-##  initLock(L)
-##
-##  for i in 0..high(thr):
-##    createThread(thr[i], threadFunc, (i*10, i*10+5))
-##  joinThreads(thr)
-##
-##  deinitLock(L)
-
-when not declared(ThisIsSystem):
-  {.error: "You must not import this module explicitly".}
+##[
+Thread support for Nim. Threads allow multiple functions to execute concurrently.
+ 
+In Nim, threads are a low-level construct and using a library like `malebolgia`, `taskpools` or `weave` is recommended.
+ 
+When creating a thread, you can pass arguments to it. As Nim's garbage collector does not use atomic references, sharing
+`ref` and other variables managed by the garbage collector between threads is not supported.
+Use global variables to do so, or pointers.
+ 
+Memory allocated using [`sharedAlloc`](./system.html#allocShared.t%2CNatural) can be used and shared between threads.
+
+To communicate between threads, consider using [channels](./system.html#Channel)
+
+Examples
+========
+
+```Nim
+import std/locks
+
+var
+  thr: array[0..4, Thread[tuple[a,b: int]]]
+  L: Lock
+
+proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
+  for i in interval.a..interval.b:
+    acquire(L) # lock stdout
+    echo i
+    release(L)
+
+initLock(L)
+
+for i in 0..high(thr):
+  createThread(thr[i], threadFunc, (i*10, i*10+5))
+joinThreads(thr)
+
+deinitLock(L)
+```
+ 
+When using a memory management strategy that supports shared heaps like `arc` or `boehm`,
+you can pass pointer to threads and share memory between them, but the memory must outlive the thread.
+The default memory management strategy, `orc`, supports this.
+The example below is **not valid** for memory management strategies that use local heaps like `refc`!
+
+```Nim
+import locks
+ 
+var l: Lock
+ 
+proc threadFunc(obj: ptr seq[int]) {.thread.} =
+  withLock l:
+    for i in 0..<100:
+      obj[].add(obj[].len * obj[].len)
+ 
+proc threadHandler() =
+  var thr: array[0..4, Thread[ptr seq[int]]]
+  var s = newSeq[int]()
+    
+  for i in 0..high(thr):
+    createThread(thr[i], threadFunc, s.addr)
+  joinThreads(thr)
+  echo s
+ 
+initLock(l)
+threadHandler()
+deinitLock(l)
+```
+]##
+
+
+import std/private/[threadtypes]
+export Thread
+
+import system/ansi_c
 
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-const
-  hasAllocStack = defined(zephyr) # maybe freertos too?
+when defined(genode):
+  import genode/env
 
-when hasAllocStack or defined(zephyr) or defined(freertos):
+when hostOS == "any":
+  {.error: "Threads not implemented for os:any. Please compile with --threads:off.".}
+
+when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or
+    defined(cpu16) or defined(cpu8):
   const
-    nimThreadStackSize {.intdefine.} = 8192 
+    nimThreadStackSize {.intdefine.} = 8192
     nimThreadStackGuard {.intdefine.} = 128
 
-    StackGuardSize = nimThreadStackGuard 
-    ThreadStackSize = nimThreadStackSize - nimThreadStackGuard 
+    StackGuardSize = nimThreadStackGuard
+    ThreadStackSize = nimThreadStackSize - nimThreadStackGuard
 else:
   const
     StackGuardSize = 4096
@@ -71,6 +109,14 @@ else:
 
     ThreadStackSize = ThreadStackMask+1 - StackGuardSize
 
+
+when defined(gcDestructors):
+  proc allocThreadStorage(size: int): pointer =
+    result = c_malloc(csize_t size)
+    zeroMem(result, size)
+else:
+  template allocThreadStorage(size: untyped): untyped = allocShared0(size)
+
 #const globalsSlot = ThreadVarSlot(0)
 #sysAssert checkSlot.int == globalsSlot.int
 
@@ -80,146 +126,25 @@ when defined(zephyr):
   #include <pthread.h>
   """.}
 
-# create for the main thread. Note: do not insert this data into the list
-# of all threads; it's not to be stopped etc.
-when not defined(useNimRtl):
-  #when not defined(createNimRtl): initStackBottom()
-  when declared(initGC):
-    initGC()
-    when not emulatedThreadVars:
-      type ThreadType {.pure.} = enum
-        None = 0,
-        NimThread = 1,
-        ForeignThread = 2
-      var
-        threadType {.rtlThreadVar.}: ThreadType
-
-      threadType = ThreadType.NimThread
 
 # We jump through some hops here to ensure that Nim thread procs can have
 # the Nim calling convention. This is needed because thread procs are
 # ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just
 # use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway.
 
-type
-  Thread*[TArg] = object
-    core: PGcThread
-    sys: SysThread
-    when TArg is void:
-      dataFn: proc () {.nimcall, gcsafe.}
-    else:
-      dataFn: proc (m: TArg) {.nimcall, gcsafe.}
-      data: TArg
-    when hasAllocStack:
-      rawStack: pointer
-
-proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.}
 
-var
-  threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}]
-
-proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) =
-  ## Registers a *thread local* handler that is called at the thread's
-  ## destruction.
-  ##
-  ## A thread is destructed when the `.thread` proc returns
-  ## normally or when it raises an exception. Note that unhandled exceptions
-  ## in a thread nevertheless cause the whole process to die.
-  threadDestructionHandlers.add handler
-
-template afterThreadRuns() =
-  for i in countdown(threadDestructionHandlers.len-1, 0):
-    threadDestructionHandlers[i]()
-
-when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
-  proc deallocOsPages() {.rtl, raises: [].}
-
-proc threadTrouble() {.raises: [], gcsafe.}
-  ## defined in system/excpt.nim
-
-when defined(boehmgc):
-  type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
-  proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
-    {.importc: "GC_call_with_stack_base", boehmGC.}
-  proc boehmGC_register_my_thread(sb: pointer)
-    {.importc: "GC_register_my_thread", boehmGC.}
-  proc boehmGC_unregister_my_thread()
-    {.importc: "GC_unregister_my_thread", boehmGC.}
-
-  proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} =
-    boehmGC_register_my_thread(sb)
-    try:
-      let thrd = cast[ptr Thread[TArg]](thrd)
-      when TArg is void:
-        thrd.dataFn()
-      else:
-        thrd.dataFn(thrd.data)
-    except:
-      threadTrouble()
-    finally:
-      afterThreadRuns()
-    boehmGC_unregister_my_thread()
-else:
-  proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
-    try:
-      when TArg is void:
-        thrd.dataFn()
-      else:
-        when defined(nimV2):
-          thrd.dataFn(thrd.data)
-        else:
-          var x: TArg
-          deepCopy(x, thrd.data)
-          thrd.dataFn(x)
-    except:
-      threadTrouble()
-    finally:
-      afterThreadRuns()
-      when hasAllocStack:
-        deallocShared(thrd.rawStack)
-
-proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
-  when defined(boehmgc):
-    boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
-  elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors:
-    var p {.volatile.}: pointer
-    # init the GC for refc/markandsweep
-    nimGC_setStackBottom(addr(p))
-    initGC()
-    when declared(threadType):
-      threadType = ThreadType.NimThread
-    threadProcWrapDispatch[TArg](thrd)
-    when declared(deallocOsPages): deallocOsPages()
-  else:
-    threadProcWrapDispatch(thrd)
-
-template threadProcWrapperBody(closure: untyped): untyped =
-  var thrd = cast[ptr Thread[TArg]](closure)
-  var core = thrd.core
-  when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
-  threadProcWrapStackFrame(thrd)
-  # Since an unhandled exception terminates the whole process (!), there is
-  # no need for a ``try finally`` here, nor would it be correct: The current
-  # exception is tried to be re-raised by the code-gen after the ``finally``!
-  # However this is doomed to fail, because we already unmapped every heap
-  # page!
-
-  # mark as not running anymore:
-  thrd.core = nil
-  thrd.dataFn = nil
-  deallocShared(cast[pointer](core))
 
 {.push stack_trace:off.}
 when defined(windows):
   proc threadProcWrapper[TArg](closure: pointer): int32 {.stdcall.} =
-    threadProcWrapperBody(closure)
+    nimThreadProcWrapperBody(closure)
     # implicitly return 0
 elif defined(genode):
   proc threadProcWrapper[TArg](closure: pointer) {.noconv.} =
-    threadProcWrapperBody(closure)
+    nimThreadProcWrapperBody(closure)
 else:
   proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} =
-    threadProcWrapperBody(closure)
+    nimThreadProcWrapperBody(closure)
 {.pop.}
 
 proc running*[TArg](t: Thread[TArg]): bool {.inline.} =
@@ -278,7 +203,7 @@ when false:
     t.dataFn = nil
     ## if thread `t` already exited, `t.core` will be `null`.
     if not isNil(t.core):
-      deallocShared(t.core)
+      deallocThreadStorage(t.core)
       t.core = nil
 
 when hostOS == "windows":
@@ -290,7 +215,7 @@ when hostOS == "windows":
     ## Entry point is the proc `tp`.
     ## `param` is passed to `tp`. `TArg` can be `void` if you
     ## don't need to pass any data to the thread.
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
 
     when TArg isnot void: t.data = param
     t.dataFn = tp
@@ -315,7 +240,7 @@ elif defined(genode):
   proc createThread*[TArg](t: var Thread[TArg],
                            tp: proc (arg: TArg) {.thread, nimcall.},
                            param: TArg) =
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
 
     when TArg isnot void: t.data = param
     t.dataFn = tp
@@ -339,7 +264,7 @@ else:
     ## Entry point is the proc `tp`. `param` is passed to `tp`.
     ## `TArg` can be `void` if you
     ## don't need to pass any data to the thread.
-    t.core = cast[PGcThread](allocShared0(sizeof(GcThread)))
+    t.core = cast[PGcThread](allocThreadStorage(sizeof(GcThread)))
 
     when TArg isnot void: t.data = param
     t.dataFn = tp
@@ -348,7 +273,7 @@ else:
     doAssert pthread_attr_init(a) == 0
     when hasAllocStack:
       var
-        rawstk = allocShared0(ThreadStackSize + StackGuardSize)
+        rawstk = allocThreadStorage(ThreadStackSize + StackGuardSize)
         stk = cast[pointer](cast[uint](rawstk) + StackGuardSize)
       let setstacksizeResult = pthread_attr_setstack(addr a, stk, ThreadStackSize)
       t.rawStack = rawstk
@@ -377,4 +302,4 @@ proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) =
   createThread[void](t, tp)
 
 when not defined(gcOrc):
-  include threadids
+  include system/threadids
diff --git a/lib/std/varints.nim b/lib/std/varints.nim
index 0d18b9069..32fe2fffb 100644
--- a/lib/std/varints.nim
+++ b/lib/std/varints.nim
@@ -82,29 +82,29 @@ proc writeVu64*(z: var openArray[byte], x: uint64): int =
       z[3] = cast[uint8](y)
       return 4
     z[0] = 251
-    varintWrite32(toOpenArray(z, 1, z.high-1), y)
+    varintWrite32(toOpenArray(z, 1, 4), y)
     return 5
   if w <= 255:
     z[0] = 252
     z[1] = cast[uint8](w)
-    varintWrite32(toOpenArray(z, 2, z.high-2), y)
+    varintWrite32(toOpenArray(z, 2, 5), y)
     return 6
   if w <= 65535:
     z[0] = 253
     z[1] = cast[uint8](w shr 8)
     z[2] = cast[uint8](w)
-    varintWrite32(toOpenArray(z, 3, z.high-3), y)
+    varintWrite32(toOpenArray(z, 3, 6), y)
     return 7
   if w <= 16777215:
     z[0] = 254
     z[1] = cast[uint8](w shr 16)
     z[2] = cast[uint8](w shr 8)
     z[3] = cast[uint8](w)
-    varintWrite32(toOpenArray(z, 4, z.high-4), y)
+    varintWrite32(toOpenArray(z, 4, 7), y)
     return 8
   z[0] = 255
-  varintWrite32(toOpenArray(z, 1, z.high-1), w)
-  varintWrite32(toOpenArray(z, 5, z.high-5), y)
+  varintWrite32(toOpenArray(z, 1, 4), w)
+  varintWrite32(toOpenArray(z, 5, 8), y)
   return 9
 
 proc sar(a, b: int64): int64 =
diff --git a/lib/std/widestrs.nim b/lib/std/widestrs.nim
new file mode 100644
index 000000000..2ddf80d14
--- /dev/null
+++ b/lib/std/widestrs.nim
@@ -0,0 +1,239 @@
+#
+#
+#            Nim's Runtime Library
+#        (c) Copyright 2012 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## Nim support for C/C++'s `wide strings`:idx:.
+
+#when not declared(ThisIsSystem):
+#  {.error: "You must not import this module explicitly".}
+
+type
+  Utf16Char* = distinct int16
+
+when not (defined(cpu16) or defined(cpu8)):
+  when defined(nimv2):
+
+    type
+      WideCString* = ptr UncheckedArray[Utf16Char]
+
+      WideCStringObj* = object
+        bytes: int
+        data: WideCString
+
+    const arcLike = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+    when defined(nimAllowNonVarDestructor) and arcLike:
+      proc `=destroy`(a: WideCStringObj) =
+        if a.data != nil:
+          when compileOption("threads"):
+            deallocShared(a.data)
+          else:
+            dealloc(a.data)
+    else:
+      proc `=destroy`(a: var WideCStringObj) =
+        if a.data != nil:
+          when compileOption("threads"):
+            deallocShared(a.data)
+          else:
+            dealloc(a.data)
+
+    proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.}
+
+    proc `=sink`(a: var WideCStringObj; b: WideCStringObj) =
+      a.bytes = b.bytes
+      a.data = b.data
+
+    proc createWide(a: var WideCStringObj; bytes: int) =
+      a.bytes = bytes
+      when compileOption("threads"):
+        a.data = cast[typeof(a.data)](allocShared0(bytes))
+      else:
+        a.data = cast[typeof(a.data)](alloc0(bytes))
+
+    template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx]
+    template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val
+
+    template nullWide(): untyped = WideCStringObj(bytes: 0, data: nil)
+
+    converter toWideCString*(x: WideCStringObj): WideCString {.inline.} =
+      result = x.data
+
+  else:
+    template nullWide(): untyped = nil
+
+    type
+      WideCString* = ref UncheckedArray[Utf16Char]
+      WideCStringObj* = WideCString
+
+    template createWide(a; L) =
+      unsafeNew(a, L)
+
+  proc ord(arg: Utf16Char): int = int(cast[uint16](arg))
+
+  proc len*(w: WideCString): int =
+    ## returns the length of a widestring. This traverses the whole string to
+    ## find the binary zero end marker!
+    result = 0
+    while int16(w[result]) != 0'i16: inc result
+
+  const
+    UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
+    UNI_MAX_BMP = 0x0000FFFF
+    UNI_MAX_UTF16 = 0x0010FFFF
+    # UNI_MAX_UTF32 = 0x7FFFFFFF
+    # UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
+
+    halfShift = 10
+    halfBase = 0x0010000
+    halfMask = 0x3FF
+
+    UNI_SUR_HIGH_START = 0xD800
+    UNI_SUR_HIGH_END = 0xDBFF
+    UNI_SUR_LOW_START = 0xDC00
+    UNI_SUR_LOW_END = 0xDFFF
+    UNI_REPL = 0xFFFD
+
+  template ones(n: untyped): untyped = ((1 shl n)-1)
+
+  template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) =
+    ## Returns the unicode character `s[i]` in `result`. If `doInc == true`
+    ## `i` is incremented by the number of bytes that have been processed.
+    bind ones
+
+    if ord(s[i]) <= 127:
+      result = ord(s[i])
+      when doInc: inc(i)
+    elif ord(s[i]) shr 5 == 0b110:
+      #assert(ord(s[i+1]) shr 6 == 0b10)
+      if i <= L - 2:
+        result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
+        when doInc: inc(i, 2)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    elif ord(s[i]) shr 4 == 0b1110:
+      if i <= L - 3:
+        #assert(ord(s[i+1]) shr 6 == 0b10)
+        #assert(ord(s[i+2]) shr 6 == 0b10)
+        result = (ord(s[i]) and ones(4)) shl 12 or
+                (ord(s[i+1]) and ones(6)) shl 6 or
+                (ord(s[i+2]) and ones(6))
+        when doInc: inc(i, 3)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    elif ord(s[i]) shr 3 == 0b11110:
+      if i <= L - 4:
+        #assert(ord(s[i+1]) shr 6 == 0b10)
+        #assert(ord(s[i+2]) shr 6 == 0b10)
+        #assert(ord(s[i+3]) shr 6 == 0b10)
+        result = (ord(s[i]) and ones(3)) shl 18 or
+                (ord(s[i+1]) and ones(6)) shl 12 or
+                (ord(s[i+2]) and ones(6)) shl 6 or
+                (ord(s[i+3]) and ones(6))
+        when doInc: inc(i, 4)
+      else:
+        result = UNI_REPL
+        when doInc: inc(i)
+    else:
+      result = 0xFFFD
+      when doInc: inc(i)
+
+  iterator runes(s: cstring, L: int): int =
+    var
+      i = 0
+      result: int
+    while i < L:
+      fastRuneAt(s, i, L, result, true)
+      yield result
+
+  proc newWideCString*(size: int): WideCStringObj =
+    createWide(result, size * 2 + 2)
+
+  proc newWideCString*(source: cstring, L: int): WideCStringObj =
+    ## Warning:: `source` needs to be preallocated with the length `L`
+    createWide(result, L * 2 + 2)
+    var d = 0
+    for ch in runes(source, L):
+
+      if ch <= UNI_MAX_BMP:
+        if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_LOW_END:
+          result[d] = UNI_REPLACEMENT_CHAR
+        else:
+          result[d] = cast[Utf16Char](uint16(ch))
+      elif ch > UNI_MAX_UTF16:
+        result[d] = UNI_REPLACEMENT_CHAR
+      else:
+        let ch = ch - halfBase
+        result[d] = cast[Utf16Char](uint16((ch shr halfShift) + UNI_SUR_HIGH_START))
+        inc d
+        result[d] = cast[Utf16Char](uint16((ch and halfMask) + UNI_SUR_LOW_START))
+      inc d
+    result[d] = Utf16Char(0)
+
+  proc newWideCString*(s: cstring): WideCStringObj =
+    if s.isNil: return nullWide
+
+    result = newWideCString(s, s.len)
+
+  proc newWideCString*(s: string): WideCStringObj =
+    result = newWideCString(cstring s, s.len)
+
+  proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
+    result = newStringOfCap(estimate + estimate shr 2)
+
+    var i = 0
+    while w[i].int16 != 0'i16:
+      var ch = ord(w[i])
+      inc i
+      if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
+        # If the 16 bits following the high surrogate are in the source buffer...
+        let ch2 = ord(w[i])
+
+        # If it's a low surrogate, convert to UTF32:
+        if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
+          ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
+          inc i
+        else:
+          #invalid UTF-16
+          ch = replacement
+      elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
+        #invalid UTF-16
+        ch = replacement
+
+      if ch < 0x80:
+        result.add chr(ch)
+      elif ch < 0x800:
+        result.add chr((ch shr 6) or 0xc0)
+        result.add chr((ch and 0x3f) or 0x80)
+      elif ch < 0x10000:
+        result.add chr((ch shr 12) or 0xe0)
+        result.add chr(((ch shr 6) and 0x3f) or 0x80)
+        result.add chr((ch and 0x3f) or 0x80)
+      elif ch <= 0x10FFFF:
+        result.add chr((ch shr 18) or 0xf0)
+        result.add chr(((ch shr 12) and 0x3f) or 0x80)
+        result.add chr(((ch shr 6) and 0x3f) or 0x80)
+        result.add chr((ch and 0x3f) or 0x80)
+      else:
+        # replacement char(in case user give very large number):
+        result.add chr(0xFFFD shr 12 or 0b1110_0000)
+        result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
+        result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
+
+  proc `$`*(s: WideCString): string =
+    result = s $ 80
+
+  when defined(nimv2):
+    proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string =
+      `$`(s.data, estimate, replacement)
+
+    proc `$`*(s: WideCStringObj): string =
+      $(s.data)
+
+    proc len*(w: WideCStringObj): int {.inline.} =
+      len(w.data)
diff --git a/lib/std/with.nim b/lib/std/with.nim
index c7338b4e4..c2eaa4bef 100644
--- a/lib/std/with.nim
+++ b/lib/std/with.nim
@@ -14,7 +14,7 @@
 ##
 ## **Since:** version 1.2.
 
-import macros, private / underscored_calls
+import std/[macros, private / underscored_calls]
 
 macro with*(arg: typed; calls: varargs[untyped]): untyped =
   ## This macro provides `chaining`:idx: of function calls.
@@ -35,5 +35,14 @@ macro with*(arg: typed; calls: varargs[untyped]): untyped =
       -= 5
     doAssert a == 43
 
+    # Nesting works for object types too!
+    var foo = (bar: 1, qux: (baz: 2))
+    with foo:
+      bar = 2
+      with qux:
+        baz = 3
+    doAssert foo.bar == 2
+    doAssert foo.qux.baz == 3
+
   result = newNimNode(nnkStmtList, arg)
   underscoredCalls(result, calls, arg)
diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim
index 7dcfc7f59..9333f880b 100644
--- a/lib/std/wordwrap.nim
+++ b/lib/std/wordwrap.nim
@@ -9,7 +9,7 @@
 
 ## This module contains an algorithm to wordwrap a Unicode string.
 
-import strutils, unicode
+import std/[strutils, unicode]
 
 proc olen(s: string; start, lastExclusive: int): int =
   var i = start
diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim
index facba85fa..0b75c270e 100644
--- a/lib/std/wrapnils.nim
+++ b/lib/std/wrapnils.nim
@@ -13,7 +13,7 @@ consider handling indexing operations, eg:
 doAssert ?.default(seq[int])[3] == default(int)
 ]#
 
-import macros
+import std/macros
 
 runnableExamples:
   type Foo = ref object
@@ -60,7 +60,7 @@ proc finalize(n: NimNode, lhs: NimNode, level: int): NimNode =
   else:
     result = quote: (let `lhs` = `n`)
 
-proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
+proc process(n: NimNode, lhs: NimNode, label: NimNode, level: int): NimNode =
   var n = n.copyNimTree
   var it = n
   let addr2 = bindSym"addr"
@@ -78,7 +78,7 @@ proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
       let okSet = check[1]
       let kind1 = check[2]
       let tmp = genSym(nskLet, "tmpCase")
-      let body = process(objRef, tmp, level + 1)
+      let body = process(objRef, tmp, label, level + 1)
       let tmp3 = nnkDerefExpr.newTree(tmp)
       it[0][0] = tmp3
       let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]])
@@ -87,17 +87,17 @@ proc process(n: NimNode, lhs: NimNode, level: int): NimNode =
       let assgn = finalize(n, lhs, level)
       result = quote do:
         `body`
-        if `tmp3`.`kind1` notin `okSet`: break
+        if `tmp3`.`kind1` notin `okSet`: break `label`
         `assgn`
       break
     elif it.kind in {nnkHiddenDeref, nnkDerefExpr}:
       let tmp = genSym(nskLet, "tmp")
-      let body = process(it[0], tmp, level + 1)
+      let body = process(it[0], tmp, label, level + 1)
       it[0] = tmp
       let assgn = finalize(n, lhs, level)
       result = quote do:
         `body`
-        if `tmp` == nil: break
+        if `tmp` == nil: break `label`
         `assgn`
       break
     elif it.kind == nnkCall: # consider extending to `nnkCallKinds`
@@ -113,15 +113,16 @@ macro `?.`*(a: typed): auto =
   ## presence of intermediate nil pointers/references, in which case a default
   ## value is produced.
   let lhs = genSym(nskVar, "lhs")
-  let body = process(a, lhs, 0)
+  let label = genSym(nskLabel, "label")
+  let body = process(a, lhs, label, 0)
   result = quote do:
     var `lhs`: type(`a`)
-    block:
+    block `label`:
       `body`
     `lhs`
 
 # the code below is not needed for `?.`
-from options import Option, isSome, get, option, unsafeGet, UnpackDefect
+from std/options import Option, isSome, get, option, unsafeGet, UnpackDefect
 
 macro `??.`*(a: typed): Option =
   ## Same as `?.` but returns an `Option`.
@@ -144,10 +145,11 @@ macro `??.`*(a: typed): Option =
 
   let lhs = genSym(nskVar, "lhs")
   let lhs2 = genSym(nskVar, "lhs")
-  let body = process(a, lhs2, 0)
+  let label = genSym(nskLabel, "label")
+  let body = process(a, lhs2, label, 0)
   result = quote do:
     var `lhs`: Option[type(`a`)]
-    block:
+    block `label`:
       var `lhs2`: type(`a`)
       `body`
       `lhs` = option(`lhs2`)
diff --git a/lib/system.nim b/lib/system.nim
index 5b191f1e9..2f9cdc5f9 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -22,97 +22,59 @@
 ## .. include:: ./system_overview.rst
 
 
-type
-  float* {.magic: Float.}     ## Default floating point type.
-  float32* {.magic: Float32.} ## 32 bit floating point type.
-  float64* {.magic: Float.}   ## 64 bit floating point type.
+include "system/basic_types"
 
-# 'float64' is now an alias to 'float'; this solves many problems
+func zeroDefault*[T](_: typedesc[T]): T {.magic: "ZeroDefault".} =
+  ## Returns the binary zeros representation of the type `T`. It ignores
+  ## default fields of an object.
+  ##
+  ## See also:
+  ## * `default <#default,typedesc[T]>`_
 
-type
-  char* {.magic: Char.}         ## Built-in 8 bit character type (unsigned).
-  string* {.magic: String.}     ## Built-in string type.
-  cstring* {.magic: Cstring.}   ## Built-in cstring (*compatible string*) type.
-  pointer* {.magic: Pointer.}   ## Built-in pointer type, use the `addr`
-                                ## operator to get a pointer to a variable.
+include "system/compilation"
 
-  typedesc* {.magic: TypeDesc.} ## Meta type to denote a type description.
+{.push warning[GcMem]: off, warning[Uninit]: off.}
+# {.push hints: off.}
 
 type
-  `ptr`*[T] {.magic: Pointer.}   ## Built-in generic untraced pointer type.
-  `ref`*[T] {.magic: Pointer.}   ## Built-in generic traced pointer type.
-
-  `nil` {.magic: "Nil".}
-
-  void* {.magic: "VoidType".}    ## Meta type to denote the absence of any type.
-  auto* {.magic: Expr.}          ## Meta type for automatic type determination.
-  any* {.deprecated: "Deprecated since v1.5; Use auto instead.".} = distinct auto  ## Deprecated; Use `auto` instead. See https://github.com/nim-lang/RFCs/issues/281
-  untyped* {.magic: Expr.}       ## Meta type to denote an expression that
-                                 ## is not resolved (for templates).
-  typed* {.magic: Stmt.}         ## Meta type to denote an expression that
-                                 ## is resolved (for templates).
+  `static`*[T] {.magic: "Static".}
+    ## Meta type representing all values that can be evaluated at compile-time.
+    ##
+    ## The type coercion `static(x)` can be used to force the compile-time
+    ## evaluation of the given expression `x`.
 
-include "system/basic_types"
+  `type`*[T] {.magic: "Type".}
+    ## Meta type representing the type of all type values.
+    ##
+    ## The coercion `type(x)` can be used to obtain the type of the given
+    ## expression `x`.
 
+type
+  TypeOfMode* = enum ## Possible modes of `typeof`.
+    typeOfProc,      ## Prefer the interpretation that means `x` is a proc call.
+    typeOfIter       ## Prefer the interpretation that means `x` is an iterator call.
 
-proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} =
-  ## A section you should use to mark `runnable example`:idx: code with.
-  ##
-  ## - In normal debug and release builds code within
-  ##   a `runnableExamples` section is ignored.
-  ## - The documentation generator is aware of these examples and considers them
-  ##   part of the `##` doc comment. As the last step of documentation
-  ##   generation each runnableExample is put in its own file `$file_examples$i.nim`,
-  ##   compiled and tested. The collected examples are
-  ##   put into their own module to ensure the examples do not refer to
-  ##   non-exported symbols.
-  runnableExamples:
-    proc timesTwo*(x: int): int =
-      ## This proc doubles a number.
-      runnableExamples:
-        # at module scope
-        const exported* = 123
-        assert timesTwo(5) == 10
-        block: # at block scope
-          defer: echo "done"
-      runnableExamples "-d:foo -b:cpp":
-        import std/compilesettings
-        assert querySetting(backend) == "cpp"
-        assert defined(foo)
-      runnableExamples "-r:off": ## this one is only compiled
-         import std/browsers
-         openDefaultBrowser "https://forum.nim-lang.org/"
-      2 * x
-
-proc compileOption*(option: string): bool {.
-  magic: "CompileOption", noSideEffect.} =
-  ## Can be used to determine an `on|off` compile-time option.
-  ##
-  ## See also:
-  ## * `compileOption <#compileOption,string,string>`_ for enum options
-  ## * `defined <#defined,untyped>`_
-  ## * `std/compilesettings module <compilesettings.html>`_
-  runnableExamples("--floatChecks:off"):
-    static: doAssert not compileOption("floatchecks")
-    {.push floatChecks: on.}
-    static: doAssert compileOption("floatchecks")
-    # floating point NaN and Inf checks enabled in this scope
-    {.pop.}
-
-proc compileOption*(option, arg: string): bool {.
-  magic: "CompileOptionArg", noSideEffect.} =
-  ## Can be used to determine an enum compile-time option.
-  ##
-  ## See also:
-  ## * `compileOption <#compileOption,string>`_ for `on|off` options
-  ## * `defined <#defined,untyped>`_
-  ## * `std/compilesettings module <compilesettings.html>`_
+proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
+  magic: "TypeOf", noSideEffect, compileTime.} =
+  ## Builtin `typeof` operation for accessing the type of an expression.
+  ## Since version 0.20.0.
   runnableExamples:
-    when compileOption("opt", "size") and compileOption("gc", "boehm"):
-      discard "compiled with optimization for size and uses Boehm's GC"
+    proc myFoo(): float = 0.0
+    iterator myFoo(): string = yield "abc"
+    iterator myFoo2(): string = yield "abc"
+    iterator myFoo3(): string {.closure.} = yield "abc"
+    doAssert type(myFoo()) is string
+    doAssert typeof(myFoo()) is string
+    doAssert typeof(myFoo(), typeOfIter) is string
+    doAssert typeof(myFoo3) is iterator
 
-{.push warning[GcMem]: off, warning[Uninit]: off.}
-# {.push hints: off.}
+    doAssert typeof(myFoo(), typeOfProc) is float
+    doAssert typeof(0.0, typeOfProc) is float
+    doAssert typeof(myFoo3, typeOfProc) is iterator
+    doAssert not compiles(typeof(myFoo2(), typeOfProc))
+      # this would give: Error: attempting to call routine: 'myFoo2'
+      # since `typeOfProc` expects a typed expression and `myFoo2()` can
+      # only be used in a `for` context.
 
 proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
   ## Constructs an `or` meta class.
@@ -123,72 +85,16 @@ proc `and`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
 proc `not`*(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
   ## Constructs an `not` meta class.
 
-
-type
-  SomeFloat* = float|float32|float64
-    ## Type class matching all floating point number types.
-
-  SomeNumber* = SomeInteger|SomeFloat
-    ## Type class matching all number types.
-
-proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
-  ## Special compile-time procedure that checks whether `x` is
-  ## defined.
-  ##
-  ## `x` is an external symbol introduced through the compiler's
-  ## `-d:x switch <nimc.html#compiler-usage-compileminustime-symbols>`_ to enable
-  ## build time conditionals:
-  ##   ```
-  ##   when not defined(release):
-  ##     # Do here programmer friendly expensive sanity checks.
-  ##   # Put here the normal code
-  ##   ```
-  ##
-  ## See also:
-  ## * `compileOption <#compileOption,string>`_ for `on|off` options
-  ## * `compileOption <#compileOption,string,string>`_ for enum options
-  ## * `define pragmas <manual.html#implementation-specific-pragmas-compileminustime-define-pragmas>`_
-
 when defined(nimHasIterable):
   type
     iterable*[T] {.magic: IterableType.}  ## Represents an expression that yields `T`
 
-when defined(nimHashOrdinalFixed):
-  type
-    Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer,
-                                   ## bool, character, and enumeration types
-                                   ## as well as their subtypes. See also
-                                   ## `SomeOrdinal`.
-else:
-  # bootstrap < 1.2.0
-  type
-    OrdinalImpl[T] {.magic: Ordinal.}
-    Ordinal* = OrdinalImpl | uint | uint64
-
-when defined(nimHasDeclaredMagic):
-  proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.}
-    ## Special compile-time procedure that checks whether `x` is
-    ## declared. `x` has to be an identifier or a qualified identifier.
-    ##
-    ## This can be used to check whether a library provides a certain
-    ## feature or not:
-    ##   ```
-    ##   when not declared(strutils.toUpper):
-    ##     # provide our own toUpper proc here, because strutils is
-    ##     # missing it.
-    ##   ```
-    ##
-    ## See also:
-    ## * `declaredInScope <#declaredInScope,untyped>`_
-else:
-  proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
+type
+  Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer,
+                                  ## bool, character, and enumeration types
+                                  ## as well as their subtypes. See also
+                                  ## `SomeOrdinal`.
 
-when defined(nimHasDeclaredMagic):
-  proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.}
-    ## Special compile-time procedure that checks whether `x` is
-    ## declared in the current scope. `x` has to be an identifier.
-else:
-  proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.}
 
 proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
   ## Builtin `addr` operator for taking the address of a memory location.
@@ -202,7 +108,7 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
   ##
   ## Cannot be overloaded.
   ##
-  ##   ```
+  ##   ```nim
   ##   var
   ##     buf: seq[char] = @['a','b','c']
   ##     p = buf[1].addr
@@ -211,89 +117,53 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
   ##   ```
   discard
 
-proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect,
-    deprecated: "'unsafeAddr' is a deprecated alias for 'addr'".} =
-  ## Builtin `addr` operator for taking the address of a memory
-  ## location.
-  ##
-  ## .. note:: This works for `let` variables or parameters
-  ##   for better interop with C. When you use it to write a wrapper
-  ##   for a C library and take the address of `let` variables or parameters,
-  ##   you should always check that the original library
-  ##   does never write to data behind the pointer that is returned from
-  ##   this procedure.
-  ##
-  ## Cannot be overloaded.
+proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
+  ## .. warning:: `unsafeAddr` is a deprecated alias for `addr`,
+  ##    use `addr` instead.
   discard
 
-type
-  `static`*[T] {.magic: "Static".}
-    ## Meta type representing all values that can be evaluated at compile-time.
-    ##
-    ## The type coercion `static(x)` can be used to force the compile-time
-    ## evaluation of the given expression `x`.
-
-  `type`*[T] {.magic: "Type".}
-    ## Meta type representing the type of all type values.
-    ##
-    ## The coercion `type(x)` can be used to obtain the type of the given
-    ## expression `x`.
-
-type
-  TypeOfMode* = enum ## Possible modes of `typeof`.
-    typeOfProc,      ## Prefer the interpretation that means `x` is a proc call.
-    typeOfIter       ## Prefer the interpretation that means `x` is an iterator call.
-
-proc typeof*(x: untyped; mode = typeOfIter): typedesc {.
-  magic: "TypeOf", noSideEffect, compileTime.} =
-  ## Builtin `typeof` operation for accessing the type of an expression.
-  ## Since version 0.20.0.
-  runnableExamples:
-    proc myFoo(): float = 0.0
-    iterator myFoo(): string = yield "abc"
-    iterator myFoo2(): string = yield "abc"
-    iterator myFoo3(): string {.closure.} = yield "abc"
-    doAssert type(myFoo()) is string
-    doAssert typeof(myFoo()) is string
-    doAssert typeof(myFoo(), typeOfIter) is string
-    doAssert typeof(myFoo3) is "iterator"
-
-    doAssert typeof(myFoo(), typeOfProc) is float
-    doAssert typeof(0.0, typeOfProc) is float
-    doAssert typeof(myFoo3, typeOfProc) is "iterator"
-    doAssert not compiles(typeof(myFoo2(), typeOfProc))
-      # this would give: Error: attempting to call routine: 'myFoo2'
-      # since `typeOfProc` expects a typed expression and `myFoo2()` can
-      # only be used in a `for` context.
 
 const ThisIsSystem = true
 
-proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
-  ## Leaked implementation detail. Do not use.
+proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {.
+  magic: "NewFinalize", noSideEffect.}
+  ## Creates a new object of type `T` and returns a safe (traced)
+  ## reference to it in `a`.
+  ##
+  ## When the garbage collector frees the object, `finalizer` is called.
+  ## The `finalizer` may not keep a reference to the
+  ## object pointed to by `x`. The `finalizer` cannot prevent the GC from
+  ## freeing the object.
+  ##
+  ## **Note**: The `finalizer` refers to the type `T`, not to the object!
+  ## This means that for each object of type `T` the finalizer will be called!
 
-when true:
-  proc new*[T](a: var ref T, finalizer: proc (x: ref T) {.nimcall.}) {.
-    magic: "NewFinalize", noSideEffect.}
-    ## Creates a new object of type `T` and returns a safe (traced)
-    ## reference to it in `a`.
-    ##
-    ## When the garbage collector frees the object, `finalizer` is called.
-    ## The `finalizer` may not keep a reference to the
-    ## object pointed to by `x`. The `finalizer` cannot prevent the GC from
-    ## freeing the object.
-    ##
-    ## **Note**: The `finalizer` refers to the type `T`, not to the object!
-    ## This means that for each object of type `T` the finalizer will be called!
+proc `=wasMoved`*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} =
+  ## Generic `wasMoved`:idx: implementation that can be overridden.
 
-proc wasMoved*[T](obj: var T) {.magic: "WasMoved", noSideEffect.} =
+proc wasMoved*[T](obj: var T) {.inline, noSideEffect.} =
   ## Resets an object `obj` to its initial (binary zero) value to signify
   ## it was "moved" and to signify its destructor should do nothing and
   ## ideally be optimized away.
-  discard
+  {.cast(raises: []), cast(tags: []).}:
+    `=wasMoved`(obj)
 
 proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} =
   result = x
-  wasMoved(x)
+  {.cast(raises: []), cast(tags: []).}:
+    `=wasMoved`(x)
+
+when defined(nimHasEnsureMove):
+  proc ensureMove*[T](x: T): T {.magic: "EnsureMove", noSideEffect.} =
+    ## Ensures that `x` is moved to the new location, otherwise it gives
+    ## an error at the compile time.
+    runnableExamples:
+      proc foo =
+        var x = "Hello"
+        let y = ensureMove(x)
+        doAssert y == "Hello"
+      foo()
+    discard "implemented in injectdestructors"
 
 type
   range*[T]{.magic: "Range".}         ## Generic type to construct range types.
@@ -323,7 +193,7 @@ proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect,
   ## **This proc is deprecated**, use this one instead:
   ## * `high(typedesc) <#high,typedesc[T]>`_
   ##
-  ## ```
+  ## ```nim
   ## high(2) # => 9223372036854775807
   ## ```
 
@@ -331,7 +201,7 @@ proc high*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "High", noSideEffe
   ## Returns the highest possible value of an ordinal or enum type.
   ##
   ## `high(int)` is Nim's way of writing `INT_MAX`:idx: or `MAX_INT`:idx:.
-  ##   ```
+  ##   ```nim
   ##   high(int) # => 9223372036854775807
   ##   ```
   ##
@@ -340,7 +210,7 @@ proc high*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "High", noSideEffe
 
 proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.}
   ## Returns the highest possible index of a sequence `x`.
-  ##   ```
+  ##   ```nim
   ##   var s = @[1, 2, 3, 4, 5, 6, 7]
   ##   high(s) # => 6
   ##   for i in low(s)..high(s):
@@ -354,7 +224,7 @@ proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.}
   ## Returns the highest possible index of an array `x`.
   ##
   ## For empty arrays, the return type is `int`.
-  ##   ```
+  ##   ```nim
   ##   var arr = [1, 2, 3, 4, 5, 6, 7]
   ##   high(arr) # => 6
   ##   for i in low(arr)..high(arr):
@@ -368,7 +238,7 @@ proc high*[I, T](x: typedesc[array[I, T]]): I {.magic: "High", noSideEffect.}
   ## Returns the highest possible index of an array type.
   ##
   ## For empty arrays, the return type is `int`.
-  ##   ```
+  ##   ```nim
   ##   high(array[7, int]) # => 6
   ##   ```
   ##
@@ -384,7 +254,7 @@ proc high*(x: cstring): int {.magic: "High", noSideEffect.}
 
 proc high*(x: string): int {.magic: "High", noSideEffect.}
   ## Returns the highest possible index of a string `x`.
-  ##   ```
+  ##   ```nim
   ##   var str = "Hello world!"
   ##   high(str) # => 11
   ##   ```
@@ -400,7 +270,7 @@ proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect,
   ## **This proc is deprecated**, use this one instead:
   ## * `low(typedesc) <#low,typedesc[T]>`_
   ##
-  ## ```
+  ## ```nim
   ## low(2) # => -9223372036854775808
   ## ```
 
@@ -408,7 +278,7 @@ proc low*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "Low", noSideEffect
   ## Returns the lowest possible value of an ordinal or enum type.
   ##
   ## `low(int)` is Nim's way of writing `INT_MIN`:idx: or `MIN_INT`:idx:.
-  ##   ```
+  ##   ```nim
   ##   low(int) # => -9223372036854775808
   ##   ```
   ##
@@ -417,7 +287,7 @@ proc low*[T: Ordinal|enum|range](x: typedesc[T]): T {.magic: "Low", noSideEffect
 
 proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.}
   ## Returns the lowest possible index of a sequence `x`.
-  ##   ```
+  ##   ```nim
   ##   var s = @[1, 2, 3, 4, 5, 6, 7]
   ##   low(s) # => 0
   ##   for i in low(s)..high(s):
@@ -431,7 +301,7 @@ proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.}
   ## Returns the lowest possible index of an array `x`.
   ##
   ## For empty arrays, the return type is `int`.
-  ##   ```
+  ##   ```nim
   ##   var arr = [1, 2, 3, 4, 5, 6, 7]
   ##   low(arr) # => 0
   ##   for i in low(arr)..high(arr):
@@ -445,7 +315,7 @@ proc low*[I, T](x: typedesc[array[I, T]]): I {.magic: "Low", noSideEffect.}
   ## Returns the lowest possible index of an array type.
   ##
   ## For empty arrays, the return type is `int`.
-  ##   ```
+  ##   ```nim
   ##   low(array[7, int]) # => 0
   ##   ```
   ##
@@ -460,7 +330,7 @@ proc low*(x: cstring): int {.magic: "Low", noSideEffect.}
 
 proc low*(x: string): int {.magic: "Low", noSideEffect.}
   ## Returns the lowest possible index of a string `x`.
-  ##   ```
+  ##   ```nim
   ##   var str = "Hello world!"
   ##   low(str) # => 0
   ##   ```
@@ -468,7 +338,7 @@ proc low*(x: string): int {.magic: "Low", noSideEffect.}
   ## See also:
   ## * `high(string) <#high,string>`_
 
-when not defined(gcArc) and not defined(gcOrc):
+when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
   proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".}
     ## Use this instead of `=` for a `shallow copy`:idx:.
     ##
@@ -492,12 +362,36 @@ proc arrGet[I: Ordinal;T](a: T; i: I): T {.
 proc arrPut[I: Ordinal;T,S](a: T; i: I;
   x: S) {.noSideEffect, magic: "ArrPut".}
 
-proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
-  ## Generic `destructor`:idx: implementation that can be overridden.
-  discard
+const arcLikeMem = defined(gcArc) or defined(gcAtomicArc) or defined(gcOrc)
+
+
+when defined(nimAllowNonVarDestructor) and arcLikeMem and defined(nimPreviewNonVarDestructor):
+  proc `=destroy`*[T](x: T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
+    discard
+else:
+  proc `=destroy`*[T](x: var T) {.inline, magic: "Destroy".} =
+    ## Generic `destructor`:idx: implementation that can be overridden.
+    discard
+
+  when defined(nimAllowNonVarDestructor) and arcLikeMem:
+    proc `=destroy`*(x: string) {.inline, magic: "Destroy", enforceNoRaises.} =
+      discard
+
+    proc `=destroy`*[T](x: seq[T]) {.inline, magic: "Destroy".} =
+      discard
+
+    proc `=destroy`*[T](x: ref T) {.inline, magic: "Destroy".} =
+      discard
+
+when defined(nimHasDup):
+  proc `=dup`*[T](x: T): T {.inline, magic: "Dup".} =
+    ## Generic `dup`:idx: implementation that can be overridden.
+    discard
+
 proc `=sink`*[T](x: var T; y: T) {.inline, nodestroy, magic: "Asgn".} =
   ## Generic `sink`:idx: implementation that can be overridden.
-  when defined(gcArc) or defined(gcOrc):
+  when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
     x = y
   else:
     shallowCopy(x, y)
@@ -519,7 +413,7 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag
   ##
   ## Slices can also be used in the set constructor and in ordinal case
   ## statements, but then they are special-cased by the compiler.
-  ##   ```
+  ##   ```nim
   ##   let a = [10, 20, 30, 40, 50]
   ##   echo a[2 .. 3] # @[30, 40]
   ##   ```
@@ -528,7 +422,7 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag
 proc `..`*[T](b: sink T): HSlice[int, T]
   {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} =
   ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`.
-  ##   ```
+  ##   ```nim
   ##   let a = [10, 20, 30, 40, 50]
   ##   echo a[.. 2] # @[10, 20, 30]
   ##   ```
@@ -539,12 +433,6 @@ when defined(hotCodeReloading):
 else:
   {.pragma: hcrInline.}
 
-{.push profiler: off.}
-let nimvm* {.magic: "Nimvm", compileTime.}: bool = false
-  ## May be used only in `when` expression.
-  ## It is true in Nim VM context and false otherwise.
-{.pop.}
-
 include "system/arithmetics"
 include "system/comparisons"
 
@@ -573,10 +461,6 @@ when not defined(js) and not defined(nimSeqsV2):
       data: UncheckedArray[char]
     NimString = ptr NimStringDesc
 
-when notJSnotNims and not defined(nimSeqsV2):
-  template space(s: PGenericSeq): int {.dirty.} =
-    s.reserved and not (seqShallowFlag or strlitFlag)
-
 when notJSnotNims:
   include "system/hti"
 
@@ -592,6 +476,7 @@ type
     ## is an `int` type ranging from one to the maximum value
     ## of an `int`. This type is often useful for documentation and debugging.
 
+type
   RootObj* {.compilerproc, inheritable.} =
     object ## The root of Nim's object hierarchy.
            ##
@@ -599,8 +484,64 @@ type
            ## However, objects that have no ancestor are also allowed.
   RootRef* = ref RootObj ## Reference to `RootObj`.
 
+const NimStackTraceMsgs = compileOption("stacktraceMsgs")
+
+type
+  RootEffect* {.compilerproc.} = object of RootObj ## \
+    ## Base effect class.
+    ##
+    ## Each effect should inherit from `RootEffect` unless you know what
+    ## you're doing.
+
+type
+  StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
+                            ## to them. A `StackTraceEntry` is a single entry of the
+                            ## stack trace.
+    procname*: cstring      ## Name of the proc that is currently executing.
+    line*: int              ## Line number of the proc that is currently executing.
+    filename*: cstring      ## Filename of the proc that is currently executing.
+    when NimStackTraceMsgs:
+      frameMsg*: string     ## When a stacktrace is generated in a given frame and
+                            ## rendered at a later time, we should ensure the stacktrace
+                            ## data isn't invalidated; any pointer into PFrame is
+                            ## subject to being invalidated so shouldn't be stored.
+    when defined(nimStackTraceOverride):
+      programCounter*: uint ## Program counter - will be used to get the rest of the info,
+                            ## when `$` is called on this type. We can't use
+                            ## "cuintptr_t" in here.
+      procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename"
+
+  Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \
+    ## Base exception class.
+    ##
+    ## Each exception has to inherit from `Exception`. See the full `exception
+    ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
+    parent*: ref Exception ## Parent exception (can be used as a stack).
+    name*: cstring         ## The exception's name is its Nim identifier.
+                           ## This field is filled automatically in the
+                           ## `raise` statement.
+    msg* {.exportc: "message".}: string ## The exception's message. Not
+                                        ## providing an exception message
+                                        ## is bad style.
+    when defined(js):
+      trace*: string
+    else:
+      trace*: seq[StackTraceEntry]
+    up: ref Exception # used for stacking exceptions. Not exported!
+
+  Defect* = object of Exception ## \
+    ## Abstract base class for all exceptions that Nim's runtime raises
+    ## but that are strictly uncatchable as they can also be mapped to
+    ## a `quit` / `trap` / `exit` operation.
+
+  CatchableError* = object of Exception ## \
+    ## Abstract class for all exceptions that are catchable.
 
-include "system/exceptions"
+when defined(nimIcIntegrityChecks):
+  include "system/exceptions"
+else:
+  import system/exceptions
+  export exceptions
 
 when defined(js) or defined(nimdoc):
   type
@@ -632,7 +573,7 @@ proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.}
   ## sizeof should fallback to the `sizeof` in the C compiler. The
   ## result isn't available for the Nim compiler and therefore can't
   ## be used inside of macros.
-  ##   ```
+  ##   ```nim
   ##   sizeof('A') # => 1
   ##   sizeof(2) # => 8
   ##   ```
@@ -663,7 +604,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.}
   ## Note that the sequence will be filled with zeroed entries.
   ## After the creation of the sequence you should assign entries to
   ## the sequence instead of adding them. Example:
-  ##   ```
+  ##   ```nim
   ##   var inputStrings: seq[string]
   ##   newSeq(inputStrings, 3)
   ##   assert len(inputStrings) == 3
@@ -679,7 +620,7 @@ proc newSeq*[T](len = 0.Natural): seq[T] =
   ## Note that the sequence will be filled with zeroed entries.
   ## After the creation of the sequence you should assign entries to
   ## the sequence instead of adding them.
-  ##   ```
+  ##   ```nim
   ##   var inputStrings = newSeq[string](3)
   ##   assert len(inputStrings) == 3
   ##   inputStrings[0] = "The fourth"
@@ -690,14 +631,14 @@ proc newSeq*[T](len = 0.Natural): seq[T] =
   ##
   ## See also:
   ## * `newSeqOfCap <#newSeqOfCap,Natural>`_
-  ## * `newSeqUninitialized <#newSeqUninitialized,Natural>`_
+  ## * `newSeqUninit <#newSeqUninit,Natural>`_
   newSeq(result, len)
 
 proc newSeqOfCap*[T](cap: Natural): seq[T] {.
   magic: "NewSeqOfCap", noSideEffect.} =
   ## Creates a new sequence of type `seq[T]` with length zero and capacity
   ## `cap`. Example:
-  ##   ```
+  ##   ```nim
   ##   var x = newSeqOfCap[int](5)
   ##   assert len(x) == 0
   ##   x.add(10)
@@ -705,26 +646,6 @@ proc newSeqOfCap*[T](cap: Natural): seq[T] {.
   ##   ```
   discard
 
-when not defined(js):
-  proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] =
-    ## Creates a new sequence of type `seq[T]` with length `len`.
-    ##
-    ## Only available for numbers types. Note that the sequence will be
-    ## uninitialized. After the creation of the sequence you should assign
-    ## entries to the sequence instead of adding them.
-    ## Example:
-    ##   ```
-    ##   var x = newSeqUninitialized[int](3)
-    ##   assert len(x) == 3
-    ##   x[0] = 10
-    ##   ```
-    result = newSeqOfCap[T](len)
-    when defined(nimSeqsV2):
-      cast[ptr int](addr result)[] = len
-    else:
-      var s = cast[PGenericSeq](result)
-      s.len = len
-
 func len*[TOpenArray: openArray|varargs](x: TOpenArray): int {.magic: "LengthOpenArray".} =
   ## Returns the length of an openArray.
   runnableExamples:
@@ -800,29 +721,6 @@ func chr*(u: range[0..255]): char {.magic: "Chr".} =
     doAssertRaises(RangeDefect): discard chr(x)
     doAssertRaises(RangeDefect): discard char(x)
 
-# floating point operations:
-proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
-proc `-`*(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.}
-proc `+`*(x, y: float32): float32 {.magic: "AddF64", noSideEffect.}
-proc `-`*(x, y: float32): float32 {.magic: "SubF64", noSideEffect.}
-proc `*`*(x, y: float32): float32 {.magic: "MulF64", noSideEffect.}
-proc `/`*(x, y: float32): float32 {.magic: "DivF64", noSideEffect.}
-
-proc `+`*(x: float): float {.magic: "UnaryPlusF64", noSideEffect.}
-proc `-`*(x: float): float {.magic: "UnaryMinusF64", noSideEffect.}
-proc `+`*(x, y: float): float {.magic: "AddF64", noSideEffect.}
-proc `-`*(x, y: float): float {.magic: "SubF64", noSideEffect.}
-proc `*`*(x, y: float): float {.magic: "MulF64", noSideEffect.}
-proc `/`*(x, y: float): float {.magic: "DivF64", noSideEffect.}
-
-proc `==`*(x, y: float32): bool {.magic: "EqF64", noSideEffect.}
-proc `<=`*(x, y: float32): bool {.magic: "LeF64", noSideEffect.}
-proc `<`  *(x, y: float32): bool {.magic: "LtF64", noSideEffect.}
-
-proc `==`*(x, y: float): bool {.magic: "EqF64", noSideEffect.}
-proc `<=`*(x, y: float): bool {.magic: "LeF64", noSideEffect.}
-proc `<`*(x, y: float): bool {.magic: "LtF64", noSideEffect.}
-
 
 include "system/setops"
 
@@ -830,22 +728,25 @@ include "system/setops"
 proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} =
   ## Checks if `value` is within the range of `s`; returns true if
   ## `value >= s.a and value <= s.b`.
-  ##   ```
+  ##   ```nim
   ##   assert((1..3).contains(1) == true)
   ##   assert((1..3).contains(2) == true)
   ##   assert((1..3).contains(4) == false)
   ##   ```
   result = s.a <= value and value <= s.b
 
-template `in`*(x, y: untyped): untyped {.dirty.} = contains(y, x)
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template `in`*(x, y: untyped): untyped {.dirty, callsite.} = contains(y, x)
   ## Sugar for `contains`.
-  ##   ```
+  ##   ```nim
   ##   assert(1 in (1..3) == true)
   ##   assert(5 in (1..3) == false)
   ##   ```
-template `notin`*(x, y: untyped): untyped {.dirty.} = not contains(y, x)
+template `notin`*(x, y: untyped): untyped {.dirty, callsite.} = not contains(y, x)
   ## Sugar for `not contains`.
-  ##   ```
+  ##   ```nim
   ##   assert(1 notin (1..3) == false)
   ##   assert(5 notin (1..3) == true)
   ##   ```
@@ -855,7 +756,7 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
   ##
   ## For a negated version, use `isnot <#isnot.t,untyped,untyped>`_.
   ##
-  ##   ```
+  ##   ```nim
   ##   assert 42 is int
   ##   assert @[1, 2] is seq
   ##
@@ -868,9 +769,9 @@ proc `is`*[T, S](x: T, y: S): bool {.magic: "Is", noSideEffect.}
   ##   assert(test[int](3) == 3)
   ##   assert(test[string]("xyz") == 0)
   ##   ```
-template `isnot`*(x, y: untyped): untyped = not (x is y)
+template `isnot`*(x, y: untyped): untyped {.callsite.} = not (x is y)
   ## Negated version of `is <#is,T,S>`_. Equivalent to `not(x is y)`.
-  ##   ```
+  ##   ```nim
   ##   assert 42 isnot float
   ##   assert @[1, 2] isnot enum
   ##   ```
@@ -966,7 +867,7 @@ proc cmp*[T](x, y: T): int =
   ##
   ## This is useful for writing generic algorithms without performance loss.
   ## This generic implementation uses the `==` and `<` operators.
-  ##   ```
+  ##   ```nim
   ##   import std/algorithm
   ##   echo sorted(@[4, 2, 6, 5, 8, 7], cmp[int])
   ##   ```
@@ -987,7 +888,7 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff
   ## sequences with the array constructor: `@[1, 2, 3]` has the type
   ## `seq[int]`, while `[1, 2, 3]` has the type `array[0..2, int]`.
   ##
-  ##   ```
+  ##   ```nim
   ##   let
   ##     a = [1, 3, 5]
   ##     b = "foo"
@@ -997,34 +898,39 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff
   ##   ```
 
 proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} =
-  ## returns the default value of the type `T`.
-  runnableExamples:
+  ## Returns the default value of the type `T`. Contrary to `zeroDefault`, it takes default fields
+  ## of an object into consideration.
+  ##
+  ## See also:
+  ## * `zeroDefault <#zeroDefault,typedesc[T]>`_
+  ##
+  runnableExamples("-d:nimPreviewRangeDefault"):
     assert (int, float).default == (0, 0.0)
-    # note: `var a = default(T)` is usually the same as `var a: T` and (currently) generates
-    # a value whose binary representation is all 0, regardless of whether this
-    # would violate type constraints such as `range`, `not nil`, etc. This
-    # property is required to implement certain algorithms efficiently which
-    # may require intermediate invalid states.
     type Foo = object
       a: range[2..6]
-    var a1: range[2..6] # currently, this compiles
-    # var a2: Foo # currently, this errors: Error: The Foo type doesn't have a default value.
-    # var a3 = Foo() # ditto
-    var a3 = Foo.default # this works, but generates a `UnsafeDefault` warning.
-  # note: the doc comment also explains why `default` can't be implemented
-  # via: `template default*[T](t: typedesc[T]): T = (var v: T; v)`
+    var x = Foo.default
+    assert x.a == 2
+
 
 proc reset*[T](obj: var T) {.noSideEffect.} =
   ## Resets an object `obj` to its default value.
-  obj = default(typeof(obj))
+  when nimvm:
+    obj = default(typeof(obj))
+  else:
+    when defined(gcDestructors):
+      {.cast(noSideEffect), cast(raises: []), cast(tags: []).}:
+        `=destroy`(obj)
+        `=wasMoved`(obj)
+    else:
+      obj = default(typeof(obj))
 
 proc setLen*[T](s: var seq[T], newlen: Natural) {.
-  magic: "SetLengthSeq", noSideEffect.}
+  magic: "SetLengthSeq", noSideEffect, nodestroy.}
   ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
   ##
   ## If the current length is greater than the new length,
   ## `s` will be truncated.
-  ##   ```
+  ##   ```nim
   ##   var x = @[10, 20]
   ##   x.setLen(5)
   ##   x[4] = 50
@@ -1039,7 +945,7 @@ proc setLen*(s: var string, newlen: Natural) {.
   ##
   ## If the current length is greater than the new length,
   ## `s` will be truncated.
-  ##   ```
+  ##   ```nim
   ##   var myS = "Nim is great!!"
   ##   myS.setLen(3) # myS <- "Nim"
   ##   echo myS, " is fantastic!!"
@@ -1047,8 +953,8 @@ proc setLen*(s: var string, newlen: Natural) {.
 
 proc newString*(len: Natural): string {.
   magic: "NewString", importc: "mnewString", noSideEffect.}
-  ## Returns a new string of length `len` but with uninitialized
-  ## content. One needs to fill the string character after character
+  ## Returns a new string of length `len`.
+  ## One needs to fill the string character after character
   ## with the index operator `s[i]`.
   ##
   ## This procedure exists only for optimization purposes;
@@ -1064,25 +970,25 @@ proc newStringOfCap*(cap: Natural): string {.
 proc `&`*(x: string, y: char): string {.
   magic: "ConStrStr", noSideEffect.}
   ## Concatenates `x` with `y`.
-  ##   ```
+  ##   ```nim
   ##   assert("ab" & 'c' == "abc")
   ##   ```
 proc `&`*(x, y: char): string {.
   magic: "ConStrStr", noSideEffect.}
   ## Concatenates characters `x` and `y` into a string.
-  ##   ```
+  ##   ```nim
   ##   assert('a' & 'b' == "ab")
   ##   ```
 proc `&`*(x, y: string): string {.
   magic: "ConStrStr", noSideEffect.}
   ## Concatenates strings `x` and `y`.
-  ##   ```
+  ##   ```nim
   ##   assert("ab" & "cd" == "abcd")
   ##   ```
 proc `&`*(x: char, y: string): string {.
   magic: "ConStrStr", noSideEffect.}
   ## Concatenates `x` with `y`.
-  ##   ```
+  ##   ```nim
   ##   assert('a' & "bc" == "abc")
   ##   ```
 
@@ -1091,7 +997,7 @@ proc `&`*(x: char, y: string): string {.
 
 proc add*(x: var string, y: char) {.magic: "AppendStrCh", noSideEffect.}
   ## Appends `y` to `x` in place.
-  ##   ```
+  ##   ```nim
   ##   var tmp = ""
   ##   tmp.add('a')
   ##   tmp.add('b')
@@ -1113,18 +1019,6 @@ type
     littleEndian, bigEndian
 
 const
-  isMainModule* {.magic: "IsMainModule".}: bool = false
-    ## True only when accessed in the main module. This works thanks to
-    ## compiler magic. It is useful to embed testing code in a module.
-
-  CompileDate* {.magic: "CompileDate".}: string = "0000-00-00"
-    ## The date (in UTC) of compilation as a string of the form
-    ## `YYYY-MM-DD`. This works thanks to compiler magic.
-
-  CompileTime* {.magic: "CompileTime".}: string = "00:00:00"
-    ## The time (in UTC) of compilation as a string of the form
-    ## `HH:MM:SS`. This works thanks to compiler magic.
-
   cpuEndian* {.magic: "CpuEndian".}: Endianness = littleEndian
     ## The endianness of the target CPU. This is a valuable piece of
     ## information for low-level code only. This works thanks to compiler
@@ -1143,7 +1037,7 @@ const
     ## Possible values:
     ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`,
     ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`,
-    ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, '"loongarch64"'.
+    ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, `"loongarch64"`.
 
   seqShallowFlag = low(int)
   strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \
@@ -1154,6 +1048,10 @@ const
   hasThreadSupport = compileOption("threads") and not defined(nimscript)
   hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
 
+when notJSnotNims and not defined(nimSeqsV2):
+  template space(s: PGenericSeq): int =
+    s.reserved and not (seqShallowFlag or strlitFlag)
+
 when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
   # tcc doesn't support TLS
   {.error: "`--tlsEmulation:on` must be used when using threads with tcc backend".}
@@ -1174,7 +1072,8 @@ when defined(boehmgc):
     const boehmLib = "libgc.so.1"
   {.pragma: boehmGC, noconv, dynlib: boehmLib.}
 
-type TaintedString* {.deprecated: "Deprecated since 1.5".} = string
+when not defined(nimPreviewSlimSystem):
+  type TaintedString* {.deprecated: "Deprecated since 1.5".} = string
 
 
 when defined(profiler) and not defined(nimscript):
@@ -1198,6 +1097,8 @@ when not defined(js) and hostOS != "standalone":
     ## deprecated, prefer `quit` or `exitprocs.getProgramResult`, `exitprocs.setProgramResult`.
 
 import std/private/since
+import system/ctypes
+export ctypes
 
 proc align(address, alignment: int): int =
   if alignment == 0: # Actually, this is illegal. This branch exists to actively
@@ -1206,56 +1107,9 @@ proc align(address, alignment: int): int =
   else:
     result = (address + (alignment - 1)) and not (alignment - 1)
 
-when defined(nimNoQuit):
-  proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit"
-    ## Stops the program immediately with an exit code.
-    ##
-    ## Before stopping the program the "exit procedures" are called in the
-    ## opposite order they were added with `addExitProc <exitprocs.html#addExitProc,proc)>`_.
-    ##
-    ## The proc `quit(QuitSuccess)` is called implicitly when your nim
-    ## program finishes without incident for platforms where this is the
-    ## expected behavior. A raised unhandled exception is
-    ## equivalent to calling `quit(QuitFailure)`.
-    ##
-    ## Note that this is a *runtime* call and using `quit` inside a macro won't
-    ## have any compile time effect. If you need to stop the compiler inside a
-    ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
-    ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
-    ##
-    ## .. danger:: In almost all cases, in particular in library code, prefer
-    ##   alternatives, e.g. `doAssert false` or raise a `Defect`.
-    ##   `quit` bypasses regular control flow in particular `defer`,
-    ##   `try`, `catch`, `finally` and `destructors`, and exceptions that may have been
-    ##   raised by an `addExitProc` proc, as well as cleanup code in other threads.
-    ##   It does *not* call the garbage collector to free all the memory,
-    ##   unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_.
-
-elif defined(nimdoc):
-  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
-
-elif defined(genode):
-  include genode/env
-
-  var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr
-
-  type GenodeEnv* = GenodeEnvPtr
-    ## Opaque type representing Genode environment.
-
-  proc quit*(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn,
-    importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "<base/sleep.h>".}
-
-  proc quit*(errorcode: int = QuitSuccess) =
-    systemEnv.quit(errorcode)
-
-elif defined(js) and defined(nodejs) and not defined(nimscript):
-  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
-    importc: "process.exit", noreturn.}
-
-else:
-  proc quit*(errorcode: int = QuitSuccess) {.
-    magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
-
+include system/rawquits
+when defined(genode):
+  export GenodeEnv
 
 template sysAssert(cond: bool, msg: string) =
   when defined(useSysAssert):
@@ -1263,12 +1117,10 @@ template sysAssert(cond: bool, msg: string) =
       cstderr.rawWrite "[SYSASSERT] "
       cstderr.rawWrite msg
       cstderr.rawWrite "\n"
-      quit 1
+      rawQuit 1
 
 const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
 
-when notJSnotNims and hostOS != "standalone" and hostOS != "any":
-  include "system/cgprocs"
 when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
   proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
 
@@ -1280,6 +1132,11 @@ when defined(nimscript) or not defined(nimSeqsV2):
     ## containers should also call their adding proc `add` for consistency.
     ## Generic code becomes much easier to write if the Nim naming scheme is
     ## respected.
+    ##   ```nim
+    ##   var s: seq[string] = @["test2","test2"]
+    ##   s.add("test")
+    ##   assert s == @["test2", "test2", "test"]
+    ##   ```
 
 when false: # defined(gcDestructors):
   proc add*[T](x: var seq[T], y: sink openArray[T]) {.noSideEffect.} =
@@ -1289,7 +1146,7 @@ when false: # defined(gcDestructors):
     ## containers should also call their adding proc `add` for consistency.
     ## Generic code becomes much easier to write if the Nim naming scheme is
     ## respected.
-    ##   ```
+    ##   ```nim
     ##   var s: seq[string] = @["test2","test2"]
     ##   s.add("test") # s <- @[test2, test2, test]
     ##   ```
@@ -1314,13 +1171,17 @@ else:
     ## containers should also call their adding proc `add` for consistency.
     ## Generic code becomes much easier to write if the Nim naming scheme is
     ## respected.
-    ##   ```
-    ##   var s: seq[string] = @["test2","test2"]
-    ##   s.add("test") # s <- @[test2, test2, test]
-    ##   ```
     ##
     ## See also:
     ## * `& proc <#&,seq[T],seq[T]>`_
+    runnableExamples:
+      var a = @["a1", "a2"]
+      a.add(["b1", "b2"])
+      assert a == @["a1", "a2", "b1", "b2"]
+      var c = @["c0", "c1", "c2", "c3"]
+      a.add(c.toOpenArray(1, 2))
+      assert a == @["a1", "a2", "b1", "b2", "c1", "c2"]
+
     {.noSideEffect.}:
       let xl = x.len
       setLen(x, xl + y.len)
@@ -1328,10 +1189,10 @@ else:
 
 
 when defined(nimSeqsV2):
-  template movingCopy(a, b) =
+  template movingCopy(a, b: typed) =
     a = move(b)
 else:
-  template movingCopy(a, b) =
+  template movingCopy(a, b: typed) =
     shallowCopy(a, b)
 
 proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
@@ -1351,7 +1212,7 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
 
 proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} =
   ## Inserts `item` into `x` at position `i`.
-  ##   ```
+  ##   ```nim
   ##   var i = @[1, 3, 5]
   ##   i.insert(99, 0) # i <- @[99, 1, 3, 5]
   ##   ```
@@ -1382,89 +1243,32 @@ when not defined(nimV2):
     ##
     ## It works even for complex data graphs with cycles. This is a great
     ## debugging tool.
-    ##   ```
+    ##   ```nim
     ##   var s: seq[string] = @["test2", "test2"]
     ##   var i = @[1, 2, 3, 4, 5]
     ##   echo repr(s) # => 0x1055eb050[0x1055ec050"test2", 0x1055ec078"test2"]
     ##   echo repr(i) # => 0x1055ed050[1, 2, 3, 4, 5]
     ##   ```
 
-type
-  ByteAddress* = int
-    ## is the signed integer type that should be used for converting
-    ## pointers to integer addresses for readability.
-
-  BiggestFloat* = float64
-    ## is an alias for the biggest floating point type the Nim
-    ## compiler supports. Currently this is `float64`, but it is
-    ## platform-dependent in general.
+when not defined(nimPreviewSlimSystem):
+  type
+    csize* {.importc: "size_t", nodecl, deprecated: "use `csize_t` instead".} = int
+      ## This isn't the same as `size_t` in *C*. Don't use it.
 
-when defined(js):
-  type BiggestUInt* = uint32
-    ## is an alias for the biggest unsigned integer type the Nim compiler
-    ## supports. Currently this is `uint32` for JS and `uint64` for other
-    ## targets.
-else:
-  type BiggestUInt* = uint64
-    ## is an alias for the biggest unsigned integer type the Nim compiler
-    ## supports. Currently this is `uint32` for JS and `uint64` for other
-    ## targets.
+const
+  Inf* = 0x7FF0000000000000'f64
+    ## Contains the IEEE floating point value of positive infinity.
+  NegInf* = 0xFFF0000000000000'f64
+    ## Contains the IEEE floating point value of negative infinity.
+  NaN* = 0x7FF7FFFFFFFFFFFF'f64
+    ## Contains an IEEE floating point value of *Not A Number*.
+    ##
+    ## Note that you cannot compare a floating point value to this value
+    ## and expect a reasonable result - use the `isNaN` or `classify` procedure
+    ## in the `math module <math.html>`_ for checking for NaN.
 
-when defined(windows):
-  type
-    clong* {.importc: "long", nodecl.} = int32
-      ## This is the same as the type `long` in *C*.
-    culong* {.importc: "unsigned long", nodecl.} = uint32
-      ## This is the same as the type `unsigned long` in *C*.
-else:
-  type
-    clong* {.importc: "long", nodecl.} = int
-      ## This is the same as the type `long` in *C*.
-    culong* {.importc: "unsigned long", nodecl.} = uint
-      ## This is the same as the type `unsigned long` in *C*.
-
-type # these work for most platforms:
-  cchar* {.importc: "char", nodecl.} = char
-    ## This is the same as the type `char` in *C*.
-  cschar* {.importc: "signed char", nodecl.} = int8
-    ## This is the same as the type `signed char` in *C*.
-  cshort* {.importc: "short", nodecl.} = int16
-    ## This is the same as the type `short` in *C*.
-  cint* {.importc: "int", nodecl.} = int32
-    ## This is the same as the type `int` in *C*.
-  csize* {.importc: "size_t", nodecl, deprecated: "use `csize_t` instead".} = int
-    ## This isn't the same as `size_t` in *C*. Don't use it.
-  csize_t* {.importc: "size_t", nodecl.} = uint
-    ## This is the same as the type `size_t` in *C*.
-  clonglong* {.importc: "long long", nodecl.} = int64
-    ## This is the same as the type `long long` in *C*.
-  cfloat* {.importc: "float", nodecl.} = float32
-    ## This is the same as the type `float` in *C*.
-  cdouble* {.importc: "double", nodecl.} = float64
-    ## This is the same as the type `double` in *C*.
-  clongdouble* {.importc: "long double", nodecl.} = BiggestFloat
-    ## This is the same as the type `long double` in *C*.
-    ## This C type is not supported by Nim's code generator.
-
-  cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char
-    ## Deprecated: Use `uint8` instead.
-  cushort* {.importc: "unsigned short", nodecl.} = uint16
-    ## This is the same as the type `unsigned short` in *C*.
-  cuint* {.importc: "unsigned int", nodecl.} = uint32
-    ## This is the same as the type `unsigned int` in *C*.
-  culonglong* {.importc: "unsigned long long", nodecl.} = uint64
-    ## This is the same as the type `unsigned long long` in *C*.
-
-  cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring]
-    ## This is binary compatible to the type `char**` in *C*. The array's
-    ## high value is large enough to disable bounds checking in practice.
-    ## Use `cstringArrayToSeq proc <#cstringArrayToSeq,cstringArray,Natural>`_
-    ## to convert it into a `seq[string]`.
-
-  PFloat32* = ptr float32    ## An alias for `ptr float32`.
-  PFloat64* = ptr float64    ## An alias for `ptr float64`.
-  PInt64* = ptr int64        ## An alias for `ptr int64`.
-  PInt32* = ptr int32        ## An alias for `ptr int32`.
+proc high*(T: typedesc[SomeFloat]): T = Inf
+proc low*(T: typedesc[SomeFloat]): T = NegInf
 
 proc toFloat*(i: int): float {.noSideEffect, inline.} =
   ## Converts an integer `i` into a `float`. Same as `float(i)`.
@@ -1472,7 +1276,7 @@ proc toFloat*(i: int): float {.noSideEffect, inline.} =
   ## If the conversion fails, `ValueError` is raised.
   ## However, on most platforms the conversion cannot fail.
   ##
-  ##   ```
+  ##   ```nim
   ##   let
   ##     a = 2
   ##     b = 3.7
@@ -1495,7 +1299,7 @@ proc toInt*(f: float): int {.noSideEffect.} =
   ##
   ## Note that some floating point numbers (e.g. infinity or even 1e19)
   ## cannot be accurately converted.
-  ##   ```
+  ##   ```nim
   ##   doAssert toInt(0.49) == 0
   ##   doAssert toInt(0.5) == 1
   ##   doAssert toInt(-0.5) == -1 # rounding is symmetrical
@@ -1506,18 +1310,68 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} =
   ## Same as `toInt <#toInt,float>`_ but for `BiggestFloat` to `BiggestInt`.
   if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5)
 
-proc addQuitProc*(quitProc: proc() {.noconv.}) {.
-  importc: "atexit", header: "<stdlib.h>", deprecated: "use exitprocs.addExitProc".}
-  ## Adds/registers a quit procedure.
+proc `/`*(x, y: int): float {.inline, noSideEffect.} =
+  ## Division of integers that results in a float.
+  ##   ```nim
+  ##   echo 7 / 5 # => 1.4
+  ##   ```
+  ##
+  ## See also:
+  ## * `div <system.html#div,int,int>`_
+  ## * `mod <system.html#mod,int,int>`_
+  result = toFloat(x) / toFloat(y)
+
+{.push stackTrace: off.}
+
+when defined(js):
+  proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".}
+else:
+  proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "<math.h>".}
+  proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "<math.h>".}
+
+proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} =
+  when nimvm:
+    if x < 0.0: result = -x
+    elif x == 0.0: result = 0.0 # handle 0.0, -0.0
+    else: result = x # handle NaN, > 0
+  else:
+    when defined(js): result = js_abs(x)
+    else:
+      when T is float64:
+        result = c_fabs(x)
+      else:
+        result = c_fabsf(x)
+
+func abs*(x: int): int {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int8): int8 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int16): int16 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int32): int32 {.magic: "AbsI", inline.} =
+  if x < 0: -x else: x
+func abs*(x: int64): int64 {.magic: "AbsI", inline.} =
+  ## Returns the absolute value of `x`.
   ##
-  ## Each call to `addQuitProc` registers another quit procedure. Up to 30
-  ## procedures can be registered. They are executed on a last-in, first-out
-  ## basis (that is, the last function registered is the first to be executed).
-  ## `addQuitProc` raises an EOutOfIndex exception if `quitProc` cannot be
-  ## registered.
-  # Support for addQuitProc() is done by Ansi C's facilities here.
-  # In case of an unhandled exception the exit handlers should
-  # not be called explicitly! The user may decide to do this manually though.
+  ## If `x` is `low(x)` (that is -MININT for its type),
+  ## an overflow exception is thrown (if overflow checking is turned on).
+  result = if x < 0: -x else: x
+
+{.pop.} # stackTrace: off
+
+when not defined(nimPreviewSlimSystem):
+  proc addQuitProc*(quitProc: proc() {.noconv.}) {.
+    importc: "atexit", header: "<stdlib.h>", deprecated: "use exitprocs.addExitProc".}
+    ## Adds/registers a quit procedure.
+    ##
+    ## Each call to `addQuitProc` registers another quit procedure. Up to 30
+    ## procedures can be registered. They are executed on a last-in, first-out
+    ## basis (that is, the last function registered is the first to be executed).
+    ## `addQuitProc` raises an EOutOfIndex exception if `quitProc` cannot be
+    ## registered.
+    # Support for addQuitProc() is done by Ansi C's facilities here.
+    # In case of an unhandled exception the exit handlers should
+    # not be called explicitly! The user may decide to do this manually though.
 
 proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
   ## Swaps the values `a` and `b`.
@@ -1525,7 +1379,7 @@ proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.}
   ## This is often more efficient than `tmp = a; a = b; b = tmp`.
   ## Particularly useful for sorting algorithms.
   ##
-  ##   ```
+  ##   ```nim
   ##   var
   ##     a = 5
   ##     b = 9
@@ -1543,18 +1397,16 @@ when not defined(js) and not defined(booting) and defined(nimTrMacros):
     # unnecessary slow down in this case.
     swap(cast[ptr pointer](addr arr[a])[], cast[ptr pointer](addr arr[b])[])
 
-const
-  Inf* = 0x7FF0000000000000'f64
-    ## Contains the IEEE floating point value of positive infinity.
-  NegInf* = 0xFFF0000000000000'f64
-    ## Contains the IEEE floating point value of negative infinity.
-  NaN* = 0x7FF7FFFFFFFFFFFF'f64
-    ## Contains an IEEE floating point value of *Not A Number*.
-    ##
-    ## Note that you cannot compare a floating point value to this value
-    ## and expect a reasonable result - use the `isNaN` or `classify` procedure
-    ## in the `math module <math.html>`_ for checking for NaN.
+when not defined(nimscript):
+  {.push stackTrace: off, profiler: off.}
 
+  when not defined(nimPreviewSlimSystem):
+    import std/sysatomics
+    export sysatomics
+  else:
+    import std/sysatomics
+
+  {.pop.}
 
 include "system/memalloc"
 
@@ -1564,50 +1416,9 @@ proc `|`*(a, b: typedesc): typedesc = discard
 include "system/iterators_1"
 
 
-{.push stackTrace: off.}
-
-
-when defined(js):
-  proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".}
-else:
-  proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "<math.h>".}
-  proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "<math.h>".}
-
-proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} =
-  when nimvm:
-    if x < 0.0: result = -x
-    elif x == 0.0: result = 0.0 # handle 0.0, -0.0
-    else: result = x # handle NaN, > 0
-  else:
-    when defined(js): result = js_abs(x)
-    else:
-      when T is float64:
-        result = c_fabs(x)
-      else:
-        result = c_fabsf(x)
-
-proc min*(x, y: float32): float32 {.noSideEffect, inline.} =
-  if x <= y or y != y: x else: y
-proc min*(x, y: float64): float64 {.noSideEffect, inline.} =
-  if x <= y or y != y: x else: y
-proc max*(x, y: float32): float32 {.noSideEffect, inline.} =
-  if y <= x or y != y: x else: y
-proc max*(x, y: float64): float64 {.noSideEffect, inline.} =
-  if y <= x or y != y: x else: y
-proc min*[T: not SomeFloat](x, y: T): T {.inline.} =
-  if x <= y: x else: y
-proc max*[T: not SomeFloat](x, y: T): T {.inline.} =
-  if y <= x: x else: y
-
-{.pop.} # stackTrace: off
-
-
-proc high*(T: typedesc[SomeFloat]): T = Inf
-proc low*(T: typedesc[SomeFloat]): T = NegInf
-
 proc len*[U: Ordinal; V: Ordinal](x: HSlice[U, V]): int {.noSideEffect, inline.} =
   ## Length of ordinal slice. When x.b < x.a returns zero length.
-  ##   ```
+  ##   ```nim
   ##   assert((0..5).len == 6)
   ##   assert((5..2).len == 0)
   ##   ```
@@ -1618,11 +1429,10 @@ proc isNil*[T](x: ref T): bool {.noSideEffect, magic: "IsNil".}
 proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".}
 proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".}
 proc isNil*(x: cstring): bool {.noSideEffect, magic: "IsNil".}
-proc isNil*[T: proc](x: T): bool {.noSideEffect, magic: "IsNil".}
+proc isNil*[T: proc | iterator {.closure.}](x: T): bool {.noSideEffect, magic: "IsNil".}
   ## Fast check whether `x` is nil. This is sometimes more efficient than
   ## `== nil`.
 
-
 when defined(nimHasTopDownInference):
   # magic used for seq type inference
   proc `@`*[T](a: openArray[T]): seq[T] {.magic: "OpenArrayToSeq".} =
@@ -1648,7 +1458,7 @@ when defined(nimSeqsV2):
     ## Concatenates two sequences.
     ##
     ## Requires copying of the sequences.
-    ##   ```
+    ##   ```nim
     ##   assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6])
     ##   ```
     ##
@@ -1664,7 +1474,7 @@ when defined(nimSeqsV2):
     ## Appends element y to the end of the sequence.
     ##
     ## Requires copying of the sequence.
-    ##   ```
+    ##   ```nim
     ##   assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4])
     ##   ```
     ##
@@ -1679,7 +1489,7 @@ when defined(nimSeqsV2):
     ## Prepends the element x to the beginning of the sequence.
     ##
     ## Requires copying of the sequence.
-    ##   ```
+    ##   ```nim
     ##   assert(1 & @[2, 3, 4] == @[1, 2, 3, 4])
     ##   ```
     newSeq(result, y.len + 1)
@@ -1693,7 +1503,7 @@ else:
     ## Concatenates two sequences.
     ##
     ## Requires copying of the sequences.
-    ##   ```
+    ##   ```nim
     ##   assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6])
     ##   ```
     ##
@@ -1709,7 +1519,7 @@ else:
     ## Appends element y to the end of the sequence.
     ##
     ## Requires copying of the sequence.
-    ##   ```
+    ##   ```nim
     ##   assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4])
     ##   ```
     ##
@@ -1724,7 +1534,7 @@ else:
     ## Prepends the element x to the beginning of the sequence.
     ##
     ## Requires copying of the sequence.
-    ##   ```
+    ##   ```nim
     ##   assert(1 & @[2, 3, 4] == @[1, 2, 3, 4])
     ##   ```
     newSeq(result, y.len + 1)
@@ -1733,10 +1543,6 @@ else:
       result[i+1] = y[i]
 
 
-proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
-  ## Converts the AST of `x` into a string representation. This is very useful
-  ## for debugging.
-
 proc instantiationInfo*(index = -1, fullPaths = false): tuple[
   filename: string, line: int, column: int] {.magic: "InstantiationInfo", noSideEffect.}
   ## Provides access to the compiler's instantiation stack line information
@@ -1749,7 +1555,7 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
   ## to retrieve information about the current filename and line number.
   ## Example:
   ##
-  ##   ```
+  ##   ```nim
   ##   import std/strutils
   ##
   ##   template testException(exception, code: untyped): typed =
@@ -1773,15 +1579,6 @@ proc instantiationInfo*(index = -1, fullPaths = false): tuple[
   ##     # --> Test failure at example.nim:20 with 'tester(1)'
   ##   ```
 
-proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} =
-  ## Special compile-time procedure that checks whether `x` can be compiled
-  ## without any semantic error.
-  ## This can be used to check whether a type supports some operation:
-  ##   ```
-  ##   when compiles(3 + 4):
-  ##     echo "'+' for integers is available"
-  ##   ```
-  discard
 
 when notJSnotNims:
   import system/ansi_c
@@ -1791,8 +1588,7 @@ when notJSnotNims:
 {.push stackTrace: off.}
 
 when not defined(js) and hasThreadSupport and hostOS != "standalone":
-  const insideRLocksModule = false
-  include "system/syslocks"
+  import std/private/syslocks
   include "system/threadlocalstorage"
 
 when not defined(js) and defined(nimV2):
@@ -1801,17 +1597,100 @@ when not defined(js) and defined(nimV2):
     TNimTypeV2 {.compilerproc.} = object
       destructor: pointer
       size: int
-      align: int
-      name: cstring
+      align: int16
+      depth: int16
+      display: ptr UncheckedArray[uint32] # classToken
+      when defined(nimTypeNames) or defined(nimArcIds):
+        name: cstring
       traceImpl: pointer
       typeInfoV1: pointer # for backwards compat, usually nil
       flags: int
+      when defined(gcDestructors):
+        when defined(cpp):
+          vTable: ptr UncheckedArray[pointer] # vtable for types
+        else:
+          vTable: UncheckedArray[pointer] # vtable for types
     PNimTypeV2 = ptr TNimTypeV2
 
+proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
+
 when notJSnotNims and defined(nimSeqsV2):
   include "system/strs_v2"
   include "system/seqs_v2"
 
+when not defined(js):
+  template newSeqImpl(T, len) =
+    result = newSeqOfCap[T](len)
+    {.cast(noSideEffect).}:
+      when defined(nimSeqsV2):
+        cast[ptr int](addr result)[] = len
+      else:
+        var s = cast[PGenericSeq](result)
+        s.len = len
+
+  proc newSeqUninitialized*[T: SomeNumber](len: Natural): seq[T] {.deprecated: "Use `newSeqUninit` instead".} =
+    ## Creates a new sequence of type `seq[T]` with length `len`.
+    ##
+    ## Only available for numbers types. Note that the sequence will be
+    ## uninitialized. After the creation of the sequence you should assign
+    ## entries to the sequence instead of adding them.
+    ## Example:
+    ##   ```nim
+    ##   var x = newSeqUninitialized[int](3)
+    ##   assert len(x) == 3
+    ##   x[0] = 10
+    ##   ```
+    result = newSeqOfCap[T](len)
+    when defined(nimSeqsV2):
+      cast[ptr int](addr result)[] = len
+    else:
+      var s = cast[PGenericSeq](result)
+      s.len = len
+
+  func newSeqUninit*[T](len: Natural): seq[T] =
+    ## Creates a new sequence of type `seq[T]` with length `len`.
+    ##
+    ## Only available for types, which don't contain
+    ## managed memory or have destructors.
+    ## Note that the sequence will be uninitialized.
+    ## After the creation of the sequence you should assign
+    ## entries to the sequence instead of adding them.
+    runnableExamples:
+      var x = newSeqUninit[int](3)
+      assert len(x) == 3
+      x[0] = 10
+    when supportsCopyMem(T):
+      when nimvm:
+        result = newSeq[T](len)
+      else:
+        newSeqImpl(T, len)
+    else:
+      {.error: "The type T cannot contain managed memory or have destructors".}
+
+  proc newStringUninit*(len: Natural): string =
+    ## Returns a new string of length `len` but with uninitialized
+    ## content. One needs to fill the string character after character
+    ## with the index operator `s[i]`.
+    ##
+    ## This procedure exists only for optimization purposes;
+    ## the same effect can be achieved with the `&` operator or with `add`.
+    when nimvm:
+      result = newString(len)
+    else:
+      result = newStringOfCap(len)
+      when defined(nimSeqsV2):
+        let s = cast[ptr NimStringV2](addr result)
+        if len > 0:
+          s.len = len
+          s.p.data[len] = '\0'
+      else:
+        let s = cast[NimString](result)
+        s.len = len
+        s.data[len] = '\0'
+else:
+  proc newStringUninit*(len: Natural): string {.
+    magic: "NewString", importc: "mnewString", noSideEffect.}
+
 {.pop.}
 
 when not defined(nimscript):
@@ -1823,27 +1702,33 @@ when not defined(nimscript):
 when not declared(sysFatal):
   include "system/fatal"
 
-when not defined(nimscript):
-  {.push stackTrace: off, profiler: off.}
-
-  proc atomicInc*(memLoc: var int, x: int = 1): int {.inline,
-    discardable, benign.}
-    ## Atomic increment of `memLoc`. Returns the value after the operation.
-
-  proc atomicDec*(memLoc: var int, x: int = 1): int {.inline,
-    discardable, benign.}
-    ## Atomic decrement of `memLoc`. Returns the value after the operation.
-
-  include "system/atomics"
-
-  {.pop.}
-
+type
+  PFrame* = ptr TFrame  ## Represents a runtime frame of the call stack;
+                        ## part of the debugger API.
+  # keep in sync with nimbase.h `struct TFrame_`
+  TFrame* {.importc, nodecl, final.} = object ## The frame itself.
+    prev*: PFrame       ## Previous frame; used for chaining the call stack.
+    procname*: cstring  ## Name of the proc that is currently executing.
+    line*: int          ## Line number of the proc that is currently executing.
+    filename*: cstring  ## Filename of the proc that is currently executing.
+    len*: int16         ## Length of the inspectable slots.
+    calldepth*: int16   ## Used for max call depth checking.
+    when NimStackTraceMsgs:
+      frameMsgLen*: int   ## end position in frameMsgBuf for this frame.
 
 when defined(nimV2):
+  var
+    framePtr {.threadvar.}: PFrame
+
   include system/arc
 
+template newException*(exceptn: typedesc, message: string;
+                       parentException: ref Exception = nil): untyped =
+  ## Creates an exception object of type `exceptn` and sets its `msg` field
+  ## to `message`. Returns the new exception object.
+  (ref exceptn)(msg: message, parent: parentException)
+
 when not defined(nimPreviewSlimSystem):
-  {.deprecated: "assertions is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/assertions`".}
   import std/assertions
   export assertions
 
@@ -1866,7 +1751,7 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
   ##
   ## This allows the `in` operator: `a.contains(item)` is the same as
   ## `item in a`.
-  ##   ```
+  ##   ```nim
   ##   var a = @[1, 3, 5]
   ##   assert a.contains(5)
   ##   assert 3 in a
@@ -1877,6 +1762,8 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}=
 proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} =
   ## Returns the last item of `s` and decreases `s.len` by one. This treats
   ## `s` as a stack and implements the common *pop* operation.
+  ##
+  ## Raises `IndexDefect` if `s` is empty.
   runnableExamples:
     var a = @[1, 3, 5, 7]
     let b = pop(a)
@@ -1958,7 +1845,7 @@ when notJSnotNims:
       ##
       ## `outOfMemHook` can be used to raise an exception in case of OOM like so:
       ##
-      ##   ```
+      ##   ```nim
       ##   var gOutOfMem: ref EOutOfMemory
       ##   new(gOutOfMem) # need to be allocated *before* OOM really happened!
       ##   gOutOfMem.msg = "out of memory"
@@ -1977,20 +1864,6 @@ when notJSnotNims:
       ## writes an error message and terminates the program, except when
       ## using `--os:any`
 
-type
-  PFrame* = ptr TFrame  ## Represents a runtime frame of the call stack;
-                        ## part of the debugger API.
-  # keep in sync with nimbase.h `struct TFrame_`
-  TFrame* {.importc, nodecl, final.} = object ## The frame itself.
-    prev*: PFrame       ## Previous frame; used for chaining the call stack.
-    procname*: cstring  ## Name of the proc that is currently executing.
-    line*: int          ## Line number of the proc that is currently executing.
-    filename*: cstring  ## Filename of the proc that is currently executing.
-    len*: int16         ## Length of the inspectable slots.
-    calldepth*: int16   ## Used for max call depth checking.
-    when NimStackTraceMsgs:
-      frameMsgLen*: int   ## end position in frameMsgBuf for this frame.
-
 when defined(js) or defined(nimdoc):
   proc add*(x: var string, y: cstring) {.asmNoStackFrame.} =
     ## Appends `y` to `x` in place.
@@ -1999,14 +1872,14 @@ when defined(js) or defined(nimdoc):
       tmp.add(cstring("ab"))
       tmp.add(cstring("cd"))
       doAssert tmp == "abcd"
-    asm """
+    {.emit: """
       if (`x` === null) { `x` = []; }
       var off = `x`.length;
       `x`.length += `y`.length;
       for (var i = 0; i < `y`.length; ++i) {
         `x`[off+i] = `y`.charCodeAt(i);
       }
-    """
+    """.}
   proc add*(x: var cstring, y: cstring) {.magic: "AppendStrStr".} =
     ## Appends `y` to `x` in place.
     ## Only implemented for JS backend.
@@ -2049,16 +1922,10 @@ proc debugEcho*(x: varargs[typed, `$`]) {.magic: "Echo", noSideEffect,
   ## for debugging routines marked as `noSideEffect
   ## <manual.html#pragmas-nosideeffect-pragma>`_.
 
-template newException*(exceptn: typedesc, message: string;
-                       parentException: ref Exception = nil): untyped =
-  ## Creates an exception object of type `exceptn` and sets its `msg` field
-  ## to `message`. Returns the new exception object.
-  (ref exceptn)(msg: message, parent: parentException)
-
 when hostOS == "standalone" and defined(nogc):
   proc nimToCStringConv(s: NimString): cstring {.compilerproc, inline.} =
     if s == nil or s.len == 0: result = cstring""
-    else: result = cstring(addr s.data)
+    else: result = cast[cstring](addr s.data)
 
 proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
   ## Get type information for `x`.
@@ -2066,22 +1933,6 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.}
   ## Ordinary code should not use this, but the `typeinfo module
   ## <typeinfo.html>`_ instead.
 
-{.push stackTrace: off.}
-func abs*(x: int): int {.magic: "AbsI", inline.} =
-  if x < 0: -x else: x
-func abs*(x: int8): int8 {.magic: "AbsI", inline.} =
-  if x < 0: -x else: x
-func abs*(x: int16): int16 {.magic: "AbsI", inline.} =
-  if x < 0: -x else: x
-func abs*(x: int32): int32 {.magic: "AbsI", inline.} =
-  if x < 0: -x else: x
-func abs*(x: int64): int64 {.magic: "AbsI", inline.} =
-  ## Returns the absolute value of `x`.
-  ##
-  ## If `x` is `low(x)` (that is -MININT for its type),
-  ## an overflow exception is thrown (if overflow checking is turned on).
-  result = if x < 0: -x else: x
-{.pop.}
 
 when not defined(js):
 
@@ -2094,7 +1945,7 @@ template likely*(val: bool): bool =
   ## You can use this template to decorate a branch condition. On certain
   ## platforms this can help the processor predict better which branch is
   ## going to be run. Example:
-  ##   ```
+  ##   ```nim
   ##   for value in inputValues:
   ##     if likely(value <= 100):
   ##       process(value)
@@ -2118,7 +1969,7 @@ template unlikely*(val: bool): bool =
   ## You can use this proc to decorate a branch condition. On certain
   ## platforms this can help the processor predict better which branch is
   ## going to be run. Example:
-  ##   ```
+  ##   ```nim
   ##   for value in inputValues:
   ##     if unlikely(value > 100):
   ##       echo "Value too big!"
@@ -2136,22 +1987,6 @@ template unlikely*(val: bool): bool =
     else:
       unlikelyProc(val)
 
-const
-  NimMajor* {.intdefine.}: int = 1
-    ## is the major number of Nim's version. Example:
-    ##   ```
-    ##   when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard
-    ##   ```
-    # see also std/private/since
-
-  NimMinor* {.intdefine.}: int = 7
-    ## is the minor number of Nim's version.
-    ## Odd for devel, even for releases.
-
-  NimPatch* {.intdefine.}: int = 3
-    ## is the patch number of Nim's version.
-    ## Odd for devel, even for releases.
-
 import system/dollars
 export dollars
 
@@ -2160,15 +1995,11 @@ when defined(nimAuditDelete):
 else:
   {.pragma: auditDelete.}
 
-proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, auditDelete.} =
+proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, systemRaisesDefect, auditDelete.} =
   ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position.
   ##
   ## This is an `O(n)` operation.
   ##
-  ## .. note:: With `-d:nimStrictDelete`, an index error is produced when the index passed
-  ##    to it was out of bounds. `-d:nimStrictDelete` will become the default
-  ##    in upcoming versions.
-  ##
   ## See also:
   ## * `del <#del,seq[T],Natural>`_ for O(1) operation
   ##
@@ -2177,7 +2008,7 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, auditDelete.} =
     s.delete(2)
     doAssert s == @[1, 2, 4, 5]
 
-  when defined(nimStrictDelete):
+  when not defined(nimAuditDelete):
     if i > high(x):
       # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring
       raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed")
@@ -2200,16 +2031,6 @@ const
   NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
     ## is the version of Nim as a string.
 
-
-type
-  FileSeekPos* = enum ## Position relative to which seek should happen.
-                      # The values are ordered so that they match with stdio
-                      # SEEK_SET, SEEK_CUR and SEEK_END respectively.
-    fspSet            ## Seek to absolute value
-    fspCur            ## Seek relative to current position
-    fspEnd            ## Seek relative to end
-
-
 when not defined(js):
   {.push stackTrace: off, profiler: off.}
 
@@ -2262,9 +2083,10 @@ when notJSnotNims:
   proc equalMem(a, b: pointer, size: Natural): bool =
     nimCmpMem(a, b, size) == 0
   proc cmpMem(a, b: pointer, size: Natural): int =
-    nimCmpMem(a, b, size)
+    nimCmpMem(a, b, size).int
 
-when not defined(js):
+when not defined(js) or defined(nimscript):
+  # nimscript can be defined if config file for js compilation
   proc cmp(x, y: string): int =
     when nimvm:
       if x < y: result = -1
@@ -2281,12 +2103,14 @@ when not defined(js):
     proc cstringArrayToSeq*(a: cstringArray, len: Natural): seq[string] =
       ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
       ## of length `len`.
+      if a == nil: return @[]
       newSeq(result, len)
       for i in 0..len-1: result[i] = $a[i]
 
     proc cstringArrayToSeq*(a: cstringArray): seq[string] =
       ## Converts a `cstringArray` to a `seq[string]`. `a` is supposed to be
       ## terminated by `nil`.
+      if a == nil: return @[]
       var L = 0
       while a[L] != nil: inc(L)
       result = cstringArrayToSeq(a, L)
@@ -2311,7 +2135,7 @@ when not defined(js) and declared(alloc0) and declared(dealloc):
       inc(i)
     dealloc(a)
 
-when notJSnotNims:
+when notJSnotNims and not gotoBasedExceptions:
   type
     PSafePoint = ptr TSafePoint
     TSafePoint {.compilerproc, final.} = object
@@ -2324,7 +2148,12 @@ when not defined(js):
   when declared(initAllocator):
     initAllocator()
   when hasThreadSupport:
-    when hostOS != "standalone": include "system/threads"
+    when hostOS != "standalone":
+      include system/threadimpl
+      when not defined(nimPreviewSlimSystem):
+        import std/typedthreads
+        export typedthreads
+
   elif not defined(nogc) and not defined(nimscript):
     when not defined(useNimRtl) and not defined(createNimRtl): initStackBottom()
     when declared(initGC): initGC()
@@ -2335,7 +2164,7 @@ when notJSnotNims:
     ## is pressed. Only one such hook is supported.
     ## Example:
     ##
-    ##   ```
+    ##   ```nim
     ##   proc ctrlc() {.noconv.} =
     ##     echo "Ctrl+C fired!"
     ##     # do clean up stuff
@@ -2368,10 +2197,7 @@ when notJSnotNims:
 
   # we cannot compile this with stack tracing on
   # as it would recurse endlessly!
-  when defined(nimNewIntegerOps):
-    include "system/integerops"
-  else:
-    include "system/arithm"
+  include "system/integerops"
   {.pop.}
 
 
@@ -2384,6 +2210,16 @@ when not defined(js):
 
 when notJSnotNims:
   when hostOS != "standalone" and hostOS != "any":
+    type
+      LibHandle = pointer       # private type
+      ProcAddr = pointer        # library loading and loading of procs:
+
+    proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.}
+    proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.}
+    proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.}
+
+    proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.}
+
     include "system/dyncalls"
 
   import system/countbits_impl
@@ -2433,7 +2269,8 @@ when notJSnotNims and hasAlloc:
     include "system/repr"
 
 when notJSnotNims and hasThreadSupport and hostOS != "standalone":
-  include "system/channels_builtin"
+  when not defined(nimPreviewSlimSystem):
+    include "system/channels_builtin"
 
 
 when notJSnotNims and hostOS != "standalone":
@@ -2465,47 +2302,133 @@ when notJSnotNims:
     include "system/profiler"
   {.pop.}
 
-  proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} =
+  proc rawProc*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} =
     ## Retrieves the raw proc pointer of the closure `x`. This is
-    ## useful for interfacing closures with C/C++, hash compuations, etc.
-    when T is "closure":
-      #[
-      The conversion from function pointer to `void*` is a tricky topic, but this
-      should work at least for c++ >= c++11, e.g. for `dlsym` support.
-      refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869,
-      https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c
-      ]#
-      {.emit: """
-      `result` = (void*)`x`.ClP_0;
-      """.}
-    else:
-      {.error: "Only closure function and iterator are allowed!".}
+    ## useful for interfacing closures with C/C++, hash computations, etc.
+    ## If `rawEnv(x)` returns `nil`, the proc which the result points to
+    ## takes as many parameters as `x`, but with `{.nimcall.}` as its calling
+    ## convention instead of `{.closure.}`, otherwise it takes one more parameter
+    ## which is a `pointer`, and it still has `{.nimcall.}` as its calling convention.
+    ## To invoke the resulted proc, what this returns has to be casted into a `proc`,
+    ## not a `ptr proc`, and, in a case where `rawEnv(x)` returns non-`nil`,
+    ## the last and additional argument has to be the result of `rawEnv(x)`.
+    ## This is not available for the JS target.
+    #[
+    The conversion from function pointer to `void*` is a tricky topic, but this
+    should work at least for c++ >= c++11, e.g. for `dlsym` support.
+    refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869,
+    https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c
+    ]#
+    runnableExamples:
+      proc makeClosure(x: int): (proc(y: int): int) =
+        var n = x
+        result = (
+          proc(y: int): int =
+            n += y
+            return n
+        )
+
+      var
+        c1 = makeClosure(10)
+        e = c1.rawEnv()
+        p = c1.rawProc()
+
+      if e.isNil():
+        let c2 = cast[proc(y: int): int {.nimcall.}](p)
+        echo c2(2)
+      else:
+        let c3 = cast[proc(y: int; env: pointer): int {.nimcall.}](p)
+        echo c3(3, e)
 
-  proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} =
+    {.emit: """
+    `result` = (void*)`x`.ClP_0;
+    """.}
+
+  proc rawEnv*[T: proc {.closure.} | iterator {.closure.}](x: T): pointer {.noSideEffect, inline.} =
     ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`.
-    when T is "closure":
-      {.emit: """
-      `result` = `x`.ClE_0;
-      """.}
-    else:
-      {.error: "Only closure function and iterator are allowed!".}
-
-  proc finished*[T: proc](x: T): bool {.noSideEffect, inline, magic: "Finished".} =
-    ## It can be used to determine if a first class iterator has finished.
-    when T is "iterator":
-      {.emit: """
-      `result` = ((NI*) `x`.ClE_0)[1] < 0;
-      """.}
-    else:
-      {.error: "Only closure iterator is allowed!".}
+    ## This is not available for the JS target.
+    {.emit: """
+    `result` = `x`.ClE_0;
+    """.}
+
+proc finished*[T: iterator {.closure.}](x: T): bool {.noSideEffect, inline, magic: "Finished".} =
+  ## It can be used to determine if a first class iterator has finished.
+  when defined(js):
+    # TODO: mangle `:state`
+    {.emit: """
+    `result` = (`x`.ClE_0).HEX3Astate < 0;
+    """.}
+  else:
+    {.emit: """
+    `result` = ((NI*) `x`.ClE_0)[1] < 0;
+    """.}
 
 from std/private/digitsutils import addInt
 export addInt
 
-when defined(js):
+when defined(js) and not defined(nimscript):
+  # nimscript can be defined if config file for js compilation
   include "system/jssys"
   include "system/reprjs"
 
+
+when defined(nimNoQuit):
+  proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit"
+
+elif defined(nimdoc):
+  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.}
+    ## Stops the program immediately with an exit code.
+    ##
+    ## Before stopping the program the "exit procedures" are called in the
+    ## opposite order they were added with `addExitProc <exitprocs.html#addExitProc,proc)>`_.
+    ##
+    ## The proc `quit(QuitSuccess)` is called implicitly when your nim
+    ## program finishes without incident for platforms where this is the
+    ## expected behavior. A raised unhandled exception is
+    ## equivalent to calling `quit(QuitFailure)`.
+    ##
+    ## Note that this is a *runtime* call and using `quit` inside a macro won't
+    ## have any compile time effect. If you need to stop the compiler inside a
+    ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
+    ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
+    ##
+    ## .. warning:: `errorcode` gets saturated when it exceeds the valid range
+    ##    on the specific platform. On Posix, the valid range is `low(int8)..high(int8)`.
+    ##    On Windows, the valid range is `low(int32)..high(int32)`. For instance,
+    ##    `quit(int(0x100000000))` is equal to `quit(127)` on Linux.
+    ##
+    ## .. danger:: In almost all cases, in particular in library code, prefer
+    ##   alternatives, e.g. `raiseAssert` or raise a `Defect`.
+    ##   `quit` bypasses regular control flow in particular `defer`,
+    ##   `try`, `catch`, `finally` and `destructors`, and exceptions that may have been
+    ##   raised by an `addExitProc` proc, as well as cleanup code in other threads.
+    ##   It does *not* call the garbage collector to free all the memory,
+    ##   unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_.
+
+elif defined(genode):
+  proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    rawQuit(errorcode)
+
+elif defined(js) and defined(nodejs) and not defined(nimscript):
+  proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit",
+    importc: "process.exit", noreturn.}
+
+else:
+  proc quit*(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    when defined(posix): # posix uses low 8 bits
+      type ExitCodeRange = int8
+    else: # win32 uses low 32 bits
+      type ExitCodeRange = cint
+    when sizeof(errorcode) > sizeof(ExitCodeRange):
+      if errorcode < low(ExitCodeRange):
+        rawQuit(low(ExitCodeRange).cint)
+      elif errorcode > high(ExitCodeRange):
+        rawQuit(high(ExitCodeRange).cint)
+      else:
+        rawQuit(errorcode.cint)
+    else:
+      rawQuit(errorcode.cint)
+
 proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
   ## A shorthand for `echo(errormsg); quit(errorcode)`.
   when defined(nimscript) or defined(js) or (hostOS == "standalone"):
@@ -2521,253 +2444,11 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} =
 {.pop.} # checks: off
 # {.pop.} # hints: off
 
-proc `/`*(x, y: int): float {.inline, noSideEffect.} =
-  ## Division of integers that results in a float.
-  ##   ```
-  ##   echo 7 / 5 # => 1.4
-  ##   ```
-  ##
-  ## See also:
-  ## * `div <#div,int,int>`_
-  ## * `mod <#mod,int,int>`_
-  result = toFloat(x) / toFloat(y)
-
-type
-  BackwardsIndex* = distinct int ## Type that is constructed by `^` for
-                                 ## reversed array accesses.
-                                 ## (See `^ template <#^.t,int>`_)
-
-template `^`*(x: int): BackwardsIndex = BackwardsIndex(x)
-  ## Builtin `roof`:idx: operator that can be used for convenient array access.
-  ## `a[^x]` is a shortcut for `a[a.len-x]`.
-  ##
-  ##   ```
-  ##   let
-  ##     a = [1, 3, 5, 7, 9]
-  ##     b = "abcdefgh"
-  ##
-  ##   echo a[^1] # => 9
-  ##   echo b[^2] # => g
-  ##   ```
-
-template `..^`*(a, b: untyped): untyped =
-  ## A shortcut for `.. ^` to avoid the common gotcha that a space between
-  ## '..' and '^' is required.
-  a .. ^b
-
-template `..<`*(a, b: untyped): untyped =
-  ## A shortcut for `a .. pred(b)`.
-  ##   ```
-  ##   for i in 5 ..< 9:
-  ##     echo i # => 5; 6; 7; 8
-  ##   ```
-  a .. (when b is BackwardsIndex: succ(b) else: pred(b))
-
-template spliceImpl(s, a, L, b: untyped): untyped =
-  # make room for additional elements or cut:
-  var shift = b.len - max(0,L)  # ignore negative slice size
-  var newLen = s.len + shift
-  if shift > 0:
-    # enlarge:
-    setLen(s, newLen)
-    for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift])
-  else:
-    for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift])
-    # cut down:
-    setLen(s, newLen)
-  # fill the hole:
-  for i in 0 ..< b.len: s[a+i] = b[i]
-
-template `^^`(s, i: untyped): untyped =
-  (when i is BackwardsIndex: s.len - int(i) else: int(i))
-
-template `[]`*(s: string; i: int): char = arrGet(s, i)
-template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val)
-
-proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline.} =
-  ## Slice operation for strings.
-  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
-  ##   ```
-  ##   var s = "abcdef"
-  ##   assert s[1..3] == "bcd"
-  ##   ```
-  let a = s ^^ x.a
-  let L = (s ^^ x.b) - a + 1
-  result = newString(L)
-  for i in 0 ..< L: result[i] = s[i + a]
-
-proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) =
-  ## Slice assignment for strings.
-  ##
-  ## If `b.len` is not exactly the number of elements that are referred to
-  ## by `x`, a `splice`:idx: is performed:
-  ##
-  runnableExamples:
-    var s = "abcdefgh"
-    s[1 .. ^2] = "xyz"
-    assert s == "axyzh"
-
-  var a = s ^^ x.a
-  var L = (s ^^ x.b) - a + 1
-  if L == b.len:
-    for i in 0..<L: s[i+a] = b[i]
-  else:
-    spliceImpl(s, a, L, b)
-
-proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] =
-  ## Slice operation for arrays.
-  ## Returns the inclusive range `[a[x.a], a[x.b]]`:
-  ##   ```
-  ##   var a = [1, 2, 3, 4]
-  ##   assert a[0..2] == @[1, 2, 3]
-  ##   ```
-  let xa = a ^^ x.a
-  let L = (a ^^ x.b) - xa + 1
-  result = newSeq[T](L)
-  for i in 0..<L: result[i] = a[Idx(i + xa)]
-
-proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) =
-  ## Slice assignment for arrays.
-  ##   ```
-  ##   var a = [10, 20, 30, 40, 50]
-  ##   a[1..2] = @[99, 88]
-  ##   assert a == [10, 99, 88, 40, 50]
-  ##   ```
-  let xa = a ^^ x.a
-  let L = (a ^^ x.b) - xa + 1
-  if L == b.len:
-    for i in 0..<L: a[Idx(i + xa)] = b[i]
-  else:
-    sysFatal(RangeDefect, "different lengths for slice assignment")
-
-proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] =
-  ## Slice operation for sequences.
-  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
-  ##   ```
-  ##   var s = @[1, 2, 3, 4]
-  ##   assert s[0..2] == @[1, 2, 3]
-  ##   ```
-  let a = s ^^ x.a
-  let L = (s ^^ x.b) - a + 1
-  newSeq(result, L)
-  for i in 0 ..< L: result[i] = s[i + a]
-
-proc `[]=`*[T; U, V: Ordinal](s: var seq[T], x: HSlice[U, V], b: openArray[T]) =
-  ## Slice assignment for sequences.
-  ##
-  ## If `b.len` is not exactly the number of elements that are referred to
-  ## by `x`, a `splice`:idx: is performed.
-  runnableExamples:
-    var s = @"abcdefgh"
-    s[1 .. ^2] = @"xyz"
-    assert s == @"axyzh"
-
-  let a = s ^^ x.a
-  let L = (s ^^ x.b) - a + 1
-  if L == b.len:
-    for i in 0 ..< L: s[i+a] = b[i]
-  else:
-    spliceImpl(s, a, L, b)
-
-proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline.} =
-  system.`[]`(s, s.len - int(i))
-
-proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))]
-proc `[]`*(s: string; i: BackwardsIndex): char {.inline.} = s[s.len - int(i)]
-
-proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} =
-  system.`[]`(s, s.len - int(i))
-proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))]
-proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline.} = s[s.len - int(i)]
-
-proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} =
-  system.`[]=`(s, s.len - int(i), x)
-proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline.} =
-  a[Idx(a.len - int(i) + int low(a))] = x
-proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline.} =
-  s[s.len - int(i)] = x
-
-proc slurp*(filename: string): string {.magic: "Slurp".}
-  ## This is an alias for `staticRead <#staticRead,string>`_.
-
-proc staticRead*(filename: string): string {.magic: "Slurp".}
-  ## Compile-time `readFile <syncio.html#readFile,string>`_ proc for easy
-  ## `resource`:idx: embedding:
-  ##
-  ## The maximum file size limit that `staticRead` and `slurp` can read is
-  ## near or equal to the *free* memory of the device you are using to compile.
-  ##   ```
-  ##   const myResource = staticRead"mydatafile.bin"
-  ##   ```
-  ##
-  ## `slurp <#slurp,string>`_ is an alias for `staticRead`.
-
-proc gorge*(command: string, input = "", cache = ""): string {.
-  magic: "StaticExec".} = discard
-  ## This is an alias for `staticExec <#staticExec,string,string,string>`_.
-
-proc staticExec*(command: string, input = "", cache = ""): string {.
-  magic: "StaticExec".} = discard
-  ## Executes an external process at compile-time and returns its text output
-  ## (stdout + stderr).
-  ##
-  ## If `input` is not an empty string, it will be passed as a standard input
-  ## to the executed program.
-  ##   ```
-  ##   const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
-  ##                     "\nCompiled on " & staticExec("uname -v")
-  ##   ```
-  ##
-  ## `gorge <#gorge,string,string,string>`_ is an alias for `staticExec`.
-  ##
-  ## Note that you can use this proc inside a pragma like
-  ## `passc <manual.html#implementation-specific-pragmas-passc-pragma>`_ or
-  ## `passl <manual.html#implementation-specific-pragmas-passl-pragma>`_.
-  ##
-  ## If `cache` is not empty, the results of `staticExec` are cached within
-  ## the `nimcache` directory. Use `--forceBuild` to get rid of this caching
-  ## behaviour then. `command & input & cache` (the concatenated string) is
-  ## used to determine whether the entry in the cache is still valid. You can
-  ## use versioning information for `cache`:
-  ##   ```
-  ##   const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")
-  ##   ```
-
-proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
-                                                              exitCode: int] =
-  ## Similar to `gorge <#gorge,string,string,string>`_ but also returns the
-  ## precious exit code.
-  discard
-
-
-proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Increments in place a floating point number.
-  x = x + y
-
-proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Decrements in place a floating point number.
-  x = x - y
-
-proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Multiplies in place a floating point number.
-  x = x * y
-
-proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} =
-  ## Divides in place a floating point number.
-  x = x / y
-
-proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} =
-  ## Divides in place a floating point number.
-  x = x / y
+include "system/indices"
 
 proc `&=`*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
   ## Appends in place to a string.
-  ##   ```
+  ##   ```nim
   ##   var a = "abc"
   ##   a &= "de" # a <- "abcde"
   ##   ```
@@ -2775,26 +2456,8 @@ proc `&=`*(x: var string, y: string) {.magic: "AppendStrStr", noSideEffect.}
 template `&=`*(x, y: typed) =
   ## Generic 'sink' operator for Nim.
   ##
-  ## For files an alias for `write`.
   ## If not specialized further, an alias for `add`.
   add(x, y)
-when declared(File):
-  template `&=`*(f: File, x: typed) = write(f, x)
-
-template currentSourcePath*: string = instantiationInfo(-1, true).filename
-  ## Returns the full file-system path of the current source.
-  ##
-  ## To get the directory containing the current source, use it with
-  ## `os.parentDir() <os.html#parentDir%2Cstring>`_ as `currentSourcePath.parentDir()`.
-  ##
-  ## The path returned by this template is set at compile time.
-  ##
-  ## See the docstring of `macros.getProjectPath() <macros.html#getProjectPath>`_
-  ## for an example to see the distinction between the `currentSourcePath`
-  ## and `getProjectPath`.
-  ##
-  ## See also:
-  ## * `getCurrentDir proc <os.html#getCurrentDir>`_
 
 when compileOption("rangechecks"):
   template rangeCheck*(cond) =
@@ -2805,28 +2468,31 @@ when compileOption("rangechecks"):
 else:
   template rangeCheck*(cond) = discard
 
-proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
-  ## Marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
-  ## perform deep copies of `s`.
-  ##
-  ## This is only useful for optimization purposes.
-  if s.len == 0: return
-  when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
-    var s = cast[PGenericSeq](s)
-    s.reserved = s.reserved or seqShallowFlag
-
-proc shallow*(s: var string) {.noSideEffect, inline.} =
-  ## Marks a string `s` as `shallow`:idx:. Subsequent assignments will not
-  ## perform deep copies of `s`.
-  ##
-  ## This is only useful for optimization purposes.
-  when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
-    var s = cast[PGenericSeq](s)
-    if s == nil:
-      s = cast[PGenericSeq](newString(0))
-    # string literals cannot become 'shallow':
-    if (s.reserved and strlitFlag) == 0:
-      s.reserved = s.reserved or seqShallowFlag
+when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
+  proc shallow*[T](s: var seq[T]) {.noSideEffect, inline.} =
+    ## Marks a sequence `s` as `shallow`:idx:. Subsequent assignments will not
+    ## perform deep copies of `s`.
+    ##
+    ## This is only useful for optimization purposes.
+    if s.len == 0: return
+    when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
+      var s = cast[PGenericSeq](s)
+      {.noSideEffect.}:
+        s.reserved = s.reserved or seqShallowFlag
+
+  proc shallow*(s: var string) {.noSideEffect, inline.} =
+    ## Marks a string `s` as `shallow`:idx:. Subsequent assignments will not
+    ## perform deep copies of `s`.
+    ##
+    ## This is only useful for optimization purposes.
+    when not defined(js) and not defined(nimscript) and not defined(nimSeqsV2):
+      var s = cast[PGenericSeq](s)
+      if s == nil:
+        s = cast[PGenericSeq](newString(0))
+      # string literals cannot become 'shallow':
+      if (s.reserved and strlitFlag) == 0:
+        {.noSideEffect.}:
+          s.reserved = s.reserved or seqShallowFlag
 
 type
   NimNodeObj = object
@@ -2834,36 +2500,44 @@ type
   NimNode* {.magic: "PNimrodNode".} = ref NimNodeObj
     ## Represents a Nim AST node. Macros operate on this type.
 
-when defined(nimV2):
-  import system/repr_v2
-  export repr_v2
+type
+  ForLoopStmt* {.compilerproc.} = object ## \
+    ## A special type that marks a macro as a `for-loop macro`:idx:.
+    ## See `"For Loop Macro" <manual.html#macros-for-loop-macro>`_.
 
 macro varargsLen*(x: varargs[untyped]): int {.since: (1, 1).} =
   ## returns number of variadic arguments in `x`
   proc varargsLenImpl(x: NimNode): NimNode {.magic: "LengthOpenArray", noSideEffect.}
   varargsLenImpl(x)
 
-when false:
-  template eval*(blk: typed): typed =
-    ## Executes a block of code at compile time just as if it was a macro.
-    ##
-    ## Optionally, the block can return an AST tree that will replace the
-    ## eval expression.
-    macro payload: typed {.gensym.} = blk
-    payload()
+when defined(nimV2):
+  import system/repr_v2
+  export repr_v2
+
+proc repr*[T, U](x: HSlice[T, U]): string =
+  ## Generic `repr` operator for slices that is lifted from the components
+  ## of `x`. Example:
+  ##   ```Nim
+  ##   $(1 .. 5) == "1 .. 5"
+  ##   ```
+  result = repr(x.a)
+  result.add(" .. ")
+  result.add(repr(x.b))
 
 when hasAlloc or defined(nimscript):
   proc insert*(x: var string, item: string, i = 0.Natural) {.noSideEffect.} =
     ## Inserts `item` into `x` at position `i`.
-    ##   ```
+    ##   ```nim
     ##   var a = "abc"
     ##   a.insert("zz", 0) # a <- "zzabc"
     ##   ```
+    if item.len == 0: # prevents self-assignment
+      return
     var xl = x.len
     setLen(x, xl+item.len)
     var j = xl-1
     while j >= i:
-      when defined(gcArc) or defined(gcOrc):
+      when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc):
         x[j+item.len] = move x[j]
       else:
         shallowCopy(x[j+item.len], x[j])
@@ -2934,7 +2608,7 @@ proc addQuoted*[T](s: var string, x: T) =
   ## Users may overload `addQuoted` for custom (string-like) types if
   ## they want to implement a customized element representation.
   ##
-  ##   ```
+  ##   ```nim
   ##   var tmp = ""
   ##   tmp.addQuoted(1)
   ##   tmp.add(", ")
@@ -2976,7 +2650,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} =
   ## the official signature says, the return type is *not* `RootObj` but a
   ## tuple of a structure that depends on the current scope. Example:
   ##
-  ##   ```
+  ##   ```nim
   ##   proc testLocals() =
   ##     var
   ##       a = "something"
@@ -3002,7 +2676,7 @@ when hasAlloc and notJSnotNims:
     ## This is also used by the code generator
     ## for the implementation of `spawn`.
     ##
-    ## For `--gc:arc` or `--gc:orc` deepcopy support has to be enabled
+    ## For `--mm:arc` or `--mm:orc` deepcopy support has to be enabled
     ## via `--deepcopy:on`.
     discard
 
@@ -3015,7 +2689,7 @@ when hasAlloc and notJSnotNims:
 proc procCall*(x: untyped) {.magic: "ProcCall", compileTime.} =
   ## Special magic to prohibit dynamic binding for `method`:idx: calls.
   ## This is similar to `super`:idx: in ordinary OO languages.
-  ##   ```
+  ##   ```nim
   ##   # 'someMethod' will be resolved fully statically:
   ##   procCall someMethod(a, b)
   ##   ```
@@ -3028,7 +2702,7 @@ proc `==`*(x, y: cstring): bool {.magic: "EqCString", noSideEffect,
   proc strcmp(a, b: cstring): cint {.noSideEffect,
     importc, header: "<string.h>".}
   if pointer(x) == pointer(y): result = true
-  elif x.isNil or y.isNil: result = false
+  elif pointer(x) == nil or pointer(y) == nil: result = false
   else: result = strcmp(x, y) == 0
 
 template closureScope*(body: untyped): untyped =
@@ -3040,7 +2714,7 @@ template closureScope*(body: untyped): untyped =
   ##
   ## Example:
   ##
-  ##   ```
+  ##   ```nim
   ##   var myClosure : proc()
   ##   # without closureScope:
   ##   for i in 0 .. 5:
@@ -3060,7 +2734,7 @@ template closureScope*(body: untyped): untyped =
 
 template once*(body: untyped): untyped =
   ## Executes a block of code only once (the first time the block is reached).
-  ##   ```
+  ##   ```nim
   ##   proc draw(t: Triangle) =
   ##     once:
   ##       graphicsInit()
@@ -3075,7 +2749,19 @@ template once*(body: untyped): untyped =
 
 {.pop.} # warning[GcMem]: off, warning[Uninit]: off
 
-proc substr*(s: string, first, last: int): string =
+proc substr*(s: openArray[char]): string =
+  ## Copies a slice of `s` into a new string and returns this new
+  ## string.
+  runnableExamples:
+    let a = "abcdefgh"
+    assert a.substr(2, 5) == "cdef"
+    assert a.substr(2) == "cdefgh"
+    assert a.substr(5, 99) == "fgh"
+  result = newString(s.len)
+  for i, ch in s:
+    result[i] = ch
+
+proc substr*(s: string, first, last: int): string = # A bug with `magic: Slice` requires this to exist this way
   ## Copies a slice of `s` into a new string and returns this new
   ## string.
   ##
@@ -3105,14 +2791,25 @@ when defined(nimconfig):
 when not defined(js):
   proc toOpenArray*[T](x: ptr UncheckedArray[T]; first, last: int): openArray[T] {.
     magic: "Slice".}
-  when defined(nimToOpenArrayCString):
-    proc toOpenArray*(x: cstring; first, last: int): openArray[char] {.
-      magic: "Slice".}
-    proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {.
-      magic: "Slice".}
+  proc toOpenArray*(x: cstring; first, last: int): openArray[char] {.
+    magic: "Slice".}
+  proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {.
+    magic: "Slice".}
 
 proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {.
   magic: "Slice".}
+  ## Allows passing the slice of `x` from the element at `first` to the element
+  ## at `last` to `openArray[T]` parameters without copying it.
+  ##
+  ## Example:
+  ##   ```nim
+  ##   proc test(x: openArray[int]) =
+  ##     doAssert x == [1, 2, 3]
+  ##
+  ##   let s = @[0, 1, 2, 3, 4]
+  ##   s.toOpenArray(1, 3).test
+  ##   ```
+
 proc toOpenArray*[T](x: openArray[T]; first, last: int): openArray[T] {.
   magic: "Slice".}
 proc toOpenArray*[I, T](x: array[I, T]; first, last: I): openArray[T] {.
@@ -3127,10 +2824,8 @@ proc toOpenArrayByte*(x: openArray[char]; first, last: int): openArray[byte] {.
 proc toOpenArrayByte*(x: seq[char]; first, last: int): openArray[byte] {.
   magic: "Slice".}
 
-type
-  ForLoopStmt* {.compilerproc.} = object ## \
-    ## A special type that marks a macro as a `for-loop macro`:idx:.
-    ## See `"For Loop Macro" <manual.html#macros-for-loop-macro>`_.
+proc toOpenArrayChar*(x: openArray[byte]; first, last: int): openArray[char] {.
+  magic: "Slice".}
 
 when defined(genode):
   var componentConstructHook*: proc (env: GenodeEnv) {.nimcall.}
@@ -3144,7 +2839,7 @@ when defined(genode):
   proc nim_component_construct(env: GenodeEnv) {.exportc.} =
     ## Procedure called during `Component::construct` by the loader.
     if componentConstructHook.isNil:
-      env.quit(programResult)
+      env.rawQuit(programResult)
         # No native Genode application initialization,
         # exit as would POSIX.
     else:
@@ -3153,15 +2848,86 @@ when defined(genode):
         # and return to thread entrypoint.
 
 
-import system/widestrs
-export widestrs
+when not defined(nimPreviewSlimSystem):
+  import std/widestrs
+  export widestrs
+
+when notJSnotNims:
+  when defined(windows) and compileOption("threads"):
+    when not declared(addSysExitProc):
+      proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
+    var echoLock: SysLock
+    initSysLock echoLock
+    addSysExitProc(proc() {.noconv.} = deinitSys(echoLock))
+
+  const stdOutLock = compileOption("threads") and
+                    not defined(windows) and
+                    not defined(android) and
+                    not defined(nintendoswitch) and
+                    not defined(freertos) and
+                    not defined(zephyr) and
+                    not defined(nuttx) and
+                    hostOS != "any"
+
+  proc raiseEIO(msg: string) {.noinline, noreturn.} =
+    raise newException(IOError, msg)
+
+  proc echoBinSafe(args: openArray[string]) {.compilerproc.} =
+    when defined(androidNDK):
+      # When running nim in android app, stdout goes nowhere, so echo gets ignored
+      # To redirect echo to the android logcat, use -d:androidNDK
+      const ANDROID_LOG_VERBOSE = 2.cint
+      proc android_log_print(prio: cint, tag: cstring, fmt: cstring): cint
+        {.importc: "__android_log_print", header: "<android/log.h>", varargs, discardable.}
+      var s = ""
+      for arg in args:
+        s.add arg
+      android_log_print(ANDROID_LOG_VERBOSE, "nim", s)
+    else:
+      # flockfile deadlocks some versions of Android 5.x.x
+      when stdOutLock:
+        proc flockfile(f: CFilePtr) {.importc, nodecl.}
+        proc funlockfile(f: CFilePtr) {.importc, nodecl.}
+        flockfile(cstdout)
+      when defined(windows) and compileOption("threads"):
+        acquireSys echoLock
+      for s in args:
+        when defined(windows):
+          # equivalent to syncio.writeWindows
+          proc writeWindows(f: CFilePtr; s: string; doRaise = false) =
+            # Don't ask why but the 'printf' family of function is the only thing
+            # that writes utf-8 strings reliably on Windows. At least on my Win 10
+            # machine. We also enable `setConsoleOutputCP(65001)` now by default.
+            # But we cannot call printf directly as the string might contain \0.
+            # So we have to loop over all the sections separated by potential \0s.
+            var i = int c_fprintf(f, "%s", s)
+            while i < s.len:
+              if s[i] == '\0':
+                let w = c_fputc('\0', f)
+                if w != 0:
+                  if doRaise: raiseEIO("cannot write string to file")
+                  break
+                inc i
+              else:
+                let w = c_fprintf(f, "%s", unsafeAddr s[i])
+                if w <= 0:
+                  if doRaise: raiseEIO("cannot write string to file")
+                  break
+                inc i, w
+          writeWindows(cstdout, s)
+        else:
+          discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, cstdout)
+      const linefeed = "\n"
+      discard c_fwrite(linefeed.cstring, linefeed.len, 1, cstdout)
+      discard c_fflush(cstdout)
+      when stdOutLock:
+        funlockfile(cstdout)
+      when defined(windows) and compileOption("threads"):
+        releaseSys echoLock
 
 when not defined(nimPreviewSlimSystem):
-  {.deprecated: "io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`".}
   import std/syncio
   export syncio
-else:
-  import std/syncio
 
 when not defined(createNimHcr) and not defined(nimscript):
   include nimhcr
@@ -3171,10 +2937,19 @@ when notJSnotNims and not defined(nimSeqsV2):
     ## String literals (e.g. "abc", etc) in the ARC/ORC mode are "copy on write",
     ## therefore you should call `prepareMutation` before modifying the strings
     ## via `addr`.
-    runnableExamples("--gc:arc"):
+    runnableExamples:
       var x = "abc"
       var y = "defgh"
       prepareMutation(y) # without this, you may get a `SIGBUS` or `SIGSEGV`
       moveMem(addr y[0], addr x[0], x.len)
       assert y == "abcgh"
     discard
+
+proc arrayWith*[T](y: T, size: static int): array[size, T] {.raises: [].} =
+  ## Creates a new array filled with `y`.
+  for i in 0..size-1:
+    when nimvm:
+      result[i] = y
+    else:
+      # TODO: fixme it should be `=dup`
+      result[i] = y
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 2c6ab4462..3de6d8713 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -11,6 +11,8 @@
 {.push profiler:off.}
 
 include osalloc
+import std/private/syslocks
+import std/sysatomics
 
 template track(op, address, size) =
   when defined(memTracker):
@@ -18,11 +20,42 @@ template track(op, address, size) =
 
 # We manage *chunks* of memory. Each chunk is a multiple of the page size.
 # Each chunk starts at an address that is divisible by the page size.
+# Small chunks may be divided into smaller cells of reusable pointers to reduce the number of page allocations.
+
+# An allocation of a small pointer looks approximately like this
+#[
+
+  alloc -> rawAlloc -> No free chunk available > Request a new page from tslf -> result = chunk.data -------------+
+              |                                                                                                   |
+              v                                                                                                   |
+    Free chunk available                                                                                          |
+              |                                                                                                   |
+              v                                                                                                   v
+      Fetch shared cells -> No free cells available -> Advance acc -> result = chunk.data + chunk.acc -------> return
+    (may not add new cells)                                                                                       ^
+              |                                                                                                   |
+              v                                                                                                   |
+     Free cells available -> result = chunk.freeList -> Advance chunk.freeList -----------------------------------+
+]#
+# so it is split into 3 paths, where the last path is preferred to prevent unnecessary allocations.
+#
+#
+# A deallocation of a small pointer then looks like this
+#[
+  dealloc -> rawDealloc -> chunk.owner == addr(a) --------------> This thread owns the chunk ------> The current chunk is active    -> Chunk is completely unused -----> Chunk references no foreign cells
+                                      |                                       |                   (Add cell into the current chunk)                 |                  Return the current chunk back to tlsf
+                                      |                                       |                                   |                                 |
+                                      v                                       v                                   v                                 v
+                      A different thread owns this chunk.     The current chunk is not active.          chunk.free was < size      Chunk references foreign cells, noop
+                      Add the cell to a.sharedFreeLists      Add the cell into the active chunk          Activate the chunk                       (end)
+                                    (end)                                    (end)                              (end)
+]#
+# So "true" deallocation is delayed for as long as possible in favor of reusing cells.
 
 const
   nimMinHeapPages {.intdefine.} = 128 # 0.5 MB
   SmallChunkSize = PageSize
-  MaxFli = 30
+  MaxFli = when sizeof(int) > 2: 30 else: 14
   MaxLog2Sli = 5 # 32, this cannot be increased without changing 'uint32'
                  # everywhere!
   MaxSli = 1 shl MaxLog2Sli
@@ -30,7 +63,7 @@ const
   RealFli = MaxFli - FliOffset
 
   # size of chunks in last matrix bin
-  MaxBigChunkSize = 1 shl MaxFli - 1 shl (MaxFli-MaxLog2Sli-1)
+  MaxBigChunkSize = int(1'i32 shl MaxFli - 1'i32 shl (MaxFli-MaxLog2Sli-1))
   HugeChunkSize = MaxBigChunkSize + 1
 
 type
@@ -44,8 +77,33 @@ type
   IntSet = object
     data: TrunkBuckets
 
+# ------------- chunk table ---------------------------------------------------
+# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
+# endings of big chunks. This is needed by the merging operation. The only
+# remaining operation is best-fit for big chunks. Since there is a size-limit
+# for big chunks (because greater than the limit means they are returned back
+# to the OS), a fixed size array can be used.
+
+type
+  PLLChunk = ptr LLChunk
+  LLChunk = object ## *low-level* chunk
+    size: int                # remaining size
+    acc: int                 # accumulator
+    next: PLLChunk           # next low-level chunk; only needed for dealloc
+
+  PAvlNode = ptr AvlNode
+  AvlNode = object
+    link: array[0..1, PAvlNode] # Left (0) and right (1) links
+    key, upperBound: int
+    level: int
+
+const
+  RegionHasLock = false # hasThreadSupport and defined(gcDestructors)
+
 type
   FreeCell {.final, pure.} = object
+    # A free cell is a pointer that has been freed, meaning it became available for reuse.
+    # It may become foreign if it is lent to a chunk that did not create it, doing so reduces the amount of needed pages.
     next: ptr FreeCell  # next free cell in chunk (overlaid with refcount)
     when not defined(gcDestructors):
       zeroField: int       # 0 means cell is not used (overlaid with typ field)
@@ -61,46 +119,27 @@ type
     prevSize: int        # size of previous chunk; for coalescing
                          # 0th bit == 1 if 'used
     size: int            # if < PageSize it is a small chunk
+    owner: ptr MemRegion
 
   SmallChunk = object of BaseChunk
     next, prev: PSmallChunk  # chunks of the same size
-    freeList: ptr FreeCell
-    free: int            # how many bytes remain
-    acc: int             # accumulator for small object allocation
-    when defined(nimAlignPragma):
-      data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
-    else:
-      data: UncheckedArray[byte]
+    freeList: ptr FreeCell   # Singly linked list of cells. They may be from foreign chunks or from the current chunk.
+                             #  Should be `nil` when the chunk isn't active in `a.freeSmallChunks`.
+    free: int32              # Bytes this chunk is able to provide using both the accumulator and free cells.
+                             # When a cell is considered foreign, its source chunk's free field is NOT adjusted until it
+                             #  reaches dealloc while the source chunk is active.
+                             # Instead, the receiving chunk gains the capacity and thus reserves space in the foreign chunk.
+    acc: uint32              # Offset from data, used when there are no free cells available but the chunk is considered free.
+    foreignCells: int        # When a free cell is given to a chunk that is not its origin,
+                             #  both the cell and the source chunk are considered foreign.
+                             # Receiving a foreign cell can happen both when deallocating from another thread or when
+                             #  the active chunk in `a.freeSmallChunks` is not the current chunk.
+                             # Freeing a chunk while `foreignCells > 0` leaks memory as all references to it become lost.
+    data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
 
   BigChunk = object of BaseChunk # not necessarily > PageSize!
     next, prev: PBigChunk    # chunks of the same (or bigger) size
-    when defined(nimAlignPragma):
-      data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
-    else:
-      data: UncheckedArray[byte]
-
-template smallChunkOverhead(): untyped = sizeof(SmallChunk)
-template bigChunkOverhead(): untyped = sizeof(BigChunk)
-
-# ------------- chunk table ---------------------------------------------------
-# We use a PtrSet of chunk starts and a table[Page, chunksize] for chunk
-# endings of big chunks. This is needed by the merging operation. The only
-# remaining operation is best-fit for big chunks. Since there is a size-limit
-# for big chunks (because greater than the limit means they are returned back
-# to the OS), a fixed size array can be used.
-
-type
-  PLLChunk = ptr LLChunk
-  LLChunk = object ## *low-level* chunk
-    size: int                # remaining size
-    acc: int                 # accumulator
-    next: PLLChunk           # next low-level chunk; only needed for dealloc
-
-  PAvlNode = ptr AvlNode
-  AvlNode = object
-    link: array[0..1, PAvlNode] # Left (0) and right (1) links
-    key, upperBound: int
-    level: int
+    data {.align: MemAlign.}: UncheckedArray[byte]      # start of usable memory
 
   HeapLinks = object
     len: int
@@ -108,23 +147,54 @@ type
     next: ptr HeapLinks
 
   MemRegion = object
-    minLargeObj, maxLargeObj: int
-    freeSmallChunks: array[0..SmallChunkSize div MemAlign-1, PSmallChunk]
+    when not defined(gcDestructors):
+      minLargeObj, maxLargeObj: int
+    freeSmallChunks: array[0..max(1, SmallChunkSize div MemAlign-1), PSmallChunk]
+      # List of available chunks per size class. Only one is expected to be active per class.
+    when defined(gcDestructors):
+      sharedFreeLists: array[0..max(1, SmallChunkSize div MemAlign-1), ptr FreeCell]
+        # When a thread frees a pointer it did not create, it must not adjust the counters.
+        # Instead, the cell is placed here and deferred until the next allocation.
     flBitmap: uint32
     slBitmap: array[RealFli, uint32]
     matrix: array[RealFli, array[MaxSli, PBigChunk]]
     llmem: PLLChunk
     currMem, maxMem, freeMem, occ: int # memory sizes (allocated from OS)
     lastSize: int # needed for the case that OS gives us pages linearly
+    when RegionHasLock:
+      lock: SysLock
+    when defined(gcDestructors):
+      sharedFreeListBigChunks: PBigChunk # make no attempt at avoiding false sharing for now for this object field
+
     chunkStarts: IntSet
-    root, deleted, last, freeAvlNodes: PAvlNode
-    locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
+    when not defined(gcDestructors):
+      root, deleted, last, freeAvlNodes: PAvlNode
+    lockActive, locked, blockChunkSizeIncrease: bool # if locked, we cannot free pages.
     nextChunkSize: int
-    bottomData: AvlNode
+    when not defined(gcDestructors):
+      bottomData: AvlNode
     heapLinks: HeapLinks
     when defined(nimTypeNames):
       allocCounter, deallocCounter: int
 
+template smallChunkOverhead(): untyped = sizeof(SmallChunk)
+template bigChunkOverhead(): untyped = sizeof(BigChunk)
+
+when hasThreadSupport:
+  template loada(x: untyped): untyped = atomicLoadN(unsafeAddr x, ATOMIC_RELAXED)
+  template storea(x, y: untyped) = atomicStoreN(unsafeAddr x, y, ATOMIC_RELAXED)
+
+  when false:
+    # not yet required
+    template atomicStatDec(x, diff: untyped) = discard atomicSubFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
+    template atomicStatInc(x, diff: untyped) = discard atomicAddFetch(unsafeAddr x, diff, ATOMIC_RELAXED)
+else:
+  template loada(x: untyped): untyped = x
+  template storea(x, y: untyped) = x = y
+
+template atomicStatDec(x, diff: untyped) = dec x, diff
+template atomicStatInc(x, diff: untyped) = inc x, diff
+
 const
   fsLookupTable: array[byte, int8] = [
     -1'i8, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
@@ -165,7 +235,7 @@ proc mappingSearch(r, fl, sl: var int) {.inline.} =
   let t = roundup((1 shl (msbit(uint32 r) - MaxLog2Sli)), PageSize) - 1
   r = r + t
   r = r and not t
-  r = min(r, MaxBigChunkSize)
+  r = min(r, MaxBigChunkSize).int
   fl = msbit(uint32 r)
   sl = (r shr (fl - MaxLog2Sli)) - MaxSli
   dec fl, FliOffset
@@ -231,11 +301,11 @@ proc addChunkToMatrix(a: var MemRegion; b: PBigChunk) =
   setBit(fl, a.flBitmap)
 
 proc incCurrMem(a: var MemRegion, bytes: int) {.inline.} =
-  inc(a.currMem, bytes)
+  atomicStatInc(a.currMem, bytes)
 
 proc decCurrMem(a: var MemRegion, bytes: int) {.inline.} =
   a.maxMem = max(a.maxMem, a.currMem)
-  dec(a.currMem, bytes)
+  atomicStatDec(a.currMem, bytes)
 
 proc getMaxMem(a: var MemRegion): int =
   # Since we update maxPagesCount only when freeing pages,
@@ -243,6 +313,20 @@ proc getMaxMem(a: var MemRegion): int =
   # maximum of these both values here:
   result = max(a.currMem, a.maxMem)
 
+const nimMaxHeap {.intdefine.} = 0
+
+proc allocPages(a: var MemRegion, size: int): pointer =
+  when nimMaxHeap != 0:
+    if a.occ + size > nimMaxHeap * 1024 * 1024:
+      raiseOutOfMem()
+  osAllocPages(size)
+
+proc tryAllocPages(a: var MemRegion, size: int): pointer =
+  when nimMaxHeap != 0:
+    if a.occ + size > nimMaxHeap * 1024 * 1024:
+      raiseOutOfMem()
+  osTryAllocPages(size)
+
 proc llAlloc(a: var MemRegion, size: int): pointer =
   # *low-level* alloc for the memory managers data structures. Deallocation
   # is done at the end of the allocator's life time.
@@ -252,49 +336,50 @@ proc llAlloc(a: var MemRegion, size: int): pointer =
     # is one page:
     sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6"
     var old = a.llmem # can be nil and is correct with nil
-    a.llmem = cast[PLLChunk](osAllocPages(PageSize))
+    a.llmem = cast[PLLChunk](allocPages(a, PageSize))
     when defined(nimAvlcorruption):
       trackLocation(a.llmem, PageSize)
     incCurrMem(a, PageSize)
     a.llmem.size = PageSize - sizeof(LLChunk)
     a.llmem.acc = sizeof(LLChunk)
     a.llmem.next = old
-  result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc)
+  result = cast[pointer](cast[int](a.llmem) + a.llmem.acc)
   dec(a.llmem.size, size)
   inc(a.llmem.acc, size)
   zeroMem(result, size)
 
-proc getBottom(a: var MemRegion): PAvlNode =
-  result = addr(a.bottomData)
-  if result.link[0] == nil:
-    result.link[0] = result
-    result.link[1] = result
-
-proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
-  if a.freeAvlNodes != nil:
-    result = a.freeAvlNodes
-    a.freeAvlNodes = a.freeAvlNodes.link[0]
-  else:
-    result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
-    when defined(nimAvlcorruption):
-      cprintf("tracking location: %p\n", result)
-  result.key = key
-  result.upperBound = upperBound
-  let bottom = getBottom(a)
-  result.link[0] = bottom
-  result.link[1] = bottom
-  result.level = 1
-  #when defined(nimAvlcorruption):
-  #  track("allocAvlNode", result, sizeof(AvlNode))
-  sysAssert(bottom == addr(a.bottomData), "bottom data")
-  sysAssert(bottom.link[0] == bottom, "bottom link[0]")
-  sysAssert(bottom.link[1] == bottom, "bottom link[1]")
-
-proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
-  n.link[0] = a.freeAvlNodes
-  a.freeAvlNodes = n
-
-proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) =
+when not defined(gcDestructors):
+  proc getBottom(a: var MemRegion): PAvlNode =
+    result = addr(a.bottomData)
+    if result.link[0] == nil:
+      result.link[0] = result
+      result.link[1] = result
+
+  proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode =
+    if a.freeAvlNodes != nil:
+      result = a.freeAvlNodes
+      a.freeAvlNodes = a.freeAvlNodes.link[0]
+    else:
+      result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode)))
+      when defined(nimAvlcorruption):
+        cprintf("tracking location: %p\n", result)
+    result.key = key
+    result.upperBound = upperBound
+    let bottom = getBottom(a)
+    result.link[0] = bottom
+    result.link[1] = bottom
+    result.level = 1
+    #when defined(nimAvlcorruption):
+    #  track("allocAvlNode", result, sizeof(AvlNode))
+    sysAssert(bottom == addr(a.bottomData), "bottom data")
+    sysAssert(bottom.link[0] == bottom, "bottom link[0]")
+    sysAssert(bottom.link[1] == bottom, "bottom link[1]")
+
+  proc deallocAvlNode(a: var MemRegion, n: PAvlNode) {.inline.} =
+    n.link[0] = a.freeAvlNodes
+    a.freeAvlNodes = n
+
+proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int): ptr HeapLinks =
   var it = addr(a.heapLinks)
   while it != nil and it.len >= it.chunks.len: it = it.next
   if it == nil:
@@ -303,12 +388,15 @@ proc addHeapLink(a: var MemRegion; p: PBigChunk, size: int) =
     a.heapLinks.next = n
     n.chunks[0] = (p, size)
     n.len = 1
+    result = n
   else:
     let L = it.len
     it.chunks[L] = (p, size)
     inc it.len
+    result = it
 
-include "system/avltree"
+when not defined(gcDestructors):
+  include "system/avltree"
 
 proc llDeallocAll(a: var MemRegion) =
   var it = a.llmem
@@ -389,8 +477,8 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} =
           var c = cast[PSmallChunk](c)
 
           let size = c.size
-          var a = cast[ByteAddress](addr(c.data))
-          let limit = a + c.acc
+          var a = cast[int](addr(c.data))
+          let limit = a + c.acc.int
           while a <% limit:
             yield cast[pointer](a)
             a = a +% size
@@ -408,13 +496,13 @@ when not defined(gcDestructors):
 
 # ------------- chunk management ----------------------------------------------
 proc pageIndex(c: PChunk): int {.inline.} =
-  result = cast[ByteAddress](c) shr PageShift
+  result = cast[int](c) shr PageShift
 
 proc pageIndex(p: pointer): int {.inline.} =
-  result = cast[ByteAddress](p) shr PageShift
+  result = cast[int](p) shr PageShift
 
 proc pageAddr(p: pointer): PChunk {.inline.} =
-  result = cast[PChunk](cast[ByteAddress](p) and not PageMask)
+  result = cast[PChunk](cast[int](p) and not PageMask)
   #sysAssert(Contains(allocator.chunkStarts, pageIndex(result)))
 
 when false:
@@ -426,49 +514,44 @@ when false:
                 it, it.next, it.prev, it.size)
       it = it.next
 
-const nimMaxHeap {.intdefine.} = 0
-
 proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
   when not defined(emscripten):
     if not a.blockChunkSizeIncrease:
       let usedMem = a.occ #a.currMem # - a.freeMem
-      when nimMaxHeap != 0:
-        if usedMem > nimMaxHeap * 1024 * 1024:
-          raiseOutOfMem()
       if usedMem < 64 * 1024:
         a.nextChunkSize = PageSize*4
       else:
         a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
-        a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize)
+        a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize).int
 
   var size = size
   if size > a.nextChunkSize:
-    result = cast[PBigChunk](osAllocPages(size))
+    result = cast[PBigChunk](allocPages(a, size))
   else:
-    result = cast[PBigChunk](osTryAllocPages(a.nextChunkSize))
+    result = cast[PBigChunk](tryAllocPages(a, a.nextChunkSize))
     if result == nil:
-      result = cast[PBigChunk](osAllocPages(size))
+      result = cast[PBigChunk](allocPages(a, size))
       a.blockChunkSizeIncrease = true
     else:
       size = a.nextChunkSize
 
   incCurrMem(a, size)
   inc(a.freeMem, size)
-  a.addHeapLink(result, size)
+  let heapLink = a.addHeapLink(result, size)
   when defined(debugHeapLinks):
     cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
-      result, result.heapLink, result.size)
+      result, heapLink, size)
 
   when defined(memtracker):
     trackLocation(addr result.size, sizeof(int))
 
-  sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1")
+  sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1")
   #zeroMem(result, size)
   result.next = nil
   result.prev = nil
   result.size = size
   # update next.prevSize:
-  var nxt = cast[ByteAddress](result) +% size
+  var nxt = cast[int](result) +% size
   sysAssert((nxt and PageMask) == 0, "requestOsChunks 2")
   var next = cast[PChunk](nxt)
   if pageIndex(next) in a.chunkStarts:
@@ -476,7 +559,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
     next.prevSize = size or (next.prevSize and 1)
   # set result.prevSize:
   var lastSize = if a.lastSize != 0: a.lastSize else: PageSize
-  var prv = cast[ByteAddress](result) -% lastSize
+  var prv = cast[int](result) -% lastSize
   sysAssert((nxt and PageMask) == 0, "requestOsChunks 3")
   var prev = cast[PChunk](prv)
   if pageIndex(prev) in a.chunkStarts and prev.size == lastSize:
@@ -522,21 +605,22 @@ proc listRemove[T](head: var T, c: T) {.inline.} =
 
 proc updatePrevSize(a: var MemRegion, c: PBigChunk,
                     prevSize: int) {.inline.} =
-  var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
-  sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize")
+  var ri = cast[PChunk](cast[int](c) +% c.size)
+  sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize")
   if isAccessible(a, ri):
     ri.prevSize = prevSize or (ri.prevSize and 1)
 
 proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
-  result = cast[PBigChunk](cast[ByteAddress](c) +% size)
+  result = cast[PBigChunk](cast[int](c) +% size)
   result.size = c.size - size
   track("result.size", addr result.size, sizeof(int))
-  # XXX check if these two nil assignments are dead code given
-  # addChunkToMatrix's implementation:
-  result.next = nil
-  result.prev = nil
+  when not defined(nimOptimizedSplitChunk):
+    # still active because of weird codegen issue on some of our CIs:
+    result.next = nil
+    result.prev = nil
   # size and not used:
   result.prevSize = size
+  result.owner = addr a
   sysAssert((size and 1) == 0, "splitChunk 2")
   sysAssert((size and PageMask) == 0,
       "splitChunk: size is not a multiple of the PageSize")
@@ -556,8 +640,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
   when coalescLeft:
     let prevSize = c.prevSize
     if prevSize != 0:
-      var le = cast[PChunk](cast[ByteAddress](c) -% prevSize)
-      sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4")
+      var le = cast[PChunk](cast[int](c) -% prevSize)
+      sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4")
       if isAccessible(a, le) and chunkUnused(le):
         sysAssert(not isSmallChunk(le), "freeBigChunk 5")
         if not isSmallChunk(le) and le.size < MaxBigChunkSize:
@@ -567,11 +651,14 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) =
           c = cast[PBigChunk](le)
           if c.size > MaxBigChunkSize:
             let rest = splitChunk2(a, c, MaxBigChunkSize)
+            when defined(nimOptimizedSplitChunk):
+              rest.next = nil
+              rest.prev = nil
             addChunkToMatrix(a, c)
             c = rest
   when coalescRight:
-    var ri = cast[PChunk](cast[ByteAddress](c) +% c.size)
-    sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2")
+    var ri = cast[PChunk](cast[int](c) +% c.size)
+    sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2")
     if isAccessible(a, ri) and chunkUnused(ri):
       sysAssert(not isSmallChunk(ri), "freeBigChunk 3")
       if not isSmallChunk(ri) and c.size < MaxBigChunkSize:
@@ -591,6 +678,13 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   mappingSearch(size, fl, sl)
   sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
   result = findSuitableBlock(a, fl, sl)
+
+  when RegionHasLock:
+    if not a.lockActive:
+      a.lockActive = true
+      initSysLock(a.lock)
+    acquireSys a.lock
+
   if result == nil:
     if size < nimMinHeapPages * PageSize:
       result = requestOsChunks(a, nimMinHeapPages * PageSize)
@@ -600,6 +694,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
       # if we over allocated split the chunk:
       if result.size > size:
         splitChunk(a, result, size)
+    result.owner = addr a
   else:
     removeChunkFromMatrix2(a, result, fl, sl)
     if result.size >= size + PageSize:
@@ -607,22 +702,33 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
   # set 'used' to to true:
   result.prevSize = 1
   track("setUsedToFalse", addr result.size, sizeof(int))
+  sysAssert result.owner == addr a, "getBigChunk: No owner set!"
 
   incl(a, a.chunkStarts, pageIndex(result))
   dec(a.freeMem, size)
+  when RegionHasLock:
+    releaseSys a.lock
 
 proc getHugeChunk(a: var MemRegion; size: int): PBigChunk =
-  result = cast[PBigChunk](osAllocPages(size))
+  result = cast[PBigChunk](allocPages(a, size))
+  when RegionHasLock:
+    if not a.lockActive:
+      a.lockActive = true
+      initSysLock(a.lock)
+    acquireSys a.lock
   incCurrMem(a, size)
   # XXX add this to the heap links. But also remove it from it later.
   when false: a.addHeapLink(result, size)
-  sysAssert((cast[ByteAddress](result) and PageMask) == 0, "getHugeChunk")
+  sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk")
   result.next = nil
   result.prev = nil
   result.size = size
   # set 'used' to to true:
   result.prevSize = 1
+  result.owner = addr a
   incl(a, a.chunkStarts, pageIndex(result))
+  when RegionHasLock:
+    releaseSys a.lock
 
 proc freeHugeChunk(a: var MemRegion; c: PBigChunk) =
   let size = c.size
@@ -686,63 +792,74 @@ else:
   template trackSize(x) = discard
   template untrackSize(x) = discard
 
-when false:
-  # not yet used by the GCs
-  proc rawTryAlloc(a: var MemRegion; requestedSize: int): pointer =
-    sysAssert(allocInv(a), "rawAlloc: begin")
-    sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
-    sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
-    var size = roundup(requestedSize, MemAlign)
-    inc a.occ, size
-    trackSize(size)
-    sysAssert(size >= requestedSize, "insufficient allocated size!")
-    #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
-    if size <= SmallChunkSize-smallChunkOverhead():
-      # allocate a small block: for small chunks, we use only its next pointer
-      var s = size div MemAlign
-      var c = a.freeSmallChunks[s]
-      if c == nil:
-        result = nil
-      else:
-        sysAssert c.size == size, "rawAlloc 6"
-        if c.freeList == nil:
-          sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
-                    "rawAlloc 7")
-          result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
-          inc(c.acc, size)
-        else:
-          result = c.freeList
-          sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
-          c.freeList = c.freeList.next
-        dec(c.free, size)
-        sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
-        if c.free < size:
-          listRemove(a.freeSmallChunks[s], c)
-          sysAssert(allocInv(a), "rawAlloc: end listRemove test")
-        sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
-                  size == 0, "rawAlloc 21")
-        sysAssert(allocInv(a), "rawAlloc: end small size")
+proc deallocBigChunk(a: var MemRegion, c: PBigChunk) =
+  when RegionHasLock:
+    acquireSys a.lock
+  dec a.occ, c.size
+  untrackSize(c.size)
+  sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
+  when not defined(gcDestructors):
+    a.deleted = getBottom(a)
+    del(a, a.root, cast[int](addr(c.data)))
+  if c.size >= HugeChunkSize: freeHugeChunk(a, c)
+  else: freeBigChunk(a, c)
+  when RegionHasLock:
+    releaseSys a.lock
+
+when defined(gcDestructors):
+  template atomicPrepend(head, elem: untyped) =
+    # see also https://en.cppreference.com/w/cpp/atomic/atomic_compare_exchange
+    when hasThreadSupport:
+      while true:
+        elem.next.storea head.loada
+        if atomicCompareExchangeN(addr head, addr elem.next, elem, weak = true, ATOMIC_RELEASE, ATOMIC_RELAXED):
+          break
     else:
-      inc size, bigChunkOverhead()
-      var fl, sl: int
-      mappingSearch(size, fl, sl)
-      sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk")
-      let c = findSuitableBlock(a, fl, sl)
-      if c != nil:
-        removeChunkFromMatrix2(a, c, fl, sl)
-        if c.size >= size + PageSize:
-          splitChunk(a, c, size)
-        # set 'used' to to true:
-        c.prevSize = 1
-        incl(a, a.chunkStarts, pageIndex(c))
-        dec(a.freeMem, size)
-        result = addr(c.data)
-        sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
-        sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
-        if a.root == nil: a.root = getBottom(a)
-        add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
-      else:
-        result = nil
+      elem.next.storea head.loada
+      head.storea elem
+
+  proc addToSharedFreeListBigChunks(a: var MemRegion; c: PBigChunk) {.inline.} =
+    sysAssert c.next == nil, "c.next pointer must be nil"
+    atomicPrepend a.sharedFreeListBigChunks, c
+
+  proc addToSharedFreeList(c: PSmallChunk; f: ptr FreeCell; size: int) {.inline.} =
+    atomicPrepend c.owner.sharedFreeLists[size], f
+
+  const MaxSteps = 20
+
+  proc compensateCounters(a: var MemRegion; c: PSmallChunk; size: int) =
+    # rawDealloc did NOT do the usual:
+    # `inc(c.free, size); dec(a.occ, size)` because it wasn't the owner of these
+    # memory locations. We have to compensate here for these for the entire list.
+    var it = c.freeList
+    var total = 0
+    while it != nil:
+      inc total, size
+      let chunk = cast[PSmallChunk](pageAddr(it))
+      if c != chunk:
+        # The cell is foreign, potentially even from a foreign thread.
+        # It must block the current chunk from being freed, as doing so would leak memory.
+        inc c.foreignCells
+      it = it.next
+    # By not adjusting the foreign chunk we reserve space in it to prevent deallocation
+    inc(c.free, total)
+    dec(a.occ, total)
+
+  proc freeDeferredObjects(a: var MemRegion; root: PBigChunk) =
+    var it = root
+    var maxIters = MaxSteps # make it time-bounded
+    while true:
+      let rest = it.next.loada
+      it.next.storea nil
+      deallocBigChunk(a, cast[PBigChunk](it))
+      if maxIters == 0:
+        if rest != nil:
+          addToSharedFreeListBigChunks(a, rest)
+          sysAssert a.sharedFreeListBigChunks != nil, "re-enqueing failed"
+        break
+      it = rest
+      dec maxIters
+      if it == nil: break
 
 proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
   when defined(nimTypeNames):
@@ -751,55 +868,104 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
   sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
   var size = roundup(requestedSize, MemAlign)
   sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small")
-
   sysAssert(size >= requestedSize, "insufficient allocated size!")
   #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size)
+
   if size <= SmallChunkSize-smallChunkOverhead():
+    template fetchSharedCells(tc: PSmallChunk) =
+      # Consumes cells from (potentially) foreign threads from `a.sharedFreeLists[s]`
+      when defined(gcDestructors):
+        if tc.freeList == nil:
+          when hasThreadSupport:
+            # Steal the entire list from `sharedFreeList`:
+            tc.freeList = atomicExchangeN(addr a.sharedFreeLists[s], nil, ATOMIC_RELAXED)
+          else:
+            tc.freeList = a.sharedFreeLists[s]
+            a.sharedFreeLists[s] = nil
+          # if `tc.freeList` isn't nil, `tc` will gain capacity.
+          # We must calculate how much it gained and how many foreign cells are included.
+          compensateCounters(a, tc, size)
+
     # allocate a small block: for small chunks, we use only its next pointer
-    var s = size div MemAlign
+    let s = size div MemAlign
     var c = a.freeSmallChunks[s]
     if c == nil:
+      # There is no free chunk of the requested size available, we need a new one.
       c = getSmallChunk(a)
+      # init all fields in case memory didn't get zeroed
       c.freeList = nil
+      c.foreignCells = 0
       sysAssert c.size == PageSize, "rawAlloc 3"
       c.size = size
-      c.acc = size
-      c.free = SmallChunkSize - smallChunkOverhead() - size
+      c.acc = size.uint32
+      c.free = SmallChunkSize - smallChunkOverhead() - size.int32
+      sysAssert c.owner == addr(a), "rawAlloc: No owner set!"
       c.next = nil
       c.prev = nil
-      listAdd(a.freeSmallChunks[s], c)
+      # Shared cells are fetched here in case `c.size * 2 >= SmallChunkSize - smallChunkOverhead()`.
+      # For those single cell chunks, we would otherwise have to allocate a new one almost every time.
+      fetchSharedCells(c)
+      if c.free >= size:
+        # Because removals from `a.freeSmallChunks[s]` only happen in the other alloc branch and during dealloc,
+        #  we must not add it to the list if it cannot be used the next time a pointer of `size` bytes is needed.
+        listAdd(a.freeSmallChunks[s], c)
       result = addr(c.data)
-      sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4")
+      sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4")
     else:
+      # There is a free chunk of the requested size available, use it.
       sysAssert(allocInv(a), "rawAlloc: begin c != nil")
       sysAssert c.next != c, "rawAlloc 5"
       #if c.size != size:
       #  c_fprintf(stdout, "csize: %lld; size %lld\n", c.size, size)
       sysAssert c.size == size, "rawAlloc 6"
       if c.freeList == nil:
-        sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize,
+        sysAssert(c.acc.int + smallChunkOverhead() + size <= SmallChunkSize,
                   "rawAlloc 7")
-        result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc)
+        result = cast[pointer](cast[int](addr(c.data)) +% c.acc.int)
         inc(c.acc, size)
       else:
+        # There are free cells available, prefer them over the accumulator
         result = c.freeList
         when not defined(gcDestructors):
           sysAssert(c.freeList.zeroField == 0, "rawAlloc 8")
         c.freeList = c.freeList.next
+        if cast[PSmallChunk](pageAddr(result)) != c:
+          # This cell isn't a blocker for the current chunk's deallocation anymore
+          dec(c.foreignCells)
+        else:
+          sysAssert(c == cast[PSmallChunk](pageAddr(result)), "rawAlloc: Bad cell")
+      # Even if the cell we return is foreign, the local chunk's capacity decreases.
+      # The capacity was previously reserved in the source chunk (when it first got allocated),
+      #  then added into the current chunk during dealloc,
+      #  so the source chunk will not be freed or leak memory because of this.
       dec(c.free, size)
-      sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9")
+      sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9")
       sysAssert(allocInv(a), "rawAlloc: end c != nil")
-    sysAssert(allocInv(a), "rawAlloc: before c.free < size")
-    if c.free < size:
-      sysAssert(allocInv(a), "rawAlloc: before listRemove test")
-      listRemove(a.freeSmallChunks[s], c)
-      sysAssert(allocInv(a), "rawAlloc: end listRemove test")
-    sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %%
+      # We fetch deferred cells *after* advancing `c.freeList`/`acc` to adjust `c.free`.
+      # If after the adjustment it turns out there's free cells available,
+      #  the chunk stays in `a.freeSmallChunks[s]` and the need for a new chunk is delayed.
+      fetchSharedCells(c)
+      sysAssert(allocInv(a), "rawAlloc: before c.free < size")
+      if c.free < size:
+        # Even after fetching shared cells the chunk has no usable memory left. It is no longer the active chunk
+        sysAssert(allocInv(a), "rawAlloc: before listRemove test")
+        listRemove(a.freeSmallChunks[s], c)
+        sysAssert(allocInv(a), "rawAlloc: end listRemove test")
+    sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %%
                size == 0, "rawAlloc 21")
     sysAssert(allocInv(a), "rawAlloc: end small size")
     inc a.occ, size
     trackSize(c.size)
   else:
+    when defined(gcDestructors):
+      when hasThreadSupport:
+        let deferredFrees = atomicExchangeN(addr a.sharedFreeListBigChunks, nil, ATOMIC_RELAXED)
+      else:
+        let deferredFrees = a.sharedFreeListBigChunks
+        a.sharedFreeListBigChunks = nil
+      if deferredFrees != nil:
+        freeDeferredObjects(a, deferredFrees)
+
     size = requestedSize + bigChunkOverhead() #  roundup(requestedSize+bigChunkOverhead(), PageSize)
     # allocate a large block
     var c = if size >= HugeChunkSize: getHugeChunk(a, size)
@@ -807,15 +973,16 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
     sysAssert c.prev == nil, "rawAlloc 10"
     sysAssert c.next == nil, "rawAlloc 11"
     result = addr(c.data)
-    sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13")
-    sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
-    if a.root == nil: a.root = getBottom(a)
-    add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size)
+    sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13")
+    sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary")
+    when not defined(gcDestructors):
+      if a.root == nil: a.root = getBottom(a)
+      add(a, a.root, cast[int](result), cast[int](result)+%size)
     inc a.occ, c.size
     trackSize(c.size)
   sysAssert(isAccessible(a, result), "rawAlloc 14")
   sysAssert(allocInv(a), "rawAlloc: end")
-  when logAlloc: cprintf("var pointer_%p = alloc(%ld)\n", result, requestedSize)
+  when logAlloc: cprintf("var pointer_%p = alloc(%ld) # %p\n", result, requestedSize, addr a)
 
 proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
   result = rawAlloc(a, requestedSize)
@@ -827,53 +994,77 @@ proc rawDealloc(a: var MemRegion, p: pointer) =
   #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
   sysAssert(allocInv(a), "rawDealloc: begin")
   var c = pageAddr(p)
+  sysAssert(c != nil, "rawDealloc: begin")
   if isSmallChunk(c):
     # `p` is within a small chunk:
     var c = cast[PSmallChunk](c)
-    var s = c.size
-    dec a.occ, s
-    untrackSize(s)
-    sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
-    sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
-               s == 0, "rawDealloc 3")
+    let s = c.size
+    #       ^ We might access thread foreign storage here.
+    # The other thread cannot possibly free this block as it's still alive.
     var f = cast[ptr FreeCell](p)
-    when not defined(gcDestructors):
-      #echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField)))
-      sysAssert(f.zeroField != 0, "rawDealloc 1")
-      f.zeroField = 0
-    f.next = c.freeList
-    c.freeList = f
-    when overwriteFree:
-      # set to 0xff to check for usage after free bugs:
-      nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
-               s -% sizeof(FreeCell))
-    # check if it is not in the freeSmallChunks[s] list:
-    if c.free < s:
-      # add it to the freeSmallChunks[s] array:
-      listAdd(a.freeSmallChunks[s div MemAlign], c)
-      inc(c.free, s)
+    if c.owner == addr(a):
+      # We own the block, there is no foreign thread involved.
+      dec a.occ, s
+      untrackSize(s)
+      sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)"
+      sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
+                s == 0, "rawDealloc 3")
+      when not defined(gcDestructors):
+        #echo("setting to nil: ", $cast[int](addr(f.zeroField)))
+        sysAssert(f.zeroField != 0, "rawDealloc 1")
+        f.zeroField = 0
+      when overwriteFree:
+        # set to 0xff to check for usage after free bugs:
+        nimSetMem(cast[pointer](cast[int](p) +% sizeof(FreeCell)), -1'i32,
+                s -% sizeof(FreeCell))
+      let activeChunk = a.freeSmallChunks[s div MemAlign]
+      if activeChunk != nil and c != activeChunk:
+        # This pointer is not part of the active chunk, lend it out
+        #  and do not adjust the current chunk (same logic as compensateCounters.)
+        # Put the cell into the active chunk,
+        #  may prevent a queue of available chunks from forming in a.freeSmallChunks[s div MemAlign].
+        #  This queue would otherwise waste memory in the form of free cells until we return to those chunks.
+        f.next = activeChunk.freeList
+        activeChunk.freeList = f # lend the cell
+        inc(activeChunk.free, s) # By not adjusting the current chunk's capacity it is prevented from being freed
+        inc(activeChunk.foreignCells) # The cell is now considered foreign from the perspective of the active chunk
+      else:
+        f.next = c.freeList
+        c.freeList = f
+        if c.free < s:
+          # The chunk could not have been active as it didn't have enough space to give
+          listAdd(a.freeSmallChunks[s div MemAlign], c)
+          inc(c.free, s)
+        else:
+          inc(c.free, s)
+          # Free only if the entire chunk is unused and there are no borrowed cells.
+          # If the chunk were to be freed while it references foreign cells,
+          #  the foreign chunks will leak memory and can never be freed.
+          if c.free == SmallChunkSize-smallChunkOverhead() and c.foreignCells == 0:
+            listRemove(a.freeSmallChunks[s div MemAlign], c)
+            c.size = SmallChunkSize
+            freeBigChunk(a, cast[PBigChunk](c))
     else:
-      inc(c.free, s)
-      if c.free == SmallChunkSize-smallChunkOverhead():
-        listRemove(a.freeSmallChunks[s div MemAlign], c)
-        c.size = SmallChunkSize
-        freeBigChunk(a, cast[PBigChunk](c))
-    sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %%
+      when logAlloc: cprintf("dealloc(pointer_%p) # SMALL FROM %p CALLER %p\n", p, c.owner, addr(a))
+
+      when defined(gcDestructors):
+        addToSharedFreeList(c, f, s div MemAlign)
+    sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %%
                s == 0, "rawDealloc 2")
   else:
     # set to 0xff to check for usage after free bugs:
     when overwriteFree: nimSetMem(p, -1'i32, c.size -% bigChunkOverhead())
-    # free big chunk
-    var c = cast[PBigChunk](c)
-    dec a.occ, c.size
-    untrackSize(c.size)
-    sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case B)"
-    a.deleted = getBottom(a)
-    del(a, a.root, cast[int](addr(c.data)))
-    if c.size >= HugeChunkSize: freeHugeChunk(a, c)
-    else: freeBigChunk(a, c)
+    when logAlloc: cprintf("dealloc(pointer_%p) # BIG %p\n", p, c.owner)
+    when defined(gcDestructors):
+      if c.owner == addr(a):
+        deallocBigChunk(a, cast[PBigChunk](c))
+      else:
+        addToSharedFreeListBigChunks(c.owner[], cast[PBigChunk](c))
+    else:
+      deallocBigChunk(a, cast[PBigChunk](c))
+
   sysAssert(allocInv(a), "rawDealloc: end")
-  when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
+  #when logAlloc: cprintf("dealloc(pointer_%p)\n", p)
 
 when not defined(gcDestructors):
   proc isAllocatedPtr(a: MemRegion, p: pointer): bool =
@@ -882,9 +1073,9 @@ when not defined(gcDestructors):
       if not chunkUnused(c):
         if isSmallChunk(c):
           var c = cast[PSmallChunk](c)
-          var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
+          var offset = (cast[int](p) and (PageSize-1)) -%
                       smallChunkOverhead()
-          result = (c.acc >% offset) and (offset %% c.size == 0) and
+          result = (c.acc.int >% offset) and (offset %% c.size == 0) and
             (cast[ptr FreeCell](p).zeroField >% 1)
         else:
           var c = cast[PBigChunk](c)
@@ -900,12 +1091,12 @@ when not defined(gcDestructors):
       if not chunkUnused(c):
         if isSmallChunk(c):
           var c = cast[PSmallChunk](c)
-          var offset = (cast[ByteAddress](p) and (PageSize-1)) -%
+          var offset = (cast[int](p) and (PageSize-1)) -%
                       smallChunkOverhead()
-          if c.acc >% offset:
-            sysAssert(cast[ByteAddress](addr(c.data)) +% offset ==
-                      cast[ByteAddress](p), "offset is not what you think it is")
-            var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +%
+          if c.acc.int >% offset:
+            sysAssert(cast[int](addr(c.data)) +% offset ==
+                      cast[int](p), "offset is not what you think it is")
+            var d = cast[ptr FreeCell](cast[int](addr(c.data)) +%
                       offset -% (offset %% c.size))
             if d.zeroField >% 1:
               result = d
@@ -932,7 +1123,7 @@ when not defined(gcDestructors):
 
 proc ptrSize(p: pointer): int =
   when not defined(gcDestructors):
-    var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
+    var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
     var c = pageAddr(p)
     sysAssert(not chunkUnused(c), "ptrSize")
     result = c.size -% sizeof(FreeCell)
@@ -950,7 +1141,7 @@ proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} =
     result = rawAlloc(allocator, size+sizeof(FreeCell))
     cast[ptr FreeCell](result).zeroField = 1 # mark it as used
     sysAssert(not isAllocatedPtr(allocator, result), "alloc")
-    result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell))
+    result = cast[pointer](cast[int](result) +% sizeof(FreeCell))
     track("alloc", result, size)
   else:
     result = rawAlloc(allocator, size)
@@ -962,7 +1153,7 @@ proc alloc0(allocator: var MemRegion, size: Natural): pointer =
 proc dealloc(allocator: var MemRegion, p: pointer) =
   when not defined(gcDestructors):
     sysAssert(p != nil, "dealloc: p is nil")
-    var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell))
+    var x = cast[pointer](cast[int](p) -% sizeof(FreeCell))
     sysAssert(x != nil, "dealloc: x is nil")
     sysAssert(isAccessible(allocator, x), "is not accessible")
     sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted")
@@ -995,7 +1186,7 @@ proc deallocOsPages(a: var MemRegion) =
       let (p, size) = it.chunks[i]
       when defined(debugHeapLinks):
         cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
-          it, it.size, next)
+          it, size, next)
       sysAssert size >= PageSize, "origSize too small"
       osDeallocPages(p, size)
     it = next
@@ -1023,7 +1214,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
       result = interiorAllocatedPtr(allocator, p)
 
     proc isAllocatedPtr*(p: pointer): bool =
-      let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell)))
+      let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell)))
       result = isAllocatedPtr(allocator, p)
 
   proc deallocOsPages = deallocOsPages(allocator)
@@ -1043,7 +1234,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
   proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer =
     result = realloc(allocator, p, newSize)
     if newSize > oldSize:
-      zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize)
+      zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
 
   when false:
     proc countFreeMem(): int =
@@ -1053,7 +1244,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
         inc(result, it.size)
         it = it.next
 
-  when hasThreadSupport:
+  when hasThreadSupport and not defined(gcDestructors):
     proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "<stdlib.h>".}
 
     var sharedHeap: MemRegion
@@ -1063,36 +1254,16 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
 
   proc getFreeMem(): int =
     #sysAssert(result == countFreeMem())
-    when hasThreadSupport and defined(gcDestructors):
-      acquireSys(heapLock)
-      result = sharedHeap.freeMem
-      releaseSys(heapLock)
-    else:
-      result = allocator.freeMem
+    result = allocator.freeMem
 
   proc getTotalMem(): int =
-    when hasThreadSupport and defined(gcDestructors):
-      acquireSys(heapLock)
-      result = sharedHeap.currMem
-      releaseSys(heapLock)
-    else:
-      result = allocator.currMem
+    result = allocator.currMem
 
   proc getOccupiedMem(): int =
-    when hasThreadSupport and defined(gcDestructors):
-      acquireSys(heapLock)
-      result = sharedHeap.occ
-      releaseSys(heapLock)
-    else:
-      result = allocator.occ #getTotalMem() - getFreeMem()
+    result = allocator.occ #getTotalMem() - getFreeMem()
 
   proc getMaxMem*(): int =
-    when hasThreadSupport and defined(gcDestructors):
-      acquireSys(heapLock)
-      result = getMaxMem(sharedHeap)
-      releaseSys(heapLock)
-    else:
-      result = getMaxMem(allocator)
+    result = getMaxMem(allocator)
 
   when defined(nimTypeNames):
     proc getMemCounters*(): (int, int) = getMemCounters(allocator)
@@ -1100,7 +1271,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
   # -------------------- shared heap region ----------------------------------
 
   proc allocSharedImpl(size: Natural): pointer =
-    when hasThreadSupport:
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       result = alloc(sharedHeap, size)
       releaseSys(heapLock)
@@ -1112,7 +1283,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
     zeroMem(result, size)
 
   proc deallocSharedImpl(p: pointer) =
-    when hasThreadSupport:
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       dealloc(sharedHeap, p)
       releaseSys(heapLock)
@@ -1120,7 +1291,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
       deallocImpl(p)
 
   proc reallocSharedImpl(p: pointer, newSize: Natural): pointer =
-    when hasThreadSupport:
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       result = realloc(sharedHeap, p, newSize)
       releaseSys(heapLock)
@@ -1128,7 +1299,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
       result = reallocImpl(p, newSize)
 
   proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer =
-    when hasThreadSupport:
+    when hasThreadSupport and not defined(gcDestructors):
       acquireSys(heapLock)
       result = realloc0(sharedHeap, p, oldSize, newSize)
       releaseSys(heapLock)
@@ -1136,20 +1307,31 @@ template instantiateForRegion(allocator: untyped) {.dirty.} =
       result = realloc0Impl(p, oldSize, newSize)
 
   when hasThreadSupport:
-    template sharedMemStatsShared(v: int) =
-      acquireSys(heapLock)
-      result = v
-      releaseSys(heapLock)
+    when defined(gcDestructors):
+      proc getFreeSharedMem(): int =
+        allocator.freeMem
+
+      proc getTotalSharedMem(): int =
+        allocator.currMem
+
+      proc getOccupiedSharedMem(): int =
+        allocator.occ
+
+    else:
+      template sharedMemStatsShared(v: int) =
+        acquireSys(heapLock)
+        result = v
+        releaseSys(heapLock)
 
-    proc getFreeSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.freeMem)
+      proc getFreeSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.freeMem)
 
-    proc getTotalSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.currMem)
+      proc getTotalSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.currMem)
 
-    proc getOccupiedSharedMem(): int =
-      sharedMemStatsShared(sharedHeap.occ)
-      #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
+      proc getOccupiedSharedMem(): int =
+        sharedMemStatsShared(sharedHeap.occ)
+        #sharedMemStatsShared(sharedHeap.currMem - sharedHeap.freeMem)
   {.pop.}
 
 {.pop.}
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 0dbded126..3098e17d6 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -65,7 +65,7 @@ elif defined(macosx) or defined(linux) or defined(freebsd) or
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(13)
-    SIG_DFL* = cast[CSighandlerT](0)
+    SIG_DFL* = CSighandlerT(nil)
 elif defined(haiku):
   const
     SIGABRT* = cint(6)
@@ -75,7 +75,7 @@ elif defined(haiku):
     SIGSEGV* = cint(11)
     SIGTERM* = cint(15)
     SIGPIPE* = cint(7)
-    SIG_DFL* = cast[CSighandlerT](0)
+    SIG_DFL* = CSighandlerT(nil)
 else:
   when defined(nimscript):
     {.error: "SIGABRT not ported to your platform".}
@@ -180,11 +180,16 @@ proc c_printf*(frmt: cstring): cint {.
 
 proc c_fputs*(c: cstring, f: CFilePtr): cint {.
   importc: "fputs", header: "<stdio.h>", discardable.}
+proc c_fputc*(c: char, f: CFilePtr): cint {.
+  importc: "fputc", header: "<stdio.h>", discardable.}
 
 proc c_sprintf*(buf, frmt: cstring): cint {.
   importc: "sprintf", header: "<stdio.h>", varargs, noSideEffect.}
   # we use it only in a way that cannot lead to security issues
 
+proc c_snprintf*(buf: cstring, n: csize_t, frmt: cstring): cint {.
+  importc: "snprintf", header: "<stdio.h>", varargs, noSideEffect.}
+
 when defined(zephyr) and not defined(zephyrUseLibcMalloc):
   proc c_malloc*(size: csize_t): pointer {.
     importc: "k_malloc", header: "<kernel.h>".}
@@ -209,10 +214,10 @@ else:
   proc c_realloc*(p: pointer, newsize: csize_t): pointer {.
     importc: "realloc", header: "<stdlib.h>".}
 
-proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): cint {.
+proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): csize_t {.
   importc: "fwrite", header: "<stdio.h>".}
 
-proc c_fflush(f: CFilePtr): cint {.
+proc c_fflush*(f: CFilePtr): cint {.
   importc: "fflush", header: "<stdio.h>".}
 
 proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonReloadable, inline.} =
@@ -222,7 +227,7 @@ proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonRe
 
 proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} =
   # we cannot throw an exception here!
-  discard c_fwrite(s, 1, cast[csize_t](s.len), f)
+  discard c_fwrite(s, 1, c_strlen(s), f)
   discard c_fflush(f)
 
 {.pop.}
diff --git a/lib/system/arc.nim b/lib/system/arc.nim
index ccf9d44e2..d001fcaa5 100644
--- a/lib/system/arc.nim
+++ b/lib/system/arc.nim
@@ -12,26 +12,6 @@ In this new runtime we simplify the object layouts a bit: The runtime type
 information is only accessed for the objects that have it and it's always
 at offset 0 then. The ``ref`` object header is independent from the
 runtime type and only contains a reference count.
-
-Object subtyping is checked via the generated 'name'. This should have
-comparable overhead to the old pointer chasing approach but has the benefit
-that it works across DLL boundaries.
-
-The generated name is a concatenation of the object names in the hierarchy
-so that a subtype check becomes a substring check. For example::
-
-  type
-    ObjectA = object of RootObj
-    ObjectB = object of ObjectA
-
-ObjectA's ``name`` is "|ObjectA|RootObj|".
-ObjectB's ``name`` is "|ObjectB|ObjectA|RootObj|".
-
-Now to check for ``x of ObjectB`` we need to check
-for ``x.typ.name.endsWith("|ObjectB|ObjectA|RootObj|")``.
-In the actual implementation, however, we could also use a
-hash of ``package & "." & module & "." & name`` to save space.
-
 ]#
 
 when defined(gcOrc):
@@ -46,6 +26,9 @@ else:
     rcMask = 0b111
     rcShift = 3      # shift by rcShift to get the reference counter
 
+const
+  orcLeakDetector = defined(nimOrcLeakDetector)
+
 type
   RefHeader = object
     rc: int # the object header is now a single RC field.
@@ -56,9 +39,21 @@ type
                    # in O(1) without doubly linked lists
     when defined(nimArcDebug) or defined(nimArcIds):
       refId: int
+    when defined(gcOrc) and orcLeakDetector:
+      filename: cstring
+      line: int
 
   Cell = ptr RefHeader
 
+template setFrameInfo(c: Cell) =
+  when orcLeakDetector:
+    if framePtr != nil and framePtr.prev != nil:
+      c.filename = framePtr.prev.filename
+      c.line = framePtr.prev.line
+    else:
+      c.filename = nil
+      c.line = 0
+
 template head(p: pointer): Cell =
   cast[Cell](cast[int](p) -% sizeof(RefHeader))
 
@@ -77,6 +72,21 @@ elif defined(nimArcIds):
 
   const traceId = -1
 
+when defined(gcAtomicArc) and hasThreadSupport:
+  template decrement(cell: Cell): untyped =
+    discard atomicDec(cell.rc, rcIncrement)
+  template increment(cell: Cell): untyped =
+    discard atomicInc(cell.rc, rcIncrement)
+  template count(x: Cell): untyped =
+    atomicLoadN(x.rc.addr, ATOMIC_ACQUIRE) shr rcShift
+else:
+  template decrement(cell: Cell): untyped =
+    dec(cell.rc, rcIncrement)
+  template increment(cell: Cell): untyped =
+    inc(cell.rc, rcIncrement)
+  template count(x: Cell): untyped =
+    x.rc shr rcShift
+
 proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
   let hdrSize = align(sizeof(RefHeader), alignment)
   let s = size + hdrSize
@@ -89,9 +99,10 @@ proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} =
     atomicInc gRefId
     if head(result).refId == traceId:
       writeStackTrace()
-      cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift)
+      cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).count)
   when traceCollector:
     cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
+  setFrameInfo head(result)
 
 proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
   # Same as 'newNewObj' but do not initialize the memory to zero.
@@ -110,21 +121,34 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} =
     atomicInc gRefId
     if head(result).refId == traceId:
       writeStackTrace()
-      cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift)
+      cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).count)
 
   when traceCollector:
     cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result)
+  setFrameInfo head(result)
 
 proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} =
-  dec head(p).rc, rcIncrement
+  decrement head(p)
+
+proc isUniqueRef*[T](x: ref T): bool {.inline.} =
+  ## Returns true if the object `x` points to is uniquely referenced. Such
+  ## an object can potentially be passed over to a different thread safely,
+  ## if great care is taken. This queries the internal reference count of
+  ## the object which is subject to lots of optimizations! In other words
+  ## the value of `isUniqueRef` can depend on the used compiler version and
+  ## optimizer setting.
+  ## Nevertheless it can be used as a very valuable debugging tool and can
+  ## be used to specify the constraints of a threading related API
+  ## via `assert isUniqueRef(x)`.
+  head(cast[pointer](x)).rc == 0
 
 proc nimIncRef(p: pointer) {.compilerRtl, inl.} =
   when defined(nimArcDebug):
     if head(p).refId == traceId:
       writeStackTrace()
-      cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift)
+      cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).count)
 
-  inc head(p).rc, rcIncrement
+  increment head(p)
   when traceCollector:
     cprintf("[INCREF] %p\n", head(p))
 
@@ -154,7 +178,7 @@ proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} =
     when defined(nimOwnedEnabled):
       if head(p).rc >= rcIncrement:
         cstderr.rawWrite "[FATAL] dangling references exist\n"
-        quit 1
+        rawQuit 1
     when defined(nimArcDebug):
       # we do NOT really free the memory here in order to reliably detect use-after-frees
       if freedCells.data == nil: init(freedCells)
@@ -193,17 +217,24 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} =
     when defined(nimArcDebug):
       if cell.refId == traceId:
         writeStackTrace()
-        cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift)
-
-    if (cell.rc and not rcMask) == 0:
-      result = true
-      when traceCollector:
-        cprintf("[ABOUT TO DESTROY] %p\n", cell)
+        cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.count)
+
+    when defined(gcAtomicArc) and hasThreadSupport:
+      # `atomicDec` returns the new value
+      if atomicDec(cell.rc, rcIncrement) == -rcIncrement:
+        result = true
+        when traceCollector:
+          cprintf("[ABOUT TO DESTROY] %p\n", cell)
     else:
-      dec cell.rc, rcIncrement
-      # According to Lins it's correct to do nothing else here.
-      when traceCollector:
-        cprintf("[DeCREF] %p\n", cell)
+      if cell.count == 0:
+        result = true
+        when traceCollector:
+          cprintf("[ABOUT TO DESTROY] %p\n", cell)
+      else:
+        decrement cell
+        # According to Lins it's correct to do nothing else here.
+        when traceCollector:
+          cprintf("[DECREF] %p\n", cell)
 
 proc GC_unref*[T](x: ref T) =
   ## New runtime only supports this operation for 'ref T'.
@@ -216,46 +247,21 @@ proc GC_ref*[T](x: ref T) =
 
 when not defined(gcOrc):
   template GC_fullCollect* =
-    ## Forces a full garbage collection pass. With `--gc:arc` a nop.
+    ## Forces a full garbage collection pass. With `--mm:arc` a nop.
     discard
 
 template setupForeignThreadGc* =
-  ## With `--gc:arc` a nop.
+  ## With `--mm:arc` a nop.
   discard
 
 template tearDownForeignThreadGc* =
-  ## With `--gc:arc` a nop.
+  ## With `--mm:arc` a nop.
   discard
 
-type ObjCheckCache = array[0..1, PNimTypeV2]
-
-proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "<string.h>".}
-
-func endsWith(s, suffix: cstring): bool {.inline.} =
-  let
-    sLen = s.len
-    suffixLen = suffix.len
-
-  if suffixLen <= sLen:
-    result = memcmp(cstring(addr s[sLen - suffixLen]), suffix, csize_t(suffixLen)) == 0
-
-proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} =
-  result = endsWith(obj.name, subclass)
-
-proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} =
-  if endsWith(obj.name, subclass):
-    cache[1] = obj
-    result = true
-  else:
-    cache[0] = obj
-    result = false
-
-proc isObjWithCache(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl.} =
-  if cache[0] == obj: result = false
-  elif cache[1] == obj: result = true
-  else:
-    result = isObjSlowPath(obj, subclass, cache)
+proc isObjDisplayCheck(source: PNimTypeV2, targetDepth: int16, token: uint32): bool {.compilerRtl, inl.} =
+  result = targetDepth <= source.depth and source.display[targetDepth] == token
 
-proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} =
-  # checks if obj is of type subclass:
-  if not isObj(obj, subclass): sysFatal(ObjectConversionDefect, "invalid object conversion")
+when defined(gcDestructors):
+  proc nimGetVTable(p: pointer, index: int): pointer
+        {.compilerRtl, inline, raises: [].} =
+    result = cast[ptr PNimTypeV2](p).vTable[index]
diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim
deleted file mode 100644
index 158f40177..000000000
--- a/lib/system/arithm.nim
+++ /dev/null
@@ -1,425 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-
-# simple integer arithmetic with overflow checking
-
-proc raiseOverflow {.compilerproc, noinline.} =
-  # a single proc to reduce code size to a minimum
-  sysFatal(OverflowDefect, "over- or underflow")
-
-proc raiseDivByZero {.compilerproc, noinline.} =
-  sysFatal(DivByZeroDefect, "division by zero")
-
-when defined(builtinOverflow):
-  # Builtin compiler functions for improved performance
-  when sizeof(clong) == 8:
-    proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_saddl_overflow", nodecl, nosideeffect.}
-
-    proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.}
-
-    proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_smull_overflow", nodecl, nosideeffect.}
-
-  elif sizeof(clonglong) == 8:
-    proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_saddll_overflow", nodecl, nosideeffect.}
-
-    proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.}
-
-    proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {.
-      importc: "__builtin_smulll_overflow", nodecl, nosideeffect.}
-
-  when sizeof(int) == 8:
-    proc addIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      addInt64Overflow(a, b, c)
-
-    proc subIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      subInt64Overflow(a, b, c)
-
-    proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} =
-      mulInt64Overflow(a, b, c)
-
-  elif sizeof(int) == 4 and sizeof(cint) == 4:
-    proc addIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_sadd_overflow", nodecl, nosideeffect.}
-
-    proc subIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_ssub_overflow", nodecl, nosideeffect.}
-
-    proc mulIntOverflow(a, b: int, c: var int): bool {.
-      importc: "__builtin_smul_overflow", nodecl, nosideeffect.}
-
-  proc addInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    if addInt64Overflow(a, b, result):
-      raiseOverflow()
-
-  proc subInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    if subInt64Overflow(a, b, result):
-      raiseOverflow()
-
-  proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    if mulInt64Overflow(a, b, result):
-      raiseOverflow()
-else:
-  proc addInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    result = a +% b
-    if (result xor a) >= int64(0) or (result xor b) >= int64(0):
-      return result
-    raiseOverflow()
-
-  proc subInt64(a, b: int64): int64 {.compilerproc, inline.} =
-    result = a -% b
-    if (result xor a) >= int64(0) or (result xor not b) >= int64(0):
-      return result
-    raiseOverflow()
-
-  #
-  # This code has been inspired by Python's source code.
-  # The native int product x*y is either exactly right or *way* off, being
-  # just the last n bits of the true product, where n is the number of bits
-  # in an int (the delivered product is the true product plus i*2**n for
-  # some integer i).
-  #
-  # The native float64 product x*y is subject to three
-  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
-  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
-  # But, unlike the native int product, it's not in *range* trouble:  even
-  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
-  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
-  # product are correct.
-  #
-  # We check these two ways against each other, and declare victory if they're
-  # approximately the same. Else, because the native int product is the only
-  # one that can lose catastrophic amounts of information, it's the native int
-  # product that must have overflowed.
-  #
-  proc mulInt64(a, b: int64): int64 {.compilerproc.} =
-    var
-      resAsFloat, floatProd: float64
-    result = a *% b
-    floatProd = toBiggestFloat(a) # conversion
-    floatProd = floatProd * toBiggestFloat(b)
-    resAsFloat = toBiggestFloat(result)
-
-    # Fast path for normal case: small multiplicands, and no info
-    # is lost in either method.
-    if resAsFloat == floatProd: return result
-
-    # 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):
-      return result
-    raiseOverflow()
-
-proc negInt64(a: int64): int64 {.compilerproc, inline.} =
-  if a != low(int64): return -a
-  raiseOverflow()
-
-proc absInt64(a: int64): int64 {.compilerproc, inline.} =
-  if a != low(int64):
-    if a >= 0: return a
-    else: return -a
-  raiseOverflow()
-
-proc divInt64(a, b: int64): int64 {.compilerproc, inline.} =
-  if b == int64(0):
-    raiseDivByZero()
-  if a == low(int64) and b == int64(-1):
-    raiseOverflow()
-  return a div b
-
-proc modInt64(a, b: int64): int64 {.compilerproc, inline.} =
-  if b == int64(0):
-    raiseDivByZero()
-  return a mod b
-
-proc absInt(a: int): int {.compilerproc, inline.} =
-  if a != low(int):
-    if a >= 0: return a
-    else: return -a
-  raiseOverflow()
-
-const
-  asmVersion = defined(i386) and (defined(vcc) or defined(wcc) or
-               defined(dmc) or defined(gcc) or defined(llvm_gcc))
-    # my Version of Borland C++Builder does not have
-    # tasm32, which is needed for assembler blocks
-    # this is why Borland is not included in the 'when'
-
-when asmVersion and not defined(gcc) and not defined(llvm_gcc):
-  # assembler optimized versions for compilers that
-  # have an intel syntax assembler:
-  proc addInt(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-    # a in eax, and b in edx
-    asm """
-        mov eax, ecx
-        add eax, edx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc subInt(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        sub eax, edx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc negInt(a: int): int {.compilerproc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        neg eax
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-  proc divInt(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-    asm """
-        test  edx, edx
-        jne   L_NOT_ZERO
-        call  `raiseDivByZero`
-      L_NOT_ZERO:
-        cmp   ecx, 0x80000000
-        jne   L_DO_DIV
-        cmp   edx, -1
-        jne   L_DO_DIV
-        call  `raiseOverflow`
-      L_DO_DIV:
-        mov   eax, ecx
-        mov   ecx, edx
-        cdq
-        idiv  ecx
-        ret
-    """
-
-  proc modInt(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-    asm """
-        test  edx, edx
-        jne   L_NOT_ZERO
-        call  `raiseDivByZero`
-      L_NOT_ZERO:
-        cmp   ecx, 0x80000000
-        jne   L_DO_DIV
-        cmp   edx, -1
-        jne   L_DO_DIV
-        call  `raiseOverflow`
-      L_DO_DIV:
-        mov   eax, ecx
-        mov   ecx, edx
-        cdq
-        idiv  ecx
-        mov   eax, edx
-        ret
-    """
-
-  proc mulInt(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-    asm """
-        mov eax, ecx
-        mov ecx, edx
-        xor edx, edx
-        imul ecx
-        jno theEnd
-        call `raiseOverflow`
-      theEnd:
-        ret
-    """
-
-elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)):
-  proc addInt(a, b: int): int {.compilerproc, inline.} =
-    # don't use a pure proc here!
-    asm """
-      "addl %%ecx, %%eax\n"
-      "jno 1\n"
-      "call _raiseOverflow\n"
-      "1: \n"
-      :"=a"(`result`)
-      :"a"(`a`), "c"(`b`)
-    """
-    #".intel_syntax noprefix"
-    #/* Intel syntax here */
-    #".att_syntax"
-
-  proc subInt(a, b: int): int {.compilerproc, inline.} =
-    asm """ "subl %%ecx,%%eax\n"
-            "jno 1\n"
-            "call _raiseOverflow\n"
-            "1: \n"
-           :"=a"(`result`)
-           :"a"(`a`), "c"(`b`)
-    """
-
-  proc mulInt(a, b: int): int {.compilerproc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "imull %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-  proc negInt(a: int): int {.compilerproc, inline.} =
-    asm """ "negl %%eax\n"
-            "jno 1\n"
-            "call _raiseOverflow\n"
-            "1: \n"
-           :"=a"(`result`)
-           :"a"(`a`)
-    """
-
-  proc divInt(a, b: int): int {.compilerproc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "idivl %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-  proc modInt(a, b: int): int {.compilerproc, inline.} =
-    asm """  "xorl %%edx, %%edx\n"
-             "idivl %%ecx\n"
-             "jno 1\n"
-             "call _raiseOverflow\n"
-             "1: \n"
-             "movl %%edx, %%eax"
-            :"=a"(`result`)
-            :"a"(`a`), "c"(`b`)
-            :"%edx"
-    """
-
-when not declared(addInt) and defined(builtinOverflow):
-  proc addInt(a, b: int): int {.compilerproc, inline.} =
-    if addIntOverflow(a, b, result):
-      raiseOverflow()
-
-when not declared(subInt) and defined(builtinOverflow):
-  proc subInt(a, b: int): int {.compilerproc, inline.} =
-    if subIntOverflow(a, b, result):
-      raiseOverflow()
-
-when not declared(mulInt) and defined(builtinOverflow):
-  proc mulInt(a, b: int): int {.compilerproc, inline.} =
-    if mulIntOverflow(a, b, result):
-      raiseOverflow()
-
-# Platform independent versions of the above (slower!)
-when not declared(addInt):
-  proc addInt(a, b: int): int {.compilerproc, inline.} =
-    result = a +% b
-    if (result xor a) >= 0 or (result xor b) >= 0:
-      return result
-    raiseOverflow()
-
-when not declared(subInt):
-  proc subInt(a, b: int): int {.compilerproc, inline.} =
-    result = a -% b
-    if (result xor a) >= 0 or (result xor not b) >= 0:
-      return result
-    raiseOverflow()
-
-when not declared(negInt):
-  proc negInt(a: int): int {.compilerproc, inline.} =
-    if a != low(int): return -a
-    raiseOverflow()
-
-when not declared(divInt):
-  proc divInt(a, b: int): int {.compilerproc, inline.} =
-    if b == 0:
-      raiseDivByZero()
-    if a == low(int) and b == -1:
-      raiseOverflow()
-    return a div b
-
-when not declared(modInt):
-  proc modInt(a, b: int): int {.compilerproc, inline.} =
-    if b == 0:
-      raiseDivByZero()
-    return a mod b
-
-when not declared(mulInt):
-  #
-  # This code has been inspired by Python's source code.
-  # The native int product x*y is either exactly right or *way* off, being
-  # just the last n bits of the true product, where n is the number of bits
-  # in an int (the delivered product is the true product plus i*2**n for
-  # some integer i).
-  #
-  # The native float64 product x*y is subject to three
-  # rounding errors: on a sizeof(int)==8 box, each cast to double can lose
-  # info, and even on a sizeof(int)==4 box, the multiplication can lose info.
-  # But, unlike the native int product, it's not in *range* trouble:  even
-  # if sizeof(int)==32 (256-bit ints), the product easily fits in the
-  # dynamic range of a float64. So the leading 50 (or so) bits of the float64
-  # product are correct.
-  #
-  # We check these two ways against each other, and declare victory if
-  # they're approximately the same. Else, because the native int product is
-  # the only one that can lose catastrophic amounts of information, it's the
-  # native int product that must have overflowed.
-  #
-  proc mulInt(a, b: int): int {.compilerproc.} =
-    var
-      resAsFloat, floatProd: float
-
-    result = a *% b
-    floatProd = toFloat(a) * toFloat(b)
-    resAsFloat = toFloat(result)
-
-    # Fast path for normal case: small multiplicands, and no info
-    # is lost in either method.
-    if resAsFloat == floatProd: return result
-
-    # 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):
-      return result
-    raiseOverflow()
-
-# We avoid setting the FPU control word here for compatibility with libraries
-# written in other languages.
-
-proc raiseFloatInvalidOp {.compilerproc, noinline.} =
-  sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result")
-
-proc nanCheck(x: float64) {.compilerproc, inline.} =
-  if x != x: raiseFloatInvalidOp()
-
-proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} =
-  if x > 0.0:
-    sysFatal(FloatOverflowDefect, "FPU operation caused an overflow")
-  else:
-    sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow")
-
-proc infCheck(x: float64) {.compilerproc, inline.} =
-  if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x)
diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim
index 0dd329495..e229a0f4b 100644
--- a/lib/system/arithmetics.nim
+++ b/lib/system/arithmetics.nim
@@ -1,4 +1,4 @@
-proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.} =
+proc succ*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Succ", noSideEffect.} =
   ## Returns the `y`-th successor (default: 1) of the value `x`.
   ##
   ## If such a value does not exist, `OverflowDefect` is raised
@@ -7,7 +7,7 @@ proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.} =
     assert succ(5) == 6
     assert succ(5, 3) == 8
 
-proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.} =
+proc pred*[T, V: Ordinal](x: T, y: V = 1): T {.magic: "Pred", noSideEffect.} =
   ## Returns the `y`-th predecessor (default: 1) of the value `x`.
   ##
   ## If such a value does not exist, `OverflowDefect` is raised
@@ -16,7 +16,7 @@ proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.} =
     assert pred(5) == 4
     assert pred(5, 3) == 2
 
-proc inc*[T: Ordinal](x: var T, y = 1) {.magic: "Inc", noSideEffect.} =
+proc inc*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Inc", noSideEffect.} =
   ## Increments the ordinal `x` by `y`.
   ##
   ## If such a value does not exist, `OverflowDefect` is raised or a compile
@@ -28,7 +28,7 @@ proc inc*[T: Ordinal](x: var T, y = 1) {.magic: "Inc", noSideEffect.} =
     inc(i, 3)
     assert i == 6
 
-proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} =
+proc dec*[T, V: Ordinal](x: var T, y: V = 1) {.magic: "Dec", noSideEffect.} =
   ## Decrements the ordinal `x` by `y`.
   ##
   ## If such a value does not exist, `OverflowDefect` is raised or a compile
@@ -45,109 +45,6 @@ proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} =
 # --------------------------------------------------------------------------
 # built-in operators
 
-when defined(nimNoZeroExtendMagic):
-  proc ze*(x: int8): int {.deprecated.} =
-    ## zero extends a smaller integer type to `int`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int](uint(cast[uint8](x)))
-
-  proc ze*(x: int16): int {.deprecated.} =
-    ## zero extends a smaller integer type to `int`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int](uint(cast[uint16](x)))
-
-  proc ze64*(x: int8): int64 {.deprecated.} =
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int64](uint64(cast[uint8](x)))
-
-  proc ze64*(x: int16): int64 {.deprecated.} =
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int64](uint64(cast[uint16](x)))
-
-  proc ze64*(x: int32): int64 {.deprecated.} =
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int64](uint64(cast[uint32](x)))
-
-  proc ze64*(x: int): int64 {.deprecated.} =
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned. Does nothing if the size of an `int` is the same as `int64`.
-    ## (This is the case on 64 bit processors.)
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int64](uint64(cast[uint](x)))
-
-  proc toU8*(x: int): int8 {.deprecated.} =
-    ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits
-    ## from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int8](x)
-
-  proc toU16*(x: int): int16 {.deprecated.} =
-    ## treats `x` as unsigned and converts it to an `int16` by taking the last
-    ## 16 bits from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int16](x)
-
-  proc toU32*(x: int64): int32 {.deprecated.} =
-    ## treats `x` as unsigned and converts it to an `int32` by taking the
-    ## last 32 bits from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-    cast[int32](x)
-
-elif not defined(js):
-  proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect, deprecated.}
-    ## zero extends a smaller integer type to `int64`. This treats `x` as
-    ## unsigned. Does nothing if the size of an `int` is the same as `int64`.
-    ## (This is the case on 64 bit processors.)
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect, deprecated.}
-    ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits
-    ## from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect, deprecated.}
-    ## treats `x` as unsigned and converts it to an `int16` by taking the last
-    ## 16 bits from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
-  proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect, deprecated.}
-    ## treats `x` as unsigned and converts it to an `int32` by taking the
-    ## last 32 bits from `x`.
-    ## **Deprecated since version 0.19.9**: Use unsigned integers instead.
-
 # integer calculations:
 proc `+`*(x: int): int {.magic: "UnaryPlusI", noSideEffect.}
   ## Unary `+` operator for an integer. Has no effect.
@@ -196,7 +93,7 @@ proc `*`*(x, y: int16): int16 {.magic: "MulI", noSideEffect.}
 proc `*`*(x, y: int32): int32 {.magic: "MulI", noSideEffect.}
 proc `*`*(x, y: int64): int64 {.magic: "MulI", noSideEffect.}
 
-proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} = 
+proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} =
   ## Computes the integer division.
   ##
   ## This is roughly the same as `math.trunc(x/y).int`.
@@ -399,6 +296,59 @@ proc `mod`*(x, y: uint16): uint16 {.magic: "ModU", noSideEffect.}
 proc `mod`*(x, y: uint32): uint32 {.magic: "ModU", noSideEffect.}
 proc `mod`*(x, y: uint64): uint64 {.magic: "ModU", noSideEffect.}
 
+proc `+=`*[T: SomeInteger](x: var T, y: T) {.
+  magic: "Inc", noSideEffect.}
+  ## Increments an integer.
+
+proc `-=`*[T: SomeInteger](x: var T, y: T) {.
+  magic: "Dec", noSideEffect.}
+  ## Decrements an integer.
+
+proc `*=`*[T: SomeInteger](x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Binary `*=` operator for integers.
+  x = x * y
+
+# floating point operations:
+proc `+`*(x: float32): float32 {.magic: "UnaryPlusF64", noSideEffect.}
+proc `-`*(x: float32): float32 {.magic: "UnaryMinusF64", noSideEffect.}
+proc `+`*(x, y: float32): float32 {.magic: "AddF64", noSideEffect.}
+proc `-`*(x, y: float32): float32 {.magic: "SubF64", noSideEffect.}
+proc `*`*(x, y: float32): float32 {.magic: "MulF64", noSideEffect.}
+proc `/`*(x, y: float32): float32 {.magic: "DivF64", noSideEffect.}
+
+proc `+`*(x: float): float {.magic: "UnaryPlusF64", noSideEffect.}
+proc `-`*(x: float): float {.magic: "UnaryMinusF64", noSideEffect.}
+proc `+`*(x, y: float): float {.magic: "AddF64", noSideEffect.}
+proc `-`*(x, y: float): float {.magic: "SubF64", noSideEffect.}
+proc `*`*(x, y: float): float {.magic: "MulF64", noSideEffect.}
+proc `/`*(x, y: float): float {.magic: "DivF64", noSideEffect.}
+
+proc `+=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Increments in place a floating point number.
+  x = x + y
+
+proc `-=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Decrements in place a floating point number.
+  x = x - y
+
+proc `*=`*[T: float|float32|float64] (x: var T, y: T) {.
+  inline, noSideEffect.} =
+  ## Multiplies in place a floating point number.
+  x = x * y
+
+proc `/=`*(x: var float64, y: float64) {.inline, noSideEffect.} =
+  ## Divides in place a floating point number.
+  x = x / y
+
+proc `/=`*[T: float|float32](x: var T, y: T) {.inline, noSideEffect.} =
+  ## Divides in place a floating point number.
+  x = x / y
+
+# the following have to be included in system, not imported for some reason:
+
 proc `+%`*(x, y: int): int {.inline.} =
   ## Treats `x` and `y` as unsigned and adds them.
   ##
@@ -453,16 +403,3 @@ proc `%%`*(x, y: int8): int8 {.inline.}   = cast[int8](cast[uint8](x) mod cast[u
 proc `%%`*(x, y: int16): int16 {.inline.} = cast[int16](cast[uint16](x) mod cast[uint16](y))
 proc `%%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) mod cast[uint32](y))
 proc `%%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) mod cast[uint64](y))
-
-proc `+=`*[T: SomeInteger](x: var T, y: T) {.
-  magic: "Inc", noSideEffect.}
-  ## Increments an integer.
-
-proc `-=`*[T: SomeInteger](x: var T, y: T) {.
-  magic: "Dec", noSideEffect.}
-  ## Decrements an integer.
-
-proc `*=`*[T: SomeInteger](x: var T, y: T) {.
-  inline, noSideEffect.} =
-  ## Binary `*=` operator for integers.
-  x = x * y
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index 20b854107..9f4cbc0fe 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -15,8 +15,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign.
 proc genericAssignAux(dest, src: pointer, n: ptr TNimNode,
                       shallow: bool) {.benign.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   case n.kind
   of nkSlot:
     genericAssignAux(cast[pointer](d +% n.offset),
@@ -56,8 +56,8 @@ template deepSeqAssignImpl(operation, additionalArg) {.dirty.} =
 
 proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   sysAssert(mt != nil, "genericAssignAux 2")
   case mt.kind
   of tyString:
@@ -89,17 +89,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) =
         var ss = nimNewSeqOfCap(mt, seq.len)
         cast[PGenericSeq](ss).len = seq.len
         unsureAsgnRef(x, ss)
-        var dst = cast[ByteAddress](cast[PPointer](dest)[])
+        var dst = cast[int](cast[PPointer](dest)[])
         copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)),
-                cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)),
+                cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align)),
                 seq.len *% mt.base.size)
       else:
         unsureAsgnRef(x, newSeq(mt, seq.len))
-        var dst = cast[ByteAddress](cast[PPointer](dest)[])
+        var dst = cast[int](cast[PPointer](dest)[])
         for i in 0..seq.len-1:
           genericAssignAux(
             cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
-            cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
+            cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ),
             mt.base, shallow)
   of tyObject:
     var it = mt.base
@@ -181,15 +181,15 @@ proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} =
 proc genericAssignOpenArray(dest, src: pointer, len: int,
                             mt: PNimType) {.compilerproc.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   for i in 0..len-1:
     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.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkNone: sysAssert(false, "objectInitAux")
   of nkSlot: objectInit(cast[pointer](d +% n.offset), n.typ)
@@ -203,7 +203,7 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} =
 proc objectInit(dest: pointer, typ: PNimType) =
   # the generic init proc that takes care of initialization of complex
   # objects on the stack or heap
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case typ.kind
   of tyObject:
     # iterate over any structural type
@@ -226,7 +226,7 @@ proc objectInit(dest: pointer, typ: PNimType) =
 
 proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, benign.}
 proc genericResetAux(dest: pointer, n: ptr TNimNode) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkNone: sysAssert(false, "genericResetAux")
   of nkSlot: genericReset(cast[pointer](d +% n.offset), n.typ)
@@ -238,7 +238,7 @@ proc genericResetAux(dest: pointer, n: ptr TNimNode) =
     zeroMem(cast[pointer](d +% n.offset), n.typ.size)
 
 proc genericReset(dest: pointer, mt: PNimType) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   sysAssert(mt != nil, "genericReset 2")
   case mt.kind
   of tyRef:
@@ -275,10 +275,12 @@ proc genericReset(dest: pointer, mt: PNimType) =
 
 proc selectBranch(discVal, L: int,
                   a: ptr array[0x7fff, ptr TNimNode]): ptr TNimNode =
-  result = a[L] # a[L] contains the ``else`` part (but may be nil)
   if discVal <% L:
-    let x = a[discVal]
-    if x != nil: result = x
+    result = a[discVal]
+    if result == nil:
+      result = a[L]
+  else:
+    result = a[L] # a[L] contains the ``else`` part (but may be nil)
 
 proc FieldDiscriminantCheck(oldDiscVal, newDiscVal: int,
                             a: ptr array[0x7fff, ptr TNimNode],
diff --git a/lib/system/basic_types.nim b/lib/system/basic_types.nim
index 7779e1ce9..bf81b9b6a 100644
--- a/lib/system/basic_types.nim
+++ b/lib/system/basic_types.nim
@@ -1,15 +1,45 @@
 type
-  int* {.magic: "Int".}         ## Default integer type; bitwidth depends on
+  int* {.magic: Int.}         ## Default integer type; bitwidth depends on
                               ## architecture, but is always the same as a pointer.
-  int8* {.magic: "Int8".}       ## Signed 8 bit integer type.
-  int16* {.magic: "Int16".}     ## Signed 16 bit integer type.
-  int32* {.magic: "Int32".}     ## Signed 32 bit integer type.
-  int64* {.magic: "Int64".}     ## Signed 64 bit integer type.
-  uint* {.magic: "UInt".}       ## Unsigned default integer type.
-  uint8* {.magic: "UInt8".}     ## Unsigned 8 bit integer type.
-  uint16* {.magic: "UInt16".}   ## Unsigned 16 bit integer type.
-  uint32* {.magic: "UInt32".}   ## Unsigned 32 bit integer type.
-  uint64* {.magic: "UInt64".}   ## Unsigned 64 bit integer type.
+  int8* {.magic: Int8.}       ## Signed 8 bit integer type.
+  int16* {.magic: Int16.}     ## Signed 16 bit integer type.
+  int32* {.magic: Int32.}     ## Signed 32 bit integer type.
+  int64* {.magic: Int64.}     ## Signed 64 bit integer type.
+  uint* {.magic: UInt.}       ## Unsigned default integer type.
+  uint8* {.magic: UInt8.}     ## Unsigned 8 bit integer type.
+  uint16* {.magic: UInt16.}   ## Unsigned 16 bit integer type.
+  uint32* {.magic: UInt32.}   ## Unsigned 32 bit integer type.
+  uint64* {.magic: UInt64.}   ## Unsigned 64 bit integer type.
+
+type
+  float* {.magic: Float.}     ## Default floating point type.
+  float32* {.magic: Float32.} ## 32 bit floating point type.
+  float64* {.magic: Float.}   ## 64 bit floating point type.
+
+# 'float64' is now an alias to 'float'; this solves many problems
+
+type
+  char* {.magic: Char.}         ## Built-in 8 bit character type (unsigned).
+  string* {.magic: String.}     ## Built-in string type.
+  cstring* {.magic: Cstring.}   ## Built-in cstring (*compatible string*) type.
+  pointer* {.magic: Pointer.}   ## Built-in pointer type, use the `addr`
+                                ## operator to get a pointer to a variable.
+
+  typedesc* {.magic: TypeDesc.} ## Meta type to denote a type description.
+
+type
+  `ptr`*[T] {.magic: Pointer.}   ## Built-in generic untraced pointer type.
+  `ref`*[T] {.magic: Pointer.}   ## Built-in generic traced pointer type.
+
+  `nil` {.magic: "Nil".}
+
+  void* {.magic: "VoidType".}    ## Meta type to denote the absence of any type.
+  auto* {.magic: Expr.}          ## Meta type for automatic type determination.
+  any* {.deprecated: "Deprecated since v1.5; Use auto instead.".} = distinct auto  ## Deprecated; Use `auto` instead. See https://github.com/nim-lang/RFCs/issues/281
+  untyped* {.magic: Expr.}       ## Meta type to denote an expression that
+                                 ## is not resolved (for templates).
+  typed* {.magic: Stmt.}         ## Meta type to denote an expression that
+                                 ## is resolved (for templates).
 
 type # we need to start a new type section here, so that ``0`` can have a type
   bool* {.magic: "Bool".} = enum ## Built-in boolean type.
@@ -29,15 +59,16 @@ type
   SomeInteger* = SomeSignedInt|SomeUnsignedInt
     ## Type class matching all integer types.
 
+  SomeFloat* = float|float32|float64
+    ## Type class matching all floating point number types.
+
+  SomeNumber* = SomeInteger|SomeFloat
+    ## Type class matching all number types.
+
   SomeOrdinal* = int|int8|int16|int32|int64|bool|enum|uint|uint8|uint16|uint32|uint64
     ## Type class matching all ordinal types; however this includes enums with
     ## holes. See also `Ordinal`
 
-  BiggestInt* = int64
-    ## is an alias for the biggest signed integer type the Nim compiler
-    ## supports. Currently this is `int64`, but it is platform-dependent
-    ## in general.
-
 
 {.push warning[GcMem]: off, warning[Uninit]: off.}
 {.push hints: off.}
diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim
index 9cb57bc45..0663247c2 100644
--- a/lib/system/bitmasks.nim
+++ b/lib/system/bitmasks.nim
@@ -10,7 +10,7 @@
 # Page size of the system; in most cases 4096 bytes. For exotic OS or
 # CPU this needs to be changed:
 const
-  PageShift = when defined(nimPage256) or defined(cpu16): 8
+  PageShift = when defined(nimPage256) or defined(cpu16): 3
               elif defined(nimPage512): 9
               elif defined(nimPage1k): 10
               else: 12 # \ # my tests showed no improvements for using larger page sizes.
diff --git a/lib/system/cellseqs_v1.nim b/lib/system/cellseqs_v1.nim
index 1952491b3..1a305aa42 100644
--- a/lib/system/cellseqs_v1.nim
+++ b/lib/system/cellseqs_v1.nim
@@ -16,18 +16,21 @@ type
     d: PCellArray
 
 proc contains(s: CellSeq, c: PCell): bool {.inline.} =
-  for i in 0 .. s.len-1:
-    if s.d[i] == c: return true
+  for i in 0 ..< s.len:
+    if s.d[i] == c:
+      return true
   return false
 
+proc resize(s: var CellSeq) =
+  s.cap = s.cap * 3 div 2
+  let d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
+  copyMem(d, s.d, s.len * sizeof(PCell))
+  dealloc(s.d)
+  s.d = d
+
 proc add(s: var CellSeq, c: PCell) {.inline.} =
   if s.len >= s.cap:
-    s.cap = s.cap * 3 div 2
-    var d = cast[PCellArray](alloc(s.cap * sizeof(PCell)))
-    copyMem(d, s.d, s.len * sizeof(PCell))
-    dealloc(s.d)
-    s.d = d
-    # XXX: realloc?
+    resize(s)
   s.d[s.len] = c
   inc(s.len)
 
diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim
index 27be48d78..c6c7b1a8e 100644
--- a/lib/system/cellseqs_v2.nim
+++ b/lib/system/cellseqs_v2.nim
@@ -16,20 +16,17 @@ type
     len, cap: int
     d: CellArray[T]
 
-proc add[T](s: var CellSeq[T], c: T; t: PNimTypeV2) {.inline.} =
+proc resize[T](s: var CellSeq[T]) =
+  s.cap = s.cap * 3 div 2
+  var newSize = s.cap * sizeof(CellTuple[T])
+  when compileOption("threads"):
+    s.d = cast[CellArray[T]](reallocShared(s.d, newSize))
+  else:
+    s.d = cast[CellArray[T]](realloc(s.d, newSize))
+
+proc add[T](s: var CellSeq[T], c: T, t: PNimTypeV2) {.inline.} =
   if s.len >= s.cap:
-    s.cap = s.cap * 3 div 2
-    when compileOption("threads"):
-      var d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T]))))
-    else:
-      var d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T])))
-    copyMem(d, s.d, s.len * sizeof(CellTuple[T]))
-    when compileOption("threads"):
-      deallocShared(s.d)
-    else:
-      dealloc(s.d)
-    s.d = d
-    # XXX: realloc?
+    s.resize()
   s.d[s.len] = (c, t)
   inc(s.len)
 
diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim
index a0f1fabf9..92036c226 100644
--- a/lib/system/cellsets.nim
+++ b/lib/system/cellsets.nim
@@ -42,7 +42,7 @@ Complete traversal is done in this way::
 
 ]#
 
-when defined(gcOrc) or defined(gcArc):
+when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
   type
     PCell = Cell
 
@@ -78,7 +78,7 @@ type
     head: PPageDesc
     data: PPageDescArray
 
-when defined(gcOrc) or defined(gcArc):
+when defined(gcOrc) or defined(gcArc) or defined(gcAtomicArc):
   discard
 else:
   include cellseqs_v1
diff --git a/lib/system/cgprocs.nim b/lib/system/cgprocs.nim
index 9d0d248c3..9a7645f9b 100644
--- a/lib/system/cgprocs.nim
+++ b/lib/system/cgprocs.nim
@@ -8,13 +8,3 @@
 #
 
 # Headers for procs that the code generator depends on ("compilerprocs")
-
-type
-  LibHandle = pointer       # private type
-  ProcAddr = pointer        # library loading and loading of procs:
-
-proc nimLoadLibrary(path: string): LibHandle {.compilerproc, hcrInline, nonReloadable.}
-proc nimUnloadLibrary(lib: LibHandle) {.compilerproc, hcrInline, nonReloadable.}
-proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {.compilerproc, hcrInline, nonReloadable.}
-
-proc nimLoadLibraryError(path: string) {.compilerproc, hcrInline, nonReloadable.}
diff --git a/lib/system/channels_builtin.nim b/lib/system/channels_builtin.nim
index 30267e375..02b4d8cbf 100644
--- a/lib/system/channels_builtin.nim
+++ b/lib/system/channels_builtin.nim
@@ -26,7 +26,7 @@
 ## The following is a simple example of two different ways to use channels:
 ## blocking and non-blocking.
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   # Be sure to compile with --threads:on.
 ##   # The channels and threads modules are part of system and should not be
 ##   # imported.
@@ -87,18 +87,20 @@
 ##
 ##   # Clean up the channel.
 ##   chan.close()
+##   ```
 ##
 ## Sample output
 ## -------------
 ## The program should output something similar to this, but keep in mind that
-## exact results may vary in the real world::
-##   Hello World!
-##   Pretend I'm doing useful work...
-##   Pretend I'm doing useful work...
-##   Pretend I'm doing useful work...
-##   Pretend I'm doing useful work...
-##   Pretend I'm doing useful work...
-##   Another message
+## exact results may vary in the real world:
+##
+##     Hello World!
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Pretend I'm doing useful work...
+##     Another message
 ##
 ## Passing Channels Safely
 ## -----------------------
@@ -112,7 +114,7 @@
 ## using e.g. `system.allocShared0` and pass these pointers through thread
 ## arguments:
 ##
-## .. code-block:: Nim
+##   ```Nim
 ##   proc worker(channel: ptr Channel[string]) =
 ##     let greeting = channel[].recv()
 ##     echo greeting
@@ -134,10 +136,13 @@
 ##     deallocShared(channel)
 ##
 ##   localChannelExample() # "Hello from the main thread!"
+##   ```
 
 when not declared(ThisIsSystem):
   {.error: "You must not import this module explicitly".}
 
+import std/private/syslocks
+
 type
   pbytes = ptr UncheckedArray[byte]
   RawChannel {.pure, final.} = object ## msg queue for a thread
@@ -182,8 +187,8 @@ when not usesDestructors:
   proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel,
                 mode: LoadStoreMode) {.benign.} =
     var
-      d = cast[ByteAddress](dest)
-      s = cast[ByteAddress](src)
+      d = cast[int](dest)
+      s = cast[int](src)
     case n.kind
     of nkSlot: storeAux(cast[pointer](d +% n.offset),
                         cast[pointer](s +% n.offset), n.typ, t, mode)
@@ -202,8 +207,8 @@ when not usesDestructors:
       cast[pointer](cast[int](p) +% x)
 
     var
-      d = cast[ByteAddress](dest)
-      s = cast[ByteAddress](src)
+      d = cast[int](dest)
+      s = cast[int](src)
     sysAssert(mt != nil, "mt == nil")
     case mt.kind
     of tyString:
@@ -242,14 +247,14 @@ when not usesDestructors:
           x[] = alloc0(t.region, align(GenericSeqSize, mt.base.align) +% seq.len *% mt.base.size)
         else:
           unsureAsgnRef(x, newSeq(mt, seq.len))
-        var dst = cast[ByteAddress](cast[PPointer](dest)[])
+        var dst = cast[int](cast[PPointer](dest)[])
         var dstseq = cast[PGenericSeq](dst)
         dstseq.len = seq.len
         dstseq.reserved = seq.len
         for i in 0..seq.len-1:
           storeAux(
             cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
-            cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +%
+            cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +%
                           i *% mt.base.size),
             mt.base, t, mode)
         if mode != mStore: dealloc(t.region, s2)
@@ -360,8 +365,8 @@ proc sendImpl(q: PRawChannel, typ: PNimType, msg: pointer, noBlock: bool): bool
 
   rawSend(q, msg, typ)
   q.elemType = typ
-  releaseSys(q.lock)
   signalSysCond(q.cond)
+  releaseSys(q.lock)
   result = true
 
 proc send*[TMsg](c: var Channel[TMsg], msg: sink TMsg) {.inline.} =
@@ -389,7 +394,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
   q.ready = false
   if typ != q.elemType:
     releaseSys(q.lock)
-    sysFatal(ValueError, "cannot receive message of wrong type")
+    raise newException(ValueError, "cannot receive message of wrong type")
   rawRecv(q, res, typ)
   if q.maxItems > 0 and q.count == q.maxItems - 1:
     # Parent thread is awaiting in send. Wake it up.
diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim
index bdf2903d2..b48855964 100644
--- a/lib/system/chcks.nim
+++ b/lib/system/chcks.nim
@@ -37,11 +37,15 @@ proc raiseFieldError(f: string) {.compilerproc, noinline.} =
 when defined(nimV2):
   proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} =
     ## raised when field is inaccessible given runtime value of discriminant
-    sysFatal(FieldError, f & $discVal & "'")
+    sysFatal(FieldDefect, f & $discVal & "'")
+
+  proc raiseFieldErrorStr(f: string, discVal: string) {.compilerproc, noinline.} =
+    ## raised when field is inaccessible given runtime value of discriminant
+    sysFatal(FieldDefect, formatFieldDefect(f, discVal))
 else:
   proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noinline.} =
     ## raised when field is inaccessible given runtime value of discriminant
-    sysFatal(FieldError, formatFieldDefect(f, discVal))
+    sysFatal(FieldDefect, formatFieldDefect(f, discVal))
 
 proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} =
   when defined(standalone):
diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim
index daa47fa59..a8d78bb93 100644
--- a/lib/system/comparisons.nim
+++ b/lib/system/comparisons.nim
@@ -35,7 +35,7 @@ proc `==`*[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.}
   ## Checks that two `ref` variables refer to the same item.
 proc `==`*[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.}
   ## Checks that two `ptr` variables refer to the same item.
-proc `==`*[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.}
+proc `==`*[T: proc | iterator](x, y: T): bool {.magic: "EqProc", noSideEffect.}
   ## Checks that two `proc` variables refer to the same procedure.
 
 proc `<=`*[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.}
@@ -125,15 +125,18 @@ proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.}
 proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.}
 proc `<`*(x, y: pointer): bool {.magic: "LtPtr", noSideEffect.}
 
-template `!=`*(x, y: untyped): untyped =
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template `!=`*(x, y: untyped): untyped {.callsite.} =
   ## Unequals operator. This is a shorthand for `not (x == y)`.
   not (x == y)
 
-template `>=`*(x, y: untyped): untyped =
+template `>=`*(x, y: untyped): untyped {.callsite.} =
   ## "is greater or equals" operator. This is the same as `y <= x`.
   y <= x
 
-template `>`*(x, y: untyped): untyped =
+template `>`*(x, y: untyped): untyped {.callsite.} =
   ## "is greater" operator. This is the same as `y < x`.
   y < x
 
@@ -206,6 +209,14 @@ proc `==`*(x, y: uint16): bool {.magic: "EqI", noSideEffect.}
 proc `==`*(x, y: uint32): bool {.magic: "EqI", noSideEffect.}
 proc `==`*(x, y: uint64): bool {.magic: "EqI", noSideEffect.}
 
+proc `<=`*(x, y: float32): bool {.magic: "LeF64", noSideEffect.}
+proc `<=`*(x, y: float): bool {.magic: "LeF64", noSideEffect.}
+
+proc `<`*(x, y: float32): bool {.magic: "LtF64", noSideEffect.}
+proc `<`*(x, y: float): bool {.magic: "LtF64", noSideEffect.}
+
+proc `==`*(x, y: float32): bool {.magic: "EqF64", noSideEffect.}
+proc `==`*(x, y: float): bool {.magic: "EqF64", noSideEffect.}
 
 {.push stackTrace: off.}
 
@@ -220,6 +231,13 @@ proc min*(x, y: int32): int32 {.magic: "MinI", noSideEffect.} =
 proc min*(x, y: int64): int64 {.magic: "MinI", noSideEffect.} =
   ## The minimum value of two integers.
   if x <= y: x else: y
+proc min*(x, y: float32): float32 {.noSideEffect, inline.} =
+  if x <= y or y != y: x else: y
+proc min*(x, y: float64): float64 {.noSideEffect, inline.} =
+  if x <= y or y != y: x else: y
+proc min*[T: not SomeFloat](x, y: T): T {.inline.} =
+  ## Generic minimum operator of 2 values based on `<=`.
+  if x <= y: x else: y
 
 proc max*(x, y: int): int {.magic: "MaxI", noSideEffect.} =
   if y <= x: x else: y
@@ -232,6 +250,13 @@ proc max*(x, y: int32): int32 {.magic: "MaxI", noSideEffect.} =
 proc max*(x, y: int64): int64 {.magic: "MaxI", noSideEffect.} =
   ## The maximum value of two integers.
   if y <= x: x else: y
+proc max*(x, y: float32): float32 {.noSideEffect, inline.} =
+  if y <= x or y != y: x else: y
+proc max*(x, y: float64): float64 {.noSideEffect, inline.} =
+  if y <= x or y != y: x else: y
+proc max*[T: not SomeFloat](x, y: T): T {.inline.} =
+  ## Generic maximum operator of 2 values based on `<=`.
+  if y <= x: x else: y
 
 
 proc min*[T](x: openArray[T]): T =
@@ -299,7 +324,7 @@ proc `==`*[T](x, y: seq[T]): bool {.noSideEffect.} =
         return true
     else:
       var sameObject = false
-      asm """`sameObject` = `x` === `y`"""
+      {.emit: """`sameObject` = `x` === `y`;""".}
       if sameObject: return true
 
   if x.len != y.len:
diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim
new file mode 100644
index 000000000..cdb976ed5
--- /dev/null
+++ b/lib/system/compilation.nim
@@ -0,0 +1,209 @@
+const
+  NimMajor* {.intdefine.}: int = 2
+    ## is the major number of Nim's version. Example:
+    ##   ```nim
+    ##   when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard
+    ##   ```
+    # see also std/private/since
+
+  NimMinor* {.intdefine.}: int = 2
+    ## is the minor number of Nim's version.
+    ## Odd for devel, even for releases.
+
+  NimPatch* {.intdefine.}: int = 1
+    ## is the patch number of Nim's version.
+    ## Odd for devel, even for releases.
+
+{.push profiler: off.}
+let nimvm* {.magic: "Nimvm", compileTime.}: bool = false
+  ## May be used only in `when` expression.
+  ## It is true in Nim VM context and false otherwise.
+{.pop.}
+
+const
+  isMainModule* {.magic: "IsMainModule".}: bool = false
+    ## True only when accessed in the main module. This works thanks to
+    ## compiler magic. It is useful to embed testing code in a module.
+
+  CompileDate* {.magic: "CompileDate".}: string = "0000-00-00"
+    ## The date (in UTC) of compilation as a string of the form
+    ## `YYYY-MM-DD`. This works thanks to compiler magic.
+
+  CompileTime* {.magic: "CompileTime".}: string = "00:00:00"
+    ## The time (in UTC) of compilation as a string of the form
+    ## `HH:MM:SS`. This works thanks to compiler magic.
+
+proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## defined.
+  ##
+  ## `x` is an external symbol introduced through the compiler's
+  ## `-d:x switch <nimc.html#compiler-usage-compileminustime-symbols>`_ to enable
+  ## build time conditionals:
+  ##   ```nim
+  ##   when not defined(release):
+  ##     # Do here programmer friendly expensive sanity checks.
+  ##   # Put here the normal code
+  ##   ```
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string>`_ for `on|off` options
+  ## * `compileOption <#compileOption,string,string>`_ for enum options
+  ## * `define pragmas <manual.html#implementation-specific-pragmas-compileminustime-define-pragmas>`_
+
+proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## declared. `x` has to be an identifier or a qualified identifier.
+  ##
+  ## This can be used to check whether a library provides a certain
+  ## feature or not:
+  ##   ```nim
+  ##   when not declared(strutils.toUpper):
+  ##     # provide our own toUpper proc here, because strutils is
+  ##     # missing it.
+  ##   ```
+  ##
+  ## See also:
+  ## * `declaredInScope <#declaredInScope,untyped>`_
+
+proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.}
+  ## Special compile-time procedure that checks whether `x` is
+  ## declared in the current scope. `x` has to be an identifier.
+
+proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} =
+  ## Special compile-time procedure that checks whether `x` can be compiled
+  ## without any semantic error.
+  ## This can be used to check whether a type supports some operation:
+  ##   ```nim
+  ##   when compiles(3 + 4):
+  ##     echo "'+' for integers is available"
+  ##   ```
+  discard
+
+proc astToStr*[T](x: T): string {.magic: "AstToStr", noSideEffect.}
+  ## Converts the AST of `x` into a string representation. This is very useful
+  ## for debugging.
+
+proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} =
+  ## A section you should use to mark `runnable example`:idx: code with.
+  ##
+  ## - In normal debug and release builds code within
+  ##   a `runnableExamples` section is ignored.
+  ## - The documentation generator is aware of these examples and considers them
+  ##   part of the `##` doc comment. As the last step of documentation
+  ##   generation each runnableExample is put in its own file `$file_examples$i.nim`,
+  ##   compiled and tested. The collected examples are
+  ##   put into their own module to ensure the examples do not refer to
+  ##   non-exported symbols.
+  runnableExamples:
+    proc timesTwo*(x: int): int =
+      ## This proc doubles a number.
+      runnableExamples:
+        # at module scope
+        const exported* = 123
+        assert timesTwo(5) == 10
+        block: # at block scope
+          defer: echo "done"
+      runnableExamples "-d:foo -b:cpp":
+        import std/compilesettings
+        assert querySetting(backend) == "cpp"
+        assert defined(foo)
+      runnableExamples "-r:off": ## this one is only compiled
+         import std/browsers
+         openDefaultBrowser "https://forum.nim-lang.org/"
+      2 * x
+
+proc compileOption*(option: string): bool {.
+  magic: "CompileOption", noSideEffect.} =
+  ## Can be used to determine an `on|off` compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string,string>`_ for enum options
+  ## * `defined <#defined,untyped>`_
+  ## * `std/compilesettings module <compilesettings.html>`_
+  runnableExamples("--floatChecks:off"):
+    static: doAssert not compileOption("floatchecks")
+    {.push floatChecks: on.}
+    static: doAssert compileOption("floatchecks")
+    # floating point NaN and Inf checks enabled in this scope
+    {.pop.}
+
+proc compileOption*(option, arg: string): bool {.
+  magic: "CompileOptionArg", noSideEffect.} =
+  ## Can be used to determine an enum compile-time option.
+  ##
+  ## See also:
+  ## * `compileOption <#compileOption,string>`_ for `on|off` options
+  ## * `defined <#defined,untyped>`_
+  ## * `std/compilesettings module <compilesettings.html>`_
+  runnableExamples:
+    when compileOption("opt", "size") and compileOption("gc", "boehm"):
+      discard "compiled with optimization for size and uses Boehm's GC"
+
+template currentSourcePath*: string = instantiationInfo(-1, true).filename
+  ## Returns the full file-system path of the current source.
+  ##
+  ## To get the directory containing the current source, use it with
+  ## `ospaths2.parentDir() <ospaths2.html#parentDir%2Cstring>`_ as
+  ## `currentSourcePath.parentDir()`.
+  ##
+  ## The path returned by this template is set at compile time.
+  ##
+  ## See the docstring of `macros.getProjectPath() <macros.html#getProjectPath>`_
+  ## for an example to see the distinction between the `currentSourcePath()`
+  ## and `getProjectPath()`.
+  ##
+  ## See also:
+  ## * `ospaths2.getCurrentDir() proc <ospaths2.html#getCurrentDir>`_
+
+proc slurp*(filename: string): string {.magic: "Slurp".}
+  ## This is an alias for `staticRead <#staticRead,string>`_.
+
+proc staticRead*(filename: string): string {.magic: "Slurp".}
+  ## Compile-time `readFile <syncio.html#readFile,string>`_ proc for easy
+  ## `resource`:idx: embedding:
+  ##
+  ## The maximum file size limit that `staticRead` and `slurp` can read is
+  ## near or equal to the *free* memory of the device you are using to compile.
+  ##   ```nim
+  ##   const myResource = staticRead"mydatafile.bin"
+  ##   ```
+  ##
+  ## `slurp <#slurp,string>`_ is an alias for `staticRead`.
+
+proc gorge*(command: string, input = "", cache = ""): string {.
+  magic: "StaticExec".} = discard
+  ## This is an alias for `staticExec <#staticExec,string,string,string>`_.
+
+proc staticExec*(command: string, input = "", cache = ""): string {.
+  magic: "StaticExec".} = discard
+  ## Executes an external process at compile-time and returns its text output
+  ## (stdout + stderr).
+  ##
+  ## If `input` is not an empty string, it will be passed as a standard input
+  ## to the executed program.
+  ##   ```nim
+  ##   const buildInfo = "Revision " & staticExec("git rev-parse HEAD") &
+  ##                     "\nCompiled on " & staticExec("uname -v")
+  ##   ```
+  ##
+  ## `gorge <#gorge,string,string,string>`_ is an alias for `staticExec`.
+  ##
+  ## Note that you can use this proc inside a pragma like
+  ## `passc <manual.html#implementation-specific-pragmas-passc-pragma>`_ or
+  ## `passl <manual.html#implementation-specific-pragmas-passl-pragma>`_.
+  ##
+  ## If `cache` is not empty, the results of `staticExec` are cached within
+  ## the `nimcache` directory. Use `--forceBuild` to get rid of this caching
+  ## behaviour then. `command & input & cache` (the concatenated string) is
+  ## used to determine whether the entry in the cache is still valid. You can
+  ## use versioning information for `cache`:
+  ##   ```nim
+  ##   const stateMachine = staticExec("dfaoptimizer", "input", "0.8.0")
+  ##   ```
+
+proc gorgeEx*(command: string, input = "", cache = ""): tuple[output: string,
+                                                              exitCode: int] =
+  ## Similar to `gorge <#gorge,string,string,string>`_ but also returns the
+  ## precious exit code.
+  discard
diff --git a/lib/system/ctypes.nim b/lib/system/ctypes.nim
new file mode 100644
index 000000000..b788274bd
--- /dev/null
+++ b/lib/system/ctypes.nim
@@ -0,0 +1,84 @@
+## Some type definitions for compatibility between different
+## backends and platforms.
+
+type
+  BiggestInt* = int64
+    ## is an alias for the biggest signed integer type the Nim compiler
+    ## supports. Currently this is `int64`, but it is platform-dependent
+    ## in general.
+
+  BiggestFloat* = float64
+    ## is an alias for the biggest floating point type the Nim
+    ## compiler supports. Currently this is `float64`, but it is
+    ## platform-dependent in general.
+
+  BiggestUInt* = uint64
+    ## is an alias for the biggest unsigned integer type the Nim compiler
+    ## supports. Currently this is `uint64`, but it is platform-dependent
+    ## in general.
+
+when defined(windows):
+  type
+    clong* {.importc: "long", nodecl.} = int32
+      ## This is the same as the type `long` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint32
+      ## This is the same as the type `unsigned long` in *C*.
+else:
+  type
+    clong* {.importc: "long", nodecl.} = int
+      ## This is the same as the type `long` in *C*.
+    culong* {.importc: "unsigned long", nodecl.} = uint
+      ## This is the same as the type `unsigned long` in *C*.
+
+type # these work for most platforms:
+  cchar* {.importc: "char", nodecl.} = char
+    ## This is the same as the type `char` in *C*.
+  cschar* {.importc: "signed char", nodecl.} = int8
+    ## This is the same as the type `signed char` in *C*.
+  cshort* {.importc: "short", nodecl.} = int16
+    ## This is the same as the type `short` in *C*.
+  cint* {.importc: "int", nodecl.} = int32
+    ## This is the same as the type `int` in *C*.
+  csize_t* {.importc: "size_t", nodecl.} = uint
+    ## This is the same as the type `size_t` in *C*.
+  clonglong* {.importc: "long long", nodecl.} = int64
+    ## This is the same as the type `long long` in *C*.
+  cfloat* {.importc: "float", nodecl.} = float32
+    ## This is the same as the type `float` in *C*.
+  cdouble* {.importc: "double", nodecl.} = float64
+    ## This is the same as the type `double` in *C*.
+  clongdouble* {.importc: "long double", nodecl.} = BiggestFloat
+    ## This is the same as the type `long double` in *C*.
+    ## This C type is not supported by Nim's code generator.
+
+  cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char
+    ## Deprecated: Use `uint8` instead.
+  cushort* {.importc: "unsigned short", nodecl.} = uint16
+    ## This is the same as the type `unsigned short` in *C*.
+  cuint* {.importc: "unsigned int", nodecl.} = uint32
+    ## This is the same as the type `unsigned int` in *C*.
+  culonglong* {.importc: "unsigned long long", nodecl.} = uint64
+    ## This is the same as the type `unsigned long long` in *C*.
+
+type
+  ByteAddress* {.deprecated: "use `uint`".} = int
+    ## is the signed integer type that should be used for converting
+    ## pointers to integer addresses for readability.
+
+  cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring]
+    ## This is binary compatible to the type `char**` in *C*. The array's
+    ## high value is large enough to disable bounds checking in practice.
+    ## Use `cstringArrayToSeq proc <#cstringArrayToSeq,cstringArray,Natural>`_
+    ## to convert it into a `seq[string]`.
+
+when not defined(nimPreviewSlimSystem):
+  # pollutes namespace
+  type
+    PFloat32* {.deprecated: "use `ptr float32`".} = ptr float32
+      ## An alias for `ptr float32`.
+    PFloat64* {.deprecated: "use `ptr float64`".} = ptr float64
+      ## An alias for `ptr float64`.
+    PInt64* {.deprecated: "use `ptr int64`".} = ptr int64
+      ## An alias for `ptr int64`.
+    PInt32* {.deprecated: "use `ptr int32`".} = ptr int32
+      ## An alias for `ptr int32`.
diff --git a/lib/system/cyclebreaker.nim b/lib/system/cyclebreaker.nim
index 6ec0f01f7..45b0a5a65 100644
--- a/lib/system/cyclebreaker.nim
+++ b/lib/system/cyclebreaker.nim
@@ -138,7 +138,8 @@ proc breakCycles(s: Cell; desc: PNimTypeV2) =
       else:
         # anyhow as a link that the produced destructor does not have to follow:
         u[] = nil
-        cprintf("[Bug] %p %s RC %ld\n", t, desc.name, t.rc shr rcShift)
+        when traceCollector:
+          cprintf("[Bug] %p %s RC %ld\n", t, desc.name, t.rc shr rcShift)
   deinit j.traceStack
 
 proc thinout*[T](x: ref T) {.inline.} =
@@ -148,7 +149,7 @@ proc thinout*[T](x: ref T) {.inline.} =
   ## and thus would keep the graph from being freed are `nil`'ed.
   ## This is a form of cycle collection that works well with Nim's ARC
   ## and its associated cost model.
-  proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect, locks: 0.}
+  proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect.}
 
   breakCycles(head(cast[pointer](x)), getDynamicTypeInfo(x[]))
 
diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim
index 1f30b8427..72d35f518 100644
--- a/lib/system/deepcopy.nim
+++ b/lib/system/deepcopy.nim
@@ -61,8 +61,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType;
 proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
                         tab: var PtrTable) {.benign.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   case n.kind
   of nkSlot:
     genericDeepCopyAux(cast[pointer](d +% n.offset),
@@ -85,8 +85,8 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode;
 
 proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   sysAssert(mt != nil, "genericDeepCopyAux 2")
   case mt.kind
   of tyString:
@@ -113,11 +113,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) =
         return
       sysAssert(dest != nil, "genericDeepCopyAux 3")
       unsureAsgnRef(x, newSeq(mt, seq.len))
-      var dst = cast[ByteAddress](cast[PPointer](dest)[])
+      var dst = cast[int](cast[PPointer](dest)[])
       for i in 0..seq.len-1:
         genericDeepCopyAux(
           cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
-          cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
+          cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size),
           mt.base, tab)
   of tyObject:
     # we need to copy m_type field for tyObject, as it could be empty for
@@ -199,8 +199,8 @@ proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} =
 proc genericDeepCopyOpenArray(dest, src: pointer, len: int,
                             mt: PNimType) {.compilerproc.} =
   var
-    d = cast[ByteAddress](dest)
-    s = cast[ByteAddress](src)
+    d = cast[int](dest)
+    s = cast[int](src)
   for i in 0..len-1:
     genericDeepCopy(cast[pointer](d +% i *% mt.base.size),
                     cast[pointer](s +% i *% mt.base.size), mt.base)
diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim
index 4ff3d0ae6..89a739d5a 100644
--- a/lib/system/dollars.nim
+++ b/lib/system/dollars.nim
@@ -41,9 +41,9 @@ proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
 proc `$`*(x: char): string {.magic: "CharToStr", noSideEffect.}
   ## The stringify operator for a character argument. Returns `x`
   ## converted to a string.
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   assert $'c' == "c"
+  ##   ```
 
 proc `$`*(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
   ## The stringify operator for a CString argument. Returns `x`
@@ -67,19 +67,20 @@ proc `$`*(t: typedesc): string {.magic: "TypeTrait".}
   ## For more procedures dealing with `typedesc`, see
   ## `typetraits module <typetraits.html>`_.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   doAssert $(typeof(42)) == "int"
   ##   doAssert $(typeof("Foo")) == "string"
   ##   static: doAssert $(typeof(@['A', 'B'])) == "seq[char]"
+  ##   ```
 
 proc `$`*[T: tuple](x: T): string =
   ## Generic `$` operator for tuples that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(23, 45) == "(23, 45)"
   ##   $(a: 23, b: 45) == "(a: 23, b: 45)"
   ##   $() == "()"
+  ##   ```
   tupleObjectDollar(result, x)
 
 when not defined(nimPreviewSlimSystem):
@@ -108,25 +109,25 @@ proc collectionToString[T](x: T, prefix, separator, suffix: string): string =
 proc `$`*[T](x: set[T]): string =
   ## Generic `$` operator for sets that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   ${23, 45} == "{23, 45}"
+  ##   ```
   collectionToString(x, "{", ", ", "}")
 
 proc `$`*[T](x: seq[T]): string =
   ## Generic `$` operator for seqs that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(@[23, 45]) == "@[23, 45]"
+  ##   ```
   collectionToString(x, "@[", ", ", "]")
 
 proc `$`*[T, U](x: HSlice[T, U]): string =
   ## Generic `$` operator for slices that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##  $(1 .. 5) == "1 .. 5"
+  ##  ```
   result = $x.a
   result.add(" .. ")
   result.add($x.b)
@@ -140,7 +141,7 @@ when not defined(nimNoArrayToString):
 proc `$`*[T](x: openArray[T]): string =
   ## Generic `$` operator for openarrays that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]"
+  ##   ```
   collectionToString(x, "[", ", ", "]")
diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim
index 36c2c5fe1..2162b234f 100644
--- a/lib/system/dyncalls.nim
+++ b/lib/system/dyncalls.nim
@@ -47,14 +47,14 @@ proc nimLoadLibraryError(path: string) =
         copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len)
       discard MessageBoxA(nil, msg[0].addr, nil, 0)
   cstderr.rawWrite("\n")
-  quit(1)
+  rawQuit(1)
 
 proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} =
   # carefully written to avoid memory allocation:
   cstderr.rawWrite("could not import: ")
   cstderr.rawWrite(name)
   cstderr.rawWrite("\n")
-  quit(1)
+  rawQuit(1)
 
 # this code was inspired from Lua's source code:
 # Lua - An Extensible Extension Language
@@ -161,7 +161,7 @@ elif defined(windows) or defined(dos):
         dec(m)
         k = k div 10
         if k == 0: break
-      result = getProcAddress(cast[THINSTANCE](lib), addr decorated)
+      result = getProcAddress(cast[THINSTANCE](lib), cast[cstring](addr decorated))
       if result != nil: return
     procAddrError(name)
 
@@ -176,23 +176,23 @@ elif defined(genode):
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     raiseAssert("nimGetProcAddr not implemented")
 
-elif defined(nintendoswitch) or defined(freertos) or defined(zephyr):
+elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nuttx):
   proc nimUnloadLibrary(lib: LibHandle) =
     cstderr.rawWrite("nimUnLoadLibrary not implemented")
     cstderr.rawWrite("\n")
-    quit(1)
+    rawQuit(1)
 
   proc nimLoadLibrary(path: string): LibHandle =
     cstderr.rawWrite("nimLoadLibrary not implemented")
     cstderr.rawWrite("\n")
-    quit(1)
+    rawQuit(1)
 
 
   proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
     cstderr.rawWrite("nimGetProAddr not implemented")
     cstderr.rawWrite(name)
     cstderr.rawWrite("\n")
-    quit(1)
+    rawQuit(1)
 
 else:
   {.error: "no implementation for dyncalls".}
diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim
index e0b053c7b..ea6776f58 100644
--- a/lib/system/embedded.nim
+++ b/lib/system/embedded.nim
@@ -19,8 +19,9 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, exportc: "nimFrame".} = discard
 proc popFrame {.compilerRtl, inl.} = discard
 
 proc setFrame(s: PFrame) {.compilerRtl, inl.} = discard
-proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard
-proc popSafePoint {.compilerRtl, inl.} = discard
+when not gotoBasedExceptions:
+  proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = discard
+  proc popSafePoint {.compilerRtl, inl.} = discard
 proc pushCurrentException(e: ref Exception) {.compilerRtl, inl.} = discard
 proc popCurrentException {.compilerRtl, inl.} = discard
 
diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim
index 5dcd77bd0..63588f858 100644
--- a/lib/system/exceptions.nim
+++ b/lib/system/exceptions.nim
@@ -1,61 +1,13 @@
-const NimStackTraceMsgs =
-  when defined(nimHasStacktraceMsgs): compileOption("stacktraceMsgs")
-  else: false
+## Exception and effect types used in Nim code.
 
 type
-  RootEffect* {.compilerproc.} = object of RootObj ## \
-    ## Base effect class.
-    ##
-    ## Each effect should inherit from `RootEffect` unless you know what
-    ## you're doing.
   TimeEffect* = object of RootEffect   ## Time effect.
   IOEffect* = object of RootEffect     ## IO effect.
   ReadIOEffect* = object of IOEffect   ## Effect describing a read IO operation.
   WriteIOEffect* = object of IOEffect  ## Effect describing a write IO operation.
   ExecIOEffect* = object of IOEffect   ## Effect describing an executing IO operation.
 
-  StackTraceEntry* = object ## In debug mode exceptions store the stack trace that led
-                            ## to them. A `StackTraceEntry` is a single entry of the
-                            ## stack trace.
-    procname*: cstring      ## Name of the proc that is currently executing.
-    line*: int              ## Line number of the proc that is currently executing.
-    filename*: cstring      ## Filename of the proc that is currently executing.
-    when NimStackTraceMsgs:
-      frameMsg*: string     ## When a stacktrace is generated in a given frame and
-                            ## rendered at a later time, we should ensure the stacktrace
-                            ## data isn't invalidated; any pointer into PFrame is
-                            ## subject to being invalidated so shouldn't be stored.
-    when defined(nimStackTraceOverride):
-      programCounter*: uint ## Program counter - will be used to get the rest of the info,
-                            ## when `$` is called on this type. We can't use
-                            ## "cuintptr_t" in here.
-      procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename"
-
-  Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \
-    ## Base exception class.
-    ##
-    ## Each exception has to inherit from `Exception`. See the full `exception
-    ## hierarchy <manual.html#exception-handling-exception-hierarchy>`_.
-    parent*: ref Exception ## Parent exception (can be used as a stack).
-    name*: cstring         ## The exception's name is its Nim identifier.
-                           ## This field is filled automatically in the
-                           ## `raise` statement.
-    msg* {.exportc: "message".}: string ## The exception's message. Not
-                                        ## providing an exception message
-                                        ## is bad style.
-    when defined(js):
-      trace: string
-    else:
-      trace: seq[StackTraceEntry]
-    up: ref Exception # used for stacking exceptions. Not exported!
-
-  Defect* = object of Exception ## \
-    ## Abstract base class for all exceptions that Nim's runtime raises
-    ## but that are strictly uncatchable as they can also be mapped to
-    ## a `quit` / `trap` / `exit` operation.
-
-  CatchableError* = object of Exception ## \
-    ## Abstract class for all exceptions that are catchable.
+type
   IOError* = object of CatchableError ## \
     ## Raised if an IO error occurred.
   EOFError* = object of IOError ## \
@@ -144,25 +96,27 @@ type
     ##
     ## This is only raised if the `segfaults module <segfaults.html>`_ was imported!
 
-  ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect
-  DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect
-  OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect
-  AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect
-  AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect
-  OutOfMemError* {.deprecated: "See corresponding Defect".} = OutOfMemDefect
-  IndexError* {.deprecated: "See corresponding Defect".} = IndexDefect
+when not defined(nimPreviewSlimSystem):
+  type
+    ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect
+    DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect
+    OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect
+    AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect
+    AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect
+    OutOfMemError* {.deprecated: "See corresponding Defect".} = OutOfMemDefect
+    IndexError* {.deprecated: "See corresponding Defect".} = IndexDefect
 
-  FieldError* {.deprecated: "See corresponding Defect".} = FieldDefect
-  RangeError* {.deprecated: "See corresponding Defect".} = RangeDefect
-  StackOverflowError* {.deprecated: "See corresponding Defect".} = StackOverflowDefect
-  ReraiseError* {.deprecated: "See corresponding Defect".} = ReraiseDefect
-  ObjectAssignmentError* {.deprecated: "See corresponding Defect".} = ObjectAssignmentDefect
-  ObjectConversionError* {.deprecated: "See corresponding Defect".} = ObjectConversionDefect
-  FloatingPointError* {.deprecated: "See corresponding Defect".} = FloatingPointDefect
-  FloatInvalidOpError* {.deprecated: "See corresponding Defect".} = FloatInvalidOpDefect
-  FloatDivByZeroError* {.deprecated: "See corresponding Defect".} = FloatDivByZeroDefect
-  FloatOverflowError* {.deprecated: "See corresponding Defect".} = FloatOverflowDefect
-  FloatUnderflowError* {.deprecated: "See corresponding Defect".} = FloatUnderflowDefect
-  FloatInexactError* {.deprecated: "See corresponding Defect".} = FloatInexactDefect
-  DeadThreadError* {.deprecated: "See corresponding Defect".} = DeadThreadDefect
-  NilAccessError* {.deprecated: "See corresponding Defect".} = NilAccessDefect
+    FieldError* {.deprecated: "See corresponding Defect".} = FieldDefect
+    RangeError* {.deprecated: "See corresponding Defect".} = RangeDefect
+    StackOverflowError* {.deprecated: "See corresponding Defect".} = StackOverflowDefect
+    ReraiseError* {.deprecated: "See corresponding Defect".} = ReraiseDefect
+    ObjectAssignmentError* {.deprecated: "See corresponding Defect".} = ObjectAssignmentDefect
+    ObjectConversionError* {.deprecated: "See corresponding Defect".} = ObjectConversionDefect
+    FloatingPointError* {.deprecated: "See corresponding Defect".} = FloatingPointDefect
+    FloatInvalidOpError* {.deprecated: "See corresponding Defect".} = FloatInvalidOpDefect
+    FloatDivByZeroError* {.deprecated: "See corresponding Defect".} = FloatDivByZeroDefect
+    FloatOverflowError* {.deprecated: "See corresponding Defect".} = FloatOverflowDefect
+    FloatUnderflowError* {.deprecated: "See corresponding Defect".} = FloatUnderflowDefect
+    FloatInexactError* {.deprecated: "See corresponding Defect".} = FloatInexactDefect
+    DeadThreadError* {.deprecated: "See corresponding Defect".} = DeadThreadDefect
+    NilAccessError* {.deprecated: "See corresponding Defect".} = NilAccessDefect
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index a71328c14..dae5c4a4a 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -73,26 +73,45 @@ type
 
 when NimStackTraceMsgs:
   var frameMsgBuf* {.threadvar.}: string
+
+when not defined(nimV2):
+  var
+    framePtr {.threadvar.}: PFrame
+
 var
-  framePtr {.threadvar.}: PFrame
-  excHandler {.threadvar.}: PSafePoint
-    # list of exception handlers
-    # a global variable for the root of all try blocks
   currException {.threadvar.}: ref Exception
-  gcFramePtr {.threadvar.}: GcFrame
 
-type
-  FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
-                     excHandler: PSafePoint, currException: ref Exception]
+when not gotoBasedExceptions:
+  var
+    excHandler {.threadvar.}: PSafePoint
+      # list of exception handlers
+      # a global variable for the root of all try blocks
+    gcFramePtr {.threadvar.}: GcFrame
+
+when gotoBasedExceptions:
+  type
+    FrameState = tuple[framePtr: PFrame,
+                      currException: ref Exception]
+else:
+  type
+    FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
+                      excHandler: PSafePoint, currException: ref Exception]
 
 proc getFrameState*(): FrameState {.compilerRtl, inl.} =
-  return (gcFramePtr, framePtr, excHandler, currException)
+  when gotoBasedExceptions:
+    return (framePtr, currException)
+  else:
+    return (gcFramePtr, framePtr, excHandler, currException)
 
 proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
-  gcFramePtr = state.gcFramePtr
-  framePtr = state.framePtr
-  excHandler = state.excHandler
-  currException = state.currException
+  when gotoBasedExceptions:
+    framePtr = state.framePtr
+    currException = state.currException
+  else:
+    gcFramePtr = state.gcFramePtr
+    framePtr = state.framePtr
+    excHandler = state.excHandler
+    currException = state.currException
 
 proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
 
@@ -114,20 +133,21 @@ when false:
 proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
   framePtr = s
 
-proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
-proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
-proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
-proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
-  s.prev = gcFramePtr
-  zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
-  gcFramePtr = s
+when not gotoBasedExceptions:
+  proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
+  proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
+  proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
+  proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
+    s.prev = gcFramePtr
+    zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
+    gcFramePtr = s
 
-proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
-  s.prev = excHandler
-  excHandler = s
+  proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
+    s.prev = excHandler
+    excHandler = s
 
-proc popSafePoint {.compilerRtl, inl.} =
-  excHandler = excHandler.prev
+  proc popSafePoint {.compilerRtl, inl.} =
+    excHandler = excHandler.prev
 
 proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
   e.up = currException
@@ -397,9 +417,9 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} =
     xadd(buf, e.name, e.name.len)
     add(buf, "]\n")
     if onUnhandledException != nil:
-      onUnhandledException($buf.addr)
+      onUnhandledException($cast[cstring](buf.addr))
     else:
-      showErrorMessage(buf.addr, L)
+      showErrorMessage(cast[cstring](buf.addr), L)
 
 proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} =
   if unhandledExceptionHook != nil:
@@ -407,15 +427,16 @@ proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} =
   when hostOS != "any":
     reportUnhandledErrorAux(e)
 
-proc nimLeaveFinally() {.compilerRtl.} =
-  when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
-    {.emit: "throw;".}
-  else:
-    if excHandler != nil:
-      c_longjmp(excHandler.context, 1)
+when not gotoBasedExceptions:
+  proc nimLeaveFinally() {.compilerRtl.} =
+    when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
+      {.emit: "throw;".}
     else:
-      reportUnhandledError(currException)
-      quit(1)
+      if excHandler != nil:
+        c_longjmp(excHandler.context, 1)
+      else:
+        reportUnhandledError(currException)
+        rawQuit(1)
 
 when gotoBasedExceptions:
   var nimInErrorMode {.threadvar.}: bool
@@ -430,13 +451,13 @@ when gotoBasedExceptions:
     if nimInErrorMode and currException != nil:
       reportUnhandledError(currException)
       currException = nil
-      quit(1)
+      rawQuit(1)
 
 proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
   when defined(nimPanics):
     if e of Defect:
       reportUnhandledError(e)
-      quit(1)
+      rawQuit(1)
 
   if localRaiseHook != nil:
     if not localRaiseHook(e): return
@@ -448,7 +469,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
     else:
       pushCurrentException(e)
       {.emit: "throw `e`;".}
-  elif defined(nimQuirky) or gotoBasedExceptions:
+  elif quirkyExceptions or gotoBasedExceptions:
     pushCurrentException(e)
     when gotoBasedExceptions:
       inc nimInErrorMode
@@ -458,7 +479,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
       c_longjmp(excHandler.context, 1)
     else:
       reportUnhandledError(e)
-      quit(1)
+      rawQuit(1)
 
 proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
                       line: int) {.compilerRtl, nodestroy.} =
@@ -501,7 +522,7 @@ proc threadTrouble() =
     if currException != nil: reportUnhandledError(currException)
   except:
     discard
-  quit 1
+  rawQuit 1
 
 proc writeStackTrace() =
   when hasSomeStackTrace:
@@ -544,7 +565,7 @@ proc callDepthLimitReached() {.noinline.} =
       "-d:nimCallDepthLimit=<int> but really try to avoid deep " &
       "recursions instead.\n"
   showErrorMessage2(msg)
-  quit(1)
+  rawQuit(1)
 
 proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
   if framePtr == nil:
@@ -560,7 +581,7 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
 when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
     not defined(js) and not defined(nimscript) and
     hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and
-    not defined(nimQuirky):
+    not quirkyExceptions:
 
   type
     StdException {.importcpp: "std::exception", header: "<exception>".} = object
@@ -597,7 +618,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
     else:
       writeToStdErr msg & "\n"
 
-    quit 1
+    rawQuit 1
 
 when not defined(noSignalHandler) and not defined(useNimRtl):
   type Sighandler = proc (a: cint) {.noconv, benign.}
@@ -651,7 +672,7 @@ when not defined(noSignalHandler) and not defined(useNimRtl):
       # also return the correct exit code to the shell.
       discard c_raise(sign)
     else:
-      quit(1)
+      rawQuit(1)
 
   var SIG_IGN {.importc: "SIG_IGN", header: "<signal.h>".}: Sighandler
 
diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim
index 64ec9cda3..25c05e52d 100644
--- a/lib/system/fatal.nim
+++ b/lib/system/fatal.nim
@@ -9,50 +9,50 @@
 
 {.push profiler: off.}
 
-when defined(nimHasExceptionsQuery):
-  const gotoBasedExceptions = compileOption("exceptions", "goto")
-else:
-  const gotoBasedExceptions = false
+const
+  gotoBasedExceptions = compileOption("exceptions", "goto")
+  quirkyExceptions = compileOption("exceptions", "quirky")
 
 when hostOS == "standalone":
   include "$projectpath/panicoverride"
 
-  proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline.} =
     panic(message)
 
-  proc sysFatal(exceptn: typedesc, message, arg: string) {.inline.} =
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline.} =
     rawoutput(message)
     panic(arg)
 
-elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript):
+elif quirkyExceptions and not defined(nimscript):
   import ansi_c
 
-  proc name(t: typedesc): string {.magic: "TypeTrait".}
+  func name(t: typedesc): string {.magic: "TypeTrait".}
 
-  proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline, noreturn.} =
     when nimvm:
       # TODO when doAssertRaises works in CT, add a test for it
       raise (ref exceptn)(msg: message & arg)
     else:
-      writeStackTrace()
-      var buf = newStringOfCap(200)
-      add(buf, "Error: unhandled exception: ")
-      add(buf, message)
-      add(buf, arg)
-      add(buf, " [")
-      add(buf, name exceptn)
-      add(buf, "]\n")
-      cstderr.rawWrite buf
-      quit 1
-
-  proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
+      {.noSideEffect.}:
+        writeStackTrace()
+        var buf = newStringOfCap(200)
+        add(buf, "Error: unhandled exception: ")
+        add(buf, message)
+        add(buf, arg)
+        add(buf, " [")
+        add(buf, name exceptn)
+        add(buf, "]\n")
+        cstderr.rawWrite buf
+      rawQuit 1
+
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline, noreturn.} =
     sysFatal(exceptn, message, "")
 
 else:
-  proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
+  func sysFatal(exceptn: typedesc[Defect], message: string) {.inline, noreturn.} =
     raise (ref exceptn)(msg: message)
 
-  proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} =
+  func sysFatal(exceptn: typedesc[Defect], message, arg: string) {.inline, noreturn.} =
     raise (ref exceptn)(msg: message & arg)
 
 {.pop.}
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 4ab76c05e..9289c7f55 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -31,7 +31,7 @@ In Nim the compiler cannot always know if a reference
 is stored on the stack or not. This is caused by var parameters.
 Consider this example:
 
-.. code-block:: Nim
+  ```Nim
   proc setRef(r: var ref TNode) =
     new(r)
 
@@ -41,11 +41,12 @@ Consider this example:
     setRef(r) # here we should not update the reference counts, because
               # r is on the stack
     setRef(r.left) # here we should update the refcounts!
+  ```
 
 We have to decide at runtime whether the reference is on the stack or not.
 The generated code looks roughly like this:
 
-.. code-block:: C
+  ```C
   void setref(TNode** ref) {
     unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode)))
   }
@@ -53,6 +54,7 @@ The generated code looks roughly like this:
     setRef(&r)
     setRef(&r->left)
   }
+  ```
 
 Note that for systems with a continuous stack (which most systems have)
 the check whether the ref is on the stack is very cheap (only two
@@ -76,7 +78,7 @@ when defined(memProfiler):
   proc nimProfile(requestedSize: int) {.benign.}
 
 when hasThreadSupport:
-  import sharedlist
+  import std/sharedlist
 
 const
   rcIncrement = 0b1000 # so that lowest 3 bits are not touched
@@ -161,7 +163,7 @@ template gcAssert(cond: bool, msg: string) =
       writeStackTrace()
       #var x: ptr int
       #echo x[]
-      quit 1
+      rawQuit 1
 
 proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
   if (c.refcount and ZctFlag) == 0:
@@ -170,11 +172,11 @@ proc addZCT(s: var CellSeq, c: PCell) {.noinline.} =
 
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
+  result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell)))
 
 proc usrToCell(usr: pointer): PCell {.inline.} =
   # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
+  result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell)))
 
 proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
   # used for code generation concerning debugging
@@ -336,7 +338,7 @@ proc cellsetReset(s: var CellSet) =
 {.push stacktrace:off.}
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
   of nkList:
@@ -356,7 +358,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   of nkNone: sysAssert(false, "forAllSlotsAux")
 
 proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
@@ -382,7 +384,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
     of tyRef: # common case
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
+      var d = cast[int](cellToUsr(cell))
       var s = cast[PGenericSeq](d)
       if s != nil:
         for i in 0..s.len-1:
@@ -457,7 +459,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   collectCT(gch)
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small"
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   setFrameInfo(res)
@@ -507,7 +509,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
 
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
   sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc")
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   setFrameInfo(res)
@@ -549,9 +551,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
 
   var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize
   copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)),
+  zeroMem(cast[pointer](cast[int](res) +% oldsize +% sizeof(Cell)),
           newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
+  sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3")
   # This can be wrong for intermediate temps that are nevertheless on the
   # heap because of lambda lifting:
   #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4")
@@ -626,7 +628,7 @@ when logGC:
       if cycleCheckA[i] == c: return true
     if cycleCheckALen == len(cycleCheckA):
       gcAssert(false, "cycle detection overflow")
-      quit 1
+      rawQuit 1
     cycleCheckA[cycleCheckALen] = c
     inc cycleCheckALen
 
@@ -683,7 +685,7 @@ proc collectCycles(gch: var GcHeap) {.raises: [].} =
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
   sysAssert(allocInv(gch.region), "gcMark begin")
-  var c = cast[ByteAddress](p)
+  var c = cast[int](p)
   if c >% PageSize:
     # fast check: does it look like a cell?
     var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p))
@@ -848,10 +850,10 @@ when withRealTime:
         stack.bottomSaved = stack.bottom
         when stackIncreases:
           stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
+            cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize)
         else:
           stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
+            cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize)
 
     GC_step(gch, us, strongAdvice)
 
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
deleted file mode 100644
index 45d467051..000000000
--- a/lib/system/gc2.nim
+++ /dev/null
@@ -1,749 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2017 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# xxx deadcode, consider removing unless something could be reused.
-
-
-#            Garbage Collector
-#
-# The basic algorithm is an incremental mark
-# and sweep GC to free cycles. It is hard realtime in that if you play
-# according to its rules, no deadline will ever be missed.
-# Since this kind of collector is very bad at recycling dead objects
-# early, Nim's codegen emits ``nimEscape`` calls at strategic
-# places. For this to work even 'unsureAsgnRef' needs to mark things
-# so that only return values need to be considered in ``nimEscape``.
-
-{.push profiler:off.}
-
-const
-  CycleIncrease = 2 # is a multiplicative increase
-  InitialCycleThreshold = 512*1024 # start collecting after 500KB
-  ZctThreshold = 500  # we collect garbage if the ZCT's size
-                      # reaches this threshold
-                      # this seems to be a good value
-  withRealTime = defined(useRealtimeGC)
-
-when withRealTime and not declared(getTicks):
-  include "system/timers"
-when defined(memProfiler):
-  proc nimProfile(requestedSize: int) {.benign.}
-
-when hasThreadSupport:
-  include sharedlist
-
-type
-  ObjectSpaceIter = object
-    state: range[-1..0]
-
-iterToProc(allObjects, ptr ObjectSpaceIter, allObjectsAsProc)
-
-const
-  escapedBit = 0b1000 # so that lowest 3 bits are not touched
-  rcBlackOrig = 0b000
-  rcWhiteOrig = 0b001
-  rcGrey = 0b010   # traditional color for incremental mark&sweep
-  rcUnused = 0b011
-  colorMask = 0b011
-type
-  WalkOp = enum
-    waMarkGlobal,    # part of the backup mark&sweep
-    waMarkGrey,
-    waZctDecRef,
-    waDebug
-
-  Phase {.pure.} = enum
-    None, Marking, Sweeping
-  Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
-    # A ref type can have a finalizer that is called before the object's
-    # storage is freed.
-
-  GcStat = object
-    stackScans: int          # number of performed stack scans (for statistics)
-    completedCollections: int    # number of performed full collections
-    maxThreshold: int        # max threshold that has been set
-    maxStackSize: int        # max stack size
-    maxStackCells: int       # max stack cells in ``decStack``
-    cycleTableSize: int      # max entries in cycle table
-    maxPause: int64          # max measured GC pause in nanoseconds
-
-  GcStack {.final, pure.} = object
-    when nimCoroutines:
-      prev: ptr GcStack
-      next: ptr GcStack
-      maxStackSize: int      # Used to track statistics because we can not use
-                             # GcStat.maxStackSize when multiple stacks exist.
-    bottom: pointer
-
-    when withRealTime or nimCoroutines:
-      pos: pointer           # Used with `withRealTime` only for code clarity, see GC_Step().
-    when withRealTime:
-      bottomSaved: pointer
-
-  GcHeap = object # this contains the zero count and
-                  # non-zero count table
-    black, red: int # either 0 or 1.
-    stack: GcStack
-    when nimCoroutines:
-      activeStack: ptr GcStack    # current executing coroutine stack.
-    phase: Phase
-    cycleThreshold: int
-    when useCellIds:
-      idGenerator: int
-    greyStack: CellSeq
-    recGcLock: int           # prevent recursion via finalizers; no thread lock
-    when withRealTime:
-      maxPause: Nanos        # max allowed pause in nanoseconds; active if > 0
-    region: MemRegion        # garbage collected region
-    stat: GcStat
-    additionalRoots: CellSeq # explicit roots for GC_ref/unref
-    spaceIter: ObjectSpaceIter
-    pDumpHeapFile: pointer # File that is used for GC_dumpHeap
-    when hasThreadSupport:
-      toDispose: SharedList[pointer]
-    gcThreadId: int
-
-var
-  gch {.rtlThreadVar.}: GcHeap
-
-when not defined(useNimRtl):
-  instantiateForRegion(gch.region)
-
-# Which color to use for new objects is tricky: When we're marking,
-# they have to be *white* so that everything is marked that is only
-# reachable from them. However, when we are sweeping, they have to
-# be black, so that we don't free them prematuredly. In order to save
-# a comparison gch.phase == Phase.Marking, we use the pseudo-color
-# 'red' for new objects.
-template allocColor(): untyped = gch.red
-
-template gcAssert(cond: bool, msg: string) =
-  when defined(useGcAssert):
-    if not cond:
-      echo "[GCASSERT] ", msg
-      GC_disable()
-      writeStackTrace()
-      quit 1
-
-proc cellToUsr(cell: PCell): pointer {.inline.} =
-  # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
-
-proc usrToCell(usr: pointer): PCell {.inline.} =
-  # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
-
-proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
-  # used for code generation concerning debugging
-  result = usrToCell(c).typ
-
-proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
-  result = 0
-
-# this that has to equals zero, otherwise we have to round up UnitsPerPage:
-when BitsPerPage mod (sizeof(int)*8) != 0:
-  {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
-
-template color(c): untyped = c.refCount and colorMask
-template setColor(c, col) =
-  c.refcount = c.refcount and not colorMask or col
-
-template markAsEscaped(c: PCell) =
-  c.refcount = c.refcount or escapedBit
-
-template didEscape(c: PCell): bool =
-  (c.refCount and escapedBit) != 0
-
-proc writeCell(file: File; msg: cstring, c: PCell) =
-  var kind = -1
-  if c.typ != nil: kind = ord(c.typ.kind)
-  let col = if c.color == rcGrey: 'g'
-            elif c.color == gch.black: 'b'
-            else: 'w'
-  when useCellIds:
-    let id = c.id
-  else:
-    let id = c
-  when defined(nimTypeNames):
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c of type %s\n",
-              msg, id, kind, didEscape(c), col, c.typ.name)
-  elif leakDetector:
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c from %s(%ld)\n",
-              msg, id, kind, didEscape(c), col, c.filename, c.line)
-  else:
-    c_fprintf(file, "%s %p %d escaped=%ld color=%c\n",
-              msg, id, kind, didEscape(c), col)
-
-proc writeCell(msg: cstring, c: PCell) =
-  stdout.writeCell(msg, c)
-
-proc myastToStr[T](x: T): string {.magic: "AstToStr", noSideEffect.}
-
-template gcTrace(cell, state: untyped) =
-  when traceGC: writeCell(myastToStr(state), cell)
-
-# forward declarations:
-proc collectCT(gch: var GcHeap) {.benign.}
-proc isOnStack(p: pointer): bool {.noinline, benign.}
-proc forAllChildren(cell: PCell, op: WalkOp) {.benign.}
-proc doOperation(p: pointer, op: WalkOp) {.benign.}
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.}
-# we need the prototype here for debugging purposes
-
-proc nimGCref(p: pointer) {.compilerproc.} =
-  let cell = usrToCell(p)
-  markAsEscaped(cell)
-  add(gch.additionalRoots, cell)
-
-proc nimGCunref(p: pointer) {.compilerproc.} =
-  let cell = usrToCell(p)
-  var L = gch.additionalRoots.len-1
-  var i = L
-  let d = gch.additionalRoots.d
-  while i >= 0:
-    if d[i] == cell:
-      d[i] = d[L]
-      dec gch.additionalRoots.len
-      break
-    dec(i)
-
-proc nimGCunrefNoCycle(p: pointer) {.compilerproc, inline.} =
-  discard "can we do some freeing here?"
-
-proc nimGCunrefRC1(p: pointer) {.compilerproc, inline.} =
-  discard "can we do some freeing here?"
-
-template markGrey(x: PCell) =
-  if x.color != 1-gch.black and gch.phase == Phase.Marking:
-    if not isAllocatedPtr(gch.region, x):
-      c_fprintf(stdout, "[GC] markGrey proc: %p\n", x)
-      #GC_dumpHeap()
-      sysAssert(false, "wtf")
-    x.setColor(rcGrey)
-    add(gch.greyStack, x)
-
-proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
-  # the code generator calls this proc!
-  gcAssert(not isOnStack(dest), "asgnRef")
-  # BUGFIX: first incRef then decRef!
-  if src != nil:
-    let s = usrToCell(src)
-    markAsEscaped(s)
-    markGrey(s)
-  dest[] = src
-
-proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline,
-  deprecated: "old compiler compat".} = asgnRef(dest, src)
-
-proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc.} =
-  # unsureAsgnRef marks 'src' as grey only if dest is not on the
-  # stack. It is used by the code generator if it cannot decide whether a
-  # reference is in the stack or not (this can happen for var parameters).
-  if src != nil:
-    let s = usrToCell(src)
-    markAsEscaped(s)
-    if not isOnStack(dest): markGrey(s)
-  dest[] = src
-
-proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
-  case n.kind
-  of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
-  of nkList:
-    for i in 0..n.len-1:
-      forAllSlotsAux(dest, n.sons[i], op)
-  of nkCase:
-    var m = selectBranch(dest, n)
-    if m != nil: forAllSlotsAux(dest, m, op)
-  of nkNone: sysAssert(false, "forAllSlotsAux")
-
-proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
-  if dest == nil: return # nothing to do
-  if ntfNoRefs notin mt.flags:
-    case mt.kind
-    of tyRef, tyString, tySequence: # leaf:
-      doOperation(cast[PPointer](d)[], op)
-    of tyObject, tyTuple:
-      forAllSlotsAux(dest, mt.node, op)
-    of tyArray, tyArrayConstr, tyOpenArray:
-      for i in 0..(mt.size div mt.base.size)-1:
-        forAllChildrenAux(cast[pointer](d +% i *% mt.base.size), mt.base, op)
-    else: discard
-
-proc forAllChildren(cell: PCell, op: WalkOp) =
-  gcAssert(cell != nil, "forAllChildren: 1")
-  gcAssert(isAllocatedPtr(gch.region, cell), "forAllChildren: 2")
-  gcAssert(cell.typ != nil, "forAllChildren: 3")
-  gcAssert cell.typ.kind in {tyRef, tySequence, tyString}, "forAllChildren: 4"
-  let marker = cell.typ.marker
-  if marker != nil:
-    marker(cellToUsr(cell), op.int)
-  else:
-    case cell.typ.kind
-    of tyRef: # common case
-      forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
-    of tySequence:
-      var d = cast[ByteAddress](cellToUsr(cell))
-      var s = cast[PGenericSeq](d)
-      if s != nil:
-        for i in 0..s.len-1:
-          forAllChildrenAux(cast[pointer](d +% align(GenericSeqSize, cell.typ.base.align) +% i *% cell.typ.base.size), cell.typ.base, op)
-    else: discard
-
-{.push stackTrace: off, profiler:off.}
-proc gcInvariant*() =
-  sysAssert(allocInv(gch.region), "injected")
-  when declared(markForDebug):
-    markForDebug(gch)
-{.pop.}
-
-include gc_common
-
-proc initGC() =
-  when not defined(useNimRtl):
-    gch.red = (1-gch.black)
-    gch.cycleThreshold = InitialCycleThreshold
-    gch.stat.stackScans = 0
-    gch.stat.completedCollections = 0
-    gch.stat.maxThreshold = 0
-    gch.stat.maxStackSize = 0
-    gch.stat.maxStackCells = 0
-    gch.stat.cycleTableSize = 0
-    # init the rt
-    init(gch.additionalRoots)
-    init(gch.greyStack)
-    when hasThreadSupport:
-      init(gch.toDispose)
-    gch.gcThreadId = atomicInc(gHeapidGenerator) - 1
-    gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
-
-proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
-  # generates a new object and sets its reference counter to 0
-  sysAssert(allocInv(gch.region), "rawNewObj begin")
-  gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
-  collectCT(gch)
-  var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
-  # now it is buffered in the ZCT
-  res.typ = typ
-  when leakDetector and not hasThreadSupport:
-    if framePtr != nil and framePtr.prev != nil:
-      res.filename = framePtr.prev.filename
-      res.line = framePtr.prev.line
-  # refcount is zero, color is black, but mark it to be in the ZCT
-  res.refcount = allocColor()
-  sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
-  when logGC: writeCell("new cell", res)
-  gcTrace(res, csAllocated)
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  result = cellToUsr(res)
-  sysAssert(allocInv(gch.region), "rawNewObj end")
-
-{.pop.}
-
-proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = rawNewObj(typ, size, gch)
-  when defined(memProfiler): nimProfile(size)
-
-proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = rawNewObj(typ, size, gch)
-  zeroMem(result, size)
-  when defined(memProfiler): nimProfile(size)
-
-proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  # `newObj` already uses locks, so no need for them here.
-  let size = addInt(align(GenericSeqSize, typ.base.align), mulInt(len, typ.base.size))
-  result = newObj(typ, size)
-  cast[PGenericSeq](result).len = len
-  cast[PGenericSeq](result).reserved = len
-  when defined(memProfiler): nimProfile(size)
-
-proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
-  result = newObj(typ, size)
-
-proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
-  result = newSeq(typ, len)
-
-proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
-  collectCT(gch)
-  var ol = usrToCell(old)
-  sysAssert(ol.typ != nil, "growObj: 1")
-  gcAssert(ol.typ.kind in {tyString, tySequence}, "growObj: 2")
-
-  var res = cast[PCell](rawAlloc(gch.region, newsize + sizeof(Cell)))
-  var elemSize, elemAlign = 1
-  if ol.typ.kind != tyString:
-    elemSize = ol.typ.base.size
-    elemAlign = ol.typ.base.align
-  incTypeSize ol.typ, newsize
-
-  var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
-  copyMem(res, ol, oldsize + sizeof(Cell))
-  zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
-          newsize-oldsize)
-  sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
-  when false:
-    # this is wrong since seqs can be shared via 'shallow':
-    when reallyDealloc: rawDealloc(gch.region, ol)
-    else:
-      zeroMem(ol, sizeof(Cell))
-  when useCellIds:
-    inc gch.idGenerator
-    res.id = gch.idGenerator
-  result = cellToUsr(res)
-  when defined(memProfiler): nimProfile(newsize-oldsize)
-
-proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
-  result = growObj(old, newsize, gch)
-
-{.push profiler:off.}
-
-
-template takeStartTime(workPackageSize) {.dirty.} =
-  const workPackage = workPackageSize
-  var debugticker = 1000
-  when withRealTime:
-    var steps = workPackage
-    var t0: Ticks
-    if gch.maxPause > 0: t0 = getticks()
-
-template takeTime {.dirty.} =
-  when withRealTime: dec steps
-  dec debugticker
-
-template checkTime {.dirty.} =
-  if debugticker <= 0:
-    #echo "in loop"
-    debugticker = 1000
-  when withRealTime:
-    if steps == 0:
-      steps = workPackage
-      if gch.maxPause > 0:
-        let duration = getticks() - t0
-        # the GC's measuring is not accurate and needs some cleanup actions
-        # (stack unmarking), so subtract some short amount of time in
-        # order to miss deadlines less often:
-        if duration >= gch.maxPause - 50_000:
-          return false
-
-# ---------------- dump heap ----------------
-
-template dumpHeapFile(gch: var GcHeap): File =
-  cast[File](gch.pDumpHeapFile)
-
-proc debugGraph(s: PCell) =
-  c_fprintf(gch.dumpHeapFile, "child %p\n", s)
-
-proc dumpRoot(gch: var GcHeap; s: PCell) =
-  if isAllocatedPtr(gch.region, s):
-    c_fprintf(gch.dumpHeapFile, "global_root %p\n", s)
-  else:
-    c_fprintf(gch.dumpHeapFile, "global_root_invalid %p\n", s)
-
-proc GC_dumpHeap*(file: File) =
-  ## Dumps the GCed heap's content to a file. Can be useful for
-  ## debugging. Produces an undocumented text file format that
-  ## can be translated into "dot" syntax via the "heapdump2dot" tool.
-  gch.pDumpHeapFile = file
-  var spaceIter: ObjectSpaceIter
-  when false:
-    var d = gch.decStack.d
-    for i in 0 .. gch.decStack.len-1:
-      if isAllocatedPtr(gch.region, d[i]):
-        c_fprintf(file, "onstack %p\n", d[i])
-      else:
-        c_fprintf(file, "onstack_invalid %p\n", d[i])
-  if gch.gcThreadId == 0:
-    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
-  for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
-  while true:
-    let x = allObjectsAsProc(gch.region, addr spaceIter)
-    if spaceIter.state < 0: break
-    if isCell(x):
-      # cast to PCell is correct here:
-      var c = cast[PCell](x)
-      writeCell(file, "cell ", c)
-      forAllChildren(c, waDebug)
-      c_fprintf(file, "end\n")
-  gch.pDumpHeapFile = nil
-
-proc GC_dumpHeap() =
-  var f: File
-  if open(f, "heap.txt", fmWrite):
-    GC_dumpHeap(f)
-    f.close()
-  else:
-    c_fprintf(stdout, "cannot write heap.txt")
-
-# ---------------- cycle collector -------------------------------------------
-
-proc freeCyclicCell(gch: var GcHeap, c: PCell) =
-  gcAssert(isAllocatedPtr(gch.region, c), "freeCyclicCell: freed pointer?")
-  prepareDealloc(c)
-  gcTrace(c, csCycFreed)
-  when logGC: writeCell("cycle collector dealloc cell", c)
-  when reallyDealloc:
-    sysAssert(allocInv(gch.region), "free cyclic cell")
-    rawDealloc(gch.region, c)
-  else:
-    gcAssert(c.typ != nil, "freeCyclicCell")
-    zeroMem(c, sizeof(Cell))
-
-proc sweep(gch: var GcHeap): bool =
-  takeStartTime(100)
-  #echo "loop start"
-  let white = 1-gch.black
-  #c_fprintf(stdout, "black is %d\n", black)
-  while true:
-    let x = allObjectsAsProc(gch.region, addr gch.spaceIter)
-    if gch.spaceIter.state < 0: break
-    takeTime()
-    if isCell(x):
-      # cast to PCell is correct here:
-      var c = cast[PCell](x)
-      gcAssert c.color != rcGrey, "cell is still grey?"
-      if c.color == white: freeCyclicCell(gch, c)
-      # Since this is incremental, we MUST not set the object to 'white' here.
-      # We could set all the remaining objects to white after the 'sweep'
-      # completed but instead we flip the meaning of black/white to save one
-      # traversal over the heap!
-    checkTime()
-  # prepare for next iteration:
-  #echo "loop end"
-  gch.spaceIter = ObjectSpaceIter()
-  result = true
-
-proc markRoot(gch: var GcHeap, c: PCell) {.inline.} =
-  if c.color == 1-gch.black:
-    c.setColor(rcGrey)
-    add(gch.greyStack, c)
-
-proc markIncremental(gch: var GcHeap): bool =
-  var L = addr(gch.greyStack.len)
-  takeStartTime(100)
-  while L[] > 0:
-    var c = gch.greyStack.d[0]
-    if not isAllocatedPtr(gch.region, c):
-      c_fprintf(stdout, "[GC] not allocated anymore: %p\n", c)
-      #GC_dumpHeap()
-      sysAssert(false, "wtf")
-
-    #sysAssert(isAllocatedPtr(gch.region, c), "markIncremental: isAllocatedPtr")
-    gch.greyStack.d[0] = gch.greyStack.d[L[] - 1]
-    dec(L[])
-    takeTime()
-    if c.color == rcGrey:
-      c.setColor(gch.black)
-      forAllChildren(c, waMarkGrey)
-    elif c.color == (1-gch.black):
-      gcAssert false, "wtf why are there white objects in the greystack?"
-    checkTime()
-  gcAssert gch.greyStack.len == 0, "markIncremental: greystack not empty "
-  result = true
-
-proc markGlobals(gch: var GcHeap) =
-  if gch.gcThreadId == 0:
-    for i in 0 .. globalMarkersLen-1: globalMarkers[i]()
-  for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]()
-
-proc doOperation(p: pointer, op: WalkOp) =
-  if p == nil: return
-  var c: PCell = usrToCell(p)
-  gcAssert(c != nil, "doOperation: 1")
-  # the 'case' should be faster than function pointers because of easy
-  # prediction:
-  case op
-  of waZctDecRef:
-    #if not isAllocatedPtr(gch.region, c):
-    #  c_fprintf(stdout, "[GC] decref bug: %p", c)
-    gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
-    discard "use me for nimEscape?"
-  of waMarkGlobal:
-    template handleRoot =
-      if gch.dumpHeapFile.isNil:
-        markRoot(gch, c)
-      else:
-        dumpRoot(gch, c)
-    handleRoot()
-    discard allocInv(gch.region)
-  of waMarkGrey:
-    when false:
-      if not isAllocatedPtr(gch.region, c):
-        c_fprintf(stdout, "[GC] not allocated anymore: MarkGrey %p\n", c)
-        #GC_dumpHeap()
-        sysAssert(false, "wtf")
-    if c.color == 1-gch.black:
-      c.setColor(rcGrey)
-      add(gch.greyStack, c)
-  of waDebug: debugGraph(c)
-
-proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
-  doOperation(d, WalkOp(op))
-
-proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
-  # the addresses are not as cells on the stack, so turn them to cells:
-  sysAssert(allocInv(gch.region), "gcMark begin")
-  var cell = usrToCell(p)
-  var c = cast[ByteAddress](cell)
-  if c >% PageSize:
-    # fast check: does it look like a cell?
-    var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell))
-    if objStart != nil:
-      # mark the cell:
-      markRoot(gch, objStart)
-  sysAssert(allocInv(gch.region), "gcMark end")
-
-proc markStackAndRegisters(gch: var GcHeap) {.noinline, cdecl.} =
-  forEachStackSlot(gch, gcMark)
-
-proc collectALittle(gch: var GcHeap): bool =
-  case gch.phase
-  of Phase.None:
-    if getOccupiedMem(gch.region) >= gch.cycleThreshold:
-      gch.phase = Phase.Marking
-      markGlobals(gch)
-      result = collectALittle(gch)
-      #when false: c_fprintf(stdout, "collectALittle: introduced bug E %ld\n", gch.phase)
-      #discard allocInv(gch.region)
-  of Phase.Marking:
-    when hasThreadSupport:
-      for c in gch.toDispose:
-        nimGCunref(c)
-    prepareForInteriorPointerChecking(gch.region)
-    markStackAndRegisters(gch)
-    inc(gch.stat.stackScans)
-    if markIncremental(gch):
-      gch.phase = Phase.Sweeping
-      gch.red = 1 - gch.red
-  of Phase.Sweeping:
-    gcAssert gch.greyStack.len == 0, "greystack not empty"
-    when hasThreadSupport:
-      for c in gch.toDispose:
-        nimGCunref(c)
-    if sweep(gch):
-      gch.phase = Phase.None
-      # flip black/white meanings:
-      gch.black = 1 - gch.black
-      gcAssert gch.red == 1 - gch.black, "red color is wrong"
-      inc(gch.stat.completedCollections)
-      result = true
-
-proc collectCTBody(gch: var GcHeap) =
-  when withRealTime:
-    let t0 = getticks()
-  sysAssert(allocInv(gch.region), "collectCT: begin")
-
-  when not nimCoroutines:
-    gch.stat.maxStackSize = max(gch.stat.maxStackSize, stackSize())
-  #gch.stat.maxStackCells = max(gch.stat.maxStackCells, gch.decStack.len)
-  if collectALittle(gch):
-    gch.cycleThreshold = max(InitialCycleThreshold, getOccupiedMem() *
-                              CycleIncrease)
-    gch.stat.maxThreshold = max(gch.stat.maxThreshold, gch.cycleThreshold)
-  sysAssert(allocInv(gch.region), "collectCT: end")
-  when withRealTime:
-    let duration = getticks() - t0
-    gch.stat.maxPause = max(gch.stat.maxPause, duration)
-    when defined(reportMissedDeadlines):
-      if gch.maxPause > 0 and duration > gch.maxPause:
-        c_fprintf(stdout, "[GC] missed deadline: %ld\n", duration)
-
-when nimCoroutines:
-  proc currentStackSizes(): int =
-    for stack in items(gch.stack):
-      result = result + stack.stackSize()
-
-proc collectCT(gch: var GcHeap) =
-  # stackMarkCosts prevents some pathological behaviour: Stack marking
-  # becomes more expensive with large stacks and large stacks mean that
-  # cells with RC=0 are more likely to be kept alive by the stack.
-  when nimCoroutines:
-    let stackMarkCosts = max(currentStackSizes() div (16*sizeof(int)), ZctThreshold)
-  else:
-    let stackMarkCosts = max(stackSize() div (16*sizeof(int)), ZctThreshold)
-  if (gch.greyStack.len >= stackMarkCosts or (cycleGC and
-      getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and
-      gch.recGcLock == 0:
-    collectCTBody(gch)
-
-when withRealTime:
-  proc toNano(x: int): Nanos {.inline.} =
-    result = x * 1000
-
-  proc GC_setMaxPause*(MaxPauseInUs: int) =
-    gch.maxPause = MaxPauseInUs.toNano
-
-  proc GC_step(gch: var GcHeap, us: int, strongAdvice: bool) =
-    gch.maxPause = us.toNano
-    #if (getOccupiedMem(gch.region)>=gch.cycleThreshold) or
-    #    alwaysGC or strongAdvice:
-    collectCTBody(gch)
-
-  proc GC_step*(us: int, strongAdvice = false, stackSize = -1) {.noinline.} =
-    if stackSize >= 0:
-      var stackTop {.volatile.}: pointer
-      gch.getActiveStack().pos = addr(stackTop)
-
-      for stack in gch.stack.items():
-        stack.bottomSaved = stack.bottom
-        when stackIncreases:
-          stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize)
-        else:
-          stack.bottom = cast[pointer](
-            cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize)
-
-    GC_step(gch, us, strongAdvice)
-
-    if stackSize >= 0:
-      for stack in gch.stack.items():
-        stack.bottom = stack.bottomSaved
-
-when not defined(useNimRtl):
-  proc GC_disable() =
-    inc(gch.recGcLock)
-  proc GC_enable() =
-    if gch.recGcLock > 0:
-      dec(gch.recGcLock)
-
-  proc GC_setStrategy(strategy: GC_Strategy) =
-    discard
-
-  proc GC_enableMarkAndSweep() = discard
-  proc GC_disableMarkAndSweep() = discard
-
-  proc GC_fullCollect() =
-    var oldThreshold = gch.cycleThreshold
-    gch.cycleThreshold = 0 # forces cycle collection
-    collectCT(gch)
-    gch.cycleThreshold = oldThreshold
-
-  proc GC_getStatistics(): string =
-    GC_disable()
-    result = "[GC] total memory: " & $(getTotalMem()) & "\n" &
-             "[GC] occupied memory: " & $(getOccupiedMem()) & "\n" &
-             "[GC] stack scans: " & $gch.stat.stackScans & "\n" &
-             "[GC] stack cells: " & $gch.stat.maxStackCells & "\n" &
-             "[GC] completed collections: " & $gch.stat.completedCollections & "\n" &
-             "[GC] max threshold: " & $gch.stat.maxThreshold & "\n" &
-             "[GC] grey stack capacity: " & $gch.greyStack.cap & "\n" &
-             "[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
-             "[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000) & "\n"
-    when nimCoroutines:
-      result.add "[GC] number of stacks: " & $gch.stack.len & "\n"
-      for stack in items(gch.stack):
-        result.add "[GC]   stack " & stack.bottom.repr & "[GC]     max stack size " & $stack.maxStackSize & "\n"
-    else:
-      result.add "[GC] max stack size: " & $gch.stat.maxStackSize & "\n"
-    GC_enable()
-
-{.pop.}
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index ea8857ece..eb0884560 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -222,9 +222,9 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} =
 
   if pos != nil:
     when stackIncreases:
-      result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom)
+      result = cast[int](pos) -% cast[int](stack.bottom)
     else:
-      result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos)
+      result = cast[int](stack.bottom) -% cast[int](pos)
   else:
     result = 0
 
@@ -295,8 +295,8 @@ when not defined(useNimRtl):
       # the first init must be the one that defines the stack bottom:
       gch.stack.bottom = theStackBottom
     elif theStackBottom != gch.stack.bottom:
-      var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2
-      var b = cast[ByteAddress](gch.stack.bottom)
+      var a = cast[int](theStackBottom) # and not PageMask - PageSize*2
+      var b = cast[int](gch.stack.bottom)
       #c_fprintf(stdout, "old: %p new: %p;\n",gch.stack.bottom,theStackBottom)
       when stackIncreases:
         gch.stack.bottom = cast[pointer](min(a, b))
@@ -312,11 +312,11 @@ when not defined(useNimRtl):
 proc isOnStack(p: pointer): bool =
   var stackTop {.volatile, noinit.}: pointer
   stackTop = addr(stackTop)
-  var a = cast[ByteAddress](gch.getActiveStack().bottom)
-  var b = cast[ByteAddress](stackTop)
+  var a = cast[int](gch.getActiveStack().bottom)
+  var b = cast[int](stackTop)
   when not stackIncreases:
     swap(a, b)
-  var x = cast[ByteAddress](p)
+  var x = cast[int](p)
   result = a <=% x and x <=% b
 
 when defined(sparc): # For SPARC architecture.
@@ -337,7 +337,7 @@ when defined(sparc): # For SPARC architecture.
     # Addresses decrease as the stack grows.
     while sp <= max:
       gcMark(gch, sp[])
-      sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer))
+      sp = cast[PPointer](cast[int](sp) +% sizeof(pointer))
 
 elif defined(ELATE):
   {.error: "stack marking code is to be written for this architecture".}
@@ -354,8 +354,8 @@ elif stackIncreases:
 
   template forEachStackSlotAux(gch, gcMark: untyped) {.dirty.} =
     for stack in gch.stack.items():
-      var max = cast[ByteAddress](gch.stack.bottom)
-      var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer)
+      var max = cast[int](gch.stack.bottom)
+      var sp = cast[int](addr(registers)) -% sizeof(pointer)
       while sp >=% max:
         gcMark(gch, cast[PPointer](sp)[])
         sp = sp -% sizeof(pointer)
@@ -383,15 +383,14 @@ else:
     gch.getActiveStack().setPosition(addr(registers))
     if c_setjmp(registers) == 0'i32: # To fill the C stack with registers.
       for stack in gch.stack.items():
-        var max = cast[ByteAddress](stack.bottom)
-        var sp = cast[ByteAddress](addr(registers))
+        var max = cast[int](stack.bottom)
+        var sp = cast[int](addr(registers))
         when defined(amd64):
           if stack.isActiveStack():
             # words within the jmp_buf structure may not be properly aligned.
             let regEnd = sp +% sizeof(registers)
             while sp <% regEnd:
               gcMark(gch, cast[PPointer](sp)[])
-              gcMark(gch, cast[PPointer](sp +% sizeof(pointer) div 2)[])
               sp = sp +% sizeof(pointer)
         # Make sure sp is word-aligned
         sp = sp and not (sizeof(pointer) - 1)
@@ -472,7 +471,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
     inc globalMarkersLen
   else:
     cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
-    quit 1
+    rawQuit 1
 
 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
   if threadLocalMarkersLen <= high(threadLocalMarkers):
@@ -480,4 +479,4 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.}
     inc threadLocalMarkersLen
   else:
     cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
-    quit 1
+    rawQuit 1
diff --git a/lib/system/gc_hooks.nim b/lib/system/gc_hooks.nim
index 70f02e657..ace62eea0 100644
--- a/lib/system/gc_hooks.nim
+++ b/lib/system/gc_hooks.nim
@@ -24,7 +24,7 @@ proc nimRegisterGlobalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
     inc globalMarkersLen
   else:
     cstderr.rawWrite("[GC] cannot register global variable; too many global variables")
-    quit 1
+    rawQuit 1
 
 proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.} =
   if threadLocalMarkersLen <= high(threadLocalMarkers):
@@ -32,7 +32,7 @@ proc nimRegisterThreadLocalMarker(markerProc: GlobalMarkerProc) {.compilerproc.}
     inc threadLocalMarkersLen
   else:
     cstderr.rawWrite("[GC] cannot register thread local variable; too many thread local variables")
-    quit 1
+    rawQuit 1
 
 proc traverseGlobals*() =
   for i in 0..globalMarkersLen-1:
diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim
index 0675b9f2e..c885a6893 100644
--- a/lib/system/gc_ms.nim
+++ b/lib/system/gc_ms.nim
@@ -27,7 +27,7 @@ when defined(memProfiler):
   proc nimProfile(requestedSize: int)
 
 when hasThreadSupport:
-  import sharedlist
+  import std/sharedlist
 
 type
   WalkOp = enum
@@ -90,15 +90,15 @@ template gcAssert(cond: bool, msg: string) =
     if not cond:
       cstderr.rawWrite "[GCASSERT] "
       cstderr.rawWrite msg
-      quit 1
+      rawQuit 1
 
 proc cellToUsr(cell: PCell): pointer {.inline.} =
   # convert object (=pointer to refcount) to pointer to userdata
-  result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell)))
+  result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell)))
 
 proc usrToCell(usr: pointer): PCell {.inline.} =
   # convert pointer to userdata to object (=pointer to refcount)
-  result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell)))
+  result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell)))
 
 proc extGetCellType(c: pointer): PNimType {.compilerproc.} =
   # used for code generation concerning debugging
@@ -217,7 +217,7 @@ proc initGC() =
     gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID")
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   case n.kind
   of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op)
   of nkList:
@@ -229,7 +229,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} =
   of nkNone: sysAssert(false, "forAllSlotsAux")
 
 proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) =
-  var d = cast[ByteAddress](dest)
+  var d = cast[int](dest)
   if dest == nil: return # nothing to do
   if ntfNoRefs notin mt.flags:
     case mt.kind
@@ -255,7 +255,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) =
       forAllChildrenAux(cellToUsr(cell), cell.typ.base, op)
     of tySequence:
       when not defined(nimSeqsV2):
-        var d = cast[ByteAddress](cellToUsr(cell))
+        var d = cast[int](cellToUsr(cell))
         var s = cast[PGenericSeq](d)
         if s != nil:
           for i in 0..s.len-1:
@@ -268,7 +268,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
   gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
   collectCT(gch, size + sizeof(Cell))
   var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell)))
-  gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
+  gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2")
   # now it is buffered in the ZCT
   res.typ = typ
   when leakDetector and not hasThreadSupport:
@@ -336,9 +336,9 @@ when not defined(nimSeqsV2):
 
     var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize
     copyMem(res, ol, oldsize + sizeof(Cell))
-    zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)),
+    zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)),
             newsize-oldsize)
-    sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3")
+    sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3")
     when withBitvectors: incl(gch.allocated, res)
     when useCellIds:
       inc gch.idGenerator
@@ -446,7 +446,7 @@ proc markGlobals(gch: var GcHeap) =
 
 proc gcMark(gch: var GcHeap, p: pointer) {.inline.} =
   # the addresses are not as cells on the stack, so turn them to cells:
-  var c = cast[ByteAddress](p)
+  var c = cast[int](p)
   if c >% PageSize:
     # fast check: does it look like a cell?
     var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p))
diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim
index 6ced04c99..d96de7eac 100644
--- a/lib/system/gc_regions.nim
+++ b/lib/system/gc_regions.nim
@@ -7,6 +7,7 @@
 #
 
 # "Stack GC" for embedded devices or ultra performance requirements.
+import std/private/syslocks
 
 when defined(memProfiler):
   proc nimProfile(requestedSize: int) {.benign.}
diff --git a/lib/system/hti.nim b/lib/system/hti.nim
index 9acaae88b..a26aff982 100644
--- a/lib/system/hti.nim
+++ b/lib/system/hti.nim
@@ -59,7 +59,7 @@ type
     tyOwned, tyUnused1, tyUnused2,
     tyVarargsHidden,
     tyUncheckedArray,
-    tyProxyHidden,
+    tyErrorHidden,
     tyBuiltInTypeClassHidden,
     tyUserTypeClassHidden,
     tyUserTypeClassInstHidden,
diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim
index ca41f39c6..3bf0b9893 100644
--- a/lib/system/inclrtl.nim
+++ b/lib/system/inclrtl.nim
@@ -45,7 +45,6 @@ else:
   {.pragma: inl, inline.}
   {.pragma: compilerRtl, compilerproc.}
 
-{.pragma: benign, gcsafe, locks: 0.}
+{.pragma: benign, gcsafe.}
 
-when defined(nimHasSinkInference):
-  {.push sinkInference: on.}
+{.push sinkInference: on.}
diff --git a/lib/system/indices.nim b/lib/system/indices.nim
new file mode 100644
index 000000000..f2bad2528
--- /dev/null
+++ b/lib/system/indices.nim
@@ -0,0 +1,164 @@
+when not defined(nimHasSystemRaisesDefect):
+  {.pragma: systemRaisesDefect.}
+
+type
+  BackwardsIndex* = distinct int ## Type that is constructed by `^` for
+                                 ## reversed array accesses.
+                                 ## (See `^ template <#^.t,int>`_)
+
+template `^`*(x: int): BackwardsIndex = BackwardsIndex(x)
+  ## Builtin `roof`:idx: operator that can be used for convenient array access.
+  ## `a[^x]` is a shortcut for `a[a.len-x]`.
+  ##
+  ##   ```nim
+  ##   let
+  ##     a = [1, 3, 5, 7, 9]
+  ##     b = "abcdefgh"
+  ##
+  ##   echo a[^1] # => 9
+  ##   echo b[^2] # => g
+  ##   ```
+
+proc `[]`*[T](s: openArray[T]; i: BackwardsIndex): T {.inline, systemRaisesDefect.} =
+  system.`[]`(s, s.len - int(i))
+
+proc `[]`*[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))]
+proc `[]`*(s: string; i: BackwardsIndex): char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
+
+proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} =
+  system.`[]`(s, s.len - int(i))
+proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))]
+proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline, systemRaisesDefect.} = s[s.len - int(i)]
+
+proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} =
+  system.`[]=`(s, s.len - int(i), x)
+proc `[]=`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T) {.inline, systemRaisesDefect.} =
+  a[Idx(a.len - int(i) + int low(a))] = x
+proc `[]=`*(s: var string; i: BackwardsIndex; x: char) {.inline, systemRaisesDefect.} =
+  s[s.len - int(i)] = x
+
+template `..^`*(a, b: untyped): untyped =
+  ## A shortcut for `.. ^` to avoid the common gotcha that a space between
+  ## '..' and '^' is required.
+  a .. ^b
+
+template `..<`*(a, b: untyped): untyped =
+  ## A shortcut for `a .. pred(b)`.
+  ##   ```nim
+  ##   for i in 5 ..< 9:
+  ##     echo i # => 5; 6; 7; 8
+  ##   ```
+  a .. (when b is BackwardsIndex: succ(b) else: pred(b))
+
+template `[]`*(s: string; i: int): char = arrGet(s, i)
+template `[]=`*(s: string; i: int; val: char) = arrPut(s, i, val)
+
+template `^^`(s, i: untyped): untyped =
+  (when i is BackwardsIndex: s.len - int(i) else: int(i))
+
+template spliceImpl(s, a, L, b: typed): untyped =
+  # make room for additional elements or cut:
+  var shift = b.len - max(0,L)  # ignore negative slice size
+  var newLen = s.len + shift
+  if shift > 0:
+    # enlarge:
+    setLen(s, newLen)
+    for i in countdown(newLen-1, a+b.len): movingCopy(s[i], s[i-shift])
+  else:
+    for i in countup(a+b.len, newLen-1): movingCopy(s[i], s[i-shift])
+    # cut down:
+    setLen(s, newLen)
+  # fill the hole:
+  for i in 0 ..< b.len: s[a+i] = b[i]
+
+proc `[]`*[T, U: Ordinal](s: string, x: HSlice[T, U]): string {.inline, systemRaisesDefect.} =
+  ## Slice operation for strings.
+  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
+  ##   ```nim
+  ##   var s = "abcdef"
+  ##   assert s[1..3] == "bcd"
+  ##   ```
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  result = newString(L)
+  for i in 0 ..< L: result[i] = s[i + a]
+
+proc `[]=`*[T, U: Ordinal](s: var string, x: HSlice[T, U], b: string) {.systemRaisesDefect.} =
+  ## Slice assignment for strings.
+  ##
+  ## If `b.len` is not exactly the number of elements that are referred to
+  ## by `x`, a `splice`:idx: is performed:
+  ##
+  runnableExamples:
+    var s = "abcdefgh"
+    s[1 .. ^2] = "xyz"
+    assert s == "axyzh"
+
+  var a = s ^^ x.a
+  var L = (s ^^ x.b) - a + 1
+  if L == b.len:
+    for i in 0..<L: s[i+a] = b[i]
+  else:
+    spliceImpl(s, a, L, b)
+
+proc `[]`*[Idx, T; U, V: Ordinal](a: array[Idx, T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} =
+  ## Slice operation for arrays.
+  ## Returns the inclusive range `[a[x.a], a[x.b]]`:
+  ##   ```nim
+  ##   var a = [1, 2, 3, 4]
+  ##   assert a[0..2] == @[1, 2, 3]
+  ##   ```
+  ##
+  ## See also:
+  ## * `toOpenArray(array[I, T];I,I) <#toOpenArray,array[I,T],I,I>`_
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
+  result = newSeq[T](L)
+  for i in 0..<L: result[i] = a[Idx(i + xa)]
+
+proc `[]=`*[Idx, T; U, V: Ordinal](a: var array[Idx, T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} =
+  ## Slice assignment for arrays.
+  ##   ```nim
+  ##   var a = [10, 20, 30, 40, 50]
+  ##   a[1..2] = @[99, 88]
+  ##   assert a == [10, 99, 88, 40, 50]
+  ##   ```
+  let xa = a ^^ x.a
+  let L = (a ^^ x.b) - xa + 1
+  if L == b.len:
+    for i in 0..<L: a[Idx(i + xa)] = b[i]
+  else:
+    sysFatal(RangeDefect, "different lengths for slice assignment")
+
+proc `[]`*[T; U, V: Ordinal](s: openArray[T], x: HSlice[U, V]): seq[T] {.systemRaisesDefect.} =
+  ## Slice operation for sequences.
+  ## Returns the inclusive range `[s[x.a], s[x.b]]`:
+  ##   ```nim
+  ##   var s = @[1, 2, 3, 4]
+  ##   assert s[0..2] == @[1, 2, 3]
+  ##   ```
+  ##
+  ## See also:
+  ## * `toOpenArray(openArray[T];int,int) <#toOpenArray,openArray[T],int,int>`_
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  newSeq(result, L)
+  for i in 0 ..< L: result[i] = s[i + a]
+
+proc `[]=`*[T; U, V: Ordinal](s: var seq[T], x: HSlice[U, V], b: openArray[T]) {.systemRaisesDefect.} =
+  ## Slice assignment for sequences.
+  ##
+  ## If `b.len` is not exactly the number of elements that are referred to
+  ## by `x`, a `splice`:idx: is performed.
+  runnableExamples:
+    var s = @"abcdefgh"
+    s[1 .. ^2] = @"xyz"
+    assert s == @"axyzh"
+  let a = s ^^ x.a
+  let L = (s ^^ x.b) - a + 1
+  if L == b.len:
+    for i in 0 ..< L: s[i+a] = b[i]
+  else:
+    spliceImpl(s, a, L, b)
diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim
index 220e341b3..125bee98f 100644
--- a/lib/system/iterators.nim
+++ b/lib/system/iterators.nim
@@ -1,17 +1,24 @@
+## Default iterators for some Nim types.
+
 when defined(nimPreviewSlimSystem):
   import std/assertions
 
-when defined(nimHasLentIterators) and not defined(nimNoLentIterators):
+when not defined(nimNoLentIterators):
   template lent2(T): untyped = lent T
 else:
   template lent2(T): untyped = T
 
+template unCheckedInc(x) =
+  {.push overflowChecks: off.}
+  inc(x)
+  {.pop.}
+
 iterator items*[T: not char](a: openArray[T]): lent2 T {.inline.} =
   ## Iterates over each item of `a`.
   var i = 0
   while i < len(a):
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
 
 iterator items*[T: char](a: openArray[T]): T {.inline.} =
   ## Iterates over each item of `a`.
@@ -21,14 +28,14 @@ iterator items*[T: char](a: openArray[T]): T {.inline.} =
   var i = 0
   while i < len(a):
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
 
 iterator mitems*[T](a: var openArray[T]): var T {.inline.} =
   ## Iterates over each item of `a` so that you can modify the yielded value.
   var i = 0
   while i < len(a):
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
 
 iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
   ## Iterates over each item of `a`.
@@ -37,7 +44,7 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} =
     while true:
       yield a[i]
       if i >= high(IX): break
-      inc(i)
+      unCheckedInc(i)
 
 iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} =
   ## Iterates over each item of `a` so that you can modify the yielded value.
@@ -46,7 +53,7 @@ iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} =
     while true:
       yield a[i]
       if i >= high(IX): break
-      inc(i)
+      unCheckedInc(i)
 
 iterator items*[T](a: set[T]): T {.inline.} =
   ## Iterates over each element of `a`. `items` iterates only over the
@@ -54,8 +61,11 @@ iterator items*[T](a: set[T]): T {.inline.} =
   ## able to hold).
   var i = low(T).int
   while i <= high(T).int:
-    if T(i) in a: yield T(i)
-    inc(i)
+    when T is enum and not defined(js):
+      if cast[T](i) in a: yield cast[T](i)
+    else:
+      if T(i) in a: yield T(i)
+    unCheckedInc(i)
 
 iterator items*(a: cstring): char {.inline.} =
   ## Iterates over each item of `a`.
@@ -74,7 +84,7 @@ iterator items*(a: cstring): char {.inline.} =
     let n = len(a)
     while i < n:
       yield a[i]
-      inc(i)
+      unCheckedInc(i)
   when defined(js): impl()
   else:
     when nimvm:
@@ -84,7 +94,7 @@ iterator items*(a: cstring): char {.inline.} =
       var i = 0
       while a[i] != '\0':
         yield a[i]
-        inc(i)
+        unCheckedInc(i)
 
 iterator mitems*(a: var cstring): var char {.inline.} =
   ## Iterates over each item of `a` so that you can modify the yielded value.
@@ -107,7 +117,7 @@ iterator mitems*(a: var cstring): var char {.inline.} =
     let n = len(a)
     while i < n:
       yield a[i]
-      inc(i)
+      unCheckedInc(i)
   when defined(js): impl()
   else:
     when nimvm: impl()
@@ -115,7 +125,7 @@ iterator mitems*(a: var cstring): var char {.inline.} =
       var i = 0
       while a[i] != '\0':
         yield a[i]
-        inc(i)
+        unCheckedInc(i)
 
 iterator items*[T: enum and Ordinal](E: typedesc[T]): T =
   ## Iterates over the values of `E`.
@@ -138,7 +148,7 @@ iterator pairs*[T](a: openArray[T]): tuple[key: int, val: T] {.inline.} =
   var i = 0
   while i < len(a):
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
 
 iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} =
   ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
@@ -146,7 +156,7 @@ iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.}
   var i = 0
   while i < len(a):
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
 
 iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} =
   ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
@@ -155,7 +165,7 @@ iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} =
     while true:
       yield (i, a[i])
       if i >= high(IX): break
-      inc(i)
+      unCheckedInc(i)
 
 iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} =
   ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
@@ -165,7 +175,7 @@ iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inlin
     while true:
       yield (i, a[i])
       if i >= high(IX): break
-      inc(i)
+      unCheckedInc(i)
 
 iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} =
   ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
@@ -173,7 +183,7 @@ iterator pairs*[T](a: seq[T]): tuple[key: int, val: T] {.inline.} =
   let L = len(a)
   while i < L:
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the seq changed while iterating over it")
 
 iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} =
@@ -183,7 +193,7 @@ iterator mpairs*[T](a: var seq[T]): tuple[key: int, val: var T] {.inline.} =
   let L = len(a)
   while i < L:
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the seq changed while iterating over it")
 
 iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} =
@@ -192,7 +202,7 @@ iterator pairs*(a: string): tuple[key: int, val: char] {.inline.} =
   let L = len(a)
   while i < L:
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the string changed while iterating over it")
 
 iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} =
@@ -202,7 +212,7 @@ iterator mpairs*(a: var string): tuple[key: int, val: var char] {.inline.} =
   let L = len(a)
   while i < L:
     yield (i, a[i])
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the string changed while iterating over it")
 
 iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} =
@@ -212,12 +222,12 @@ iterator pairs*(a: cstring): tuple[key: int, val: char] {.inline.} =
     var L = len(a)
     while i < L:
       yield (i, a[i])
-      inc(i)
+      unCheckedInc(i)
   else:
     var i = 0
     while a[i] != '\0':
       yield (i, a[i])
-      inc(i)
+      unCheckedInc(i)
 
 iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
   ## Iterates over each item of `a`. Yields `(index, a[index])` pairs.
@@ -227,12 +237,12 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} =
     var L = len(a)
     while i < L:
       yield (i, a[i])
-      inc(i)
+      unCheckedInc(i)
   else:
     var i = 0
     while a[i] != '\0':
       yield (i, a[i])
-      inc(i)
+      unCheckedInc(i)
 
 iterator items*[T](a: seq[T]): lent2 T {.inline.} =
   ## Iterates over each item of `a`.
@@ -240,7 +250,7 @@ iterator items*[T](a: seq[T]): lent2 T {.inline.} =
   let L = len(a)
   while i < L:
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the seq changed while iterating over it")
 
 iterator mitems*[T](a: var seq[T]): var T {.inline.} =
@@ -249,7 +259,7 @@ iterator mitems*[T](a: var seq[T]): var T {.inline.} =
   let L = len(a)
   while i < L:
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the seq changed while iterating over it")
 
 iterator items*(a: string): char {.inline.} =
@@ -258,7 +268,7 @@ iterator items*(a: string): char {.inline.} =
   let L = len(a)
   while i < L:
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the string changed while iterating over it")
 
 iterator mitems*(a: var string): var char {.inline.} =
@@ -267,7 +277,7 @@ iterator mitems*(a: var string): var char {.inline.} =
   let L = len(a)
   while i < L:
     yield a[i]
-    inc(i)
+    unCheckedInc(i)
     assert(len(a) == L, "the length of the string changed while iterating over it")
 
 
diff --git a/lib/system/iterators_1.nim b/lib/system/iterators_1.nim
index be61fd62c..d00e3f823 100644
--- a/lib/system/iterators_1.nim
+++ b/lib/system/iterators_1.nim
@@ -16,7 +16,7 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} =
     let x = collect(newSeq):
       for i in countdown(7, 3):
         i
-    
+
     assert x == @[7, 6, 5, 4, 3]
 
     let y = collect(newseq):
@@ -32,7 +32,10 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} =
   elif T is IntLikeForCount and T is Ordinal:
     var res = int(a)
     while res >= int(b):
-      yield T(res)
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
       dec(res, step)
   else:
     var res = a
@@ -64,7 +67,10 @@ iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} =
   when T is IntLikeForCount and T is Ordinal:
     var res = int(a)
     while res <= int(b):
-      yield T(res)
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
       inc(res, step)
   else:
     var res = a
@@ -89,7 +95,10 @@ iterator `..`*[T](a, b: T): T {.inline.} =
   when T is IntLikeForCount and T is Ordinal:
     var res = int(a)
     while res <= int(b):
-      yield T(res)
+      when defined(nimHasCastExtendedVm):
+        yield cast[T](res)
+      else:
+        yield T(res)
       inc(res)
   else:
     var res = a
diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim
index a31de0d86..5599240fd 100644
--- a/lib/system/jssys.nim
+++ b/lib/system/jssys.nim
@@ -49,7 +49,7 @@ proc nimCharToStr(x: char): string {.compilerproc.} =
   result[0] = x
 
 proc isNimException(): bool {.asmNoStackFrame.} =
-  asm "return `lastJSError` && `lastJSError`.m_type;"
+  {.emit: "return `lastJSError` && `lastJSError`.m_type;".}
 
 proc getCurrentException*(): ref Exception {.compilerRtl, benign.} =
   if isNimException(): result = cast[ref Exception](lastJSError)
@@ -72,6 +72,10 @@ proc getCurrentExceptionMsg*(): string =
 proc setCurrentException*(exc: ref Exception) =
   lastJSError = cast[PJSError](exc)
 
+proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
+  ## Used to set up exception handling for closure iterators
+  setCurrentException(e)
+
 proc auxWriteStackTrace(f: PCallFrame): string =
   type
     TempFrame = tuple[procname: cstring, line: int, filename: cstring]
@@ -148,7 +152,7 @@ proc raiseException(e: ref Exception, ename: cstring) {.
     unhandledException(e)
   when NimStackTrace:
     e.trace = rawWriteStackTrace()
-  asm "throw `e`;"
+  {.emit: "throw `e`;".}
 
 proc reraiseException() {.compilerproc, asmNoStackFrame.} =
   if lastJSError == nil:
@@ -158,7 +162,7 @@ proc reraiseException() {.compilerproc, asmNoStackFrame.} =
       if isNimException():
         unhandledException(cast[ref Exception](lastJSError))
 
-    asm "throw lastJSError;"
+    {.emit: "throw lastJSError;".}
 
 proc raiseOverflow {.exportc: "raiseOverflow", noreturn, compilerproc.} =
   raise newException(OverflowDefect, "over- or underflow")
@@ -176,7 +180,7 @@ proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} =
   raise newException(FieldDefect, formatFieldDefect(f, discVal))
 
 proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var i = 0; i < arguments.length; ++i) {
       var x = arguments[i];
@@ -189,7 +193,7 @@ proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
       }
     }
     return result;
-  """
+  """.}
 
 proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} =
   {.emit: """
@@ -277,62 +281,64 @@ proc toJSStr(s: string): cstring {.compilerproc.} =
   result = join(res)
 
 proc mnewString(len: int): string {.asmNoStackFrame, compilerproc.} =
-  asm """
-    return new Array(`len`);
-  """
+  {.emit: """
+    var result = new Array(`len`);
+    for (var i = 0; i < `len`; i++) {result[i] = 0;}
+    return result;
+  """.}
 
 proc SetCard(a: int): int {.compilerproc, asmNoStackFrame.} =
   # argument type is a fake
-  asm """
+  {.emit: """
     var result = 0;
     for (var elem in `a`) { ++result; }
     return result;
-  """
+  """.}
 
 proc SetEq(a, b: int): bool {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     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.} =
-  asm """
+  {.emit: """
     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.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var elem in `a`) {
       if (`b`[elem]) { result[elem] = true; }
     }
     return result;
-  """
+  """.}
 
 proc SetPlus(a, b: int): int {.compilerproc, asmNoStackFrame.} =
-  asm """
+  {.emit: """
     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.} =
-  asm """
+  {.emit: """
     var result = {};
     for (var elem in `a`) {
       if (!`b`[elem]) { result[elem] = true; }
     }
     return result;
-  """
+  """.}
 
 proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`a` == `b`) return 0;
     if (!`a`) return -1;
     if (!`b`) return 1;
@@ -341,7 +347,7 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} =
       if (result != 0) return result;
     }
     return `a`.length - `b`.length;
-  """
+  """.}
 
 proc cmp(x, y: string): int =
   when nimvm:
@@ -352,7 +358,7 @@ proc cmp(x, y: string): int =
     result = cmpStrings(x, y)
 
 proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`a` == `b`) return true;
     if (`a` === null && `b`.length == 0) return true;
     if (`b` === null && `a`.length == 0) return true;
@@ -362,29 +368,29 @@ proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} =
     for (var i = 0; i < alen; ++i)
       if (`a`[i] != `b`[i]) return false;
     return true;
-  """
+  """.}
 
 when defined(kwin):
   proc rawEcho {.compilerproc, asmNoStackFrame.} =
-    asm """
+    {.emit: """
       var buf = "";
       for (var i = 0; i < arguments.length; ++i) {
         buf += `toJSStr`(arguments[i]);
       }
       print(buf);
-    """
+    """.}
 
 elif not defined(nimOldEcho):
   proc ewriteln(x: cstring) = log(x)
 
   proc rawEcho {.compilerproc, asmNoStackFrame.} =
-    asm """
+    {.emit: """
       var buf = "";
       for (var i = 0; i < arguments.length; ++i) {
         buf += `toJSStr`(arguments[i]);
       }
       console.log(buf);
-    """
+    """.}
 
 else:
   proc ewriteln(x: cstring) =
@@ -412,84 +418,84 @@ else:
 
 # Arithmetic:
 proc checkOverflowInt(a: int) {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`a` > 2147483647 || `a` < -2147483648) `raiseOverflow`();
-  """
+  """.}
 
 proc addInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` + `b`;
     `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc subInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` - `b`;
     `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc mulInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     var result = `a` * `b`;
     `checkOverflowInt`(result);
     return result;
-  """
+  """.}
 
 proc divInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`b` == 0) `raiseDivByZero`();
     if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
     return Math.trunc(`a` / `b`);
-  """
+  """.}
 
 proc modInt(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+  {.emit: """
     if (`b` == 0) `raiseDivByZero`();
     if (`b` == -1 && `a` == 2147483647) `raiseOverflow`();
     return Math.trunc(`a` % `b`);
-  """
+  """.}
 
-proc checkOverflowInt64(a: int) {.asmNoStackFrame, compilerproc.} =
-  asm """
-    if (`a` > 9223372036854775807 || `a` < -9223372036854775808) `raiseOverflow`();
-  """
+proc checkOverflowInt64(a: int64) {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`a` > 9223372036854775807n || `a` < -9223372036854775808n) `raiseOverflow`();
+  """.}
 
-proc addInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc addInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` + `b`;
     `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc subInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc subInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` - `b`;
     `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc mulInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
+proc mulInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
     var result = `a` * `b`;
     `checkOverflowInt64`(result);
     return result;
-  """
+  """.}
 
-proc divInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
-    if (`b` == 0) `raiseDivByZero`();
-    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-    return Math.trunc(`a` / `b`);
-  """
+proc divInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`b` == 0n) `raiseDivByZero`();
+    if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`();
+    return `a` / `b`;
+  """.}
 
-proc modInt64(a, b: int): int {.asmNoStackFrame, compilerproc.} =
-  asm """
-    if (`b` == 0) `raiseDivByZero`();
-    if (`b` == -1 && `a` == 9223372036854775807) `raiseOverflow`();
-    return Math.trunc(`a` % `b`);
-  """
+proc modInt64(a, b: int64): int64 {.asmNoStackFrame, compilerproc.} =
+  {.emit: """
+    if (`b` == 0n) `raiseDivByZero`();
+    if (`b` == -1n && `a` == 9223372036854775807n) `raiseOverflow`();
+    return `a` % `b`;
+  """.}
 
 proc negInt(a: int): int {.compilerproc.} =
   result = a*(-1)
@@ -503,32 +509,10 @@ proc absInt(a: int): int {.compilerproc.} =
 proc absInt64(a: int64): int64 {.compilerproc.} =
   result = if a < 0: a*(-1) else: a
 
-when not defined(nimNoZeroExtendMagic):
-  proc ze*(a: int): int {.compilerproc.} =
-    result = a
-
-  proc ze64*(a: int64): int64 {.compilerproc.} =
-    result = a
-
-  proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} =
-    asm """
-      return `a`;
-    """
-
-  proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} =
-    asm """
-      return `a`;
-    """
-
-  proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} =
-    asm """
-      return `a`;
-    """
-
 proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b
 proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b
 
-proc chckNilDisp(p: pointer) {.compilerproc.} =
+proc chckNilDisp(p: JSRef) {.compilerproc.} =
   if p == nil:
     sysFatal(NilAccessDefect, "cannot dispatch; dispatcher is nil")
 
@@ -546,22 +530,22 @@ proc nimCopyAux(dest, src: JSRef, n: ptr TNimNode) {.compilerproc.} =
   case n.kind
   of nkNone: sysAssert(false, "nimCopyAux")
   of nkSlot:
-    asm """
+    {.emit: """
       `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
-    """
+    """.}
   of nkList:
-    asm """
+    {.emit: """
     for (var i = 0; i < `n`.sons.length; i++) {
       nimCopyAux(`dest`, `src`, `n`.sons[i]);
     }
-    """
+    """.}
   of nkCase:
-    asm """
+    {.emit: """
       `dest`[`n`.offset] = nimCopy(`dest`[`n`.offset], `src`[`n`.offset], `n`.typ);
       for (var i = 0; i < `n`.sons.length; ++i) {
         nimCopyAux(`dest`, `src`, `n`.sons[i][1]);
       }
-    """
+    """.}
 
 proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
   case ti.kind
@@ -569,9 +553,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
     if not isFatPointer(ti):
       result = src
     else:
-      asm "`result` = [`src`[0], `src`[1]];"
+      {.emit: "`result` = [`src`[0], `src`[1]];".}
   of tySet:
-    asm """
+    {.emit: """
       if (`dest` === null || `dest` === undefined) {
         `dest` = {};
       }
@@ -580,18 +564,18 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
       }
       for (var key in `src`) { `dest`[key] = `src`[key]; }
       `result` = `dest`;
-    """
+    """.}
   of tyTuple, tyObject:
     if ti.base != nil: result = nimCopy(dest, src, ti.base)
     elif ti.kind == tyObject:
-      asm "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;"
+      {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {m_type: `ti`} : `dest`;".}
     else:
-      asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;"
+      {.emit: "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;".}
     nimCopyAux(result, src, ti.node)
   of tyArrayConstr, tyArray:
     # In order to prevent a type change (TypedArray -> Array) and to have better copying performance,
     # arrays constructors are considered separately
-    asm """
+    {.emit: """
       if(ArrayBuffer.isView(`src`)) { 
         if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) {
           `dest` = new `src`.constructor(`src`);
@@ -613,9 +597,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
           }
         }
       }
-    """
+    """.}
   of tySequence, tyOpenArray:
-    asm """
+    {.emit: """
       if (`src` === null) {
         `result` = null;
       }
@@ -628,55 +612,24 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef =
           `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base);
         }
       }
-    """
+    """.}
   of tyString:
-    asm """
+    {.emit: """
       if (`src` !== null) {
         `result` = `src`.slice(0);
       }
-    """
+    """.}
   else:
     result = src
 
-proc genericReset(x: JSRef, ti: PNimType): JSRef {.compilerproc.} =
-  asm "`result` = null;"
-  case ti.kind
-  of tyPtr, tyRef, tyVar, tyNil:
-    if isFatPointer(ti):
-      asm """
-        `result` = [null, 0];
-      """
-  of tySet:
-    asm """
-      `result` = {};
-    """
-  of tyTuple, tyObject:
-    if ti.kind == tyObject:
-      asm "`result` = {m_type: `ti`};"
-    else:
-      asm "`result` = {};"
-  of tySequence, tyOpenArray, tyString:
-    asm """
-      `result` = [];
-    """
-  of tyArrayConstr, tyArray:
-    asm """
-      `result` = new Array(`x`.length);
-      for (var i = 0; i < `x`.length; ++i) {
-        `result`[i] = genericReset(`x`[i], `ti`.base);
-      }
-    """
-  else:
-    discard
-
 proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.
                 asmNoStackFrame, compilerproc.} =
   # types are fake
-  asm """
+  {.emit: """
     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
@@ -705,7 +658,7 @@ proc isObj(obj, subclass: PNimType): bool {.compilerproc.} =
   return true
 
 proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} =
-  asm "`x`.push(`c`);"
+  {.emit: "`x`.push(`c`);".}
 
 {.pop.}
 
@@ -725,15 +678,20 @@ const
   IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
 
 
-proc parseFloatNative(a: string): float =
-  let a2 = a.cstring
-  asm """
-  `result` = Number(`a2`);
-  """
+proc parseFloatNative(a: openarray[char]): float =
+  var str = ""
+  for x in a:
+    str.add x
+
+  let cstr = cstring str
+
+  {.emit: """
+  `result` = Number(`cstr`);
+  """.}
 
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int {.compilerproc.} =
+proc nimParseBiggestFloat(s: openarray[char], number: var BiggestFloat): int {.compilerproc.} =
   var sign: bool
-  var i = start
+  var i = 0
   if s[i] == '+': inc(i)
   elif s[i] == '-':
     sign = true
@@ -743,14 +701,14 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int
       if s[i+2] == 'N' or s[i+2] == 'n':
         if s[i+3] notin IdentChars:
           number = NaN
-          return i+3 - start
+          return i+3
     return 0
   if s[i] == 'I' or s[i] == 'i':
     if s[i+1] == 'N' or s[i+1] == 'n':
       if s[i+2] == 'F' or s[i+2] == 'f':
         if s[i+3] notin IdentChars:
           number = if sign: -Inf else: Inf
-          return i+3 - start
+          return i+3
     return 0
 
   var buf: string
@@ -782,7 +740,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int
       addInc()
       eatUnderscores()
   number = parseFloatNative(buf)
-  result = i - start
+  result = i
 
 # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce
 # 'Math.trunc' for Nim's ``div`` and ``mod`` operators:
@@ -796,3 +754,15 @@ if (!Math.trunc) {
   };
 }
 """.}
+
+proc cmpClosures(a, b: JSRef): bool {.compilerproc, asmNoStackFrame.} =
+  # Both `a` and `b` need to be a closure
+  {.emit: """
+    if (`a` !== null && `a`.ClP_0 !== undefined &&
+        `b` !== null && `b`.ClP_0 !== undefined) {
+      return `a`.ClP_0 == `b`.ClP_0 && `a`.ClE_0 == `b`.ClE_0;
+    } else {
+      return `a` == `b`;
+    }
+  """
+  .}
diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim
index 49766e69d..a94d0995c 100644
--- a/lib/system/memalloc.nim
+++ b/lib/system/memalloc.nim
@@ -1,13 +1,13 @@
 when notJSnotNims:
   proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect,
-    tags: [], locks: 0, raises: [].}
+    tags: [], raises: [].}
     ## Overwrites the contents of the memory at `p` with the value 0.
     ##
     ## Exactly `size` bytes will be overwritten. Like any procedure
     ## dealing with raw memory this is **unsafe**.
 
   proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0, raises: [].}
+    tags: [], raises: [].}
     ## Copies the contents from the memory at `source` to the memory
     ## at `dest`.
     ## Exactly `size` bytes will be copied. The memory
@@ -15,7 +15,7 @@ when notJSnotNims:
     ## memory this is **unsafe**.
 
   proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign,
-    tags: [], locks: 0, raises: [].}
+    tags: [], raises: [].}
     ## Copies the contents from the memory at `source` to the memory
     ## at `dest`.
     ##
@@ -25,7 +25,7 @@ when notJSnotNims:
     ## dealing with raw memory this is still **unsafe**, though.
 
   proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect,
-    tags: [], locks: 0, raises: [].}
+    tags: [], raises: [].}
     ## Compares the memory blocks `a` and `b`. `size` bytes will
     ## be compared.
     ##
@@ -34,7 +34,7 @@ when notJSnotNims:
     ## **unsafe**.
 
   proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect,
-    tags: [], locks: 0, raises: [].}
+    tags: [], raises: [].}
     ## Compares the memory blocks `a` and `b`. `size` bytes will
     ## be compared.
     ##
@@ -80,7 +80,7 @@ when hasAlloc and not defined(js):
 
   when defined(nimAllocStats):
     var stats: AllocStats
-    template incStat(what: untyped) = inc stats.what
+    template incStat(what: untyped) = atomicInc stats.what
     proc getAllocStats*(): AllocStats = stats
 
   else:
@@ -116,9 +116,6 @@ when hasAlloc and not defined(js):
     ##
     ## See also:
     ## * `create <#create,typedesc>`_
-    static:
-      when sizeof(T) <= 0:
-        {.fatal: "createU does not support types T where sizeof(T) == 0".}
     cast[ptr T](alloc(T.sizeof * size))
 
   template alloc0*(size: Natural): pointer =
@@ -144,9 +141,6 @@ when hasAlloc and not defined(js):
     ##
     ## The allocated memory belongs to its allocating thread!
     ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap.
-    static:
-      when sizeof(T) <= 0:
-        {.fatal: "create does not support types T where sizeof(T) == 0".}
     cast[ptr T](alloc0(sizeof(T) * size))
 
   template realloc*(p: pointer, newSize: Natural): pointer =
diff --git a/lib/system/memory.nim b/lib/system/memory.nim
index ebda60d8d..156773c48 100644
--- a/lib/system/memory.nim
+++ b/lib/system/memory.nim
@@ -43,6 +43,7 @@ proc nimCmpMem*(a, b: pointer, size: Natural): cint {.compilerproc, nonReloadabl
       inc i
 
 proc nimCStrLen*(a: cstring): int {.compilerproc, nonReloadable, inline.} =
+  if a.isNil: return 0
   when useLibC:
     cast[int](c_strlen(a))
   else:
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
index f0c83f1fa..289f4e024 100644
--- a/lib/system/memtracker.nim
+++ b/lib/system/memtracker.nim
@@ -35,7 +35,7 @@ type
     count*: int
     disabled: bool
     data*: array[400, LogEntry]
-  TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], locks: 0, gcsafe.}
+  TrackLogger* = proc (log: TrackLog) {.nimcall, tags: [], gcsafe.}
 
 var
   gLog*: TrackLog
@@ -70,9 +70,9 @@ proc addEntry(entry: LogEntry) =
     if interesting:
       gLog.disabled = true
       cprintf("interesting %s:%ld %s\n", entry.file, entry.line, entry.op)
-      let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0, raises: [].}](writeStackTrace)
+      let x = cast[proc() {.nimcall, tags: [], gcsafe, raises: [].}](writeStackTrace)
       x()
-      quit 1
+      rawQuit 1
       #if gLog.count > high(gLog.data):
       #  gLogger(gLog)
       #  gLog.count = 0
@@ -85,7 +85,7 @@ proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.co
       size: size, file: file, line: line, thread: myThreadId())
 
 proc memTrackerOp*(op: cstring; address: pointer; size: int) {.tags: [],
-         locks: 0, gcsafe.} =
+         gcsafe.} =
   addEntry LogEntry(op: op, address: address, size: size,
       file: "", line: 0, thread: myThreadId())
 
diff --git a/lib/system/mm/go.nim b/lib/system/mm/go.nim
index 9ec25fb65..8f3aeb964 100644
--- a/lib/system/mm/go.nim
+++ b/lib/system/mm/go.nim
@@ -109,7 +109,7 @@ proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
   writebarrierptr(addr(result), newSeq(typ, len))
 
 proc nimNewSeqOfCap(typ: PNimType, cap: int): pointer {.compilerproc.} =
-  result = newObj(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size)
+  result = newObjNoInit(typ, align(GenericSeqSize, typ.base.align) + cap * typ.base.size)
   cast[PGenericSeq](result).len = 0
   cast[PGenericSeq](result).reserved = cap
   cast[PGenericSeq](result).elemSize = typ.base.size
diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim
index d41dce705..47f1a95ae 100644
--- a/lib/system/mm/malloc.nim
+++ b/lib/system/mm/malloc.nim
@@ -22,7 +22,7 @@ proc reallocImpl(p: pointer, newSize: Natural): pointer =
 proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer =
   result = realloc(p, newSize.csize_t)
   if newSize > oldSize:
-    zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize)
+    zeroMem(cast[pointer](cast[uint](result) + uint(oldSize)), newSize - oldSize)
 
 proc deallocImpl(p: pointer) =
   c_free(p)
@@ -88,7 +88,7 @@ type
 
 proc alloc(r: var MemRegion, size: int): pointer =
   result = alloc(size)
-proc alloc0Impl(r: var MemRegion, size: int): pointer =
+proc alloc0(r: var MemRegion, size: int): pointer =
   result = alloc0Impl(size)
 proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
 proc deallocOsPages(r: var MemRegion) = discard
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index e5038387f..26f2f0bbf 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -46,7 +46,7 @@ else:
 proc raiseOutOfMem() {.noinline.} =
   if outOfMemHook != nil: outOfMemHook()
   cstderr.rawWrite("out of memory\n")
-  quit(1)
+  rawQuit(1)
 
 when defined(boehmgc):
   include system / mm / boehm
diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim
index 0b49ea2e7..cf81f6d86 100644
--- a/lib/system/nimscript.nim
+++ b/lib/system/nimscript.nim
@@ -86,12 +86,12 @@ proc patchFile*(package, filename, replacement: string) =
   ## is interpreted to be local to the Nimscript file that contains
   ## the call to `patchFile`, Nim's `--path` is not used at all
   ## to resolve the filename!
+  ## The compiler also performs `path substitution <nimc.html#compiler-usage-commandminusline-switches>`_ on `replacement`.
   ##
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   patchFile("stdlib", "asyncdispatch", "patches/replacement")
+  ##   ```
   discard
 
 proc getCommand*(): string =
@@ -158,20 +158,19 @@ proc strip(s: string): string =
 template `--`*(key, val: untyped) =
   ## A shortcut for `switch <#switch,string,string>`_
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   --path:somePath # same as switch("path", "somePath")
   ##   --path:"someOtherPath" # same as switch("path", "someOtherPath")
+  ##   --hint:"[Conf]:off" # same as switch("hint", "[Conf]:off")
+  ##   ```
   switch(strip(astToStr(key)), strip(astToStr(val)))
 
 template `--`*(key: untyped) =
   ## A shortcut for `switch <#switch,string,string>`_
   ## Example:
-  ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   --listCmd # same as switch("listCmd")
+  ##   ```
   switch(strip(astToStr(key)))
 
 type
@@ -254,11 +253,12 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} =
 proc exec*(command: string) {.
   raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
   ## Executes an external process. If the external process terminates with
-  ## a non-zero exit code, an OSError exception is raised.
+  ## a non-zero exit code, an OSError exception is raised. The command is
+  ## executed relative to the current source path.
   ##
-  ## **Note:** If you need a version of `exec` that returns the exit code
-  ## and text output of the command, you can use `system.gorgeEx
-  ## <system.html#gorgeEx,string,string,string>`_.
+  ## .. note:: If you need a version of `exec` that returns the exit code
+  ##   and text output of the command, you can use `system.gorgeEx
+  ##   <system.html#gorgeEx,string,string,string>`_.
   log "exec: " & command:
     if rawExec(command) != 0:
       raise newException(OSError, "FAILED: " & command)
@@ -268,11 +268,17 @@ proc exec*(command: string, input: string, cache = "") {.
   raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
   ## Executes an external process. If the external process terminates with
   ## a non-zero exit code, an OSError exception is raised.
+  ##
+  ## .. warning:: This version of `exec` is executed relative to the nimscript
+  ##   module path, which affects how the command resolves relative paths. Thus
+  ##   it is generally better to use `gorgeEx` directly when you need more
+  ##   control over the execution environment or when working with commands
+  ##   that deal with relative paths.
   log "exec: " & command:
     let (output, exitCode) = gorgeEx(command, input, cache)
+    echo output
     if exitCode != 0:
       raise newException(OSError, "FAILED: " & command)
-    echo output
 
 proc selfExec*(command: string) {.
   raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} =
@@ -340,12 +346,12 @@ template withDir*(dir: string; body: untyped): untyped =
   ##
   ## If you need a permanent change, use the `cd() <#cd,string>`_ proc.
   ## Usage example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   # inside /some/path/
   ##   withDir "foo":
   ##     # move to /some/path/foo/
   ##   # back in /some/path/
+  ##   ```
   let curDir = getCurrentDir()
   try:
     cd(dir)
@@ -390,10 +396,10 @@ when not defined(nimble):
     ## Defines a task. Hidden tasks are supported via an empty description.
     ##
     ## Example:
-    ##
-    ## .. code-block:: nim
-    ##  task build, "default build is via the C backend":
-    ##    setCommand "c"
+    ##   ```nim
+    ##   task build, "default build is via the C backend":
+    ##     setCommand "c"
+    ##   ```
     ##
     ## For a task named `foo`, this template generates a `proc` named
     ## `fooTask`.  This is useful if you need to call one task in
@@ -401,13 +407,14 @@ when not defined(nimble):
     ##
     ## Example:
     ##
-    ## .. code-block:: nim
-    ##  task foo, "foo":        # > nim foo
-    ##    echo "Running foo"    # Running foo
+    ##   ```nim
+    ##   task foo, "foo":        # > nim foo
+    ##     echo "Running foo"    # Running foo
     ##
-    ##  task bar, "bar":        # > nim bar
-    ##    echo "Running bar"    # Running bar
-    ##    fooTask()             # Running foo
+    ##   task bar, "bar":        # > nim bar
+    ##     echo "Running bar"    # Running bar
+    ##     fooTask()             # Running foo
+    ##   ```
     proc `name Task`*() =
       setCommand "nop"
       body
diff --git a/lib/system/orc.nim b/lib/system/orc.nim
index 32c6b9adc..c02a24989 100644
--- a/lib/system/orc.nim
+++ b/lib/system/orc.nim
@@ -81,10 +81,14 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} =
 
 include threadids
 
-when logOrc:
+when logOrc or orcLeakDetector:
   proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) =
-    cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n",
-      msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId())
+    when orcLeakDetector:
+      cfprintf(cstderr, "%s %s file: %s:%ld; color: %ld; thread: %ld\n",
+        msg, desc.name, s.filename, s.line, s.color, getThreadId())
+    else:
+      cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n",
+        msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId())
 
 proc free(s: Cell; desc: PNimTypeV2) {.inline.} =
   when traceCollector:
@@ -114,7 +118,7 @@ template orcAssert(cond, msg) =
   when logOrc:
     if not cond:
       cfprintf(cstderr, "[Bug!] %s\n", msg)
-      quit 1
+      rawQuit 1
 
 when logOrc:
   proc strstr(s, sub: cstring): cstring {.header: "<string.h>", importc.}
@@ -142,8 +146,8 @@ proc unregisterCycle(s: Cell) =
   let idx = s.rootIdx-1
   when false:
     if idx >= roots.len or idx < 0:
-      cprintf("[Bug!] %ld\n", idx)
-      quit 1
+      cprintf("[Bug!] %ld %ld\n", idx, roots.len)
+      rawQuit 1
   roots.d[idx] = roots.d[roots.len-1]
   roots.d[idx][0].rootIdx = idx+1
   dec roots.len
@@ -299,6 +303,14 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) =
         t.setColor(colBlack)
         trace(t, desc, j)
 
+const
+  defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
+
+when defined(nimStressOrc):
+  const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
+else:
+  var rootsThreshold {.threadvar.}: int
+
 proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
   # pretty direct translation from
   # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf
@@ -337,20 +349,28 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) =
     s.rootIdx = 0
     collectColor(s, roots.d[i][1], colToCollect, j)
 
+  # Bug #22927: `free` calls destructors which can append to `roots`.
+  # We protect against this here by setting `roots.len` to 0 and also
+  # setting the threshold so high that no cycle collection can be triggered
+  # until we are out of this critical section:
+  when not defined(nimStressOrc):
+    let oldThreshold = rootsThreshold
+    rootsThreshold = high(int)
+  roots.len = 0
+
   for i in 0 ..< j.toFree.len:
+    when orcLeakDetector:
+      writeCell("CYCLIC OBJECT FREED", j.toFree.d[i][0], j.toFree.d[i][1])
     free(j.toFree.d[i][0], j.toFree.d[i][1])
 
+  when not defined(nimStressOrc):
+    rootsThreshold = oldThreshold
+
   inc j.freed, j.toFree.len
   deinit j.toFree
-  #roots.len = 0
-
-const
-  defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128
 
-when defined(nimStressOrc):
-  const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8
-else:
-  var rootsThreshold = defaultThreshold
+when defined(nimOrcStats):
+  var freedCyclicObjects {.threadvar.}: int
 
 proc partialCollect(lowMark: int) =
   when false:
@@ -365,6 +385,8 @@ proc partialCollect(lowMark: int) =
       roots.len - lowMark)
   roots.len = lowMark
   deinit j.traceStack
+  when defined(nimOrcStats):
+    inc freedCyclicObjects, j.freed
 
 proc collectCycles() =
   ## Collect cycles.
@@ -385,33 +407,44 @@ proc collectCycles() =
     collectCyclesBacon(j, 0)
 
   deinit j.traceStack
-  deinit roots
+  if roots.len == 0:
+    deinit roots
 
   when not defined(nimStressOrc):
     # compute the threshold based on the previous history
     # of the cycle collector's effectiveness:
     # we're effective when we collected 50% or more of the nodes
     # we touched. If we're effective, we can reset the threshold:
-    if j.keepThreshold and rootsThreshold <= defaultThreshold:
+    if j.keepThreshold:
       discard
     elif j.freed * 2 >= j.touched:
       when not defined(nimFixedOrc):
         rootsThreshold = max(rootsThreshold div 3 * 2, 16)
       else:
-        rootsThreshold = defaultThreshold
+        rootsThreshold = 0
       #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
     elif rootsThreshold < high(int) div 4:
-      rootsThreshold = rootsThreshold * 3 div 2
+      rootsThreshold = (if rootsThreshold <= 0: defaultThreshold else: rootsThreshold) * 3 div 2
   when logOrc:
     cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched,
       getOccupiedMem(), j.rcSum, j.edges)
+  when defined(nimOrcStats):
+    inc freedCyclicObjects, j.freed
+
+when defined(nimOrcStats):
+  type
+    OrcStats* = object ## Statistics of the cycle collector subsystem.
+      freedCyclicObjects*: int ## Number of freed cyclic objects.
+  proc GC_orcStats*(): OrcStats =
+    ## Returns the statistics of the cycle collector subsystem.
+    result = OrcStats(freedCyclicObjects: freedCyclicObjects)
 
 proc registerCycle(s: Cell; desc: PNimTypeV2) =
   s.rootIdx = roots.len+1
   if roots.d == nil: init(roots)
   add(roots, s, desc)
 
-  if roots.len >= rootsThreshold:
+  if roots.len - defaultThreshold >= rootsThreshold:
     collectCycles()
   when logOrc:
     writeCell("[added root]", s, desc)
@@ -424,13 +457,13 @@ proc GC_runOrc* =
   orcAssert roots.len == 0, "roots not empty!"
 
 proc GC_enableOrc*() =
-  ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc`
+  ## Enables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc`
   ## specific API. Check with `when defined(gcOrc)` for its existence.
   when not defined(nimStressOrc):
-    rootsThreshold = defaultThreshold
+    rootsThreshold = 0
 
 proc GC_disableOrc*() =
-  ## Disables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc`
+  ## Disables the cycle collector subsystem of `--mm:orc`. This is a `--mm:orc`
   ## specific API. Check with `when defined(gcOrc)` for its existence.
   when not defined(nimStressOrc):
     rootsThreshold = high(int)
@@ -441,16 +474,16 @@ proc GC_partialCollect*(limit: int) =
   partialCollect(limit)
 
 proc GC_fullCollect* =
-  ## Forces a full garbage collection pass. With `--gc:orc` triggers the cycle
+  ## Forces a full garbage collection pass. With `--mm:orc` triggers the cycle
   ## collector. This is an alias for `GC_runOrc`.
   collectCycles()
 
 proc GC_enableMarkAndSweep*() =
-  ## For `--gc:orc` an alias for `GC_enableOrc`.
+  ## For `--mm:orc` an alias for `GC_enableOrc`.
   GC_enableOrc()
 
 proc GC_disableMarkAndSweep*() =
-  ## For `--gc:orc` an alias for `GC_disableOrc`.
+  ## For `--mm:orc` an alias for `GC_disableOrc`.
   GC_disableOrc()
 
 const
@@ -485,6 +518,19 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} =
     #if cell.color == colPurple:
     rememberCycle(result, cell, cast[ptr PNimTypeV2](p)[])
 
+proc nimDecRefIsLastDyn(p: pointer): bool {.compilerRtl, inl.} =
+  if p != nil:
+    var cell = head(p)
+    if (cell.rc and not rcMask) == 0:
+      result = true
+      #cprintf("[DESTROY] %p\n", p)
+    else:
+      dec cell.rc, rcIncrement
+    #if cell.color == colPurple:
+    if result:
+      if cell.rootIdx > 0:
+        unregisterCycle(cell)
+
 proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} =
   if p != nil:
     var cell = head(p)
diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim
index 39bf65d6c..5509d0070 100644
--- a/lib/system/osalloc.nim
+++ b/lib/system/osalloc.nim
@@ -29,8 +29,8 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or
 
 
 when defined(nimAllocPagesViaMalloc):
-  when not defined(gcArc) and not defined(gcOrc):
-    {.error: "-d:nimAllocPagesViaMalloc is only supported with --gc:arc or --gc:orc".}
+  when not defined(gcArc) and not defined(gcOrc) and not defined(gcAtomicArc):
+    {.error: "-d:nimAllocPagesViaMalloc is only supported with --mm:arc or --mm:atomicArc or --mm:orc".}
 
   proc osTryAllocPages(size: int): pointer {.inline.} =
     let base = c_malloc(csize_t size + PageSize - 1 + sizeof(uint32))
@@ -80,12 +80,12 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
     let pos = cast[int](result)
 
     # Convert pointer to PageSize correct one.
-    var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize))
+    var new_pos = cast[int](pos) +% (PageSize - (pos %% PageSize))
     if (new_pos-pos) < sizeof(EmscriptenMMapBlock):
       new_pos = new_pos +% PageSize
     result = cast[pointer](new_pos)
 
-    var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock)
+    var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock)
 
     var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
     mmapDescr.realSize = realSize
@@ -96,7 +96,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize):
   proc osTryAllocPages(size: int): pointer = osAllocPages(size)
 
   proc osDeallocPages(p: pointer, size: int) {.inline.} =
-    var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock)
+    var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock)
     var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos)
     munmap(mmapDescr.realPointer, mmapDescr.realSize)
 
@@ -189,7 +189,7 @@ elif defined(windows) and not defined(StandaloneHeapSize):
     when reallyOsDealloc:
       if virtualFree(p, 0, MEM_RELEASE) == 0:
         cprintf "virtualFree failing!"
-        quit 1
+        rawQuit 1
     #VirtualFree(p, size, MEM_DECOMMIT)
 
 elif hostOS == "standalone" or defined(StandaloneHeapSize):
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index b4c9f1f06..0619f3fca 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -10,6 +10,8 @@
 ## Platform detection for NimScript. This module is included by the system module!
 ## Do not import it directly!
 
+# CPU architectures have alias names mapped in tools/niminst/makefile.nimf
+
 type
   CpuPlatform* {.pure.} = enum ## the CPU this program will run on.
     none,                      ## unknown CPU
@@ -37,7 +39,8 @@ type
     riscv64,                   ## RISC-V 64-bit processor
     wasm32,                    ## WASM, 32-bit
     e2k,                       ## MCST Elbrus 2000
-    loongarch64                ## LoongArch 64-bit processor
+    loongarch64,               ## LoongArch 64-bit processor
+    s390x                      ## IBM Z
 
   OsPlatform* {.pure.} = enum ## the OS this program will run on.
     none, dos, windows, os2, linux, morphos, skyos, solaris,
@@ -97,5 +100,6 @@ const
                elif defined(wasm32): CpuPlatform.wasm32
                elif defined(e2k): CpuPlatform.e2k
                elif defined(loongarch64): CpuPlatform.loongarch64
+               elif defined(s390x): CpuPlatform.s390x
                else: CpuPlatform.none
     ## the CPU this program will run on.
diff --git a/lib/system/profiler.nim b/lib/system/profiler.nim
index 0649f1176..e7eb6ac82 100644
--- a/lib/system/profiler.nim
+++ b/lib/system/profiler.nim
@@ -60,13 +60,13 @@ proc captureStackTrace(f: PFrame, st: var StackTrace) =
     b = b.prev
 
 var
-  profilingRequestedHook*: proc (): bool {.nimcall, locks: 0, gcsafe.}
+  profilingRequestedHook*: proc (): bool {.nimcall, gcsafe.}
     ## set this variable to provide a procedure that implements a profiler in
     ## user space. See the `nimprof` module for a reference implementation.
 
 when defined(memProfiler):
   type
-    MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, locks: 0, gcsafe.}
+    MemProfilerHook* = proc (st: StackTrace, requestedSize: int) {.nimcall, gcsafe.}
 
   var
     profilerHook*: MemProfilerHook
diff --git a/lib/system/rawquits.nim b/lib/system/rawquits.nim
new file mode 100644
index 000000000..f0ead10c6
--- /dev/null
+++ b/lib/system/rawquits.nim
@@ -0,0 +1,27 @@
+import system/ctypes
+
+when defined(nimNoQuit):
+  proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit"
+
+elif defined(genode):
+  import genode/env
+
+  var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr
+
+  type GenodeEnv = GenodeEnvPtr
+    ## Opaque type representing Genode environment.
+
+  proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn,
+    importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "<base/sleep.h>".}
+
+  proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} =
+    systemEnv.rawQuit(errorcode)
+
+
+elif defined(js) and defined(nodejs) and not defined(nimscript):
+  proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit",
+    importc: "process.exit", noreturn.}
+
+else:
+  proc rawQuit(errorcode: cint) {.
+    magic: "Exit", importc: "exit", header: "<stdlib.h>", noreturn.}
\ No newline at end of file
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index e049d18fa..13118e40b 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -17,7 +17,7 @@ proc reprFloat(x: float): string {.compilerproc.} = return $x
 
 proc reprPointer(x: pointer): string {.compilerproc.} =
   result = newString(60)
-  let n = c_sprintf(addr result[0], "%p", x)
+  let n = c_snprintf(cast[cstring](addr result[0]), csize_t(60), "%p", x)
   setLen(result, n)
 
 proc reprStrAux(result: var string, s: cstring; len: int) =
@@ -155,7 +155,7 @@ when not defined(useNimRtl):
     var bs = typ.base.size
     for i in 0..typ.size div bs - 1:
       if i > 0: add result, ", "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl)
+      reprAux(result, cast[pointer](cast[int](p) + i*bs), typ.base, cl)
     add result, "]"
 
   when defined(nimSeqsV2):
@@ -183,7 +183,7 @@ when not defined(useNimRtl):
     var bs = typ.base.size
     for i in 0..cast[PGenericSeq](p).len-1:
       if i > 0: add result, ", "
-      reprAux(result, cast[pointer](cast[ByteAddress](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs),
+      reprAux(result, cast[pointer](cast[int](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs),
               typ.base, cl)
     add result, "]"
 
@@ -194,14 +194,14 @@ when not defined(useNimRtl):
     of nkSlot:
       add result, $n.name
       add result, " = "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl)
+      reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
     of nkList:
       for i in 0..n.len-1:
         if i > 0: add result, ",\n"
         reprRecordAux(result, p, n.sons[i], cl)
     of nkCase:
       var m = selectBranch(p, n)
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl)
+      reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl)
       if m != nil: reprRecordAux(result, p, m, cl)
 
   proc reprRecord(result: var string, p: pointer, typ: PNimType,
@@ -307,7 +307,7 @@ when not defined(useNimRtl):
     var bs = elemtyp.size
     for i in 0..length - 1:
       if i > 0: add result, ", "
-      reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), elemtyp, cl)
+      reprAux(result, cast[pointer](cast[int](p) + i*bs), elemtyp, cl)
     add result, "]"
     deinitReprClosure(cl)
 
diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim
index 0e9bec0f3..d2aef536c 100644
--- a/lib/system/repr_v2.nim
+++ b/lib/system/repr_v2.nim
@@ -9,6 +9,9 @@ proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".}
 proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".}
   ## imported from typetraits
 
+proc rangeBase(T: typedesc): typedesc {.magic: "TypeTrait".}
+  # skip one level of range; return the base type of a range type
+
 proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.}
 
 proc repr*(x: int): string =
@@ -31,13 +34,14 @@ proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
   ## repr for a boolean argument. Returns `x`
   ## converted to the string "false" or "true".
 
-proc repr*(x: char): string {.noSideEffect.} =
+proc repr*(x: char): string {.noSideEffect, raises: [].} =
   ## repr for a character argument. Returns `x`
   ## converted to an escaped string.
   ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   assert repr('c') == "'c'"
-  result.add '\''
+  ##   ```
+  result = "'"
   # Elides string creations if not needed
   if x in {'\\', '\0'..'\31', '\127'..'\255'}:
     result.add '\\'
@@ -47,10 +51,10 @@ proc repr*(x: char): string {.noSideEffect.} =
     result.add x
   result.add '\''
 
-proc repr*(x: string | cstring): string {.noSideEffect.} =
+proc repr*(x: string | cstring): string {.noSideEffect, raises: [].} =
   ## repr for a string argument. Returns `x`
   ## converted to a quoted and escaped string.
-  result.add '\"'
+  result = "\""
   for i in 0..<x.len:
     if x[i] in {'"', '\\', '\0'..'\31', '\127'..'\255'}:
       result.add '\\'
@@ -63,7 +67,7 @@ proc repr*(x: string | cstring): string {.noSideEffect.} =
       result.add x[i]
   result.add '\"'
 
-proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
+proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect, raises: [].}
   ## repr for an enumeration argument. This works for
   ## any enumeration type thanks to compiler magic.
   ##
@@ -91,16 +95,21 @@ proc repr*(p: pointer): string =
         result[j] = HexChars[n and 0xF]
         n = n shr 4
 
-proc repr*(p: proc): string =
+proc repr*(p: proc | iterator {.closure.}): string =
   ## repr of a proc as its address
   repr(cast[ptr pointer](unsafeAddr p)[])
 
-template repr*(x: distinct): string =
-  repr(distinctBase(typeof(x))(x))
+template repr*[T: distinct|(range and not enum)](x: T): string =
+  when T is range: # add a branch to handle range
+    repr(rangeBase(typeof(x))(x))
+  elif T is distinct:
+    repr(distinctBase(typeof(x))(x))
+  else:
+    {.error: "cannot happen".}
 
 template repr*(t: typedesc): string = $t
 
-proc reprObject[T: tuple|object](res: var string, x: T) =
+proc reprObject[T: tuple|object](res: var string, x: T) {.noSideEffect, raises: [].} =
   res.add '('
   var firstElement = true
   const isNamed = T is object or isNamedTuple(T)
@@ -121,19 +130,19 @@ proc reprObject[T: tuple|object](res: var string, x: T) =
   res.add(')')
 
 
-proc repr*[T: tuple|object](x: T): string =
+proc repr*[T: tuple|object](x: T): string {.noSideEffect, raises: [].} =
   ## Generic `repr` operator for tuples that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(23, 45) == "(23, 45)"
   ##   $(a: 23, b: 45) == "(a: 23, b: 45)"
   ##   $() == "()"
+  ##   ```
   when T is object:
     result = $typeof(x)
   reprObject(result, x)
 
-proc repr*[T](x: ref T | ptr T): string =
+proc repr*[T](x: ref T | ptr T): string {.noSideEffect, raises: [].} =
   if isNil(x): return "nil"
   when T is object:
     result = $typeof(x)
@@ -142,7 +151,7 @@ proc repr*[T](x: ref T | ptr T): string =
     result = when typeof(x) is ref: "ref " else: "ptr "
     result.add repr(x[])
 
-proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string =
+proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string {.noSideEffect, raises: [].} =
   result = prefix
   var firstElement = true
   for value in items(x):
@@ -156,29 +165,19 @@ proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string =
 proc repr*[T](x: set[T]): string =
   ## Generic `repr` operator for sets that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   ${23, 45} == "{23, 45}"
+  ##   ```
   collectionToRepr(x, "{", ", ", "}")
 
 proc repr*[T](x: seq[T]): string =
   ## Generic `repr` operator for seqs that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(@[23, 45]) == "@[23, 45]"
+  ##   ```
   collectionToRepr(x, "@[", ", ", "]")
 
-proc repr*[T, U](x: HSlice[T, U]): string =
-  ## Generic `repr` operator for slices that is lifted from the components
-  ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
-  ##  $(1 .. 5) == "1 .. 5"
-  result = repr(x.a)
-  result.add(" .. ")
-  result.add(repr(x.b))
-
 proc repr*[T, IDX](x: array[IDX, T]): string =
   ## Generic `repr` operator for arrays that is lifted from the components.
   collectionToRepr(x, "[", ", ", "]")
@@ -186,7 +185,10 @@ proc repr*[T, IDX](x: array[IDX, T]): string =
 proc repr*[T](x: openArray[T]): string =
   ## Generic `repr` operator for openarrays that is lifted from the components
   ## of `x`. Example:
-  ##
-  ## .. code-block:: Nim
+  ##   ```Nim
   ##   $(@[23, 45].toOpenArray(0, 1)) == "[23, 45]"
+  ##   ```
   collectionToRepr(x, "[", ", ", "]")
+
+proc repr*[T](x: UncheckedArray[T]): string =
+  "[...]"
diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim
index 0818f9cc9..761d66aec 100644
--- a/lib/system/reprjs.nim
+++ b/lib/system/reprjs.nim
@@ -12,6 +12,8 @@ when defined(nimPreviewSlimSystem):
   import std/formatfloat
 
 proc reprInt(x: int64): string {.compilerproc.} = $x
+proc reprInt(x: uint64): string {.compilerproc.} = $x
+proc reprInt(x: int): string {.compilerproc.} = $x
 proc reprFloat(x: float): string {.compilerproc.} = $x
 
 proc reprPointer(p: pointer): string {.compilerproc.} =
@@ -27,7 +29,7 @@ proc reprBool(x: bool): string {.compilerRtl.} =
 proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
   var tmp: bool
   let item = typ.node.sons[e]
-  {.emit: "`tmp` = `item` !== undefined".}
+  {.emit: "`tmp` = `item` !== undefined;".}
   if tmp:
     result = makeNimstrLit(item.name)
   else:
@@ -134,7 +136,7 @@ proc reprArray(a: pointer, typ: PNimType,
   add(result, "]")
 
 proc isPointedToNil(p: pointer): bool =
-  {. emit: "if (`p` === null) {`result` = true};\n" .}
+  {. emit: "if (`p` === null) {`result` = true;}\n" .}
 
 proc reprRef(result: var string, p: pointer, typ: PNimType,
           cl: var ReprClosure) =
@@ -192,8 +194,12 @@ proc reprAux(result: var string, p: pointer, typ: PNimType,
     return
   dec(cl.recDepth)
   case typ.kind
-  of tyInt..tyInt64, tyUInt..tyUInt64:
+  of tyInt..tyInt32, tyUInt..tyUInt32:
     add(result, reprInt(cast[int](p)))
+  of tyInt64:
+    add(result, reprInt(cast[int64](p)))
+  of tyUInt64:
+    add(result, reprInt(cast[uint64](p)))
   of tyChar:
     add(result, reprChar(cast[char](p)))
   of tyBool:
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
index 42d9938c5..572e77408 100644
--- a/lib/system/seqs_v2.nim
+++ b/lib/system/seqs_v2.nim
@@ -8,10 +8,13 @@
 #
 
 
-# import typetraits
+# import std/typetraits
 # strs already imported allocateds for us.
 
-proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".}
+
+# Some optimizations here may be not to empty-seq-initialize some symbols, then StrictNotNil complains.
+{.push warning[StrictNotNil]: off.}  # See https://github.com/nim-lang/Nim/issues/21401
+
 
 ## Default seq implementation used by Nim's core.
 type
@@ -27,6 +30,10 @@ type
     len: int
     p: ptr NimSeqPayload[T]
 
+  NimRawSeq = object
+    len: int
+    p: pointer
+
 const nimSeqVersion {.core.} = 2
 
 # XXX make code memory safe for overflows in '*'
@@ -41,6 +48,15 @@ proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises
   else:
     result = nil
 
+proc newSeqPayloadUninit(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises: [].} =
+  # Used in `newSeqOfCap()`.
+  if cap > 0:
+    var p = cast[ptr NimSeqPayloadBase](alignedAlloc(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign))
+    p.cap = cap
+    result = p
+  else:
+    result = nil
+
 template `+!`(p: pointer, s: int): pointer =
   cast[pointer](cast[int](p) +% s)
 
@@ -48,7 +64,7 @@ template `-!`(p: pointer, s: int): pointer =
   cast[pointer](cast[int](p) -% s)
 
 proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
-    noSideEffect, raises: [], compilerRtl.} =
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
   {.noSideEffect.}:
     let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
     if addlen <= 0:
@@ -61,15 +77,48 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin
       var p = cast[ptr NimSeqPayloadBase](p)
       let oldCap = p.cap and not strlitFlag
       let newCap = max(resize(oldCap), len+addlen)
+      var q: ptr NimSeqPayloadBase
+      if (p.cap and strlitFlag) == strlitFlag:
+        q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
+        copyMem(q +! headerSize, p +! headerSize, len * elemSize)
+      else:
+        let oldSize = headerSize + elemSize * oldCap
+        let newSize = headerSize + elemSize * newCap
+        q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
+
+      zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
+      q.cap = newCap
+      result = q
+
+proc zeroNewElements(len: int; q: pointer; addlen, elemSize, elemAlign: int) {.
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
+  {.noSideEffect.}:
+    let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
+    zeroMem(q +! headerSize +! len * elemSize, addlen * elemSize)
+
+proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
+    noSideEffect, tags: [], raises: [], compilerRtl.} =
+  {.noSideEffect.}:
+    let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign)
+    if addlen <= 0:
+      result = p
+    elif p == nil:
+      result = newSeqPayloadUninit(len+addlen, elemSize, elemAlign)
+    else:
+      # Note: this means we cannot support things that have internal pointers as
+      # they get reallocated here. This needs to be documented clearly.
+      var p = cast[ptr NimSeqPayloadBase](p)
+      let oldCap = p.cap and not strlitFlag
+      let newCap = max(resize(oldCap), len+addlen)
       if (p.cap and strlitFlag) == strlitFlag:
-        var q = cast[ptr NimSeqPayloadBase](alignedAlloc0(headerSize + elemSize * newCap, elemAlign))
+        var q = cast[ptr NimSeqPayloadBase](alignedAlloc(headerSize + elemSize * newCap, elemAlign))
         copyMem(q +! headerSize, p +! headerSize, len * elemSize)
         q.cap = newCap
         result = q
       else:
         let oldSize = headerSize + elemSize * oldCap
         let newSize = headerSize + elemSize * newCap
-        var q = cast[ptr NimSeqPayloadBase](alignedRealloc0(p, oldSize, newSize, elemAlign))
+        var q = cast[ptr NimSeqPayloadBase](alignedRealloc(p, oldSize, newSize, elemAlign))
         q.cap = newCap
         result = q
 
@@ -83,38 +132,41 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} =
       for i in countdown(x.len - 1, newLen):
         reset x[i]
     # XXX This is wrong for const seqs that were moved into 'x'!
-    cast[ptr NimSeqV2[T]](addr x).len = newLen
+    {.noSideEffect.}:
+      cast[ptr NimSeqV2[T]](addr x).len = newLen
 
-proc grow*[T](x: var seq[T]; newLen: Natural; value: T) =
+proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} =
   let oldLen = x.len
   #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'"
   if newLen <= oldLen: return
   var xu = cast[ptr NimSeqV2[T]](addr x)
-  if xu.p == nil or xu.p.cap < newLen:
-    xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
+  if xu.p == nil or (xu.p.cap and not strlitFlag) < newLen:
+    xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newLen - oldLen, sizeof(T), alignof(T)))
   xu.len = newLen
   for i in oldLen .. newLen-1:
-    xu.p.data[i] = value
+    wasMoved(xu.p.data[i])
+    `=copy`(xu.p.data[i], value)
 
-proc add*[T](x: var seq[T]; value: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
+proc add*[T](x: var seq[T]; y: sink T) {.magic: "AppendSeqElem", noSideEffect, nodestroy.} =
   ## Generic proc for adding a data item `y` to a container `x`.
   ##
   ## For containers that have an order, `add` means *append*. New generic
   ## containers should also call their adding proc `add` for consistency.
   ## Generic code becomes much easier to write if the Nim naming scheme is
   ## respected.
-  let oldLen = x.len
-  var xu = cast[ptr NimSeqV2[T]](addr x)
-  if xu.p == nil or xu.p.cap < oldLen+1:
-    xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, 1, sizeof(T), alignof(T)))
-  xu.len = oldLen+1
-  # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
-  # copyMem(). This is fine as know by construction that
-  # in `xu.p.data[oldLen]` there is nothing to destroy.
-  # We also save the `wasMoved + destroy` pair for the sink parameter.
-  xu.p.data[oldLen] = value
-
-proc setLen[T](s: var seq[T], newlen: Natural) =
+  {.cast(noSideEffect).}:
+    let oldLen = x.len
+    var xu = cast[ptr NimSeqV2[T]](addr x)
+    if xu.p == nil or (xu.p.cap and not strlitFlag) < oldLen+1:
+      xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, 1, sizeof(T), alignof(T)))
+    xu.len = oldLen+1
+    # .nodestroy means `xu.p.data[oldLen] = value` is compiled into a
+    # copyMem(). This is fine as know by construction that
+    # in `xu.p.data[oldLen]` there is nothing to destroy.
+    # We also save the `wasMoved + destroy` pair for the sink parameter.
+    xu.p.data[oldLen] = y
+
+proc setLen[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
   {.noSideEffect.}:
     if newlen < s.len:
       shrink(s, newlen)
@@ -122,17 +174,19 @@ proc setLen[T](s: var seq[T], newlen: Natural) =
       let oldLen = s.len
       if newlen <= oldLen: return
       var xu = cast[ptr NimSeqV2[T]](addr s)
-      if xu.p == nil or xu.p.cap < newlen:
-        xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
+      if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
+        xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
       xu.len = newlen
+      for i in oldLen..<newlen:
+        xu.p.data[i] = default(T)
 
 proc newSeq[T](s: var seq[T], len: Natural) =
   shrink(s, 0)
   setLen(s, len)
 
+proc sameSeqPayload(x: pointer, y: pointer): bool {.compilerRtl, inl.} =
+  result = cast[ptr NimRawSeq](x)[].p == cast[ptr NimRawSeq](y)[].p
 
-template capacityImpl(sek: NimSeqV2): int =
-  if sek.p != nil: sek.p.cap else: 0
 
 func capacity*[T](self: seq[T]): int {.inline.} =
   ## Returns the current capacity of the seq.
@@ -142,6 +196,32 @@ func capacity*[T](self: seq[T]): int {.inline.} =
     lst.add "Nim"
     assert lst.capacity == 42
 
-  {.cast(noSideEffect).}:
-    let sek = unsafeAddr self
-    result = capacityImpl(cast[ptr NimSeqV2](sek)[])
+  let sek = cast[ptr NimSeqV2[T]](unsafeAddr self)
+  result = if sek.p != nil: sek.p.cap and not strlitFlag else: 0
+
+func setLenUninit*[T](s: var seq[T], newlen: Natural) {.nodestroy.} =
+  ## Sets the length of seq `s` to `newlen`. `T` may be any sequence type.
+  ## New slots will not be initialized.
+  ##
+  ## If the current length is greater than the new length,
+  ## `s` will be truncated.
+  ##   ```nim
+  ##   var x = @[10, 20]
+  ##   x.setLenUninit(5)
+  ##   x[4] = 50
+  ##   assert x[4] == 50
+  ##   x.setLenUninit(1)
+  ##   assert x == @[10]
+  ##   ```
+  {.noSideEffect.}:
+    if newlen < s.len:
+      shrink(s, newlen)
+    else:
+      let oldLen = s.len
+      if newlen <= oldLen: return
+      var xu = cast[ptr NimSeqV2[T]](addr s)
+      if xu.p == nil or (xu.p.cap and not strlitFlag) < newlen:
+        xu.p = cast[typeof(xu.p)](prepareSeqAddUninit(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T)))
+      xu.len = newlen
+
+{.pop.}  # See https://github.com/nim-lang/Nim/issues/21401
diff --git a/lib/system/setops.nim b/lib/system/setops.nim
index 755eafdb8..67aa3097a 100644
--- a/lib/system/setops.nim
+++ b/lib/system/setops.nim
@@ -9,7 +9,10 @@ func incl*[T](x: var set[T], y: T) {.magic: "Incl".} =
     a.incl(4)
     assert a == {1, 2, 3, 4, 5}
 
-template incl*[T](x: var set[T], y: set[T]) =
+when not defined(nimHasCallsitePragma):
+  {.pragma: callsite.}
+
+template incl*[T](x: var set[T], y: set[T]) {.callsite.} =
   ## Includes the set `y` in the set `x`.
   runnableExamples:
     var a = {1, 3, 5, 7}
@@ -23,11 +26,11 @@ func excl*[T](x: var set[T], y: T) {.magic: "Excl".} =
   ##
   ## This is the same as `x = x - {y}`, but it might be more efficient.
   runnableExamples:
-    var b = {2, 3, 5, 6, 12, 545}
+    var b = {2, 3, 5, 6, 12, 54}
     b.excl(5)
-    assert b == {2, 3, 6, 12, 545}
+    assert b == {2, 3, 6, 12, 54}
 
-template excl*[T](x: var set[T], y: set[T]) =
+template excl*[T](x: var set[T], y: set[T]) {.callsite.} =
   ## Excludes the set `y` from the set `x`.
   runnableExamples:
     var a = {1, 3, 5, 7}
diff --git a/lib/system/sets.nim b/lib/system/sets.nim
index 103c8d343..97431c296 100644
--- a/lib/system/sets.nim
+++ b/lib/system/sets.nim
@@ -9,18 +9,20 @@
 
 # set handling
 
-type
-  NimSet = array[0..4*2048-1, uint8]
 
-
-proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} =
+proc cardSetImpl(s: ptr UncheckedArray[uint8], len: int): int {.inline.} =
   var i = 0
   result = 0
+  var num = 0'u64
   when defined(x86) or defined(amd64):
     while i < len - 8:
-      inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[]))
+      copyMem(addr num, addr s[i], 8)
+      inc(result, countBits64(num))
       inc(i, 8)
 
   while i < len:
     inc(result, countBits32(uint32(s[i])))
     inc(i, 1)
+
+proc cardSet(s: ptr UncheckedArray[uint8], len: int): int {.compilerproc, inline.} =
+  result = cardSetImpl(s, len)
diff --git a/lib/system/stacktraces.nim b/lib/system/stacktraces.nim
index 8142f8c7b..42be9d94f 100644
--- a/lib/system/stacktraces.nim
+++ b/lib/system/stacktraces.nim
@@ -22,26 +22,26 @@ when defined(nimStackTraceOverride):
       ## This is the same as the type `uintptr_t` in C.
 
     StackTraceOverrideGetTracebackProc* = proc (): string {.
-      nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.}
+      nimcall, gcsafe, raises: [], tags: [], noinline.}
     StackTraceOverrideGetProgramCountersProc* = proc (maxLength: cint): seq[cuintptr_t] {.
-      nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.}
+      nimcall, gcsafe, raises: [], tags: [], noinline.}
     StackTraceOverrideGetDebuggingInfoProc* =
       proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
-        nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.}
+        nimcall, gcsafe, raises: [], tags: [], noinline.}
 
   # Default procedures (not normally used, because people opting in on this
   # override are supposed to register their own versions).
   var
     stackTraceOverrideGetTraceback: StackTraceOverrideGetTracebackProc =
-      proc (): string {.nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} =
+      proc (): string {.nimcall, gcsafe, raises: [], tags: [], noinline.} =
         discard
         #result = "Stack trace override procedure not registered.\n"
     stackTraceOverrideGetProgramCounters: StackTraceOverrideGetProgramCountersProc =
-      proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} =
+      proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, raises: [], tags: [], noinline.} =
         discard
     stackTraceOverrideGetDebuggingInfo: StackTraceOverrideGetDebuggingInfoProc =
       proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {.
-        nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} =
+        nimcall, gcsafe, raises: [], tags: [], noinline.} =
           discard
 
   # Custom procedure registration.
diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim
index feaac7817..89046253b 100644
--- a/lib/system/strmantle.nim
+++ b/lib/system/strmantle.nim
@@ -17,12 +17,20 @@ proc cmpStrings(a, b: string): int {.inline, compilerproc.} =
   let blen = b.len
   let minlen = min(alen, blen)
   if minlen > 0:
-    result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen))
+    result = c_memcmp(unsafeAddr a[0], unsafeAddr b[0], cast[csize_t](minlen)).int
     if result == 0:
       result = alen - blen
   else:
     result = alen - blen
 
+proc leStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) <= 0
+
+proc ltStrings(a, b: string): bool {.inline, compilerproc.} =
+  # required by upcoming backends (NIR).
+  cmpStrings(a, b) < 0
+
 proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
   let alen = a.len
   let blen = b.len
@@ -33,7 +41,7 @@ proc eqStrings(a, b: string): bool {.inline, compilerproc.} =
 proc hashString(s: string): int {.compilerproc.} =
   # the compiler needs exactly the same hash function!
   # this used to be used for efficient generation of string case statements
-  var h : uint = 0
+  var h = 0'u
   for i in 0..len(s)-1:
     h = h + uint(s[i])
     h = h + h shl 10
@@ -75,11 +83,11 @@ const
               1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
               1e20, 1e21, 1e22]
 
-when defined(nimHasInvariant):
-  {.push staticBoundChecks: off.}
 
-proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
-                          start = 0): int {.compilerproc.} =
+{.push staticBoundChecks: off.}
+
+proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat,
+                         ): int {.compilerproc.} =
   # This routine attempt to parse float that can parsed quickly.
   # i.e. whose integer part can fit inside a 53bits integer.
   # their real exponent must also be <= 22. If the float doesn't follow
@@ -88,7 +96,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   # This avoid the problems of decimal character portability.
   # see: http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
   var
-    i = start
+    i = 0
     sign = 1.0
     kdigits, fdigits = 0
     exponent = 0
@@ -111,7 +119,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       if s[i+2] == 'N' or s[i+2] == 'n':
         if i+3 >= s.len or s[i+3] notin IdentChars:
           number = NaN
-          return i+3 - start
+          return i+3
     return 0
 
   # Inf?
@@ -120,7 +128,7 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
       if s[i+2] == 'F' or s[i+2] == 'f':
         if i+3 >= s.len or s[i+3] notin IdentChars:
           number = Inf*sign
-          return i+3 - start
+          return i+3
     return 0
 
   if i < s.len and s[i] in {'0'..'9'}:
@@ -154,8 +162,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # if has no digits: return error
   if kdigits + fdigits <= 0 and
-     (i == start or # no char consumed (empty string).
-     (i == start + 1 and hasSign)): # or only '+' or '-
+     (i == 0 or # no char consumed (empty string).
+     (i == 1 and hasSign)): # or only '+' or '-
     return 0
 
   if i+1 < s.len and s[i] in {'e', 'E'}:
@@ -178,11 +186,13 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
 
   # if exponent greater than can be represented: +/- zero or infinity
   if absExponent > 999:
-    if expNegative:
+    if integer == 0:
+      number = 0.0
+    elif expNegative:
       number = 0.0*sign
     else:
       number = Inf*sign
-    return i - start
+    return i
 
   # if integer is representable in 53 bits:  fast path
   # max fast path integer is  1<<53 - 1 or  8999999999999999 (16 digits)
@@ -194,14 +204,14 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
         number = sign * integer.float / powtens[absExponent]
       else:
         number = sign * integer.float * powtens[absExponent]
-      return i - start
+      return i
 
     # if exponent is greater try to fit extra exponent above 22 by multiplying
     # integer part is there is space left.
     let slop = 15 - kdigits - fdigits
     if absExponent <= 22 + slop and not expNegative:
       number = sign * integer.float * powtens[slop] * powtens[absExponent-slop]
-      return i - start
+      return i
 
   # if failed: slow path with strtod.
   var t: array[500, char] # flaviu says: 325 is the longest reasonable literal
@@ -209,8 +219,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   let maxlen = t.high - "e+000".len # reserve enough space for exponent
 
   let endPos = i
-  result = endPos - start
-  i = start
+  result = endPos
+  i = 0
   # re-parse without error checking, any error should be handled by the code above.
   if i < endPos and s[i] == '.': i.inc
   while i < endPos and s[i] in {'0'..'9','+','-'}:
@@ -232,10 +242,9 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat,
   t[ti-2] = ('0'.ord + absExponent mod 10).char
   absExponent = absExponent div 10
   t[ti-3] = ('0'.ord + absExponent mod 10).char
-  number = c_strtod(addr t, nil)
+  number = c_strtod(cast[cstring](addr t), nil)
 
-when defined(nimHasInvariant):
-  {.pop.} # staticBoundChecks
+{.pop.} # staticBoundChecks
 
 proc nimBoolToStr(x: bool): string {.compilerRtl.} =
   return if x: "true" else: "false"
diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim
index 74b9e7cd9..404b4f78d 100644
--- a/lib/system/strs_v2.nim
+++ b/lib/system/strs_v2.nim
@@ -34,53 +34,72 @@ template frees(s) =
     else:
       dealloc(s.p)
 
+template allocPayload(newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](allocShared(contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](alloc(contentSize(newLen)))
+
+template allocPayload0(newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](allocShared0(contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](alloc0(contentSize(newLen)))
+
+template reallocPayload(p: pointer, newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](reallocShared(p, contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](realloc(p, contentSize(newLen)))
+
+template reallocPayload0(p: pointer; oldLen, newLen: int): ptr NimStrPayload =
+  when compileOption("threads"):
+    cast[ptr NimStrPayload](reallocShared0(p, contentSize(oldLen), contentSize(newLen)))
+  else:
+    cast[ptr NimStrPayload](realloc0(p, contentSize(oldLen), contentSize(newLen)))
+
 proc resize(old: int): int {.inline.} =
   if old <= 0: result = 4
-  elif old < 65536: result = old * 2
+  elif old <= high(int16): result = old * 2
   else: result = old * 3 div 2 # for large arrays * 3/2 is better
 
-proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} =
-  let newLen = s.len + addlen
+proc prepareAdd(s: var NimStringV2; addLen: int) {.compilerRtl.} =
+  let newLen = s.len + addLen
   if isLiteral(s):
     let oldP = s.p
     # can't mutate a literal, so we need a fresh copy here:
-    when compileOption("threads"):
-      s.p = cast[ptr NimStrPayload](allocShared0(contentSize(newLen)))
-    else:
-      s.p = cast[ptr NimStrPayload](alloc0(contentSize(newLen)))
+    s.p = allocPayload(newLen)
     s.p.cap = newLen
     if s.len > 0:
       # we are about to append, so there is no need to copy the \0 terminator:
       copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
+    elif oldP == nil:
+      # In the case of `newString(0) & ""`, since `src.len == 0`, `appendString`
+      # will not set the `\0` terminator, so we set it here.
+      s.p.data[0] = '\0'
   else:
     let oldCap = s.p.cap and not strlitFlag
     if newLen > oldCap:
       let newCap = max(newLen, resize(oldCap))
-      when compileOption("threads"):
-        s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap)))
-      else:
-        s.p = cast[ptr NimStrPayload](realloc0(s.p, contentSize(oldCap), contentSize(newCap)))
+      s.p = reallocPayload(s.p, newCap)
       s.p.cap = newCap
+      if newLen < newCap:
+        zeroMem(cast[pointer](addr s.p.data[newLen+1]), newCap - newLen)
 
-proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inline.} =
+proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inl.} =
   #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
   prepareAdd(s, 1)
   s.p.data[s.len] = c
-  s.p.data[s.len+1] = '\0'
   inc s.len
+  s.p.data[s.len] = '\0'
 
 proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} =
   if len <= 0:
     result = NimStringV2(len: 0, p: nil)
   else:
-    when compileOption("threads"):
-      var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
-    else:
-      var p = cast[ptr NimStrPayload](alloc0(contentSize(len)))
+    var p = allocPayload(len)
     p.cap = len
-    if len > 0:
-      # we are about to append, so there is no need to copy the \0 terminator:
-      copyMem(unsafeAddr p.data[0], str, len)
+    copyMem(unsafeAddr p.data[0], str, len+1)
     result = NimStringV2(len: len, p: p)
 
 proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
@@ -89,7 +108,7 @@ proc cstrToNimstr(str: cstring): NimStringV2 {.compilerRtl.} =
 
 proc nimToCStringConv(s: NimStringV2): cstring {.compilerproc, nonReloadable, inline.} =
   if s.len == 0: result = cstring""
-  else: result = cstring(unsafeAddr s.p.data)
+  else: result = cast[cstring](unsafeAddr s.p.data)
 
 proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inline.} =
   if src.len > 0:
@@ -99,29 +118,24 @@ proc appendString(dest: var NimStringV2; src: NimStringV2) {.compilerproc, inlin
 
 proc appendChar(dest: var NimStringV2; c: char) {.compilerproc, inline.} =
   dest.p.data[dest.len] = c
-  dest.p.data[dest.len+1] = '\0'
   inc dest.len
+  dest.p.data[dest.len] = '\0'
 
 proc rawNewString(space: int): NimStringV2 {.compilerproc.} =
   # this is also 'system.newStringOfCap'.
   if space <= 0:
     result = NimStringV2(len: 0, p: nil)
   else:
-    when compileOption("threads"):
-      var p = cast[ptr NimStrPayload](allocShared0(contentSize(space)))
-    else:
-      var p = cast[ptr NimStrPayload](alloc0(contentSize(space)))
+    var p = allocPayload(space)
     p.cap = space
+    p.data[0] = '\0'
     result = NimStringV2(len: 0, p: p)
 
 proc mnewString(len: int): NimStringV2 {.compilerproc.} =
   if len <= 0:
     result = NimStringV2(len: 0, p: nil)
   else:
-    when compileOption("threads"):
-      var p = cast[ptr NimStrPayload](allocShared0(contentSize(len)))
-    else:
-      var p = cast[ptr NimStrPayload](alloc0(contentSize(len)))
+    var p = allocPayload0(len)
     p.cap = len
     result = NimStringV2(len: len, p: p)
 
@@ -129,13 +143,30 @@ proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} =
   if newLen == 0:
     discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations"
   else:
-    if newLen > s.len or isLiteral(s):
-      prepareAdd(s, newLen - s.len)
+    if isLiteral(s):
+      let oldP = s.p
+      s.p = allocPayload(newLen)
+      s.p.cap = newLen
+      if s.len > 0:
+        copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen))
+        if newLen > s.len:
+          zeroMem(cast[pointer](addr s.p.data[s.len]), newLen - s.len + 1)
+        else:
+          s.p.data[newLen] = '\0'
+      else:
+        zeroMem(cast[pointer](addr s.p.data[0]), newLen + 1)
+    elif newLen > s.len:
+      let oldCap = s.p.cap and not strlitFlag
+      if newLen > oldCap:
+        let newCap = max(newLen, resize(oldCap))
+        s.p = reallocPayload0(s.p, oldCap, newCap)
+        s.p.cap = newCap
+
     s.p.data[newLen] = '\0'
   s.len = newLen
 
 proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
-  if a.p == b.p: return
+  if a.p == b.p and a.len == b.len: return
   if isLiteral(b):
     # we can shallow copy literals:
     frees(a)
@@ -147,10 +178,7 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
       # 'let y = newStringOfCap(); var x = y'
       # on the other hand... These get turned into moves now.
       frees(a)
-      when compileOption("threads"):
-        a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len)))
-      else:
-        a.p = cast[ptr NimStrPayload](alloc0(contentSize(b.len)))
+      a.p = allocPayload(b.len)
       a.p.cap = b.len
     a.len = b.len
     copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1)
@@ -158,14 +186,11 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} =
 proc nimPrepareStrMutationImpl(s: var NimStringV2) =
   let oldP = s.p
   # can't mutate a literal, so we need a fresh copy here:
-  when compileOption("threads"):
-    s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len)))
-  else:
-    s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len)))
+  s.p = allocPayload(s.len)
   s.p.cap = s.len
   copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1)
 
-proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inline.} =
+proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inl.} =
   if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag:
     nimPrepareStrMutationImpl(s)
 
@@ -176,9 +201,16 @@ proc prepareMutation*(s: var string) {.inline.} =
     let s = unsafeAddr s
     nimPrepareStrMutationV2(cast[ptr NimStringV2](s)[])
 
+proc nimAddStrV1(s: var NimStringV2; src: NimStringV2) {.compilerRtl, inl.} =
+  #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag):
+  prepareAdd(s, src.len)
+  appendString s, src
+
+proc nimDestroyStrV1(s: NimStringV2) {.compilerRtl, inl.} =
+  frees(s)
 
-template capacityImpl(str: NimStringV2): int =
-  if str.p != nil: str.p.cap else: 0
+proc nimStrAtLe(s: string; idx: int; ch: char): bool {.compilerRtl, inl.} =
+  result = idx < s.len and s[idx] <= ch
 
 func capacity*(self: string): int {.inline.} =
   ## Returns the current capacity of the string.
@@ -188,6 +220,5 @@ func capacity*(self: string): int {.inline.} =
     str.add "Nim"
     assert str.capacity == 42
 
-  {.cast(noSideEffect).}:
-    let str = unsafeAddr self
-    result = capacityImpl(cast[ptr NimStringV2](str)[])
+  let str = cast[ptr NimStringV2](unsafeAddr self)
+  result = if str.p != nil: str.p.cap and not strlitFlag else: 0
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
deleted file mode 100644
index 2f0c8b0ba..000000000
--- a/lib/system/syslocks.nim
+++ /dev/null
@@ -1,234 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# Low level system locks and condition vars.
-
-{.push stackTrace: off.}
-
-when defined(windows):
-  type
-    Handle = int
-
-    SysLock {.importc: "CRITICAL_SECTION",
-              header: "<windows.h>", final, pure.} = object # CRITICAL_SECTION in WinApi
-      DebugInfo: pointer
-      LockCount: int32
-      RecursionCount: int32
-      OwningThread: int
-      LockSemaphore: int
-      SpinCount: int
-
-    SysCond {.importc: "RTL_CONDITION_VARIABLE", header: "<windows.h>".} = object
-      thePtr {.importc: "ptr".} : Handle
-
-  proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection",
-                                     header: "<windows.h>".}
-    ## Initializes the lock `L`.
-
-  proc tryAcquireSysAux(L: var SysLock): int32 {.importc: "TryEnterCriticalSection",
-                                                 header: "<windows.h>".}
-    ## Tries to acquire the lock `L`.
-
-  proc tryAcquireSys(L: var SysLock): bool {.inline.} =
-    result = tryAcquireSysAux(L) != 0'i32
-
-  proc acquireSys(L: var SysLock) {.importc: "EnterCriticalSection",
-                                    header: "<windows.h>".}
-    ## Acquires the lock `L`.
-
-  proc releaseSys(L: var SysLock) {.importc: "LeaveCriticalSection",
-                                    header: "<windows.h>".}
-    ## Releases the lock `L`.
-
-  proc deinitSys(L: var SysLock) {.importc: "DeleteCriticalSection",
-                                   header: "<windows.h>".}
-
-  proc initializeConditionVariable(
-    conditionVariable: var SysCond
-  ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".}
-
-  proc sleepConditionVariableCS(
-    conditionVariable: var SysCond,
-    PCRITICAL_SECTION: var SysLock,
-    dwMilliseconds: int
-  ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".}
-
-
-  proc signalSysCond(hEvent: var SysCond) {.stdcall, noSideEffect,
-    dynlib: "kernel32", importc: "WakeConditionVariable".}
-
-  proc broadcastSysCond(hEvent: var SysCond) {.stdcall, noSideEffect,
-    dynlib: "kernel32", importc: "WakeAllConditionVariable".}
-
-  proc initSysCond(cond: var SysCond) {.inline.} =
-    initializeConditionVariable(cond)
-  proc deinitSysCond(cond: var SysCond) {.inline.} =
-    discard
-  proc waitSysCond(cond: var SysCond, lock: var SysLock) =
-    discard sleepConditionVariableCS(cond, lock, -1'i32)
-
-elif defined(genode):
-  const
-    Header = "genode_cpp/syslocks.h"
-  type
-    SysLock {.importcpp: "Nim::SysLock", pure, final,
-              header: Header.} = object
-    SysCond {.importcpp: "Nim::SysCond", pure, final,
-              header: Header.} = object
-
-  proc initSysLock(L: var SysLock) = discard
-  proc deinitSys(L: var SysLock) = discard
-  proc acquireSys(L: var SysLock) {.noSideEffect, importcpp.}
-  proc tryAcquireSys(L: var SysLock): bool {.noSideEffect, importcpp.}
-  proc releaseSys(L: var SysLock) {.noSideEffect, importcpp.}
-
-  proc initSysCond(L: var SysCond) = discard
-  proc deinitSysCond(L: var SysCond) = discard
-  proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
-    noSideEffect, importcpp.}
-  proc signalSysCond(cond: var SysCond) {.
-    noSideEffect, importcpp.}
-  proc broadcastSysCond(cond: var SysCond) {.
-    noSideEffect, importcpp.}
-
-else:
-  type
-    SysLockObj {.importc: "pthread_mutex_t", pure, final,
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[40 div sizeof(clong), clong]
-
-    SysLockAttr {.importc: "pthread_mutexattr_t", pure, final
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[4 div sizeof(cint), cint]  # actually a cint
-
-    SysCondObj {.importc: "pthread_cond_t", pure, final,
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[48 div sizeof(clonglong), clonglong]
-
-    SysCondAttr {.importc: "pthread_condattr_t", pure, final
-               header: """#include <sys/types.h>
-                          #include <pthread.h>""".} = object
-      when defined(linux) and defined(amd64):
-        abi: array[4 div sizeof(cint), cint]  # actually a cint
-
-    SysLockType = distinct cint
-
-  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
-    importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
-  proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
-
-  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_lock", header: "<pthread.h>".}
-  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
-    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
-
-  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
-    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
-
-  when defined(ios):
-    # iOS will behave badly if sync primitives are moved in memory. In order
-    # to prevent this once and for all, we're doing an extra malloc when
-    # initializing the primitive.
-    type
-      SysLock = ptr SysLockObj
-      SysCond = ptr SysCondObj
-
-    when not declared(c_malloc):
-      proc c_malloc(size: csize_t): pointer {.
-        importc: "malloc", header: "<stdlib.h>".}
-      proc c_free(p: pointer) {.
-        importc: "free", header: "<stdlib.h>".}
-
-    proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
-      L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj))))
-      initSysLockAux(L[], attr)
-
-    proc deinitSys(L: var SysLock) =
-      deinitSysAux(L[])
-      c_free(L)
-
-    template acquireSys(L: var SysLock) =
-      acquireSysAux(L[])
-    template tryAcquireSys(L: var SysLock): bool =
-      tryAcquireSysAux(L[]) == 0'i32
-    template releaseSys(L: var SysLock) =
-      releaseSysAux(L[])
-  else:
-    type
-      SysLock = SysLockObj
-      SysCond = SysCondObj
-
-    template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
-      initSysLockAux(L, attr)
-    template deinitSys(L: var SysLock) =
-      deinitSysAux(L)
-    template acquireSys(L: var SysLock) =
-      acquireSysAux(L)
-    template tryAcquireSys(L: var SysLock): bool =
-      tryAcquireSysAux(L) == 0'i32
-    template releaseSys(L: var SysLock) =
-      releaseSysAux(L)
-
-  when insideRLocksModule:
-    let SysLockType_Reentrant {.importc: "PTHREAD_MUTEX_RECURSIVE",
-      header: "<pthread.h>".}: SysLockType
-    proc initSysLockAttr(a: var SysLockAttr) {.
-      importc: "pthread_mutexattr_init", header: "<pthread.h>", noSideEffect.}
-    proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
-      importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
-
-  else:
-    proc initSysCondAux(cond: var SysCondObj, cond_attr: ptr SysCondAttr = nil) {.
-      importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
-    proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
-      importc: "pthread_cond_destroy", header: "<pthread.h>".}
-
-    proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {.
-      importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
-    proc signalSysCondAux(cond: var SysCondObj) {.
-      importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
-    proc broadcastSysCondAux(cond: var SysCondObj) {.
-      importc: "pthread_cond_broadcast", header: "<pthread.h>", noSideEffect.}
-
-    when defined(ios):
-      proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
-        cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj))))
-        initSysCondAux(cond[], cond_attr)
-
-      proc deinitSysCond(cond: var SysCond) =
-        deinitSysCondAux(cond[])
-        c_free(cond)
-
-      template waitSysCond(cond: var SysCond, lock: var SysLock) =
-        discard waitSysCondAux(cond[], lock[])
-      template signalSysCond(cond: var SysCond) =
-        signalSysCondAux(cond[])
-      template broadcastSysCond(cond: var SysCond) =
-        broadcastSysCondAux(cond[])
-    else:
-      template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) =
-        initSysCondAux(cond, cond_attr)
-      template deinitSysCond(cond: var SysCond) =
-        deinitSysCondAux(cond)
-
-      template waitSysCond(cond: var SysCond, lock: var SysLock) =
-        discard waitSysCondAux(cond, lock)
-      template signalSysCond(cond: var SysCond) =
-        signalSysCondAux(cond)
-      template broadcastSysCond(cond: var SysCond) =
-        broadcastSysCondAux(cond)
-
-{.pop.}
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 7655d9004..3621c4960 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -17,10 +17,10 @@
 
 
 proc dataPointer(a: PGenericSeq, elemAlign: int): pointer =
-  cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign))
+  cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign))
 
 proc dataPointer(a: PGenericSeq, elemAlign, elemSize, index: int): pointer =
-  cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize))
+  cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize))
 
 proc resize(old: int): int {.inline.} =
   if old <= 0: result = 4
@@ -47,27 +47,22 @@ else:
   template allocStrNoInit(size: untyped): untyped =
     cast[NimString](newObjNoInit(addr(strDesc), size))
 
-proc rawNewStringNoInit(space: int): NimString {.compilerproc.} =
-  var s = space
-  if s < 7: s = 7
+proc rawNewStringNoInit(space: int): NimString =
+  let s = max(space, 7)
   result = allocStrNoInit(sizeof(TGenericSeq) + s + 1)
   result.reserved = s
-  result.len = 0
   when defined(gogc):
     result.elemSize = 1
 
 proc rawNewString(space: int): NimString {.compilerproc.} =
-  var s = space
-  if s < 7: s = 7
-  result = allocStr(sizeof(TGenericSeq) + s + 1)
-  result.reserved = s
+  result = rawNewStringNoInit(space)
   result.len = 0
-  when defined(gogc):
-    result.elemSize = 1
+  result.data[0] = '\0'
 
 proc mnewString(len: int): NimString {.compilerproc.} =
-  result = rawNewString(len)
+  result = rawNewStringNoInit(len)
   result.len = len
+  zeroMem(addr result.data[0], len + 1)
 
 proc copyStrLast(s: NimString, start, last: int): NimString {.compilerproc.} =
   # This is not used by most recent versions of the compiler anymore, but
@@ -75,13 +70,10 @@ proc copyStrLast(s: NimString, start, last: int): NimString {.compilerproc.} =
   let start = max(start, 0)
   if s == nil: return nil
   let len = min(last, s.len-1) - start + 1
-  if len > 0:
-    result = rawNewStringNoInit(len)
-    result.len = len
-    copyMem(addr(result.data), addr(s.data[start]), len)
-    result.data[len] = '\0'
-  else:
-    result = rawNewString(len)
+  result = rawNewStringNoInit(len)
+  result.len = len
+  copyMem(addr(result.data), addr(s.data[start]), len)
+  result.data[len] = '\0'
 
 proc copyStr(s: NimString, start: int): NimString {.compilerproc.} =
   # This is not used by most recent versions of the compiler anymore, but
@@ -91,12 +83,13 @@ proc copyStr(s: NimString, start: int): NimString {.compilerproc.} =
 
 proc nimToCStringConv(s: NimString): cstring {.compilerproc, nonReloadable, inline.} =
   if s == nil or s.len == 0: result = cstring""
-  else: result = cstring(addr s.data)
+  else: result = cast[cstring](addr s.data)
 
 proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} =
   result = rawNewStringNoInit(len)
   result.len = len
-  copyMem(addr(result.data), str, len + 1)
+  copyMem(addr(result.data), str, len)
+  result.data[len] = '\0'
 
 proc cstrToNimstr(str: cstring): NimString {.compilerRtl.} =
   if str == nil: NimString(nil)
@@ -201,7 +194,7 @@ proc addChar(s: NimString, c: char): NimString =
 
 proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
   if dest == nil:
-    result = rawNewStringNoInit(addlen)
+    result = rawNewString(addlen)
   elif dest.len + addlen <= dest.space:
     result = dest
   else: # slow path:
@@ -227,15 +220,18 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
 proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
   let n = max(newLen, 0)
   if s == nil:
-    result = mnewString(newLen)
+    if n == 0:
+      return s
+    else:
+      result = mnewString(n)
   elif n <= s.space:
     result = s
   else:
-    let sp = max(resize(s.space), newLen)
+    let sp = max(resize(s.space), n)
     result = rawNewStringNoInit(sp)
     result.len = s.len
-    copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
-    zeroMem(addr result.data[s.len], newLen - s.len)
+    copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len)
+    zeroMem(addr result.data[s.len], n - s.len)
     result.reserved = sp
   result.len = n
   result.data[n] = '\0'
@@ -308,7 +304,10 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
     compilerRtl.} =
   sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
   if s == nil:
-    result = cast[PGenericSeq](newSeq(typ, newLen))
+    if newLen == 0:
+      result = s
+    else:
+      result = cast[PGenericSeq](newSeq(typ, newLen))
   else:
     let elemSize = typ.base.size
     let elemAlign = typ.base.align
@@ -340,3 +339,25 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
       result = s
       zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize)
     result.len = newLen
+
+func capacity*(self: string): int {.inline.} =
+  ## Returns the current capacity of the string.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var str = newStringOfCap(cap = 42)
+    str.add "Nim"
+    assert str.capacity == 42
+
+  let str = cast[NimString](self)
+  result = if str != nil: str.space else: 0
+
+func capacity*[T](self: seq[T]): int {.inline.} =
+  ## Returns the current capacity of the seq.
+  # See https://github.com/nim-lang/RFCs/issues/460
+  runnableExamples:
+    var lst = newSeqOfCap[string](cap = 42)
+    lst.add "Nim"
+    assert lst.capacity == 42
+
+  let sek = cast[PGenericSeq](self)
+  result = if sek != nil: sek.space else: 0
diff --git a/lib/system/threadimpl.nim b/lib/system/threadimpl.nim
new file mode 100644
index 000000000..285b8f5e7
--- /dev/null
+++ b/lib/system/threadimpl.nim
@@ -0,0 +1,111 @@
+var
+  nimThreadDestructionHandlers* {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}]
+when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions):
+  proc deallocOsPages() {.rtl, raises: [].}
+proc threadTrouble() {.raises: [], gcsafe.}
+# create for the main thread. Note: do not insert this data into the list
+# of all threads; it's not to be stopped etc.
+when not defined(useNimRtl):
+  #when not defined(createNimRtl): initStackBottom()
+  when declared(initGC):
+    initGC()
+    when not emulatedThreadVars:
+      type ThreadType {.pure.} = enum
+        None = 0,
+        NimThread = 1,
+        ForeignThread = 2
+      var
+        threadType {.rtlThreadVar.}: ThreadType
+
+      threadType = ThreadType.NimThread
+
+when defined(gcDestructors):
+  proc deallocThreadStorage(p: pointer) = c_free(p)
+else:
+  template deallocThreadStorage(p: pointer) = deallocShared(p)
+
+template afterThreadRuns() =
+  for i in countdown(nimThreadDestructionHandlers.len-1, 0):
+    nimThreadDestructionHandlers[i]()
+
+proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) =
+  ## Registers a *thread local* handler that is called at the thread's
+  ## destruction.
+  ##
+  ## A thread is destructed when the `.thread` proc returns
+  ## normally or when it raises an exception. Note that unhandled exceptions
+  ## in a thread nevertheless cause the whole process to die.
+  nimThreadDestructionHandlers.add handler
+
+when defined(boehmgc):
+  type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.}
+  proc boehmGC_call_with_stack_base(sbp: GCStackBaseProc, p: pointer)
+    {.importc: "GC_call_with_stack_base", boehmGC.}
+  proc boehmGC_register_my_thread(sb: pointer)
+    {.importc: "GC_register_my_thread", boehmGC.}
+  proc boehmGC_unregister_my_thread()
+    {.importc: "GC_unregister_my_thread", boehmGC.}
+
+  proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} =
+    boehmGC_register_my_thread(sb)
+    try:
+      let thrd = cast[ptr Thread[TArg]](thrd)
+      when TArg is void:
+        thrd.dataFn()
+      else:
+        thrd.dataFn(thrd.data)
+    except:
+      threadTrouble()
+    finally:
+      afterThreadRuns()
+    boehmGC_unregister_my_thread()
+else:
+  proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
+    try:
+      when TArg is void:
+        thrd.dataFn()
+      else:
+        when defined(nimV2):
+          thrd.dataFn(thrd.data)
+        else:
+          var x: TArg
+          deepCopy(x, thrd.data)
+          thrd.dataFn(x)
+    except:
+      threadTrouble()
+    finally:
+      afterThreadRuns()
+      when hasAllocStack:
+        deallocThreadStorage(thrd.rawStack)
+
+proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} =
+  when defined(boehmgc):
+    boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd)
+  elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors:
+    var p {.volatile.}: pointer
+    # init the GC for refc/markandsweep
+    nimGC_setStackBottom(addr(p))
+    when declared(initGC):
+      initGC()
+    when declared(threadType):
+      threadType = ThreadType.NimThread
+    threadProcWrapDispatch[TArg](thrd)
+    when declared(deallocOsPages): deallocOsPages()
+  else:
+    threadProcWrapDispatch(thrd)
+
+template nimThreadProcWrapperBody*(closure: untyped): untyped =
+  var thrd = cast[ptr Thread[TArg]](closure)
+  var core = thrd.core
+  when declared(globalsSlot): threadVarSetValue(globalsSlot, thrd.core)
+  threadProcWrapStackFrame(thrd)
+  # Since an unhandled exception terminates the whole process (!), there is
+  # no need for a ``try finally`` here, nor would it be correct: The current
+  # exception is tried to be re-raised by the code-gen after the ``finally``!
+  # However this is doomed to fail, because we already unmapped every heap
+  # page!
+
+  # mark as not running anymore:
+  thrd.core = nil
+  thrd.dataFn = nil
+  deallocThreadStorage(cast[pointer](core))
diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim
index b62c903c3..e6ad9dca5 100644
--- a/lib/system/threadlocalstorage.nim
+++ b/lib/system/threadlocalstorage.nim
@@ -1,83 +1,33 @@
+import std/private/threadtypes
 
 when defined(windows):
   type
-    SysThread* = Handle
-    WinThreadProc = proc (x: pointer): int32 {.stdcall.}
-
-  proc createThread(lpThreadAttributes: pointer, dwStackSize: int32,
-                     lpStartAddress: WinThreadProc,
-                     lpParameter: pointer,
-                     dwCreationFlags: int32,
-                     lpThreadId: var int32): SysThread {.
-    stdcall, dynlib: "kernel32", importc: "CreateThread".}
-
-  proc winSuspendThread(hThread: SysThread): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SuspendThread".}
-
-  proc winResumeThread(hThread: SysThread): int32 {.
-    stdcall, dynlib: "kernel32", importc: "ResumeThread".}
-
-  proc waitForSingleObject(hHandle: SysThread, dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".}
-
-  proc waitForMultipleObjects(nCount: int32,
-                              lpHandles: ptr SysThread,
-                              bWaitAll: int32,
-                              dwMilliseconds: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".}
-
-  proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {.
-    stdcall, dynlib: "kernel32", importc: "TerminateThread".}
-
-  type
     ThreadVarSlot = distinct int32
 
-  when true:
-    proc threadVarAlloc(): ThreadVarSlot {.
-      importc: "TlsAlloc", stdcall, header: "<windows.h>".}
-    proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
-      importc: "TlsSetValue", stdcall, header: "<windows.h>".}
-    proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
-      importc: "TlsGetValue", stdcall, header: "<windows.h>".}
+  proc threadVarAlloc(): ThreadVarSlot {.
+    importc: "TlsAlloc", stdcall, header: "<windows.h>".}
+  proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
+    importc: "TlsSetValue", stdcall, header: "<windows.h>".}
+  proc tlsGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
+    importc: "TlsGetValue", stdcall, header: "<windows.h>".}
 
-    proc getLastError(): uint32 {.
-      importc: "GetLastError", stdcall, header: "<windows.h>".}
-    proc setLastError(x: uint32) {.
-      importc: "SetLastError", stdcall, header: "<windows.h>".}
+  proc getLastError(): uint32 {.
+    importc: "GetLastError", stdcall, header: "<windows.h>".}
+  proc setLastError(x: uint32) {.
+    importc: "SetLastError", stdcall, header: "<windows.h>".}
 
-    proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
-      let realLastError = getLastError()
-      result = tlsGetValue(dwTlsIndex)
-      setLastError(realLastError)
-  else:
-    proc threadVarAlloc(): ThreadVarSlot {.
-      importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
-    proc threadVarSetValue(dwTlsIndex: ThreadVarSlot, lpTlsValue: pointer) {.
-      importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
-    proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer {.
-      importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
-
-  proc setThreadAffinityMask(hThread: SysThread, dwThreadAffinityMask: uint) {.
-    importc: "SetThreadAffinityMask", stdcall, header: "<windows.h>".}
+  proc threadVarGetValue(dwTlsIndex: ThreadVarSlot): pointer =
+    let realLastError = getLastError()
+    result = tlsGetValue(dwTlsIndex)
+    setLastError(realLastError)
 
 elif defined(genode):
-  import genode/env
   const
     GenodeHeader = "genode_cpp/threads.h"
+
   type
-    SysThread* {.importcpp: "Nim::SysThread",
-                 header: GenodeHeader, final, pure.} = object
-    GenodeThreadProc = proc (x: pointer) {.noconv.}
     ThreadVarSlot = int
 
-  proc initThread(s: var SysThread,
-                  env: GenodeEnv,
-                  stackSize: culonglong,
-                  entry: GenodeThreadProc,
-                  arg: pointer,
-                  affinity: cuint) {.
-    importcpp: "#.initThread(@)".}
-
   proc threadVarAlloc(): ThreadVarSlot = 0
 
   proc offMainThread(): bool {.
@@ -113,62 +63,18 @@ else:
   when not defined(haiku):
     {.passc: "-pthread".}
 
-  const
-    schedh = "#define _GNU_SOURCE\n#include <sched.h>"
-    pthreadh = "#define _GNU_SOURCE\n#include <pthread.h>"
-
-  when not declared(Time):
-    when defined(linux):
-      type Time = clong
-    else:
-      type Time = int
-
   when (defined(linux) or defined(nintendoswitch)) and defined(amd64):
     type
-      SysThread* {.importc: "pthread_t",
-                  header: "<sys/types.h>" .} = distinct culong
-      Pthread_attr {.importc: "pthread_attr_t",
-                    header: "<sys/types.h>".} = object
-        abi: array[56 div sizeof(clong), clong]
       ThreadVarSlot {.importc: "pthread_key_t",
                     header: "<sys/types.h>".} = distinct cuint
   elif defined(openbsd) and defined(amd64):
     type
-      SysThread* {.importc: "pthread_t", header: "<pthread.h>".} = object
-      Pthread_attr {.importc: "pthread_attr_t",
-                       header: "<pthread.h>".} = object
       ThreadVarSlot {.importc: "pthread_key_t",
                      header: "<pthread.h>".} = cint
   else:
     type
-      SysThread* {.importc: "pthread_t", header: "<sys/types.h>".} = int
-      Pthread_attr {.importc: "pthread_attr_t",
-                       header: "<sys/types.h>".} = object
       ThreadVarSlot {.importc: "pthread_key_t",
                      header: "<sys/types.h>".} = object
-  type
-    Timespec {.importc: "struct timespec", header: "<time.h>".} = object
-      tv_sec: Time
-      tv_nsec: clong
-
-  proc pthread_attr_init(a1: var Pthread_attr): cint {.
-    importc, header: pthreadh.}
-  proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {.
-    importc, header: pthreadh.}
-  proc pthread_attr_setstacksize(a1: var Pthread_attr, a2: int): cint {.
-    importc, header: pthreadh.}
-  proc pthread_attr_destroy(a1: var Pthread_attr): cint {.
-    importc, header: pthreadh.}
-
-  proc pthread_create(a1: var SysThread, a2: var Pthread_attr,
-            a3: proc (x: pointer): pointer {.noconv.},
-            a4: pointer): cint {.importc: "pthread_create",
-            header: pthreadh.}
-  proc pthread_join(a1: SysThread, a2: ptr pointer): cint {.
-    importc, header: pthreadh.}
-
-  proc pthread_cancel(a1: SysThread): cint {.
-    importc: "pthread_cancel", header: pthreadh.}
 
   proc pthread_getspecific(a1: ThreadVarSlot): pointer {.
     importc: "pthread_getspecific", header: pthreadh.}
@@ -188,59 +94,13 @@ else:
   proc threadVarGetValue(s: ThreadVarSlot): pointer {.inline.} =
     result = pthread_getspecific(s)
 
-  type CpuSet {.importc: "cpu_set_t", header: schedh.} = object
-     when defined(linux) and defined(amd64):
-       abi: array[1024 div (8 * sizeof(culong)), culong]
-
-  proc cpusetZero(s: var CpuSet) {.importc: "CPU_ZERO", header: schedh.}
-  proc cpusetIncl(cpu: cint; s: var CpuSet) {.
-    importc: "CPU_SET", header: schedh.}
-
-  when defined(android):
-    # libc of android doesn't implement pthread_setaffinity_np,
-    # it exposes pthread_gettid_np though, so we can use that in combination
-    # with sched_setaffinity to set the thread affinity.
-    type Pid {.importc: "pid_t", header: "<sys/types.h>".} = int32 # From posix_other.nim
-
-    proc setAffinityTID(tid: Pid; setsize: csize_t; s: var CpuSet) {.
-      importc: "sched_setaffinity", header: schedh.}
-
-    proc pthread_gettid_np(thread: SysThread): Pid {.
-      importc: "pthread_gettid_np", header: pthreadh.}
-
-    proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) =
-      setAffinityTID(pthread_gettid_np(thread), setsize, s)
-  else:
-    proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) {.
-      importc: "pthread_setaffinity_np", header: pthreadh.}
-
-
-const
-  emulatedThreadVars = compileOption("tlsEmulation")
 
 when emulatedThreadVars:
   # the compiler generates this proc for us, so that we can get the size of
   # the thread local var block; we use this only for sanity checking though
   proc nimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".}
 
-# we preallocate a fixed size for thread local storage, so that no heap
-# allocations are needed. Currently less than 16K are used on a 64bit machine.
-# We use `float` for proper alignment:
-const nimTlsSize {.intdefine.} = 16000
-type
-  ThreadLocalStorage = array[0..(nimTlsSize div sizeof(float)), float]
-  PGcThread = ptr GcThread
-  GcThread {.pure, inheritable.} = object
-    when emulatedThreadVars:
-      tls: ThreadLocalStorage
-    else:
-      nil
-    when hasSharedHeap:
-      next, prev: PGcThread
-      stackBottom, stackTop: pointer
-      stackSize: int
-    else:
-      nil
+
 
 when emulatedThreadVars:
   var globalsSlot: ThreadVarSlot
@@ -262,4 +122,4 @@ when not defined(useNimRtl):
     if nimThreadVarsSize() > sizeof(ThreadLocalStorage):
       c_fprintf(cstderr, """too large thread local storage size requested,
 use -d:\"nimTlsSize=X\" to setup even more or stop using unittest.nim""")
-      quit 1
+      rawQuit 1
diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim
deleted file mode 100644
index bb348fd67..000000000
--- a/lib/system/widestrs.nim
+++ /dev/null
@@ -1,229 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-# Nim support for C/C++'s `wide strings`:idx:. This is part of the system
-# module! Do not import it directly!
-
-#when not declared(ThisIsSystem):
-#  {.error: "You must not import this module explicitly".}
-
-type
-  Utf16Char* = distinct int16
-
-when defined(nimv2):
-
-  type
-    WideCString* = ptr UncheckedArray[Utf16Char]
-
-    WideCStringObj* = object
-      bytes: int
-      data: WideCString
-
-  proc `=destroy`(a: var WideCStringObj) =
-    if a.data != nil:
-      when compileOption("threads"):
-        deallocShared(a.data)
-      else:
-        dealloc(a.data)
-
-  proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.}
-
-  proc `=sink`(a: var WideCStringObj; b: WideCStringObj) =
-    a.bytes = b.bytes
-    a.data = b.data
-
-  proc createWide(a: var WideCStringObj; bytes: int) =
-    a.bytes = bytes
-    when compileOption("threads"):
-      a.data = cast[typeof(a.data)](allocShared0(bytes))
-    else:
-      a.data = cast[typeof(a.data)](alloc0(bytes))
-
-  template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx]
-  template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val
-
-  template nullWide(): untyped = WideCStringObj(bytes: 0, data: nil)
-
-  converter toWideCString*(x: WideCStringObj): WideCString {.inline.} =
-    result = x.data
-
-else:
-  template nullWide(): untyped = nil
-
-  type
-    WideCString* = ref UncheckedArray[Utf16Char]
-    WideCStringObj* = WideCString
-
-  template createWide(a; L) =
-    unsafeNew(a, L)
-
-proc ord(arg: Utf16Char): int = int(cast[uint16](arg))
-
-proc len*(w: WideCString): int =
-  ## returns the length of a widestring. This traverses the whole string to
-  ## find the binary zero end marker!
-  result = 0
-  while int16(w[result]) != 0'i16: inc result
-
-const
-  UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16)
-  UNI_MAX_BMP = 0x0000FFFF
-  UNI_MAX_UTF16 = 0x0010FFFF
-  # UNI_MAX_UTF32 = 0x7FFFFFFF
-  # UNI_MAX_LEGAL_UTF32 = 0x0010FFFF
-
-  halfShift = 10
-  halfBase = 0x0010000
-  halfMask = 0x3FF
-
-  UNI_SUR_HIGH_START = 0xD800
-  UNI_SUR_HIGH_END = 0xDBFF
-  UNI_SUR_LOW_START = 0xDC00
-  UNI_SUR_LOW_END = 0xDFFF
-  UNI_REPL = 0xFFFD
-
-template ones(n: untyped): untyped = ((1 shl n)-1)
-
-template fastRuneAt(s: cstring, i, L: int, result: untyped, doInc = true) =
-  ## Returns the unicode character `s[i]` in `result`. If `doInc == true`
-  ## `i` is incremented by the number of bytes that have been processed.
-  bind ones
-
-  if ord(s[i]) <= 127:
-    result = ord(s[i])
-    when doInc: inc(i)
-  elif ord(s[i]) shr 5 == 0b110:
-    #assert(ord(s[i+1]) shr 6 == 0b10)
-    if i <= L - 2:
-      result = (ord(s[i]) and (ones(5))) shl 6 or (ord(s[i+1]) and ones(6))
-      when doInc: inc(i, 2)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  elif ord(s[i]) shr 4 == 0b1110:
-    if i <= L - 3:
-      #assert(ord(s[i+1]) shr 6 == 0b10)
-      #assert(ord(s[i+2]) shr 6 == 0b10)
-      result = (ord(s[i]) and ones(4)) shl 12 or
-               (ord(s[i+1]) and ones(6)) shl 6 or
-               (ord(s[i+2]) and ones(6))
-      when doInc: inc(i, 3)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  elif ord(s[i]) shr 3 == 0b11110:
-    if i <= L - 4:
-      #assert(ord(s[i+1]) shr 6 == 0b10)
-      #assert(ord(s[i+2]) shr 6 == 0b10)
-      #assert(ord(s[i+3]) shr 6 == 0b10)
-      result = (ord(s[i]) and ones(3)) shl 18 or
-               (ord(s[i+1]) and ones(6)) shl 12 or
-               (ord(s[i+2]) and ones(6)) shl 6 or
-               (ord(s[i+3]) and ones(6))
-      when doInc: inc(i, 4)
-    else:
-      result = UNI_REPL
-      when doInc: inc(i)
-  else:
-    result = 0xFFFD
-    when doInc: inc(i)
-
-iterator runes(s: cstring, L: int): int =
-  var
-    i = 0
-    result: int
-  while i < L:
-    fastRuneAt(s, i, L, result, true)
-    yield result
-
-proc newWideCString*(size: int): WideCStringObj =
-  createWide(result, size * 2 + 2)
-
-proc newWideCString*(source: cstring, L: int): WideCStringObj =
-  createWide(result, L * 2 + 2)
-  var d = 0
-  for ch in runes(source, L):
-
-    if ch <= UNI_MAX_BMP:
-      if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_LOW_END:
-        result[d] = UNI_REPLACEMENT_CHAR
-      else:
-        result[d] = cast[Utf16Char](uint16(ch))
-    elif ch > UNI_MAX_UTF16:
-      result[d] = UNI_REPLACEMENT_CHAR
-    else:
-      let ch = ch - halfBase
-      result[d] = cast[Utf16Char](uint16((ch shr halfShift) + UNI_SUR_HIGH_START))
-      inc d
-      result[d] = cast[Utf16Char](uint16((ch and halfMask) + UNI_SUR_LOW_START))
-    inc d
-  result[d] = Utf16Char(0)
-
-proc newWideCString*(s: cstring): WideCStringObj =
-  if s.isNil: return nullWide
-
-  result = newWideCString(s, s.len)
-
-proc newWideCString*(s: string): WideCStringObj =
-  result = newWideCString(cstring s, s.len)
-
-proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string =
-  result = newStringOfCap(estimate + estimate shr 2)
-
-  var i = 0
-  while w[i].int16 != 0'i16:
-    var ch = ord(w[i])
-    inc i
-    if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END:
-      # If the 16 bits following the high surrogate are in the source buffer...
-      let ch2 = ord(w[i])
-
-      # If it's a low surrogate, convert to UTF32:
-      if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END:
-        ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase
-        inc i
-      else:
-        #invalid UTF-16
-        ch = replacement
-    elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END:
-      #invalid UTF-16
-      ch = replacement
-
-    if ch < 0x80:
-      result.add chr(ch)
-    elif ch < 0x800:
-      result.add chr((ch shr 6) or 0xc0)
-      result.add chr((ch and 0x3f) or 0x80)
-    elif ch < 0x10000:
-      result.add chr((ch shr 12) or 0xe0)
-      result.add chr(((ch shr 6) and 0x3f) or 0x80)
-      result.add chr((ch and 0x3f) or 0x80)
-    elif ch <= 0x10FFFF:
-      result.add chr((ch shr 18) or 0xf0)
-      result.add chr(((ch shr 12) and 0x3f) or 0x80)
-      result.add chr(((ch shr 6) and 0x3f) or 0x80)
-      result.add chr((ch and 0x3f) or 0x80)
-    else:
-      # replacement char(in case user give very large number):
-      result.add chr(0xFFFD shr 12 or 0b1110_0000)
-      result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00)
-      result.add chr(0xFFFD and ones(6) or 0b10_0000_00)
-
-proc `$`*(s: WideCString): string =
-  result = s $ 80
-
-when defined(nimv2):
-  proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string =
-    `$`(s.data, estimate, replacement)
-
-  proc `$`*(s: WideCStringObj): string =
-    $(s.data)
-
-  proc len*(w: WideCStringObj): int {.inline.} =
-    len(w.data)
diff --git a/lib/system_overview.rst b/lib/system_overview.rst
index 768fdcd6d..cc0643bf1 100644
--- a/lib/system_overview.rst
+++ b/lib/system_overview.rst
@@ -5,9 +5,10 @@ The System module imports several separate modules, and their documentation
 is in separate files:
 
 * `iterators <iterators.html>`_
+* `exceptions <exceptions.html>`_
 * `assertions <assertions.html>`_
 * `dollars <dollars.html>`_
-* `widestrs <widestrs.html>`_
+* `ctypes <ctypes.html>`_
 
 
 Here is a short overview of the most commonly used functions from the
@@ -98,6 +99,7 @@ Proc                                Usage
 ===============================     ======================================
 
 **See also:**
+* `setutils module <setutils.html>`_ for bit set convenience functions
 * `sets module <sets.html>`_ for hash sets
 * `intsets module <intsets.html>`_ for efficient int sets
 
diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim
index 44e4e4420..207172f8c 100644
--- a/lib/windows/registry.nim
+++ b/lib/windows/registry.nim
@@ -9,7 +9,10 @@
 
 ## This module is experimental and its interface may change.
 
-import winlean, os
+import std/oserrors
+
+when defined(nimPreviewSlimSystem):
+  import std/widestrs
 
 type
   HKEY* = uint
diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim
index 97dc537be..79681376b 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.
 
-import dynlib
+import std/dynlib
 
 when defined(nimHasStyleChecks):
   {.push styleChecks: off.}
@@ -19,14 +19,9 @@ when defined(nimHasStyleChecks):
 
 when defined(nimPreviewSlimSystem):
   from std/syncio import FileHandle
+  import std/widestrs
 
-const
-  useWinUnicode* = not defined(useWinAnsi)
-
-when useWinUnicode:
-  type WinChar* = Utf16Char
-else:
-  type WinChar* = char
+type WinChar* = Utf16Char
 
 # See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types
 type
@@ -47,12 +42,12 @@ type
   HGLRC* = Handle
   BYTE* = uint8
 
-  SECURITY_ATTRIBUTES* {.final, pure.} = object
+  SECURITY_ATTRIBUTES* = object
     nLength*: int32
     lpSecurityDescriptor*: pointer
     bInheritHandle*: WINBOOL
 
-  STARTUPINFO* {.final, pure.} = object
+  STARTUPINFO* = object
     cb*: int32
     lpReserved*: cstring
     lpDesktop*: cstring
@@ -72,17 +67,17 @@ type
     hStdOutput*: Handle
     hStdError*: Handle
 
-  PROCESS_INFORMATION* {.final, pure.} = object
+  PROCESS_INFORMATION* = object
     hProcess*: Handle
     hThread*: Handle
     dwProcessId*: int32
     dwThreadId*: int32
 
-  FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
+  FILETIME* = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT
     dwLowDateTime*: DWORD
     dwHighDateTime*: DWORD
 
-  BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object
+  BY_HANDLE_FILE_INFORMATION* = object
     dwFileAttributes*: DWORD
     ftCreationTime*: FILETIME
     ftLastAccessTime*: FILETIME
@@ -94,7 +89,7 @@ type
     nFileIndexHigh*: DWORD
     nFileIndexLow*: DWORD
 
-  OSVERSIONINFO* {.final, pure.} = object
+  OSVERSIONINFO* = object
     dwOSVersionInfoSize*: DWORD
     dwMajorVersion*: DWORD
     dwMinorVersion*: DWORD
@@ -183,26 +178,14 @@ proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil,
                     lpBytesLeftThisMessage: ptr int32 = nil): bool {.
     stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".}
 
-when useWinUnicode:
-  proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
-                     lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
-                     lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
-                     bInheritHandles: WINBOOL, dwCreationFlags: int32,
-                     lpEnvironment, lpCurrentDirectory: WideCString,
-                     lpStartupInfo: var STARTUPINFO,
-                     lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
-    stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.}
-
-else:
-  proc createProcessA*(lpApplicationName, lpCommandLine: cstring,
-                       lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
-                       lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
-                       bInheritHandles: WINBOOL, dwCreationFlags: int32,
-                       lpEnvironment: pointer, lpCurrentDirectory: cstring,
-                       lpStartupInfo: var STARTUPINFO,
-                       lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
-      stdcall, dynlib: "kernel32", importc: "CreateProcessA", sideEffect.}
-
+proc createProcessW*(lpApplicationName, lpCommandLine: WideCString,
+                   lpProcessAttributes: ptr SECURITY_ATTRIBUTES,
+                   lpThreadAttributes: ptr SECURITY_ATTRIBUTES,
+                   bInheritHandles: WINBOOL, dwCreationFlags: int32,
+                   lpEnvironment, lpCurrentDirectory: WideCString,
+                   lpStartupInfo: var STARTUPINFO,
+                   lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{.
+  stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.}
 
 proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32",
     importc: "SuspendThread", sideEffect.}
@@ -231,67 +214,37 @@ proc getLastError*(): int32 {.importc: "GetLastError",
 proc setLastError*(error: int32) {.importc: "SetLastError",
     stdcall, dynlib: "kernel32", sideEffect.}
 
-when useWinUnicode:
-  proc formatMessageW*(dwFlags: int32, lpSource: pointer,
-                      dwMessageId, dwLanguageId: int32,
-                      lpBuffer: pointer, nSize: int32,
-                      arguments: pointer): int32 {.
-                      importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
-else:
-  proc formatMessageA*(dwFlags: int32, lpSource: pointer,
+proc formatMessageW*(dwFlags: int32, lpSource: pointer,
                     dwMessageId, dwLanguageId: int32,
                     lpBuffer: pointer, nSize: int32,
                     arguments: pointer): int32 {.
-                    importc: "FormatMessageA", stdcall, dynlib: "kernel32".}
+                    importc: "FormatMessageW", stdcall, dynlib: "kernel32".}
 
 proc localFree*(p: pointer) {.
   importc: "LocalFree", stdcall, dynlib: "kernel32".}
 
-when useWinUnicode:
-  proc getCurrentDirectoryW*(nBufferLength: int32,
-                             lpBuffer: WideCString): int32 {.
-    importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
-  proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
-    importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
-  proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
-    importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
-  proc removeDirectoryW*(lpPathName: WideCString): int32 {.
-    importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
-  proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.}
-
-  proc getModuleFileNameW*(handle: Handle, buf: WideCString,
-                           size: int32): int32 {.importc: "GetModuleFileNameW",
-    dynlib: "kernel32", stdcall.}
-else:
-  proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {.
-    importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
-  proc setCurrentDirectoryA*(lpPathName: cstring): int32 {.
-    importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
-  proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {.
-    importc: "CreateDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
-  proc removeDirectoryA*(lpPathName: cstring): int32 {.
-    importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall, sideEffect.}
-  proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {.
-    stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.}
-
-  proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {.
-    importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.}
-
-when useWinUnicode:
-  proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
-                         flags: DWORD): int32 {.
-    importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.}
-  proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
-                         security: pointer=nil): int32 {.
-    importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.}
-else:
-  proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring,
-                           flags: DWORD): int32 {.
-    importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall, sideEffect.}
-  proc createHardLinkA*(lpFileName, lpExistingFileName: cstring,
-                           security: pointer=nil): int32 {.
-    importc:"CreateHardLinkA", dynlib: "kernel32", stdcall, sideEffect.}
+proc getCurrentDirectoryW*(nBufferLength: int32,
+                           lpBuffer: WideCString): int32 {.
+  importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {.
+  importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {.
+  importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc removeDirectoryW*(lpPathName: WideCString): int32 {.
+  importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
+proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {.
+  stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.}
+
+proc getModuleFileNameW*(handle: Handle, buf: WideCString,
+                         size: int32): int32 {.importc: "GetModuleFileNameW",
+  dynlib: "kernel32", stdcall.}
+
+proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
+                       flags: DWORD): int32 {.
+  importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.}
+proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString,
+                       security: pointer=nil): int32 {.
+  importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.}
 
 const
   FILE_ATTRIBUTE_READONLY* = 0x00000001'i32
@@ -342,90 +295,51 @@ type
     cFileName*: array[0..(MAX_PATH) - 1, WinChar]
     cAlternateFileName*: array[0..13, WinChar]
 
-when useWinUnicode:
-  proc findFirstFileW*(lpFileName: WideCString,
-                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
-      stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.}
-  proc findNextFileW*(hFindFile: Handle,
-                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
-      stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.}
-else:
-  proc findFirstFileA*(lpFileName: cstring,
-                      lpFindFileData: var WIN32_FIND_DATA): Handle {.
-      stdcall, dynlib: "kernel32", importc: "FindFirstFileA", sideEffect.}
-  proc findNextFileA*(hFindFile: Handle,
-                     lpFindFileData: var WIN32_FIND_DATA): int32 {.
-      stdcall, dynlib: "kernel32", importc: "FindNextFileA", sideEffect.}
+proc findFirstFileW*(lpFileName: WideCString,
+                    lpFindFileData: var WIN32_FIND_DATA): Handle {.
+    stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.}
+proc findNextFileW*(hFindFile: Handle,
+                   lpFindFileData: var WIN32_FIND_DATA): int32 {.
+    stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.}
 
 proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32",
   importc: "FindClose".}
 
-when useWinUnicode:
-  proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
-                        lpBuffer: WideCString,
-                        lpFilePart: var WideCString): int32 {.
+proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32,
+                      lpBuffer: WideCString,
+                      lpFilePart: var WideCString): int32 {.
+                      stdcall, dynlib: "kernel32",
+                      importc: "GetFullPathNameW", sideEffect.}
+proc getFileAttributesW*(lpFileName: WideCString): int32 {.
                         stdcall, dynlib: "kernel32",
-                        importc: "GetFullPathNameW", sideEffect.}
-  proc getFileAttributesW*(lpFileName: WideCString): int32 {.
-                          stdcall, dynlib: "kernel32",
-                          importc: "GetFileAttributesW", sideEffect.}
-  proc setFileAttributesW*(lpFileName: WideCString,
-                           dwFileAttributes: int32): WINBOOL {.
-      stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.}
-
-  proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
-                 bFailIfExists: WINBOOL): WINBOOL {.
-    importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.}
-
-  proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
-    importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.}
-  proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
-                    flags: DWORD): WINBOOL {.
-    importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.}
-
-  proc getEnvironmentStringsW*(): WideCString {.
-    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.}
-  proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
-    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.}
-
-  proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
-    stdcall, dynlib: "kernel32", sideEffect.}
+                        importc: "GetFileAttributesW", sideEffect.}
+proc setFileAttributesW*(lpFileName: WideCString,
+                         dwFileAttributes: int32): WINBOOL {.
+    stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.}
 
-else:
-  proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32,
-                        lpBuffer: cstring, lpFilePart: var cstring): int32 {.
-                        stdcall, dynlib: "kernel32",
-                        importc: "GetFullPathNameA", sideEffect.}
-  proc getFileAttributesA*(lpFileName: cstring): int32 {.
-                          stdcall, dynlib: "kernel32",
-                          importc: "GetFileAttributesA", sideEffect.}
-  proc setFileAttributesA*(lpFileName: cstring,
-                           dwFileAttributes: int32): WINBOOL {.
-      stdcall, dynlib: "kernel32", importc: "SetFileAttributesA", sideEffect.}
-
-  proc copyFileA*(lpExistingFileName, lpNewFileName: cstring,
-                 bFailIfExists: cint): cint {.
-    importc: "CopyFileA", stdcall, dynlib: "kernel32", sideEffect.}
-
-  proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {.
-    importc: "MoveFileA", stdcall, dynlib: "kernel32", sideEffect.}
-  proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring,
-                    flags: DWORD): WINBOOL {.
-    importc: "MoveFileExA", stdcall, dynlib: "kernel32", sideEffect.}
-
-  proc getEnvironmentStringsA*(): cstring {.
-    stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA", sideEffect.}
-  proc freeEnvironmentStringsA*(para1: cstring): int32 {.
-    stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA", sideEffect.}
-
-  proc getCommandLineA*(): cstring {.
-    importc: "GetCommandLineA", stdcall, dynlib: "kernel32", sideEffect.}
+proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
+               bFailIfExists: WINBOOL): WINBOOL {.
+  importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.}
+
+proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {.
+  importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.}
+proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString,
+                  flags: DWORD): WINBOOL {.
+  importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.}
+
+proc getEnvironmentStringsW*(): WideCString {.
+  stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.}
+proc freeEnvironmentStringsW*(para1: WideCString): int32 {.
+  stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.}
+
+proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW",
+  stdcall, dynlib: "kernel32", sideEffect.}
 
 proc rdFileTime*(f: FILETIME): int64 =
-  result = ze64(f.dwLowDateTime) or (ze64(f.dwHighDateTime) shl 32)
+  result = int64(cast[uint32](f.dwLowDateTime)) or (int64(cast[uint32](f.dwHighDateTime)) shl 32)
 
 proc rdFileSize*(f: WIN32_FIND_DATA): int64 =
-  result = ze64(f.nFileSizeLow) or (ze64(f.nFileSizeHigh) shl 32)
+  result = int64(cast[uint32](f.nFileSizeLow)) or (int64(cast[uint32](f.nFileSizeHigh)) shl 32)
 
 proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
   importc: "GetSystemTimeAsFileTime", dynlib: "kernel32", stdcall, sideEffect.}
@@ -433,17 +347,10 @@ proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {.
 proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32",
                                     importc: "Sleep", sideEffect.}
 
-when useWinUnicode:
-  proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile,
-                     lpParameters, lpDirectory: WideCString,
-                     nShowCmd: int32): Handle{.
-      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.}
-
-else:
-  proc shellExecuteA*(hwnd: Handle, lpOperation, lpFile,
-                     lpParameters, lpDirectory: cstring,
-                     nShowCmd: int32): Handle{.
-      stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA", sideEffect.}
+proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile,
+                   lpParameters, lpDirectory: WideCString,
+                   nShowCmd: int32): Handle{.
+    stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.}
 
 proc getFileInformationByHandle*(hFile: Handle,
   lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{.
@@ -793,13 +700,6 @@ proc createFileMappingW*(hFile: Handle,
                        lpName: pointer): Handle {.
   stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".}
 
-when not useWinUnicode:
-  proc createFileMappingA*(hFile: Handle,
-                           lpFileMappingAttributes: pointer,
-                           flProtect, dwMaximumSizeHigh: DWORD,
-                           dwMaximumSizeLow: DWORD, lpName: cstring): Handle {.
-      stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".}
-
 proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall,
     dynlib: "kernel32", importc: "UnmapViewOfFile".}
 
@@ -819,7 +719,7 @@ type
   POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD,
       para3: POVERLAPPED){.stdcall.}
 
-  GUID* {.final, pure.} = object
+  GUID* = object
     D1*: int32
     D2*: int16
     D3*: int16
@@ -975,7 +875,7 @@ proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring,
                   stringBufSize: int32): cstring {.stdcall.} =
   var ver: OSVERSIONINFO
   ver.dwOSVersionInfoSize = sizeof(ver).DWORD
-  let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr)
+  let res = getVersionExW(ver.addr)
   if res == 0:
     result = nil
   elif ver.dwMajorVersion >= 6:
@@ -1059,16 +959,10 @@ proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL,
                     dwProcessId: DWORD): Handle
      {.stdcall, dynlib: "kernel32", importc: "OpenProcess".}
 
-when defined(useWinAnsi):
-  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
-                    bManualReset: DWORD, bInitialState: DWORD,
-                    lpName: cstring): Handle
-       {.stdcall, dynlib: "kernel32", importc: "CreateEventA".}
-else:
-  proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
-                    bManualReset: DWORD, bInitialState: DWORD,
-                    lpName: ptr Utf16Char): Handle
-       {.stdcall, dynlib: "kernel32", importc: "CreateEventW".}
+proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES,
+                  bManualReset: DWORD, bInitialState: DWORD,
+                  lpName: ptr Utf16Char): Handle
+     {.stdcall, dynlib: "kernel32", importc: "CreateEventW".}
 
 proc setEvent*(hEvent: Handle): cint
      {.stdcall, dynlib: "kernel32", importc: "SetEvent".}
@@ -1100,7 +994,7 @@ proc wsaResetEvent*(hEvent: Handle): bool
      {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".}
 
 type
-  KEY_EVENT_RECORD* {.final, pure.} = object
+  KEY_EVENT_RECORD* = object
     eventType*: int16
     bKeyDown*: WINBOOL
     wRepeatCount*: int16
@@ -1109,14 +1003,9 @@ type
     uChar*: int16
     dwControlKeyState*: DWORD
 
-when defined(useWinAnsi):
-  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
-                        lpNumberOfEventsRead: ptr cint): cint
-       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".}
-else:
-  proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
-                        lpNumberOfEventsRead: ptr cint): cint
-       {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
+proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint,
+                      lpNumberOfEventsRead: ptr cint): cint
+     {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".}
 
 type
   LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.}
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
deleted file mode 100644
index 2fefe4a8b..000000000
--- a/lib/wrappers/mysql.nim
+++ /dev/null
@@ -1,1115 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2010 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-{.push, callconv: cdecl.}
-when defined(nimHasStyleChecks):
-  {.push styleChecks: off.}
-
-when defined(unix):
-  when defined(macosx):
-    const
-      lib = "(libmysqlclient|libmariadbclient)(|.20|.19|.18|.17|.16|.15).dylib"
-  else:
-    const
-      lib = "(libmysqlclient|libmariadbclient).so(|.20|.19|.18|.17|.16|.15)"
-when defined(windows):
-  const
-    lib = "(libmysql.dll|libmariadb.dll)"
-type
-  my_bool* = bool
-  Pmy_bool* = ptr my_bool
-  PVIO* = pointer
-  Pgptr* = ptr gptr
-  gptr* = cstring
-  Pmy_socket* = ptr my_socket
-  my_socket* = cint
-  PPByte* = pointer
-  cuint* = cint
-
-#  ------------ Start of declaration in "mysql_com.h"   ---------------------
-#
-#  ** Common definition between mysql server & client
-#
-# Field/table name length
-
-const
-  NAME_LEN* = 64
-  HOSTNAME_LENGTH* = 60
-  USERNAME_LENGTH* = 16
-  SERVER_VERSION_LENGTH* = 60
-  SQLSTATE_LENGTH* = 5
-  LOCAL_HOST* = "localhost"
-  LOCAL_HOST_NAMEDPIPE* = '.'
-
-const
-  NAMEDPIPE* = "MySQL"
-  SERVICENAME* = "MySQL"
-
-type
-  Enum_server_command* = enum
-    COM_SLEEP, COM_QUIT, COM_INIT_DB, COM_QUERY, COM_FIELD_LIST, COM_CREATE_DB,
-    COM_DROP_DB, COM_REFRESH, COM_SHUTDOWN, COM_STATISTICS, COM_PROCESS_INFO,
-    COM_CONNECT, COM_PROCESS_KILL, COM_DEBUG, COM_PING, COM_TIME,
-    COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP, COM_TABLE_DUMP,
-    COM_CONNECT_OUT, COM_REGISTER_SLAVE, COM_STMT_PREPARE, COM_STMT_EXECUTE,
-    COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE, COM_STMT_RESET, COM_SET_OPTION,
-    COM_STMT_FETCH, COM_END
-{.deprecated: [Tenum_server_command: Enum_server_command].}
-
-const
-  SCRAMBLE_LENGTH* = 20 # Length of random string sent by server on handshake;
-                        # this is also length of obfuscated password,
-                        # received from client
-  SCRAMBLE_LENGTH_323* = 8    # length of password stored in the db:
-                              # new passwords are preceded with '*'
-  SCRAMBLED_PASSWORD_CHAR_LENGTH* = SCRAMBLE_LENGTH * 2 + 1
-  SCRAMBLED_PASSWORD_CHAR_LENGTH_323* = SCRAMBLE_LENGTH_323 * 2
-  NOT_NULL_FLAG* = 1          #  Field can't be NULL
-  PRI_KEY_FLAG* = 2           #  Field is part of a primary key
-  UNIQUE_KEY_FLAG* = 4        #  Field is part of a unique key
-  MULTIPLE_KEY_FLAG* = 8      #  Field is part of a key
-  BLOB_FLAG* = 16             #  Field is a blob
-  UNSIGNED_FLAG* = 32         #  Field is unsigned
-  ZEROFILL_FLAG* = 64         #  Field is zerofill
-  BINARY_FLAG* = 128          #  Field is binary
-                              # The following are only sent to new clients
-  ENUM_FLAG* = 256            # field is an enum
-  AUTO_INCREMENT_FLAG* = 512  # field is a autoincrement field
-  TIMESTAMP_FLAG* = 1024      # Field is a timestamp
-  SET_FLAG* = 2048            # field is a set
-  NO_DEFAULT_VALUE_FLAG* = 4096 # Field doesn't have default value
-  NUM_FLAG* = 32768           # Field is num (for clients)
-  PART_KEY_FLAG* = 16384      # Intern; Part of some key
-  GROUP_FLAG* = 32768         # Intern: Group field
-  UNIQUE_FLAG* = 65536        # Intern: Used by sql_yacc
-  BINCMP_FLAG* = 131072       # Intern: Used by sql_yacc
-  REFRESH_GRANT* = 1          # Refresh grant tables
-  REFRESH_LOG* = 2            # Start on new log file
-  REFRESH_TABLES* = 4         # close all tables
-  REFRESH_HOSTS* = 8          # Flush host cache
-  REFRESH_STATUS* = 16        # Flush status variables
-  REFRESH_THREADS* = 32       # Flush thread cache
-  REFRESH_SLAVE* = 64         # Reset master info and restart slave thread
-  REFRESH_MASTER* = 128 # Remove all bin logs in the index and truncate the index
-                        # The following can't be set with mysql_refresh()
-  REFRESH_READ_LOCK* = 16384  # Lock tables for read
-  REFRESH_FAST* = 32768       # Intern flag
-  REFRESH_QUERY_CACHE* = 65536 # RESET (remove all queries) from query cache
-  REFRESH_QUERY_CACHE_FREE* = 0x00020000 # pack query cache
-  REFRESH_DES_KEY_FILE* = 0x00040000
-  REFRESH_USER_RESOURCES* = 0x00080000
-  CLIENT_LONG_PASSWORD* = 1   # new more secure passwords
-  CLIENT_FOUND_ROWS* = 2      # Found instead of affected rows
-  CLIENT_LONG_FLAG* = 4       # Get all column flags
-  CLIENT_CONNECT_WITH_DB* = 8 # One can specify db on connect
-  CLIENT_NO_SCHEMA* = 16      # Don't allow database.table.column
-  CLIENT_COMPRESS* = 32       # Can use compression protocol
-  CLIENT_ODBC* = 64           # Odbc client
-  CLIENT_LOCAL_FILES* = 128   # Can use LOAD DATA LOCAL
-  CLIENT_IGNORE_SPACE* = 256  # Ignore spaces before '('
-  CLIENT_PROTOCOL_41* = 512   # New 4.1 protocol
-  CLIENT_INTERACTIVE* = 1024  # This is an interactive client
-  CLIENT_SSL* = 2048          # Switch to SSL after handshake
-  CLIENT_IGNORE_SIGPIPE* = 4096 # IGNORE sigpipes
-  CLIENT_TRANSACTIONS* = 8192 # Client knows about transactions
-  CLIENT_RESERVED* = 16384    # Old flag for 4.1 protocol
-  CLIENT_SECURE_CONNECTION* = 32768 # New 4.1 authentication
-  CLIENT_MULTI_STATEMENTS* = 65536 # Enable/disable multi-stmt support
-  CLIENT_MULTI_RESULTS* = 131072 # Enable/disable multi-results
-  CLIENT_REMEMBER_OPTIONS*: int = 1 shl 31
-  SERVER_STATUS_IN_TRANS* = 1 # Transaction has started
-  SERVER_STATUS_AUTOCOMMIT* = 2 # Server in auto_commit mode
-  SERVER_STATUS_MORE_RESULTS* = 4 # More results on server
-  SERVER_MORE_RESULTS_EXISTS* = 8 # Multi query - next query exists
-  SERVER_QUERY_NO_GOOD_INDEX_USED* = 16
-  SERVER_QUERY_NO_INDEX_USED* = 32 # The server was able to fulfill the clients request and opened a
-                                   #      read-only non-scrollable cursor for a query. This flag comes
-                                   #      in reply to COM_STMT_EXECUTE and COM_STMT_FETCH commands.
-  SERVER_STATUS_CURSOR_EXISTS* = 64 # This flag is sent when a read-only cursor is exhausted, in reply to
-                                    #      COM_STMT_FETCH command.
-  SERVER_STATUS_LAST_ROW_SENT* = 128
-  SERVER_STATUS_DB_DROPPED* = 256 # A database was dropped
-  SERVER_STATUS_NO_BACKSLASH_ESCAPES* = 512
-  ERRMSG_SIZE* = 200
-  NET_READ_TIMEOUT* = 30      # Timeout on read
-  NET_WRITE_TIMEOUT* = 60     # Timeout on write
-  NET_WAIT_TIMEOUT* = 8 * 60 * 60 # Wait for new query
-  ONLY_KILL_QUERY* = 1
-
-const
-  MAX_TINYINT_WIDTH* = 3      # Max width for a TINY w.o. sign
-  MAX_SMALLINT_WIDTH* = 5     # Max width for a SHORT w.o. sign
-  MAX_MEDIUMINT_WIDTH* = 8    # Max width for a INT24 w.o. sign
-  MAX_INT_WIDTH* = 10         # Max width for a LONG w.o. sign
-  MAX_BIGINT_WIDTH* = 20      # Max width for a LONGLONG
-  MAX_CHAR_WIDTH* = 255       # Max length for a CHAR column
-  MAX_BLOB_WIDTH* = 8192      # Default width for blob
-
-type
-  Pst_net* = ptr St_net
-  St_net*{.final.} = object
-    vio*: PVIO
-    buff*: cstring
-    buff_end*: cstring
-    write_pos*: cstring
-    read_pos*: cstring
-    fd*: my_socket            # For Perl DBI/dbd
-    max_packet*: int
-    max_packet_size*: int
-    pkt_nr*: cuint
-    compress_pkt_nr*: cuint
-    write_timeout*: cuint
-    read_timeout*: cuint
-    retry_count*: cuint
-    fcntl*: cint
-    compress*: my_bool #   The following variable is set if we are doing several queries in one
-                       #        command ( as in LOAD TABLE ... FROM MASTER ),
-                       #        and do not want to confuse the client with OK at the wrong time
-    remain_in_buf*: int
-    len*: int
-    buf_length*: int
-    where_b*: int
-    return_status*: ptr cint
-    reading_or_writing*: char
-    save_char*: cchar
-    no_send_ok*: my_bool      # For SPs and other things that do multiple stmts
-    no_send_eof*: my_bool     # For SPs' first version read-only cursors
-    no_send_error*: my_bool # Set if OK packet is already sent, and
-                            # we do not need to send error messages
-                            #   Pointer to query object in query cache, do not equal NULL (0) for
-                            #        queries in cache that have not stored its results yet
-                            # $endif
-    last_error*: array[0..(ERRMSG_SIZE) - 1, char]
-    sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char]
-    last_errno*: cuint
-    error*: char
-    query_cache_query*: gptr
-    report_error*: my_bool    # We should report error (we have unreported error)
-    return_errno*: my_bool
-
-  NET* = St_net
-  PNET* = ptr NET
-{.deprecated: [Tst_net: St_net, TNET: NET].}
-
-const
-  packet_error* = - 1
-
-type
-  Enum_field_types* = enum    # For backward compatibility
-    TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE,
-    TYPE_NULL, TYPE_TIMESTAMP, TYPE_LONGLONG, TYPE_INT24, TYPE_DATE, TYPE_TIME,
-    TYPE_DATETIME, TYPE_YEAR, TYPE_NEWDATE, TYPE_VARCHAR, TYPE_BIT,
-    TYPE_NEWDECIMAL = 246, TYPE_ENUM = 247, TYPE_SET = 248,
-    TYPE_TINY_BLOB = 249, TYPE_MEDIUM_BLOB = 250, TYPE_LONG_BLOB = 251,
-    TYPE_BLOB = 252, TYPE_VAR_STRING = 253, TYPE_STRING = 254,
-    TYPE_GEOMETRY = 255
-{.deprecated: [Tenum_field_types: Enum_field_types].}
-
-const
-  CLIENT_MULTI_QUERIES* = CLIENT_MULTI_STATEMENTS
-  FIELD_TYPE_DECIMAL* = TYPE_DECIMAL
-  FIELD_TYPE_NEWDECIMAL* = TYPE_NEWDECIMAL
-  FIELD_TYPE_TINY* = TYPE_TINY
-  FIELD_TYPE_SHORT* = TYPE_SHORT
-  FIELD_TYPE_LONG* = TYPE_LONG
-  FIELD_TYPE_FLOAT* = TYPE_FLOAT
-  FIELD_TYPE_DOUBLE* = TYPE_DOUBLE
-  FIELD_TYPE_NULL* = TYPE_NULL
-  FIELD_TYPE_TIMESTAMP* = TYPE_TIMESTAMP
-  FIELD_TYPE_LONGLONG* = TYPE_LONGLONG
-  FIELD_TYPE_INT24* = TYPE_INT24
-  FIELD_TYPE_DATE* = TYPE_DATE
-  FIELD_TYPE_TIME* = TYPE_TIME
-  FIELD_TYPE_DATETIME* = TYPE_DATETIME
-  FIELD_TYPE_YEAR* = TYPE_YEAR
-  FIELD_TYPE_NEWDATE* = TYPE_NEWDATE
-  FIELD_TYPE_ENUM* = TYPE_ENUM
-  FIELD_TYPE_SET* = TYPE_SET
-  FIELD_TYPE_TINY_BLOB* = TYPE_TINY_BLOB
-  FIELD_TYPE_MEDIUM_BLOB* = TYPE_MEDIUM_BLOB
-  FIELD_TYPE_LONG_BLOB* = TYPE_LONG_BLOB
-  FIELD_TYPE_BLOB* = TYPE_BLOB
-  FIELD_TYPE_VAR_STRING* = TYPE_VAR_STRING
-  FIELD_TYPE_STRING* = TYPE_STRING
-  FIELD_TYPE_CHAR* = TYPE_TINY
-  FIELD_TYPE_INTERVAL* = TYPE_ENUM
-  FIELD_TYPE_GEOMETRY* = TYPE_GEOMETRY
-  FIELD_TYPE_BIT* = TYPE_BIT  # Shutdown/kill enums and constants
-                              # Bits for THD::killable.
-  SHUTDOWN_KILLABLE_CONNECT* = chr(1 shl 0)
-  SHUTDOWN_KILLABLE_TRANS* = chr(1 shl 1)
-  SHUTDOWN_KILLABLE_LOCK_TABLE* = chr(1 shl 2)
-  SHUTDOWN_KILLABLE_UPDATE* = chr(1 shl 3)
-
-type
-  Enum_shutdown_level* = enum
-    SHUTDOWN_DEFAULT = 0, SHUTDOWN_WAIT_CONNECTIONS = 1,
-    SHUTDOWN_WAIT_TRANSACTIONS = 2, SHUTDOWN_WAIT_UPDATES = 8,
-    SHUTDOWN_WAIT_ALL_BUFFERS = 16, SHUTDOWN_WAIT_CRITICAL_BUFFERS = 17,
-    KILL_QUERY = 254, KILL_CONNECTION = 255
-  Enum_cursor_type* = enum    # options for mysql_set_option
-    CURSOR_TYPE_NO_CURSOR = 0, CURSOR_TYPE_READ_ONLY = 1,
-    CURSOR_TYPE_FOR_UPDATE = 2, CURSOR_TYPE_SCROLLABLE = 4
-  Enum_mysql_set_option* = enum
-    OPTION_MULTI_STATEMENTS_ON, OPTION_MULTI_STATEMENTS_OFF
-{.deprecated: [Tenum_shutdown_level: Enum_shutdown_level,
-              Tenum_cursor_type: Enum_cursor_type,
-              Tenum_mysql_set_option: Enum_mysql_set_option].}
-
-proc my_net_init*(net: PNET, vio: PVIO): my_bool{.cdecl, dynlib: lib,
-    importc: "my_net_init".}
-proc my_net_local_init*(net: PNET){.cdecl, dynlib: lib,
-                                    importc: "my_net_local_init".}
-proc net_end*(net: PNET){.cdecl, dynlib: lib, importc: "net_end".}
-proc net_clear*(net: PNET){.cdecl, dynlib: lib, importc: "net_clear".}
-proc net_realloc*(net: PNET, len: int): my_bool{.cdecl, dynlib: lib,
-    importc: "net_realloc".}
-proc net_flush*(net: PNET): my_bool{.cdecl, dynlib: lib, importc: "net_flush".}
-proc my_net_write*(net: PNET, packet: cstring, length: int): my_bool{.cdecl,
-    dynlib: lib, importc: "my_net_write".}
-proc net_write_command*(net: PNET, command: char, header: cstring,
-                        head_len: int, packet: cstring, length: int): my_bool{.
-    cdecl, dynlib: lib, importc: "net_write_command".}
-proc net_real_write*(net: PNET, packet: cstring, length: int): cint{.cdecl,
-    dynlib: lib, importc: "net_real_write".}
-proc my_net_read*(net: PNET): int{.cdecl, dynlib: lib, importc: "my_net_read".}
-  # The following function is not meant for normal usage
-  #      Currently it's used internally by manager.c
-type
-  Psockaddr* = ptr Sockaddr
-  Sockaddr*{.final.} = object  # undefined structure
-{.deprecated: [Tsockaddr: Sockaddr].}
-
-proc my_connect*(s: my_socket, name: Psockaddr, namelen: cuint, timeout: cuint): cint{.
-    cdecl, dynlib: lib, importc: "my_connect".}
-type
-  Prand_struct* = ptr Rand_struct
-  Rand_struct*{.final.} = object # The following is for user defined functions
-    seed1*: int
-    seed2*: int
-    max_value*: int
-    max_value_dbl*: cdouble
-
-  Item_result* = enum
-    STRING_RESULT, REAL_RESULT, INT_RESULT, ROW_RESULT, DECIMAL_RESULT
-  PItem_result* = ptr Item_result
-  Pst_udf_args* = ptr St_udf_args
-  St_udf_args*{.final.} = object
-    arg_count*: cuint         # Number of arguments
-    arg_type*: PItem_result   # Pointer to item_results
-    args*: cstringArray       # Pointer to item_results
-    lengths*: ptr int         # Length of string arguments
-    maybe_null*: cstring      # Length of string arguments
-    attributes*: cstringArray # Pointer to attribute name
-    attribute_lengths*: ptr int # Length of attribute arguments
-
-  UDF_ARGS* = St_udf_args
-  PUDF_ARGS* = ptr UDF_ARGS   # This holds information about the result
-  Pst_udf_init* = ptr St_udf_init
-  St_udf_init*{.final.} = object
-    maybe_null*: my_bool      # 1 if function can return NULL
-    decimals*: cuint          # for real functions
-    max_length*: int          # For string functions
-    theptr*: cstring          # free pointer for function data
-    const_item*: my_bool      # free pointer for function data
-
-  UDF_INIT* = St_udf_init
-  PUDF_INIT* = ptr UDF_INIT   # Constants when using compression
-{.deprecated: [Trand_stuct: Rand_struct, TItem_result: Item_result,
-              Tst_udf_args: St_udf_args, TUDF_ARGS: UDF_ARGS,
-              Tst_udf_init: St_udf_init, TUDF_INIT: UDF_INIT].}
-
-const
-  NET_HEADER_SIZE* = 4        # standard header size
-  COMP_HEADER_SIZE* = 3 # compression header extra size
-                        # Prototypes to password functions
-                        # These functions are used for authentication by client and server and
-                        #      implemented in sql/password.c
-
-proc randominit*(para1: Prand_struct, seed1: int, seed2: int){.cdecl,
-    dynlib: lib, importc: "randominit".}
-proc my_rnd*(para1: Prand_struct): cdouble{.cdecl, dynlib: lib,
-    importc: "my_rnd".}
-proc create_random_string*(fto: cstring, len: cuint, rand_st: Prand_struct){.
-    cdecl, dynlib: lib, importc: "create_random_string".}
-proc hash_password*(fto: int, password: cstring, password_len: cuint){.cdecl,
-    dynlib: lib, importc: "hash_password".}
-proc make_scrambled_password_323*(fto: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "make_scrambled_password_323".}
-proc scramble_323*(fto: cstring, message: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "scramble_323".}
-proc check_scramble_323*(para1: cstring, message: cstring, salt: int): my_bool{.
-    cdecl, dynlib: lib, importc: "check_scramble_323".}
-proc get_salt_from_password_323*(res: ptr int, password: cstring){.cdecl,
-    dynlib: lib, importc: "get_salt_from_password_323".}
-proc make_password_from_salt_323*(fto: cstring, salt: ptr int){.cdecl,
-    dynlib: lib, importc: "make_password_from_salt_323".}
-proc octet2hex*(fto: cstring, str: cstring, length: cuint): cstring{.cdecl,
-    dynlib: lib, importc: "octet2hex".}
-proc make_scrambled_password*(fto: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "make_scrambled_password".}
-proc scramble*(fto: cstring, message: cstring, password: cstring){.cdecl,
-    dynlib: lib, importc: "scramble".}
-proc check_scramble*(reply: cstring, message: cstring, hash_stage2: pointer): my_bool{.
-    cdecl, dynlib: lib, importc: "check_scramble".}
-proc get_salt_from_password*(res: pointer, password: cstring){.cdecl,
-    dynlib: lib, importc: "get_salt_from_password".}
-proc make_password_from_salt*(fto: cstring, hash_stage2: pointer){.cdecl,
-    dynlib: lib, importc: "make_password_from_salt".}
-  # end of password.c
-proc get_tty_password*(opt_message: cstring): cstring{.cdecl, dynlib: lib,
-    importc: "get_tty_password".}
-proc errno_to_sqlstate*(errno: cuint): cstring{.cdecl, dynlib: lib,
-    importc: "mysql_errno_to_sqlstate".}
-  # Some other useful functions
-proc modify_defaults_file*(file_location: cstring, option: cstring,
-                           option_value: cstring, section_name: cstring,
-                           remove_option: cint): cint{.cdecl, dynlib: lib,
-    importc: "load_defaults".}
-proc load_defaults*(conf_file: cstring, groups: cstringArray, argc: ptr cint,
-                    argv: ptr cstringArray): cint{.cdecl, dynlib: lib,
-    importc: "load_defaults".}
-proc my_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_init".}
-proc my_thread_init*(): my_bool{.cdecl, dynlib: lib, importc: "my_thread_init".}
-proc my_thread_end*(){.cdecl, dynlib: lib, importc: "my_thread_end".}
-const
-  NULL_LENGTH*: int = int(not (0)) # For net_store_length
-
-const
-  STMT_HEADER* = 4
-  LONG_DATA_HEADER* = 6 #  ------------ Stop of declaration in "mysql_com.h"   -----------------------
-                        # $include "mysql_time.h"
-                        # $include "mysql_version.h"
-                        # $include "typelib.h"
-                        # $include "my_list.h" /* for LISTs used in 'MYSQL' and 'MYSQL_STMT' */
-                        #      var
-                        #         mysql_port : cuint;cvar;external;
-                        #         mysql_unix_port : Pchar;cvar;external;
-
-const
-  CLIENT_NET_READ_TIMEOUT* = 365 * 24 * 3600 # Timeout on read
-  CLIENT_NET_WRITE_TIMEOUT* = 365 * 24 * 3600 # Timeout on write
-
-type
-  Pst_mysql_field* = ptr St_mysql_field
-  St_mysql_field*{.final.} = object
-    name*: cstring            # Name of column
-    org_name*: cstring        # Original column name, if an alias
-    table*: cstring           # Table of column if column was a field
-    org_table*: cstring       # Org table name, if table was an alias
-    db*: cstring              # Database for table
-    catalog*: cstring         # Catalog for table
-    def*: cstring             # Default value (set by mysql_list_fields)
-    len*: int                 # Width of column (create length)
-    max_length*: int          # Max width for selected set
-    name_length*: cuint
-    org_name_length*: cuint
-    table_length*: cuint
-    org_table_length*: cuint
-    db_length*: cuint
-    catalog_length*: cuint
-    def_length*: cuint
-    flags*: cuint             # Div flags
-    decimals*: cuint          # Number of decimals in field
-    charsetnr*: cuint         # Character set
-    ftype*: Enum_field_types  # Type of field. See mysql_com.h for types
-    extension*: pointer
-
-  FIELD* = St_mysql_field
-  PFIELD* = ptr FIELD
-  PROW* = ptr ROW             # return data as array of strings
-  ROW* = cstringArray
-  PFIELD_OFFSET* = ptr FIELD_OFFSET # offset to current field
-  FIELD_OFFSET* = cuint
-{.deprecated: [Tst_mysql_field: St_mysql_field, TFIELD: FIELD, TROW: ROW,
-              TFIELD_OFFSET: FIELD_OFFSET].}
-
-proc IS_PRI_KEY*(n: int32): bool
-proc IS_NOT_NULL*(n: int32): bool
-proc IS_BLOB*(n: int32): bool
-proc IS_NUM*(t: Enum_field_types): bool
-proc INTERNAL_NUM_FIELD*(f: Pst_mysql_field): bool
-proc IS_NUM_FIELD*(f: Pst_mysql_field): bool
-type
-  my_ulonglong* = int64
-  Pmy_ulonglong* = ptr my_ulonglong
-
-const
-  COUNT_ERROR* = not (my_ulonglong(0))
-
-type
-  Pst_mysql_rows* = ptr St_mysql_rows
-  St_mysql_rows*{.final.} = object
-    next*: Pst_mysql_rows     # list of rows
-    data*: ROW
-    len*: int
-
-  ROWS* = St_mysql_rows
-  PROWS* = ptr ROWS
-  PROW_OFFSET* = ptr ROW_OFFSET # offset to current row
-  ROW_OFFSET* = ROWS
-{.deprecated: [Tst_mysql_rows: St_mysql_rows, TROWS: ROWS,
-              TROW_OFFSET: ROW_OFFSET].}
-
-const
-  ALLOC_MAX_BLOCK_TO_DROP* = 4096
-  ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP* = 10 # struct for once_alloc (block)
-
-type
-  Pst_used_mem* = ptr St_used_mem
-  St_used_mem*{.final.} = object
-    next*: Pst_used_mem       # Next block in use
-    left*: cuint              # memory left in block
-    size*: cuint              # size of block
-
-  USED_MEM* = St_used_mem
-  PUSED_MEM* = ptr USED_MEM
-  Pst_mem_root* = ptr St_mem_root
-  St_mem_root*{.final.} = object
-    free*: PUSED_MEM          # blocks with free memory in it
-    used*: PUSED_MEM          # blocks almost without free memory
-    pre_alloc*: PUSED_MEM     # preallocated block
-    min_malloc*: cuint        # if block have less memory it will be put in 'used' list
-    block_size*: cuint        # initial block size
-    block_num*: cuint # allocated blocks counter
-                      #    first free block in queue test counter (if it exceed
-                      #       MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)
-    first_block_usage*: cuint
-    error_handler*: proc (){.cdecl.}
-
-  MEM_ROOT* = St_mem_root
-  PMEM_ROOT* = ptr MEM_ROOT   #  ------------ Stop of declaration in "my_alloc.h"    ----------------------
-{.deprecated: [Tst_used_mem: St_used_mem, TUSED_MEM: USED_MEM,
-              Tst_mem_root: St_mem_root, TMEM_ROOT: MEM_ROOT].}
-
-type
-  Pst_mysql_data* = ptr St_mysql_data
-  St_mysql_data*{.final.} = object
-    rows*: my_ulonglong
-    fields*: cuint
-    data*: PROWS
-    alloc*: MEM_ROOT
-    prev_ptr*: ptr PROWS
-
-  DATA* = St_mysql_data
-  PDATA* = ptr DATA
-  Option* = enum
-    OPT_CONNECT_TIMEOUT, OPT_COMPRESS, OPT_NAMED_PIPE, INIT_COMMAND,
-    READ_DEFAULT_FILE, READ_DEFAULT_GROUP, SET_CHARSET_DIR, SET_CHARSET_NAME,
-    OPT_LOCAL_INFILE, OPT_PROTOCOL, SHARED_MEMORY_BASE_NAME, OPT_READ_TIMEOUT,
-    OPT_WRITE_TIMEOUT, OPT_USE_RESULT, OPT_USE_REMOTE_CONNECTION,
-    OPT_USE_EMBEDDED_CONNECTION, OPT_GUESS_CONNECTION, SET_CLIENT_IP,
-    SECURE_AUTH, REPORT_DATA_TRUNCATION, OPT_RECONNECT
-{.deprecated: [Tst_mysql_data: St_mysql_data, TDATA: DATA, Toption: Option].}
-
-const
-  MAX_MYSQL_MANAGER_ERR* = 256
-  MAX_MYSQL_MANAGER_MSG* = 256
-  MANAGER_OK* = 200
-  MANAGER_INFO* = 250
-  MANAGER_ACCESS* = 401
-  MANAGER_CLIENT_ERR* = 450
-  MANAGER_INTERNAL_ERR* = 500
-
-type
-  St_dynamic_array*{.final.} = object
-    buffer*: cstring
-    elements*: cuint
-    max_element*: cuint
-    alloc_increment*: cuint
-    size_of_element*: cuint
-
-  DYNAMIC_ARRAY* = St_dynamic_array
-  Pst_dynamic_array* = ptr St_dynamic_array
-  Pst_mysql_options* = ptr St_mysql_options
-  St_mysql_options*{.final.} = object
-    connect_timeout*: cuint
-    read_timeout*: cuint
-    write_timeout*: cuint
-    port*: cuint
-    protocol*: cuint
-    client_flag*: int
-    host*: cstring
-    user*: cstring
-    password*: cstring
-    unix_socket*: cstring
-    db*: cstring
-    init_commands*: Pst_dynamic_array
-    my_cnf_file*: cstring
-    my_cnf_group*: cstring
-    charset_dir*: cstring
-    charset_name*: cstring
-    ssl_key*: cstring         # PEM key file
-    ssl_cert*: cstring        # PEM cert file
-    ssl_ca*: cstring          # PEM CA file
-    ssl_capath*: cstring      # PEM directory of CA-s?
-    ssl_cipher*: cstring      # cipher to use
-    shared_memory_base_name*: cstring
-    max_allowed_packet*: int
-    use_ssl*: my_bool         # if to use SSL or not
-    compress*: my_bool
-    named_pipe*: my_bool #  On connect, find out the replication role of the server, and
-                         #       establish connections to all the peers
-    rpl_probe*: my_bool #  Each call to mysql_real_query() will parse it to tell if it is a read
-                        #       or a write, and direct it to the slave or the master
-    rpl_parse*: my_bool #  If set, never read from a master, only from slave, when doing
-                        #       a read that is replication-aware
-    no_master_reads*: my_bool
-    separate_thread*: my_bool
-    methods_to_use*: Option
-    client_ip*: cstring
-    secure_auth*: my_bool     # Refuse client connecting to server if it uses old (pre-4.1.1) protocol
-    report_data_truncation*: my_bool # 0 - never report, 1 - always report (default)
-                                     # function pointers for local infile support
-    local_infile_init*: proc (para1: var pointer, para2: cstring, para3: pointer): cint{.
-        cdecl.}
-    local_infile_read*: proc (para1: pointer, para2: cstring, para3: cuint): cint
-    local_infile_end*: proc (para1: pointer)
-    local_infile_error*: proc (para1: pointer, para2: cstring, para3: cuint): cint
-    local_infile_userdata*: pointer
-
-  Status* = enum
-    STATUS_READY, STATUS_GET_RESULT, STATUS_USE_RESULT
-  Protocol_type* = enum  # There are three types of queries - the ones that have to go to
-                          # the master, the ones that go to a slave, and the administrative
-                          # type which must happen on the pivot connection
-    PROTOCOL_DEFAULT, PROTOCOL_TCP, PROTOCOL_SOCKET, PROTOCOL_PIPE,
-    PROTOCOL_MEMORY
-  Rpl_type* = enum
-    RPL_MASTER, RPL_SLAVE, RPL_ADMIN
-  Charset_info_st*{.final.} = object
-    number*: cuint
-    primary_number*: cuint
-    binary_number*: cuint
-    state*: cuint
-    csname*: cstring
-    name*: cstring
-    comment*: cstring
-    tailoring*: cstring
-    ftype*: cstring
-    to_lower*: cstring
-    to_upper*: cstring
-    sort_order*: cstring
-    contractions*: ptr int16
-    sort_order_big*: ptr ptr int16
-    tab_to_uni*: ptr int16
-    tab_from_uni*: pointer    # was ^MY_UNI_IDX
-    state_map*: cstring
-    ident_map*: cstring
-    strxfrm_multiply*: cuint
-    mbminlen*: cuint
-    mbmaxlen*: cuint
-    min_sort_char*: int16
-    max_sort_char*: int16
-    escape_with_backslash_is_dangerous*: my_bool
-    cset*: pointer            # was ^MY_CHARSET_HANDLER
-    coll*: pointer            # was ^MY_COLLATION_HANDLER;
-
-  CHARSET_INFO* = Charset_info_st
-  Pcharset_info_st* = ptr Charset_info_st
-  Pcharacter_set* = ptr Character_set
-  Character_set*{.final.} = object
-    number*: cuint
-    state*: cuint
-    csname*: cstring
-    name*: cstring
-    comment*: cstring
-    dir*: cstring
-    mbminlen*: cuint
-    mbmaxlen*: cuint
-
-  MY_CHARSET_INFO* = Character_set
-  PMY_CHARSET_INFO* = ptr MY_CHARSET_INFO
-  Pst_mysql_methods* = ptr St_mysql_methods
-  Pst_mysql* = ptr St_mysql
-  St_mysql*{.final.} = object
-    net*: NET                 # Communication parameters
-    connector_fd*: gptr       # ConnectorFd for SSL
-    host*: cstring
-    user*: cstring
-    passwd*: cstring
-    unix_socket*: cstring
-    server_version*: cstring
-    host_info*: cstring
-    info*: cstring
-    db*: cstring
-    charset*: Pcharset_info_st
-    fields*: PFIELD
-    field_alloc*: MEM_ROOT
-    affected_rows*: my_ulonglong
-    insert_id*: my_ulonglong  # id if insert on table with NEXTNR
-    extra_info*: my_ulonglong # Used by mysqlshow, not used by mysql 5.0 and up
-    thread_id*: int           # Id for connection in server
-    packet_length*: int
-    port*: cuint
-    client_flag*: int
-    server_capabilities*: int
-    protocol_version*: cuint
-    field_count*: cuint
-    server_status*: cuint
-    server_language*: cuint
-    warning_count*: cuint
-    options*: St_mysql_options
-    status*: Status
-    free_me*: my_bool         # If free in mysql_close
-    reconnect*: my_bool       # set to 1 if automatic reconnect
-    scramble*: array[0..(SCRAMBLE_LENGTH + 1) - 1, char] # session-wide random string
-                                                         #  Set if this is the original connection, not a master or a slave we have
-                                                         #       added though mysql_rpl_probe() or mysql_set_master()/ mysql_add_slave()
-    rpl_pivot*: my_bool #   Pointers to the master, and the next slave connections, points to
-                        #        itself if lone connection.
-    master*: Pst_mysql
-    next_slave*: Pst_mysql
-    last_used_slave*: Pst_mysql # needed for round-robin slave pick
-    last_used_con*: Pst_mysql # needed for send/read/store/use result to work correctly with replication
-    stmts*: pointer           # was PList, list of all statements
-    methods*: Pst_mysql_methods
-    thd*: pointer #   Points to boolean flag in MYSQL_RES  or MYSQL_STMT. We set this flag
-                  #        from mysql_stmt_close if close had to cancel result set of this object.
-    unbuffered_fetch_owner*: Pmy_bool
-
-  MySQL* = St_mysql
-  PMySQL* = ptr MySQL
-  Pst_mysql_res* = ptr St_mysql_res
-  St_mysql_res*{.final.} = object
-    row_count*: my_ulonglong
-    fields*: PFIELD
-    data*: PDATA
-    data_cursor*: PROWS
-    lengths*: ptr int         # column lengths of current row
-    handle*: PMySQL                # for unbuffered reads
-    field_alloc*: MEM_ROOT
-    field_count*: cuint
-    current_field*: cuint
-    row*: ROW                 # If unbuffered read
-    current_row*: ROW         # buffer to current row
-    eof*: my_bool             # Used by mysql_fetch_row
-    unbuffered_fetch_cancelled*: my_bool # mysql_stmt_close() had to cancel this result
-    methods*: Pst_mysql_methods
-
-  RES* = St_mysql_res
-  PRES* = ptr RES
-  Pst_mysql_stmt* = ptr St_mysql_stmt
-  PSTMT* = ptr STMT
-  St_mysql_methods*{.final.} = object
-    read_query_result*: proc (MySQL:  PMySQL): my_bool{.cdecl.}
-    advanced_command*: proc (MySQL: PMySQL, command: Enum_server_command, header: cstring,
-                             header_length: int, arg: cstring, arg_length: int,
-                             skip_check: my_bool): my_bool
-    read_rows*: proc (MySQL: PMySQL, fields: PFIELD, fields_count: cuint): PDATA
-    use_result*: proc (MySQL: PMySQL): PRES
-    fetch_lengths*: proc (fto: ptr int, column: ROW, field_count: cuint)
-    flush_use_result*: proc (MySQL: PMySQL)
-    list_fields*: proc (MySQL: PMySQL): PFIELD
-    read_prepare_result*: proc (MySQL: PMySQL, stmt: PSTMT): my_bool
-    stmt_execute*: proc (stmt: PSTMT): cint
-    read_binary_rows*: proc (stmt: PSTMT): cint
-    unbuffered_fetch*: proc (MySQL: PMySQL, row: cstringArray): cint
-    free_embedded_thd*: proc (MySQL: PMySQL)
-    read_statistics*: proc (MySQL: PMySQL): cstring
-    next_result*: proc (MySQL: PMySQL): my_bool
-    read_change_user_result*: proc (MySQL: PMySQL, buff: cstring, passwd: cstring): cint
-    read_rowsfrom_cursor*: proc (stmt: PSTMT): cint
-
-  METHODS* = St_mysql_methods
-  PMETHODS* = ptr METHODS
-  Pst_mysql_manager* = ptr St_mysql_manager
-  St_mysql_manager*{.final.} = object
-    net*: NET
-    host*: cstring
-    user*: cstring
-    passwd*: cstring
-    port*: cuint
-    free_me*: my_bool
-    eof*: my_bool
-    cmd_status*: cint
-    last_errno*: cint
-    net_buf*: cstring
-    net_buf_pos*: cstring
-    net_data_end*: cstring
-    net_buf_size*: cint
-    last_error*: array[0..(MAX_MYSQL_MANAGER_ERR) - 1, char]
-
-  MANAGER* = St_mysql_manager
-  PMANAGER* = ptr MANAGER
-  Pst_mysql_parameters* = ptr St_mysql_parameters
-  St_mysql_parameters*{.final.} = object
-    p_max_allowed_packet*: ptr int
-    p_net_buffer_length*: ptr int
-
-  PARAMETERS* = St_mysql_parameters
-  PPARAMETERS* = ptr PARAMETERS
-  Enum_mysql_stmt_state* = enum
-    STMT_INIT_DONE = 1, STMT_PREPARE_DONE, STMT_EXECUTE_DONE, STMT_FETCH_DONE
-  Pst_mysql_bind* = ptr St_mysql_bind
-  St_mysql_bind*{.final.} = object
-    len*: int                 # output length pointer
-    is_null*: Pmy_bool        # Pointer to null indicator
-    buffer*: pointer          # buffer to get/put data
-    error*: Pmy_bool          # set this if you want to track data truncations happened during fetch
-    buffer_type*: Enum_field_types # buffer type
-    buffer_length*: int       # buffer length, must be set for str/binary
-                              # Following are for internal use. Set by mysql_stmt_bind_param
-    row_ptr*: ptr byte        # for the current data position
-    offset*: int              # offset position for char/binary fetch
-    length_value*: int        #  Used if length is 0
-    param_number*: cuint      # For null count and error messages
-    pack_length*: cuint       # Internal length for packed data
-    error_value*: my_bool     # used if error is 0
-    is_unsigned*: my_bool     # set if integer type is unsigned
-    long_data_used*: my_bool  # If used with mysql_send_long_data
-    is_null_value*: my_bool   # Used if is_null is 0
-    store_param_func*: proc (net: PNET, param: Pst_mysql_bind){.cdecl.}
-    fetch_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte)
-    skip_result*: proc (para1: Pst_mysql_bind, para2: PFIELD, row: PPByte)
-
-  BIND* = St_mysql_bind
-  PBIND* = ptr BIND           # statement handler
-  St_mysql_stmt*{.final.} = object
-    mem_root*: MEM_ROOT       # root allocations
-    mysql*: PMySQL                      # connection handle
-    params*: PBIND            # input parameters
-    `bind`*: PBIND            # input parameters
-    fields*: PFIELD           # result set metadata
-    result*: DATA             # cached result set
-    data_cursor*: PROWS       # current row in cached result
-    affected_rows*: my_ulonglong # copy of mysql->affected_rows after statement execution
-    insert_id*: my_ulonglong
-    read_row_func*: proc (stmt: Pst_mysql_stmt, row: PPByte): cint{.cdecl.}
-    stmt_id*: int             # Id for prepared statement
-    flags*: int               # i.e. type of cursor to open
-    prefetch_rows*: int       # number of rows per one COM_FETCH
-    server_status*: cuint # Copied from mysql->server_status after execute/fetch to know
-                          # server-side cursor status for this statement.
-    last_errno*: cuint        # error code
-    param_count*: cuint       # input parameter count
-    field_count*: cuint       # number of columns in result set
-    state*: Enum_mysql_stmt_state # statement state
-    last_error*: array[0..(ERRMSG_SIZE) - 1, char] # error message
-    sqlstate*: array[0..(SQLSTATE_LENGTH + 1) - 1, char]
-    send_types_to_server*: my_bool # Types of input parameters should be sent to server
-    bind_param_done*: my_bool # input buffers were supplied
-    bind_result_done*: char   # output buffers were supplied
-    unbuffered_fetch_cancelled*: my_bool
-    update_max_length*: my_bool
-
-  STMT* = St_mysql_stmt
-
-  Enum_stmt_attr_type* = enum
-    STMT_ATTR_UPDATE_MAX_LENGTH, STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS
-{.deprecated: [Tst_dynamic_array: St_dynamic_array, Tst_mysql_options: St_mysql_options,
-              TDYNAMIC_ARRAY: DYNAMIC_ARRAY, Tprotocol_type: Protocol_type,
-              Trpl_type: Rpl_type, Tcharset_info_st: Charset_info_st,
-              TCHARSET_INFO: CHARSET_INFO, Tcharacter_set: Character_set,
-              TMY_CHARSET_INFO: MY_CHARSET_INFO, Tst_mysql: St_mysql,
-              Tst_mysql_methods: St_mysql_methods, TMySql: MySql,
-              Tst_mysql_res: St_mysql_res, TMETHODS: METHODS, TRES: RES,
-              Tst_mysql_manager: St_mysql_manager, TMANAGER: MANAGER,
-              Tst_mysql_parameters: St_mysql_parameters, TPARAMETERS: PARAMETERS,
-              Tenum_mysql_stmt_state: Enum_mysql_stmt_state,
-              Tst_mysql_bind: St_mysql_bind, TBIND: BIND, Tst_mysql_stmt: St_mysql_stmt,
-              TSTMT: STMT, Tenum_stmt_attr_type: Enum_stmt_attr_type,
-              Tstatus: Status].}
-
-proc server_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{.
-    cdecl, dynlib: lib, importc: "mysql_server_init".}
-proc server_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".}
-  # mysql_server_init/end need to be called when using libmysqld or
-  #      libmysqlclient (exactly, mysql_server_init() is called by mysql_init() so
-  #      you don't need to call it explicitly; but you need to call
-  #      mysql_server_end() to free memory). The names are a bit misleading
-  #      (mysql_SERVER* to be used when using libmysqlCLIENT). So we add more general
-  #      names which suit well whether you're using libmysqld or libmysqlclient. We
-  #      intend to promote these aliases over the mysql_server* ones.
-proc library_init*(argc: cint, argv: cstringArray, groups: cstringArray): cint{.
-    cdecl, dynlib: lib, importc: "mysql_server_init".}
-proc library_end*(){.cdecl, dynlib: lib, importc: "mysql_server_end".}
-proc get_parameters*(): PPARAMETERS{.stdcall, dynlib: lib,
-                                     importc: "mysql_get_parameters".}
-  # Set up and bring down a thread; these function should be called
-  #      for each thread in an application which opens at least one MySQL
-  #      connection.  All uses of the connection(s) should be between these
-  #      function calls.
-proc thread_init*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_thread_init".}
-proc thread_end*(){.stdcall, dynlib: lib, importc: "mysql_thread_end".}
-  # Functions to get information from the MYSQL and MYSQL_RES structures
-  #      Should definitely be used if one uses shared libraries.
-proc num_rows*(res: PRES): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_num_rows".}
-proc num_fields*(res: PRES): cuint{.stdcall, dynlib: lib,
-                                    importc: "mysql_num_fields".}
-proc eof*(res: PRES): my_bool{.stdcall, dynlib: lib, importc: "mysql_eof".}
-proc fetch_field_direct*(res: PRES, fieldnr: cuint): PFIELD{.stdcall,
-    dynlib: lib, importc: "mysql_fetch_field_direct".}
-proc fetch_fields*(res: PRES): PFIELD{.stdcall, dynlib: lib,
-                                       importc: "mysql_fetch_fields".}
-proc row_tell*(res: PRES): ROW_OFFSET{.stdcall, dynlib: lib,
-                                       importc: "mysql_row_tell".}
-proc field_tell*(res: PRES): FIELD_OFFSET{.stdcall, dynlib: lib,
-    importc: "mysql_field_tell".}
-proc field_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                               importc: "mysql_field_count".}
-proc affected_rows*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib,
-                                        importc: "mysql_affected_rows".}
-proc insert_id*(MySQL: PMySQL): my_ulonglong{.stdcall, dynlib: lib,
-                                    importc: "mysql_insert_id".}
-proc errno*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib, importc: "mysql_errno".}
-proc error*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_error".}
-proc sqlstate*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_sqlstate".}
-proc warning_count*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                                 importc: "mysql_warning_count".}
-proc info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_info".}
-proc thread_id*(MySQL: PMySQL): int{.stdcall, dynlib: lib, importc: "mysql_thread_id".}
-proc character_set_name*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                        importc: "mysql_character_set_name".}
-proc set_character_set*(MySQL: PMySQL, csname: cstring): int32{.stdcall, dynlib: lib,
-    importc: "mysql_set_character_set".}
-proc init*(MySQL: PMySQL): PMySQL{.stdcall, dynlib: lib, importc: "mysql_init".}
-proc ssl_set*(MySQL: PMySQL, key: cstring, cert: cstring, ca: cstring, capath: cstring,
-              cipher: cstring): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_ssl_set".}
-proc change_user*(MySQL: PMySQL, user: cstring, passwd: cstring, db: cstring): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_change_user".}
-proc real_connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring,
-                   db: cstring, port: cuint, unix_socket: cstring,
-                   clientflag: int): PMySQL{.stdcall, dynlib: lib,
-                                        importc: "mysql_real_connect".}
-proc select_db*(MySQL: PMySQL, db: cstring): cint{.stdcall, dynlib: lib,
-    importc: "mysql_select_db".}
-proc query*(MySQL: PMySQL, q: cstring): cint{.stdcall, dynlib: lib, importc: "mysql_query".}
-proc send_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib,
-    importc: "mysql_send_query".}
-proc real_query*(MySQL: PMySQL, q: cstring, len: int): cint{.stdcall, dynlib: lib,
-    importc: "mysql_real_query".}
-proc store_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib,
-                               importc: "mysql_store_result".}
-proc use_result*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib, importc: "mysql_use_result".}
-  # perform query on master
-proc master_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_master_query".}
-proc master_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall,
-    dynlib: lib, importc: "mysql_master_send_query".}
-  # perform query on slave
-proc slave_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_slave_query".}
-proc slave_send_query*(MySQL: PMySQL, q: cstring, len: int): my_bool{.stdcall,
-    dynlib: lib, importc: "mysql_slave_send_query".}
-proc get_character_set_info*(MySQL: PMySQL, charset: PMY_CHARSET_INFO){.stdcall,
-    dynlib: lib, importc: "mysql_get_character_set_info".}
-  # local infile support
-const
-  LOCAL_INFILE_ERROR_LEN* = 512
-
-# procedure mysql_set_local_infile_handler(mysql:PMYSQL; local_infile_init:function (para1:Ppointer; para2:Pchar; para3:pointer):longint; local_infile_read:function (para1:pointer; para2:Pchar; para3:dword):longint; local_infile_end:procedure (_pa
-# para6:pointer);cdecl;external mysqllib name 'mysql_set_local_infile_handler';
-
-proc set_local_infile_default*(MySQL: PMySQL){.cdecl, dynlib: lib,
-                                     importc: "mysql_set_local_infile_default".}
-  # enable/disable parsing of all queries to decide if they go on master or
-  #      slave
-proc enable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                             importc: "mysql_enable_rpl_parse".}
-proc disable_rpl_parse*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                              importc: "mysql_disable_rpl_parse".}
-  # get the value of the parse flag
-proc rpl_parse_enabled*(MySQL: PMySQL): cint{.stdcall, dynlib: lib,
-                                    importc: "mysql_rpl_parse_enabled".}
-  #  enable/disable reads from master
-proc enable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib,
-                                     importc: "mysql_enable_reads_from_master".}
-proc disable_reads_from_master*(MySQL: PMySQL){.stdcall, dynlib: lib, importc: "mysql_disable_reads_from_master".}
-  # get the value of the master read flag
-proc reads_from_master_enabled*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_reads_from_master_enabled".}
-proc rpl_query_type*(q: cstring, length: cint): Rpl_type{.stdcall, dynlib: lib,
-    importc: "mysql_rpl_query_type".}
-  # discover the master and its slaves
-proc rpl_probe*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rpl_probe".}
-  # set the master, close/free the old one, if it is not a pivot
-proc set_master*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{.
-    stdcall, dynlib: lib, importc: "mysql_set_master".}
-proc add_slave*(MySQL: PMySQL, host: cstring, port: cuint, user: cstring, passwd: cstring): cint{.
-    stdcall, dynlib: lib, importc: "mysql_add_slave".}
-proc shutdown*(MySQL: PMySQL, shutdown_level: Enum_shutdown_level): cint{.stdcall,
-    dynlib: lib, importc: "mysql_shutdown".}
-proc dump_debug_info*(MySQL: PMySQL): cint{.stdcall, dynlib: lib,
-                                  importc: "mysql_dump_debug_info".}
-proc refresh*(sql: PMySQL, refresh_options: cuint): cint{.stdcall, dynlib: lib,
-    importc: "mysql_refresh".}
-proc kill*(MySQL: PMySQL, pid: int): cint{.stdcall, dynlib: lib, importc: "mysql_kill".}
-proc set_server_option*(MySQL: PMySQL, option: Enum_mysql_set_option): cint{.stdcall,
-    dynlib: lib, importc: "mysql_set_server_option".}
-proc ping*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_ping".}
-proc stat*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib, importc: "mysql_stat".}
-proc get_server_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                     importc: "mysql_get_server_info".}
-proc get_client_info*(): cstring{.stdcall, dynlib: lib,
-                                  importc: "mysql_get_client_info".}
-proc get_client_version*(): int{.stdcall, dynlib: lib,
-                                 importc: "mysql_get_client_version".}
-proc get_host_info*(MySQL: PMySQL): cstring{.stdcall, dynlib: lib,
-                                   importc: "mysql_get_host_info".}
-proc get_server_version*(MySQL: PMySQL): int{.stdcall, dynlib: lib,
-                                    importc: "mysql_get_server_version".}
-proc get_proto_info*(MySQL: PMySQL): cuint{.stdcall, dynlib: lib,
-                                  importc: "mysql_get_proto_info".}
-proc list_dbs*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_list_dbs".}
-proc list_tables*(MySQL: PMySQL, wild: cstring): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_list_tables".}
-proc list_processes*(MySQL: PMySQL): PRES{.stdcall, dynlib: lib,
-                                 importc: "mysql_list_processes".}
-proc options*(MySQL: PMySQL, option: Option, arg: cstring): cint{.stdcall, dynlib: lib,
-    importc: "mysql_options".}
-proc free_result*(result: PRES){.stdcall, dynlib: lib,
-                                 importc: "mysql_free_result".}
-proc data_seek*(result: PRES, offset: my_ulonglong){.stdcall, dynlib: lib,
-    importc: "mysql_data_seek".}
-proc row_seek*(result: PRES, offset: ROW_OFFSET): ROW_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_row_seek".}
-proc field_seek*(result: PRES, offset: FIELD_OFFSET): FIELD_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_field_seek".}
-proc fetch_row*(result: PRES): ROW{.stdcall, dynlib: lib,
-                                    importc: "mysql_fetch_row".}
-proc fetch_lengths*(result: PRES): ptr int{.stdcall, dynlib: lib,
-    importc: "mysql_fetch_lengths".}
-proc fetch_field*(result: PRES): PFIELD{.stdcall, dynlib: lib,
-    importc: "mysql_fetch_field".}
-proc list_fields*(MySQL: PMySQL, table: cstring, wild: cstring): PRES{.stdcall,
-    dynlib: lib, importc: "mysql_list_fields".}
-proc escape_string*(fto: cstring, `from`: cstring, from_length: int): int{.
-    stdcall, dynlib: lib, importc: "mysql_escape_string".}
-proc hex_string*(fto: cstring, `from`: cstring, from_length: int): int{.stdcall,
-    dynlib: lib, importc: "mysql_hex_string".}
-proc real_escape_string*(MySQL: PMySQL, fto: cstring, `from`: cstring, len: int): int{.
-    stdcall, dynlib: lib, importc: "mysql_real_escape_string".}
-proc debug*(debug: cstring){.stdcall, dynlib: lib, importc: "mysql_debug".}
-  #    function mysql_odbc_escape_string(mysql:PMYSQL; fto:Pchar; to_length:dword; from:Pchar; from_length:dword;
-  #               param:pointer; extend_buffer:function (para1:pointer; to:Pchar; length:Pdword):Pchar):Pchar;stdcall;external mysqllib name 'mysql_odbc_escape_string';
-proc myodbc_remove_escape*(MySQL: PMySQL, name: cstring){.stdcall, dynlib: lib,
-    importc: "myodbc_remove_escape".}
-proc thread_safe*(): cuint{.stdcall, dynlib: lib, importc: "mysql_thread_safe".}
-proc embedded*(): my_bool{.stdcall, dynlib: lib, importc: "mysql_embedded".}
-proc manager_init*(con: PMANAGER): PMANAGER{.stdcall, dynlib: lib,
-    importc: "mysql_manager_init".}
-proc manager_connect*(con: PMANAGER, host: cstring, user: cstring,
-                      passwd: cstring, port: cuint): PMANAGER{.stdcall,
-    dynlib: lib, importc: "mysql_manager_connect".}
-proc manager_close*(con: PMANAGER){.stdcall, dynlib: lib,
-                                    importc: "mysql_manager_close".}
-proc manager_command*(con: PMANAGER, cmd: cstring, cmd_len: cint): cint{.
-    stdcall, dynlib: lib, importc: "mysql_manager_command".}
-proc manager_fetch_line*(con: PMANAGER, res_buf: cstring, res_buf_size: cint): cint{.
-    stdcall, dynlib: lib, importc: "mysql_manager_fetch_line".}
-proc read_query_result*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-                                       importc: "mysql_read_query_result".}
-proc stmt_init*(MySQL: PMySQL): PSTMT{.stdcall, dynlib: lib, importc: "mysql_stmt_init".}
-proc stmt_prepare*(stmt: PSTMT, query: cstring, len: int): cint{.stdcall,
-    dynlib: lib, importc: "mysql_stmt_prepare".}
-proc stmt_execute*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-                                       importc: "mysql_stmt_execute".}
-proc stmt_fetch*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-                                     importc: "mysql_stmt_fetch".}
-proc stmt_fetch_column*(stmt: PSTMT, `bind`: PBIND, column: cuint, offset: int): cint{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_fetch_column".}
-proc stmt_store_result*(stmt: PSTMT): cint{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_store_result".}
-proc stmt_param_count*(stmt: PSTMT): int{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_param_count".}
-proc stmt_attr_set*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_attr_set".}
-proc stmt_attr_get*(stmt: PSTMT, attr_type: Enum_stmt_attr_type, attr: pointer): my_bool{.
-    stdcall, dynlib: lib, importc: "mysql_stmt_attr_get".}
-proc stmt_bind_param*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_bind_param".}
-proc stmt_bind_result*(stmt: PSTMT, bnd: PBIND): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_bind_result".}
-proc stmt_close*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_close".}
-proc stmt_reset*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_reset".}
-proc stmt_free_result*(stmt: PSTMT): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_free_result".}
-proc stmt_send_long_data*(stmt: PSTMT, param_number: cuint, data: cstring,
-                          len: int): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_send_long_data".}
-proc stmt_result_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_result_metadata".}
-proc stmt_param_metadata*(stmt: PSTMT): PRES{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_param_metadata".}
-proc stmt_errno*(stmt: PSTMT): cuint{.stdcall, dynlib: lib,
-                                      importc: "mysql_stmt_errno".}
-proc stmt_error*(stmt: PSTMT): cstring{.stdcall, dynlib: lib,
-                                        importc: "mysql_stmt_error".}
-proc stmt_sqlstate*(stmt: PSTMT): cstring{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_sqlstate".}
-proc stmt_row_seek*(stmt: PSTMT, offset: ROW_OFFSET): ROW_OFFSET{.stdcall,
-    dynlib: lib, importc: "mysql_stmt_row_seek".}
-proc stmt_row_tell*(stmt: PSTMT): ROW_OFFSET{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_row_tell".}
-proc stmt_data_seek*(stmt: PSTMT, offset: my_ulonglong){.stdcall, dynlib: lib,
-    importc: "mysql_stmt_data_seek".}
-proc stmt_num_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_num_rows".}
-proc stmt_affected_rows*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_affected_rows".}
-proc stmt_insert_id*(stmt: PSTMT): my_ulonglong{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_insert_id".}
-proc stmt_field_count*(stmt: PSTMT): cuint{.stdcall, dynlib: lib,
-    importc: "mysql_stmt_field_count".}
-proc commit*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_commit".}
-proc rollback*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib, importc: "mysql_rollback".}
-proc autocommit*(MySQL: PMySQL, auto_mode: my_bool): my_bool{.stdcall, dynlib: lib,
-    importc: "mysql_autocommit".}
-proc more_results*(MySQL: PMySQL): my_bool{.stdcall, dynlib: lib,
-                                  importc: "mysql_more_results".}
-proc next_result*(MySQL: PMySQL): cint{.stdcall, dynlib: lib, importc: "mysql_next_result".}
-proc close*(sock: PMySQL){.stdcall, dynlib: lib, importc: "mysql_close".}
-  # status return codes
-const
-  NO_DATA* = 100
-  DATA_TRUNCATED* = 101
-
-proc reload*(x: PMySQL): cint
-when defined(USE_OLD_FUNCTIONS):
-  proc connect*(MySQL: PMySQL, host: cstring, user: cstring, passwd: cstring): PMySQL{.stdcall,
-      dynlib: lib, importc: "mysql_connect".}
-  proc create_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib,
-      importc: "mysql_create_db".}
-  proc drop_db*(MySQL: PMySQL, DB: cstring): cint{.stdcall, dynlib: lib,
-      importc: "mysql_drop_db".}
-proc net_safe_read*(MySQL: PMySQL): cuint{.cdecl, dynlib: lib, importc: "net_safe_read".}
-
-proc IS_PRI_KEY(n: int32): bool =
-  result = (n and PRI_KEY_FLAG) != 0
-
-proc IS_NOT_NULL(n: int32): bool =
-  result = (n and NOT_NULL_FLAG) != 0
-
-proc IS_BLOB(n: int32): bool =
-  result = (n and BLOB_FLAG) != 0
-
-proc IS_NUM_FIELD(f: Pst_mysql_field): bool =
-  result = (f.flags and NUM_FLAG) != 0
-
-proc IS_NUM(t: Enum_field_types): bool =
-  result = (t <= FIELD_TYPE_INT24) or (t == FIELD_TYPE_YEAR) or
-      (t == FIELD_TYPE_NEWDECIMAL)
-
-proc INTERNAL_NUM_FIELD(f: Pst_mysql_field): bool =
-  result = (f.ftype <= FIELD_TYPE_INT24) and
-      ((f.ftype != FIELD_TYPE_TIMESTAMP) or (f.len == 14) or (f.len == 8)) or
-      (f.ftype == FIELD_TYPE_YEAR)
-
-proc reload(x: PMySQL): cint =
-  result = refresh(x, REFRESH_GRANT)
-
-{.pop.}
-when defined(nimHasStyleChecks):
-  {.pop.}
diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim
deleted file mode 100644
index 4e37e1a17..000000000
--- a/lib/wrappers/odbcsql.nim
+++ /dev/null
@@ -1,841 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2015 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-when not defined(ODBCVER):
-  const
-    ODBCVER = 0x0351 ## define ODBC version 3.51 by default
-
-when defined(windows):
-  {.push callconv: stdcall.}
-  const odbclib = "odbc32.dll"
-else:
-  {.push callconv: cdecl.}
-  const odbclib = "libodbc.so"
-
-when defined(nimHasStyleChecks):
-  {.push styleChecks: off.}
-
-# DATA TYPES CORRESPONDENCE
-#   BDE fields  ODBC types
-#   ----------  ------------------
-#   ftBlob      SQL_BINARY
-#   ftBoolean   SQL_BIT
-#   ftDate      SQL_TYPE_DATE
-#   ftTime      SQL_TYPE_TIME
-#   ftDateTime  SQL_TYPE_TIMESTAMP
-#   ftInteger   SQL_INTEGER
-#   ftSmallint  SQL_SMALLINT
-#   ftFloat     SQL_DOUBLE
-#   ftString    SQL_CHAR
-#   ftMemo      SQL_BINARY // SQL_VARCHAR
-#
-
-type
-  TSqlChar* = char
-  TSqlSmallInt* = int16
-  SqlUSmallInt* = int16
-  SqlHandle* = pointer
-  SqlHEnv* = SqlHandle
-  SqlHDBC* = SqlHandle
-  SqlHStmt* = SqlHandle
-  SqlHDesc* = SqlHandle
-  TSqlInteger* = int32
-  SqlUInteger* = int32
-  TSqlLen* = int
-  TSqlULen* = uint
-  SqlPointer* = pointer
-  TSqlReal* = cfloat
-  TSqlDouble* = cdouble
-  TSqlFloat* = cdouble
-  SqlHWND* = pointer
-  PSQLCHAR* = cstring
-  PSQLINTEGER* = ptr TSqlInteger
-  PSQLUINTEGER* = ptr SqlUInteger
-  PSQLSMALLINT* = ptr TSqlSmallInt
-  PSQLUSMALLINT* = ptr SqlUSmallInt
-  PSQLREAL* = ptr TSqlReal
-  PSQLDOUBLE* = ptr TSqlDouble
-  PSQLFLOAT* = ptr TSqlFloat
-  PSQLHANDLE* = ptr SqlHandle
-
-const                         # SQL data type codes
-  SQL_UNKNOWN_TYPE* = 0
-  SQL_LONGVARCHAR* = (- 1)
-  SQL_BINARY* = (- 2)
-  SQL_VARBINARY* = (- 3)
-  SQL_LONGVARBINARY* = (- 4)
-  SQL_BIGINT* = (- 5)
-  SQL_TINYINT* = (- 6)
-  SQL_BIT* = (- 7)
-  SQL_WCHAR* = (- 8)
-  SQL_WVARCHAR* = (- 9)
-  SQL_WLONGVARCHAR* = (- 10)
-  SQL_CHAR* = 1
-  SQL_NUMERIC* = 2
-  SQL_DECIMAL* = 3
-  SQL_INTEGER* = 4
-  SQL_SMALLINT* = 5
-  SQL_FLOAT* = 6
-  SQL_REAL* = 7
-  SQL_DOUBLE* = 8
-  SQL_DATETIME* = 9
-  SQL_VARCHAR* = 12
-  SQL_TYPE_DATE* = 91
-  SQL_TYPE_TIME* = 92
-  SQL_TYPE_TIMESTAMP* = 93
-  SQL_DATE* = 9
-  SQL_TIME* = 10
-  SQL_TIMESTAMP* = 11
-  SQL_INTERVAL* = 10
-  SQL_GUID* = - 11            # interval codes
-
-when ODBCVER >= 0x0300:
-  const
-    SQL_CODE_YEAR* = 1
-    SQL_CODE_MONTH* = 2
-    SQL_CODE_DAY* = 3
-    SQL_CODE_HOUR* = 4
-    SQL_CODE_MINUTE* = 5
-    SQL_CODE_SECOND* = 6
-    SQL_CODE_YEAR_TO_MONTH* = 7
-    SQL_CODE_DAY_TO_HOUR* = 8
-    SQL_CODE_DAY_TO_MINUTE* = 9
-    SQL_CODE_DAY_TO_SECOND* = 10
-    SQL_CODE_HOUR_TO_MINUTE* = 11
-    SQL_CODE_HOUR_TO_SECOND* = 12
-    SQL_CODE_MINUTE_TO_SECOND* = 13
-    SQL_INTERVAL_YEAR* = 100 + SQL_CODE_YEAR
-    SQL_INTERVAL_MONTH* = 100 + SQL_CODE_MONTH
-    SQL_INTERVAL_DAY* = 100 + SQL_CODE_DAY
-    SQL_INTERVAL_HOUR* = 100 + SQL_CODE_HOUR
-    SQL_INTERVAL_MINUTE* = 100 + SQL_CODE_MINUTE
-    SQL_INTERVAL_SECOND* = 100 + SQL_CODE_SECOND
-    SQL_INTERVAL_YEAR_TO_MONTH* = 100 + SQL_CODE_YEAR_TO_MONTH
-    SQL_INTERVAL_DAY_TO_HOUR* = 100 + SQL_CODE_DAY_TO_HOUR
-    SQL_INTERVAL_DAY_TO_MINUTE* = 100 + SQL_CODE_DAY_TO_MINUTE
-    SQL_INTERVAL_DAY_TO_SECOND* = 100 + SQL_CODE_DAY_TO_SECOND
-    SQL_INTERVAL_HOUR_TO_MINUTE* = 100 + SQL_CODE_HOUR_TO_MINUTE
-    SQL_INTERVAL_HOUR_TO_SECOND* = 100 + SQL_CODE_HOUR_TO_SECOND
-    SQL_INTERVAL_MINUTE_TO_SECOND* = 100 + SQL_CODE_MINUTE_TO_SECOND
-else:
-  const
-    SQL_INTERVAL_YEAR* = - 80
-    SQL_INTERVAL_MONTH* = - 81
-    SQL_INTERVAL_YEAR_TO_MONTH* = - 82
-    SQL_INTERVAL_DAY* = - 83
-    SQL_INTERVAL_HOUR* = - 84
-    SQL_INTERVAL_MINUTE* = - 85
-    SQL_INTERVAL_SECOND* = - 86
-    SQL_INTERVAL_DAY_TO_HOUR* = - 87
-    SQL_INTERVAL_DAY_TO_MINUTE* = - 88
-    SQL_INTERVAL_DAY_TO_SECOND* = - 89
-    SQL_INTERVAL_HOUR_TO_MINUTE* = - 90
-    SQL_INTERVAL_HOUR_TO_SECOND* = - 91
-    SQL_INTERVAL_MINUTE_TO_SECOND* = - 92
-
-
-when ODBCVER < 0x0300:
-  const
-    SQL_UNICODE* = - 95
-    SQL_UNICODE_VARCHAR* = - 96
-    SQL_UNICODE_LONGVARCHAR* = - 97
-    SQL_UNICODE_CHAR* = SQL_UNICODE
-else:
-  # The previous definitions for SQL_UNICODE_ are historical and obsolete
-  const
-    SQL_UNICODE* = SQL_WCHAR
-    SQL_UNICODE_VARCHAR* = SQL_WVARCHAR
-    SQL_UNICODE_LONGVARCHAR* = SQL_WLONGVARCHAR
-    SQL_UNICODE_CHAR* = SQL_WCHAR
-const                         # C datatype to SQL datatype mapping
-  SQL_C_CHAR* = SQL_CHAR
-  SQL_C_LONG* = SQL_INTEGER
-  SQL_C_SHORT* = SQL_SMALLINT
-  SQL_C_FLOAT* = SQL_REAL
-  SQL_C_DOUBLE* = SQL_DOUBLE
-  SQL_C_NUMERIC* = SQL_NUMERIC
-  SQL_C_DEFAULT* = 99
-  SQL_SIGNED_OFFSET* = - 20
-  SQL_UNSIGNED_OFFSET* = - 22
-  SQL_C_DATE* = SQL_DATE
-  SQL_C_TIME* = SQL_TIME
-  SQL_C_TIMESTAMP* = SQL_TIMESTAMP
-  SQL_C_TYPE_DATE* = SQL_TYPE_DATE
-  SQL_C_TYPE_TIME* = SQL_TYPE_TIME
-  SQL_C_TYPE_TIMESTAMP* = SQL_TYPE_TIMESTAMP
-  SQL_C_INTERVAL_YEAR* = SQL_INTERVAL_YEAR
-  SQL_C_INTERVAL_MONTH* = SQL_INTERVAL_MONTH
-  SQL_C_INTERVAL_DAY* = SQL_INTERVAL_DAY
-  SQL_C_INTERVAL_HOUR* = SQL_INTERVAL_HOUR
-  SQL_C_INTERVAL_MINUTE* = SQL_INTERVAL_MINUTE
-  SQL_C_INTERVAL_SECOND* = SQL_INTERVAL_SECOND
-  SQL_C_INTERVAL_YEAR_TO_MONTH* = SQL_INTERVAL_YEAR_TO_MONTH
-  SQL_C_INTERVAL_DAY_TO_HOUR* = SQL_INTERVAL_DAY_TO_HOUR
-  SQL_C_INTERVAL_DAY_TO_MINUTE* = SQL_INTERVAL_DAY_TO_MINUTE
-  SQL_C_INTERVAL_DAY_TO_SECOND* = SQL_INTERVAL_DAY_TO_SECOND
-  SQL_C_INTERVAL_HOUR_TO_MINUTE* = SQL_INTERVAL_HOUR_TO_MINUTE
-  SQL_C_INTERVAL_HOUR_TO_SECOND* = SQL_INTERVAL_HOUR_TO_SECOND
-  SQL_C_INTERVAL_MINUTE_TO_SECOND* = SQL_INTERVAL_MINUTE_TO_SECOND
-  SQL_C_BINARY* = SQL_BINARY
-  SQL_C_BIT* = SQL_BIT
-  SQL_C_SBIGINT* = SQL_BIGINT + SQL_SIGNED_OFFSET # SIGNED BIGINT
-  SQL_C_UBIGINT* = SQL_BIGINT + SQL_UNSIGNED_OFFSET # UNSIGNED BIGINT
-  SQL_C_TINYINT* = SQL_TINYINT
-  SQL_C_SLONG* = SQL_C_LONG + SQL_SIGNED_OFFSET # SIGNED INTEGER
-  SQL_C_SSHORT* = SQL_C_SHORT + SQL_SIGNED_OFFSET # SIGNED SMALLINT
-  SQL_C_STINYINT* = SQL_TINYINT + SQL_SIGNED_OFFSET # SIGNED TINYINT
-  SQL_C_ULONG* = SQL_C_LONG + SQL_UNSIGNED_OFFSET # UNSIGNED INTEGER
-  SQL_C_USHORT* = SQL_C_SHORT + SQL_UNSIGNED_OFFSET # UNSIGNED SMALLINT
-  SQL_C_UTINYINT* = SQL_TINYINT + SQL_UNSIGNED_OFFSET # UNSIGNED TINYINT
-  SQL_C_BOOKMARK* = SQL_C_ULONG # BOOKMARK
-  SQL_C_GUID* = SQL_GUID
-  SQL_TYPE_NULL* = 0
-
-when ODBCVER < 0x0300:
-  const
-    SQL_TYPE_MIN* = SQL_BIT
-    SQL_TYPE_MAX* = SQL_VARCHAR
-
-const
-  SQL_C_VARBOOKMARK* = SQL_C_BINARY
-  SQL_API_SQLDESCRIBEPARAM* = 58
-  SQL_NO_TOTAL* = - 4
-
-type
-  SQL_DATE_STRUCT* {.final, pure.} = object
-    Year*: TSqlSmallInt
-    Month*: SqlUSmallInt
-    Day*: SqlUSmallInt
-
-  PSQL_DATE_STRUCT* = ptr SQL_DATE_STRUCT
-  SQL_TIME_STRUCT* {.final, pure.} = object
-    Hour*: SqlUSmallInt
-    Minute*: SqlUSmallInt
-    Second*: SqlUSmallInt
-
-  PSQL_TIME_STRUCT* = ptr SQL_TIME_STRUCT
-  SQL_TIMESTAMP_STRUCT* {.final, pure.} = object
-    Year*: SqlUSmallInt
-    Month*: SqlUSmallInt
-    Day*: SqlUSmallInt
-    Hour*: SqlUSmallInt
-    Minute*: SqlUSmallInt
-    Second*: SqlUSmallInt
-    Fraction*: SqlUInteger
-
-  PSQL_TIMESTAMP_STRUCT* = ptr SQL_TIMESTAMP_STRUCT
-
-const
-  SQL_NAME_LEN* = 128
-  SQL_OV_ODBC3* = 3
-  SQL_OV_ODBC2* = 2
-  SQL_ATTR_ODBC_VERSION* = 200 # Options for SQLDriverConnect
-  SQL_DRIVER_NOPROMPT* = 0
-  SQL_DRIVER_COMPLETE* = 1
-  SQL_DRIVER_PROMPT* = 2
-  SQL_DRIVER_COMPLETE_REQUIRED* = 3
-  SQL_IS_POINTER* = (- 4)  # whether an attribute is a pointer or not
-  SQL_IS_UINTEGER* = (- 5)
-  SQL_IS_INTEGER* = (- 6)
-  SQL_IS_USMALLINT* = (- 7)
-  SQL_IS_SMALLINT* = (- 8)    # SQLExtendedFetch "fFetchType" values
-  SQL_FETCH_BOOKMARK* = 8
-  SQL_SCROLL_OPTIONS* = 44    # SQL_USE_BOOKMARKS options
-  SQL_UB_OFF* = 0
-  SQL_UB_ON* = 1
-  SQL_UB_DEFAULT* = SQL_UB_OFF
-  SQL_UB_FIXED* = SQL_UB_ON
-  SQL_UB_VARIABLE* = 2        # SQL_SCROLL_OPTIONS masks
-  SQL_SO_FORWARD_ONLY* = 0x00000001
-  SQL_SO_KEYSET_DRIVEN* = 0x00000002
-  SQL_SO_DYNAMIC* = 0x00000004
-  SQL_SO_MIXED* = 0x00000008
-  SQL_SO_STATIC* = 0x00000010
-  SQL_BOOKMARK_PERSISTENCE* = 82
-  SQL_STATIC_SENSITIVITY* = 83 # SQL_BOOKMARK_PERSISTENCE values
-  SQL_BP_CLOSE* = 0x00000001
-  SQL_BP_DELETE* = 0x00000002
-  SQL_BP_DROP* = 0x00000004
-  SQL_BP_TRANSACTION* = 0x00000008
-  SQL_BP_UPDATE* = 0x00000010
-  SQL_BP_OTHER_HSTMT* = 0x00000020
-  SQL_BP_SCROLL* = 0x00000040
-  SQL_DYNAMIC_CURSOR_ATTRIBUTES1* = 144
-  SQL_DYNAMIC_CURSOR_ATTRIBUTES2* = 145
-  SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1* = 146
-  SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2* = 147
-  SQL_INDEX_KEYWORDS* = 148
-  SQL_INFO_SCHEMA_VIEWS* = 149
-  SQL_KEYSET_CURSOR_ATTRIBUTES1* = 150
-  SQL_KEYSET_CURSOR_ATTRIBUTES2* = 151
-  SQL_STATIC_CURSOR_ATTRIBUTES1* = 167
-  SQL_STATIC_CURSOR_ATTRIBUTES2* = 168 # supported SQLFetchScroll FetchOrientation's
-  SQL_CA1_NEXT* = 1
-  SQL_CA1_ABSOLUTE* = 2
-  SQL_CA1_RELATIVE* = 4
-  SQL_CA1_BOOKMARK* = 8       # supported SQLSetPos LockType's
-  SQL_CA1_LOCK_NO_CHANGE* = 0x00000040
-  SQL_CA1_LOCK_EXCLUSIVE* = 0x00000080
-  SQL_CA1_LOCK_UNLOCK* = 0x00000100 # supported SQLSetPos Operations
-  SQL_CA1_POS_POSITION* = 0x00000200
-  SQL_CA1_POS_UPDATE* = 0x00000400
-  SQL_CA1_POS_DELETE* = 0x00000800
-  SQL_CA1_POS_REFRESH* = 0x00001000 # positioned updates and deletes
-  SQL_CA1_POSITIONED_UPDATE* = 0x00002000
-  SQL_CA1_POSITIONED_DELETE* = 0x00004000
-  SQL_CA1_SELECT_FOR_UPDATE* = 0x00008000 # supported SQLBulkOperations operations
-  SQL_CA1_BULK_ADD* = 0x00010000
-  SQL_CA1_BULK_UPDATE_BY_BOOKMARK* = 0x00020000
-  SQL_CA1_BULK_DELETE_BY_BOOKMARK* = 0x00040000
-  SQL_CA1_BULK_FETCH_BY_BOOKMARK* = 0x00080000 # supported values for SQL_ATTR_SCROLL_CONCURRENCY
-  SQL_CA2_READ_ONLY_CONCURRENCY* = 1
-  SQL_CA2_LOCK_CONCURRENCY* = 2
-  SQL_CA2_OPT_ROWVER_CONCURRENCY* = 4
-  SQL_CA2_OPT_VALUES_CONCURRENCY* = 8 # sensitivity of the cursor to its own inserts, deletes, and updates
-  SQL_CA2_SENSITIVITY_ADDITIONS* = 0x00000010
-  SQL_CA2_SENSITIVITY_DELETIONS* = 0x00000020
-  SQL_CA2_SENSITIVITY_UPDATES* = 0x00000040 #  semantics of SQL_ATTR_MAX_ROWS
-  SQL_CA2_MAX_ROWS_SELECT* = 0x00000080
-  SQL_CA2_MAX_ROWS_INSERT* = 0x00000100
-  SQL_CA2_MAX_ROWS_DELETE* = 0x00000200
-  SQL_CA2_MAX_ROWS_UPDATE* = 0x00000400
-  SQL_CA2_MAX_ROWS_CATALOG* = 0x00000800
-  SQL_CA2_MAX_ROWS_AFFECTS_ALL* = (SQL_CA2_MAX_ROWS_SELECT or
-      SQL_CA2_MAX_ROWS_INSERT or SQL_CA2_MAX_ROWS_DELETE or
-      SQL_CA2_MAX_ROWS_UPDATE or SQL_CA2_MAX_ROWS_CATALOG) # semantics of
-                                                           # SQL_DIAG_CURSOR_ROW_COUNT
-  SQL_CA2_CRC_EXACT* = 0x00001000
-  SQL_CA2_CRC_APPROXIMATE* = 0x00002000 #  the kinds of positioned statements that can be simulated
-  SQL_CA2_SIMULATE_NON_UNIQUE* = 0x00004000
-  SQL_CA2_SIMULATE_TRY_UNIQUE* = 0x00008000
-  SQL_CA2_SIMULATE_UNIQUE* = 0x00010000 #  Operations in SQLBulkOperations
-  SQL_ADD* = 4
-  SQL_SETPOS_MAX_OPTION_VALUE* = SQL_ADD
-  SQL_UPDATE_BY_BOOKMARK* = 5
-  SQL_DELETE_BY_BOOKMARK* = 6
-  SQL_FETCH_BY_BOOKMARK* = 7  # Operations in SQLSetPos
-  SQL_POSITION* = 0
-  SQL_REFRESH* = 1
-  SQL_UPDATE* = 2
-  SQL_DELETE* = 3             # Lock options in SQLSetPos
-  SQL_LOCK_NO_CHANGE* = 0
-  SQL_LOCK_EXCLUSIVE* = 1
-  SQL_LOCK_UNLOCK* = 2        # SQLExtendedFetch "rgfRowStatus" element values
-  SQL_ROW_SUCCESS* = 0
-  SQL_ROW_DELETED* = 1
-  SQL_ROW_UPDATED* = 2
-  SQL_ROW_NOROW* = 3
-  SQL_ROW_ADDED* = 4
-  SQL_ROW_ERROR* = 5
-  SQL_ROW_SUCCESS_WITH_INFO* = 6
-  SQL_ROW_PROCEED* = 0
-  SQL_ROW_IGNORE* = 1
-  SQL_MAX_DSN_LENGTH* = 32    # maximum data source name size
-  SQL_MAX_OPTION_STRING_LENGTH* = 256
-  SQL_ODBC_CURSORS* = 110
-  SQL_ATTR_ODBC_CURSORS* = SQL_ODBC_CURSORS # SQL_ODBC_CURSORS options
-  SQL_CUR_USE_IF_NEEDED* = 0
-  SQL_CUR_USE_ODBC* = 1
-  SQL_CUR_USE_DRIVER* = 2
-  SQL_CUR_DEFAULT* = SQL_CUR_USE_DRIVER
-  SQL_PARAM_TYPE_UNKNOWN* = 0
-  SQL_PARAM_INPUT* = 1
-  SQL_PARAM_INPUT_OUTPUT* = 2
-  SQL_RESULT_COL* = 3
-  SQL_PARAM_OUTPUT* = 4
-  SQL_RETURN_VALUE* = 5       # special length/indicator values
-  SQL_NULL_DATA* = (- 1)
-  SQL_DATA_AT_EXEC* = (- 2)
-  SQL_SUCCESS* = 0
-  SQL_SUCCESS_WITH_INFO* = 1
-  SQL_NO_DATA* = 100
-  SQL_ERROR* = (- 1)
-  SQL_INVALID_HANDLE* = (- 2)
-  SQL_STILL_EXECUTING* = 2
-  SQL_NEED_DATA* = 99         # flags for null-terminated string
-  SQL_NTS* = (- 3)            # maximum message length
-  SQL_MAX_MESSAGE_LENGTH* = 512 # date/time length constants
-  SQL_DATE_LEN* = 10
-  SQL_TIME_LEN* = 8           # add P+1 if precision is nonzero
-  SQL_TIMESTAMP_LEN* = 19     # add P+1 if precision is nonzero
-                              # handle type identifiers
-  SQL_HANDLE_ENV* = 1
-  SQL_HANDLE_DBC* = 2
-  SQL_HANDLE_STMT* = 3
-  SQL_HANDLE_DESC* = 4        # environment attribute
-  SQL_ATTR_OUTPUT_NTS* = 10001 # connection attributes
-  SQL_ATTR_AUTO_IPD* = 10001
-  SQL_ATTR_METADATA_ID* = 10014 # statement attributes
-  SQL_ATTR_APP_ROW_DESC* = 10010
-  SQL_ATTR_APP_PARAM_DESC* = 10011
-  SQL_ATTR_IMP_ROW_DESC* = 10012
-  SQL_ATTR_IMP_PARAM_DESC* = 10013
-  SQL_ATTR_CURSOR_SCROLLABLE* = (- 1)
-  SQL_ATTR_CURSOR_SENSITIVITY* = (- 2)
-  SQL_QUERY_TIMEOUT* = 0
-  SQL_MAX_ROWS* = 1
-  SQL_NOSCAN* = 2
-  SQL_MAX_LENGTH* = 3
-  SQL_ASYNC_ENABLE* = 4       # same as SQL_ATTR_ASYNC_ENABLE */
-  SQL_BIND_TYPE* = 5
-  SQL_CURSOR_TYPE* = 6
-  SQL_CONCURRENCY* = 7
-  SQL_KEYSET_SIZE* = 8
-  SQL_ROWSET_SIZE* = 9
-  SQL_SIMULATE_CURSOR* = 10
-  SQL_RETRIEVE_DATA* = 11
-  SQL_USE_BOOKMARKS* = 12
-  SQL_GET_BOOKMARK* = 13      #      GetStmtOption Only */
-  SQL_ROW_NUMBER* = 14        #      GetStmtOption Only */
-  SQL_ATTR_CURSOR_TYPE* = SQL_CURSOR_TYPE
-  SQL_ATTR_CONCURRENCY* = SQL_CONCURRENCY
-  SQL_ATTR_FETCH_BOOKMARK_PTR* = 16
-  SQL_ATTR_ROW_STATUS_PTR* = 25
-  SQL_ATTR_ROWS_FETCHED_PTR* = 26
-  SQL_AUTOCOMMIT* = 102
-  SQL_ATTR_AUTOCOMMIT* = SQL_AUTOCOMMIT
-  SQL_ATTR_ROW_NUMBER* = SQL_ROW_NUMBER
-  SQL_TXN_ISOLATION* = 108
-  SQL_ATTR_TXN_ISOLATION* = SQL_TXN_ISOLATION
-  SQL_ATTR_MAX_ROWS* = SQL_MAX_ROWS
-  SQL_ATTR_USE_BOOKMARKS* = SQL_USE_BOOKMARKS #* connection attributes */
-  SQL_ACCESS_MODE* = 101      #  SQL_AUTOCOMMIT              =102;
-  SQL_LOGIN_TIMEOUT* = 103
-  SQL_OPT_TRACE* = 104
-  SQL_OPT_TRACEFILE* = 105
-  SQL_TRANSLATE_DLL* = 106
-  SQL_TRANSLATE_OPTION* = 107 #  SQL_TXN_ISOLATION           =108;
-  SQL_CURRENT_QUALIFIER* = 109 #  SQL_ODBC_CURSORS            =110;
-  SQL_QUIET_MODE* = 111
-  SQL_PACKET_SIZE* = 112      #* connection attributes with new names */
-  SQL_ATTR_ACCESS_MODE* = SQL_ACCESS_MODE #  SQL_ATTR_AUTOCOMMIT                       =SQL_AUTOCOMMIT;
-  SQL_ATTR_CONNECTION_DEAD* = 1209 #* GetConnectAttr only */
-  SQL_ATTR_CONNECTION_TIMEOUT* = 113
-  SQL_ATTR_CURRENT_CATALOG* = SQL_CURRENT_QUALIFIER
-  SQL_ATTR_DISCONNECT_BEHAVIOR* = 114
-  SQL_ATTR_ENLIST_IN_DTC* = 1207
-  SQL_ATTR_ENLIST_IN_XA* = 1208
-  SQL_ATTR_LOGIN_TIMEOUT* = SQL_LOGIN_TIMEOUT #  SQL_ATTR_ODBC_CURSORS             =SQL_ODBC_CURSORS;
-  SQL_ATTR_PACKET_SIZE* = SQL_PACKET_SIZE
-  SQL_ATTR_QUIET_MODE* = SQL_QUIET_MODE
-  SQL_ATTR_TRACE* = SQL_OPT_TRACE
-  SQL_ATTR_TRACEFILE* = SQL_OPT_TRACEFILE
-  SQL_ATTR_TRANSLATE_LIB* = SQL_TRANSLATE_DLL
-  SQL_ATTR_TRANSLATE_OPTION* = SQL_TRANSLATE_OPTION #  SQL_ATTR_TXN_ISOLATION                  =SQL_TXN_ISOLATION;
-                                                    #* SQL_ACCESS_MODE options */
-  SQL_MODE_READ_WRITE* = 0
-  SQL_MODE_READ_ONLY* = 1
-  SQL_MODE_DEFAULT* = SQL_MODE_READ_WRITE #* SQL_AUTOCOMMIT options */
-  SQL_AUTOCOMMIT_OFF* = 0
-  SQL_AUTOCOMMIT_ON* = 1
-  SQL_AUTOCOMMIT_DEFAULT* = SQL_AUTOCOMMIT_ON # SQL_ATTR_CURSOR_SCROLLABLE values
-  SQL_NONSCROLLABLE* = 0
-  SQL_SCROLLABLE* = 1         # SQL_CURSOR_TYPE options
-  SQL_CURSOR_FORWARD_ONLY* = 0
-  SQL_CURSOR_KEYSET_DRIVEN* = 1
-  SQL_CURSOR_DYNAMIC* = 2
-  SQL_CURSOR_STATIC* = 3
-  SQL_CURSOR_TYPE_DEFAULT* = SQL_CURSOR_FORWARD_ONLY # Default value
-                                                     # SQL_CONCURRENCY options
-  SQL_CONCUR_READ_ONLY* = 1
-  SQL_CONCUR_LOCK* = 2
-  SQL_CONCUR_ROWVER* = 3
-  SQL_CONCUR_VALUES* = 4
-  SQL_CONCUR_DEFAULT* = SQL_CONCUR_READ_ONLY # Default value
-                                             # identifiers of fields in the SQL descriptor
-  SQL_DESC_COUNT* = 1001
-  SQL_DESC_TYPE* = 1002
-  SQL_DESC_LENGTH* = 1003
-  SQL_DESC_OCTET_LENGTH_PTR* = 1004
-  SQL_DESC_PRECISION* = 1005
-  SQL_DESC_SCALE* = 1006
-  SQL_DESC_DATETIME_INTERVAL_CODE* = 1007
-  SQL_DESC_NULLABLE* = 1008
-  SQL_DESC_INDICATOR_PTR* = 1009
-  SQL_DESC_DATA_PTR* = 1010
-  SQL_DESC_NAME* = 1011
-  SQL_DESC_UNNAMED* = 1012
-  SQL_DESC_OCTET_LENGTH* = 1013
-  SQL_DESC_ALLOC_TYPE* = 1099 # identifiers of fields in the diagnostics area
-  SQL_DIAG_RETURNCODE* = 1
-  SQL_DIAG_NUMBER* = 2
-  SQL_DIAG_ROW_COUNT* = 3
-  SQL_DIAG_SQLSTATE* = 4
-  SQL_DIAG_NATIVE* = 5
-  SQL_DIAG_MESSAGE_TEXT* = 6
-  SQL_DIAG_DYNAMIC_FUNCTION* = 7
-  SQL_DIAG_CLASS_ORIGIN* = 8
-  SQL_DIAG_SUBCLASS_ORIGIN* = 9
-  SQL_DIAG_CONNECTION_NAME* = 10
-  SQL_DIAG_SERVER_NAME* = 11
-  SQL_DIAG_DYNAMIC_FUNCTION_CODE* = 12 # dynamic function codes
-  SQL_DIAG_ALTER_TABLE* = 4
-  SQL_DIAG_CREATE_INDEX* = (- 1)
-  SQL_DIAG_CREATE_TABLE* = 77
-  SQL_DIAG_CREATE_VIEW* = 84
-  SQL_DIAG_DELETE_WHERE* = 19
-  SQL_DIAG_DROP_INDEX* = (- 2)
-  SQL_DIAG_DROP_TABLE* = 32
-  SQL_DIAG_DROP_VIEW* = 36
-  SQL_DIAG_DYNAMIC_DELETE_CURSOR* = 38
-  SQL_DIAG_DYNAMIC_UPDATE_CURSOR* = 81
-  SQL_DIAG_GRANT* = 48
-  SQL_DIAG_INSERT* = 50
-  SQL_DIAG_REVOKE* = 59
-  SQL_DIAG_SELECT_CURSOR* = 85
-  SQL_DIAG_UNKNOWN_STATEMENT* = 0
-  SQL_DIAG_UPDATE_WHERE* = 82 # Statement attribute values for cursor sensitivity
-  SQL_UNSPECIFIED* = 0
-  SQL_INSENSITIVE* = 1
-  SQL_SENSITIVE* = 2          # GetTypeInfo() request for all data types
-  SQL_ALL_TYPES* = 0          # Default conversion code for SQLBindCol(), SQLBindParam() and SQLGetData()
-  SQL_DEFAULT* = 99 # SQLGetData() code indicating that the application row descriptor
-                    #    specifies the data type
-  SQL_ARD_TYPE* = (- 99)      # SQL date/time type subcodes
-  SQL_CODE_DATE* = 1
-  SQL_CODE_TIME* = 2
-  SQL_CODE_TIMESTAMP* = 3     # CLI option values
-  SQL_FALSE* = 0
-  SQL_TRUE* = 1               # values of NULLABLE field in descriptor
-  SQL_NO_NULLS* = 0
-  SQL_NULLABLE* = 1 # Value returned by SQLGetTypeInfo() to denote that it is
-                    # not known whether or not a data type supports null values.
-  SQL_NULLABLE_UNKNOWN* = 2
-  SQL_CLOSE* = 0
-  SQL_DROP* = 1
-  SQL_UNBIND* = 2
-  SQL_RESET_PARAMS* = 3 # Codes used for FetchOrientation in SQLFetchScroll(),
-                        #   and in SQLDataSources()
-  SQL_FETCH_NEXT* = 1
-  SQL_FETCH_FIRST* = 2
-  SQL_FETCH_FIRST_USER* = 31
-  SQL_FETCH_FIRST_SYSTEM* = 32 # Other codes used for FetchOrientation in SQLFetchScroll()
-  SQL_FETCH_LAST* = 3
-  SQL_FETCH_PRIOR* = 4
-  SQL_FETCH_ABSOLUTE* = 5
-  SQL_FETCH_RELATIVE* = 6
-  SQL_NULL_HENV* = SqlHEnv(nil)
-  SQL_NULL_HDBC* = SqlHDBC(nil)
-  SQL_NULL_HSTMT* = SqlHStmt(nil)
-  SQL_NULL_HDESC* = SqlHDesc(nil) #* null handle used in place of parent handle when allocating HENV */
-  SQL_NULL_HANDLE* = SqlHandle(nil) #* Values that may appear in the result set of SQLSpecialColumns() */
-  SQL_SCOPE_CURROW* = 0
-  SQL_SCOPE_TRANSACTION* = 1
-  SQL_SCOPE_SESSION* = 2      #* Column types and scopes in SQLSpecialColumns.  */
-  SQL_BEST_ROWID* = 1
-  SQL_ROWVER* = 2
-  SQL_ROW_IDENTIFIER* = 1     #* Reserved values for UNIQUE argument of SQLStatistics() */
-  SQL_INDEX_UNIQUE* = 0
-  SQL_INDEX_ALL* = 1          #* Reserved values for RESERVED argument of SQLStatistics() */
-  SQL_QUICK* = 0
-  SQL_ENSURE* = 1             #* Values that may appear in the result set of SQLStatistics() */
-  SQL_TABLE_STAT* = 0
-  SQL_INDEX_CLUSTERED* = 1
-  SQL_INDEX_HASHED* = 2
-  SQL_INDEX_OTHER* = 3
-  SQL_SCROLL_CONCURRENCY* = 43
-  SQL_TXN_CAPABLE* = 46
-  SQL_TRANSACTION_CAPABLE* = SQL_TXN_CAPABLE
-  SQL_USER_NAME* = 47
-  SQL_TXN_ISOLATION_OPTION* = 72
-  SQL_TRANSACTION_ISOLATION_OPTION* = SQL_TXN_ISOLATION_OPTION
-  SQL_OJ_CAPABILITIES* = 115
-  SQL_OUTER_JOIN_CAPABILITIES* = SQL_OJ_CAPABILITIES
-  SQL_XOPEN_CLI_YEAR* = 10000
-  SQL_CURSOR_SENSITIVITY* = 10001
-  SQL_DESCRIBE_PARAMETER* = 10002
-  SQL_CATALOG_NAME* = 10003
-  SQL_COLLATION_SEQ* = 10004
-  SQL_MAX_IDENTIFIER_LEN* = 10005
-  SQL_MAXIMUM_IDENTIFIER_LENGTH* = SQL_MAX_IDENTIFIER_LEN
-  SQL_SCCO_READ_ONLY* = 1
-  SQL_SCCO_LOCK* = 2
-  SQL_SCCO_OPT_ROWVER* = 4
-  SQL_SCCO_OPT_VALUES* = 8    #* SQL_TXN_CAPABLE values */
-  SQL_TC_NONE* = 0
-  SQL_TC_DML* = 1
-  SQL_TC_ALL* = 2
-  SQL_TC_DDL_COMMIT* = 3
-  SQL_TC_DDL_IGNORE* = 4      #* SQL_TXN_ISOLATION_OPTION bitmasks */
-  SQL_TXN_READ_UNCOMMITTED* = 1
-  SQL_TRANSACTION_READ_UNCOMMITTED* = SQL_TXN_READ_UNCOMMITTED
-  SQL_TXN_READ_COMMITTED* = 2
-  SQL_TRANSACTION_READ_COMMITTED* = SQL_TXN_READ_COMMITTED
-  SQL_TXN_REPEATABLE_READ* = 4
-  SQL_TRANSACTION_REPEATABLE_READ* = SQL_TXN_REPEATABLE_READ
-  SQL_TXN_SERIALIZABLE* = 8
-  SQL_TRANSACTION_SERIALIZABLE* = SQL_TXN_SERIALIZABLE
-  SQL_SS_ADDITIONS* = 1
-  SQL_SS_DELETIONS* = 2
-  SQL_SS_UPDATES* = 4         # SQLColAttributes defines
-  SQL_COLUMN_COUNT* = 0
-  SQL_COLUMN_NAME* = 1
-  SQL_COLUMN_TYPE* = 2
-  SQL_COLUMN_LENGTH* = 3
-  SQL_COLUMN_PRECISION* = 4
-  SQL_COLUMN_SCALE* = 5
-  SQL_COLUMN_DISPLAY_SIZE* = 6
-  SQL_COLUMN_NULLABLE* = 7
-  SQL_COLUMN_UNSIGNED* = 8
-  SQL_COLUMN_MONEY* = 9
-  SQL_COLUMN_UPDATABLE* = 10
-  SQL_COLUMN_AUTO_INCREMENT* = 11
-  SQL_COLUMN_CASE_SENSITIVE* = 12
-  SQL_COLUMN_SEARCHABLE* = 13
-  SQL_COLUMN_TYPE_NAME* = 14
-  SQL_COLUMN_TABLE_NAME* = 15
-  SQL_COLUMN_OWNER_NAME* = 16
-  SQL_COLUMN_QUALIFIER_NAME* = 17
-  SQL_COLUMN_LABEL* = 18
-  SQL_COLATT_OPT_MAX* = SQL_COLUMN_LABEL
-  SQL_COLUMN_DRIVER_START* = 1000
-  SQL_DESC_ARRAY_SIZE* = 20
-  SQL_DESC_ARRAY_STATUS_PTR* = 21
-  SQL_DESC_AUTO_UNIQUE_VALUE* = SQL_COLUMN_AUTO_INCREMENT
-  SQL_DESC_BASE_COLUMN_NAME* = 22
-  SQL_DESC_BASE_TABLE_NAME* = 23
-  SQL_DESC_BIND_OFFSET_PTR* = 24
-  SQL_DESC_BIND_TYPE* = 25
-  SQL_DESC_CASE_SENSITIVE* = SQL_COLUMN_CASE_SENSITIVE
-  SQL_DESC_CATALOG_NAME* = SQL_COLUMN_QUALIFIER_NAME
-  SQL_DESC_CONCISE_TYPE* = SQL_COLUMN_TYPE
-  SQL_DESC_DATETIME_INTERVAL_PRECISION* = 26
-  SQL_DESC_DISPLAY_SIZE* = SQL_COLUMN_DISPLAY_SIZE
-  SQL_DESC_FIXED_PREC_SCALE* = SQL_COLUMN_MONEY
-  SQL_DESC_LABEL* = SQL_COLUMN_LABEL
-  SQL_DESC_LITERAL_PREFIX* = 27
-  SQL_DESC_LITERAL_SUFFIX* = 28
-  SQL_DESC_LOCAL_TYPE_NAME* = 29
-  SQL_DESC_MAXIMUM_SCALE* = 30
-  SQL_DESC_MINIMUM_SCALE* = 31
-  SQL_DESC_NUM_PREC_RADIX* = 32
-  SQL_DESC_PARAMETER_TYPE* = 33
-  SQL_DESC_ROWS_PROCESSED_PTR* = 34
-  SQL_DESC_SCHEMA_NAME* = SQL_COLUMN_OWNER_NAME
-  SQL_DESC_SEARCHABLE* = SQL_COLUMN_SEARCHABLE
-  SQL_DESC_TYPE_NAME* = SQL_COLUMN_TYPE_NAME
-  SQL_DESC_TABLE_NAME* = SQL_COLUMN_TABLE_NAME
-  SQL_DESC_UNSIGNED* = SQL_COLUMN_UNSIGNED
-  SQL_DESC_UPDATABLE* = SQL_COLUMN_UPDATABLE #* SQLEndTran() options */
-  SQL_COMMIT* = 0
-  SQL_ROLLBACK* = 1
-  SQL_ATTR_ROW_ARRAY_SIZE* = 27 #* SQLConfigDataSource() options */
-  ODBC_ADD_DSN* = 1
-  ODBC_CONFIG_DSN* = 2
-  ODBC_REMOVE_DSN* = 3
-  ODBC_ADD_SYS_DSN* = 4
-  ODBC_CONFIG_SYS_DSN* = 5
-  ODBC_REMOVE_SYS_DSN* = 6
-
-  SQL_ACTIVE_CONNECTIONS* = 0   # SQLGetInfo
-  SQL_DATA_SOURCE_NAME* = 2
-  SQL_DATA_SOURCE_READ_ONLY* = 25
-  SQL_DATABASE_NAME* = 2
-  SQL_DBMS_NAME* = 17
-  SQL_DBMS_VERSION* = 18
-  SQL_DRIVER_HDBC* = 3
-  SQL_DRIVER_HENV* = 4
-  SQL_DRIVER_HSTMT* = 5
-  SQL_DRIVER_NAME* = 6
-  SQL_DRIVER_VER* = 7
-  SQL_FETCH_DIRECTION* = 8
-  SQL_ODBC_VER* = 10
-  SQL_DRIVER_ODBC_VER* = 77
-  SQL_SERVER_NAME* = 13
-  SQL_ACTIVE_ENVIRONMENTS* = 116
-  SQL_ACTIVE_STATEMENTS* = 1
-  SQL_SQL_CONFORMANCE* = 118
-  SQL_DATETIME_LITERALS* = 119
-  SQL_ASYNC_MODE* = 10021
-  SQL_BATCH_ROW_COUNT* = 120
-  SQL_BATCH_SUPPORT* = 121
-  SQL_CATALOG_LOCATION* = 114
-  #SQL_CATALOG_NAME* = 10003
-  SQL_CATALOG_NAME_SEPARATOR* = 41
-  SQL_CATALOG_TERM* = 42
-  SQL_CATALOG_USAGE* = 92
-  #SQL_COLLATION_SEQ* = 10004
-  SQL_COLUMN_ALIAS* = 87
-  #SQL_USER_NAME* = 47
-
-proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle,
-                     OutputHandlePtr: var SqlHandle): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
-                    Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger,
-                    Value: SqlPointer, BufferLength: TSqlInteger,
-                    StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLFreeHandle*(HandleType: TSqlSmallInt, Handle: SqlHandle): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetDiagRec*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                    RecNumber: TSqlSmallInt, Sqlstate: PSQLCHAR,
-                    NativeError: var TSqlInteger, MessageText: PSQLCHAR,
-                    BufferLength: TSqlSmallInt, TextLength: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetDiagField*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                      RecNumber: TSqlSmallInt, DiagIdentifier: TSqlSmallInt,
-                      DiagInfoPtr: SqlPointer, BufferLength: TSqlSmallInt,
-                      StringLengthPtr: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLConnect*(ConnectionHandle: SqlHDBC, ServerName: PSQLCHAR,
-                 NameLength1: TSqlSmallInt, UserName: PSQLCHAR,
-                 NameLength2: TSqlSmallInt, Authentication: PSQLCHAR,
-                 NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLDisconnect*(ConnectionHandle: SqlHDBC): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLDriverConnect*(hdbc: SqlHDBC, hwnd: SqlHWND, szCsin: cstring,
-                       szCLen: TSqlSmallInt, szCsout: cstring,
-                       cbCSMax: TSqlSmallInt, cbCsOut: var TSqlSmallInt,
-                       f: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLBrowseConnect*(hdbc: SqlHDBC, szConnStrIn: PSQLCHAR,
-                       cbConnStrIn: TSqlSmallInt, szConnStrOut: PSQLCHAR,
-                       cbConnStrOutMax: TSqlSmallInt,
-                       cbConnStrOut: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLExecDirect*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR,
-                    TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLExecDirectW*(StatementHandle: SqlHStmt, StatementText: WideCString,
-                    TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLPrepare*(StatementHandle: SqlHStmt, StatementText: PSQLCHAR,
-                 TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLPrepareW*(StatementHandle: SqlHStmt, StatementText: WideCString,
-                 TextLength: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLCloseCursor*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLExecute*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLFetch*(StatementHandle: SqlHStmt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLNumResultCols*(StatementHandle: SqlHStmt, ColumnCount: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLDescribeCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                     ColumnName: PSQLCHAR, BufferLength: TSqlSmallInt,
-                     NameLength: var TSqlSmallInt, DataType: var TSqlSmallInt,
-                     ColumnSize: var TSqlULen,
-                     DecimalDigits: var TSqlSmallInt, Nullable: var TSqlSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLFetchScroll*(StatementHandle: SqlHStmt, FetchOrientation: TSqlSmallInt,
-                     FetchOffset: TSqlLen): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLExtendedFetch*(hstmt: SqlHStmt, fFetchType: SqlUSmallInt,
-                       irow: TSqlLen, pcrow: var TSqlULen,
-                       rgfRowStatus: PSQLUSMALLINT): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLGetData*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                 TargetType: TSqlSmallInt, TargetValue: SqlPointer,
-                 BufferLength: TSqlLen, StrLen_or_Ind: ptr TSqlLen): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger,
-                     Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger,
-                     Value: SqlPointer, BufferLength: TSqlInteger,
-                     StringLength: PSQLINTEGER): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLGetInfo*(ConnectionHandle: SqlHDBC, InfoType: SqlUSmallInt,
-                 InfoValue: SqlPointer, BufferLength: TSqlSmallInt,
-                 StringLength: PSQLSMALLINT): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLBulkOperations*(StatementHandle: SqlHStmt, Operation: SqlUSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLPutData*(StatementHandle: SqlHStmt, Data: SqlPointer,
-                 StrLen_or_Ind: TSQLLEN): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLBindCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                 TargetType: TSqlSmallInt, TargetValue: SqlPointer,
-                 BufferLength: TSqlLEN, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetPos*(hstmt: SqlHStmt, irow: SqlUSmallInt, fOption: SqlUSmallInt,
-                fLock: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLDataSources*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt,
-                     ServerName: PSQLCHAR, BufferLength1: TSqlSmallInt,
-                     NameLength1: PSQLSMALLINT, Description: PSQLCHAR,
-                     BufferLength2: TSqlSmallInt, NameLength2: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLDrivers*(EnvironmentHandle: SqlHEnv, Direction: SqlUSmallInt,
-                 DriverDescription: PSQLCHAR, BufferLength1: TSqlSmallInt,
-                 DescriptionLength1: PSQLSMALLINT, DriverAttributes: PSQLCHAR,
-                 BufferLength2: TSqlSmallInt, AttributesLength2: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetConnectAttr*(ConnectionHandle: SqlHDBC, Attribute: TSqlInteger,
-                        Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLGetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR,
-                       BufferLength: TSqlSmallInt, NameLength: PSQLSMALLINT): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLSetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR,
-                       NameLength: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLRowCount*(StatementHandle: SqlHStmt, RowCount: var TSQLLEN): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLBindParameter*(hstmt: SqlHStmt, ipar: SqlUSmallInt,
-                       fParamType: TSqlSmallInt, fCType: TSqlSmallInt,
-                       fSqlType: TSqlSmallInt, cbColDef: TSQLULEN,
-                       ibScale: TSqlSmallInt, rgbValue: SqlPointer,
-                       cbValueMax: TSQLLEN, pcbValue: var TSQLLEN): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLFreeStmt*(StatementHandle: SqlHStmt, Option: SqlUSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLColAttribute*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt,
-                      FieldIdentifier: SqlUSmallInt,
-                      CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt,
-                      StringLength: PSQLSMALLINT,
-                      NumericAttribute: TSQLLEN): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: SqlHandle,
-                 CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLTables*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                cbTableName: TSqlSmallInt, szTableType: PSQLCHAR,
-                cbTableType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLColumns*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                 cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                 cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                 cbTableName: TSqlSmallInt, szColumnName: PSQLCHAR,
-                 cbColumnName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.}
-proc SQLSpecialColumns*(StatementHandle: SqlHStmt, IdentifierType: SqlUSmallInt,
-                        CatalogName: PSQLCHAR, NameLength1: TSqlSmallInt,
-                        SchemaName: PSQLCHAR, NameLength2: TSqlSmallInt,
-                        TableName: PSQLCHAR, NameLength3: TSqlSmallInt,
-                        Scope: SqlUSmallInt,
-                        Nullable: SqlUSmallInt): TSqlSmallInt{.
-    dynlib: odbclib, importc.}
-proc SQLProcedures*(hstmt: SqlHStmt, szTableQualifier: PSQLCHAR,
-                    cbTableQualifier: TSqlSmallInt, szTableOwner: PSQLCHAR,
-                    cbTableOwner: TSqlSmallInt, szTableName: PSQLCHAR,
-                    cbTableName: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLPrimaryKeys*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                     NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                     NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
-                     NameLength3: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLProcedureColumns*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                          NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                          NameLength2: TSqlSmallInt, ProcName: PSQLCHAR,
-                          NameLength3: TSqlSmallInt, ColumnName: PSQLCHAR,
-                          NameLength4: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib,
-    importc.}
-proc SQLStatistics*(hstmt: SqlHStmt, CatalogName: PSQLCHAR,
-                    NameLength1: TSqlSmallInt, SchemaName: PSQLCHAR,
-                    NameLength2: TSqlSmallInt, TableName: PSQLCHAR,
-                    NameLength3: TSqlSmallInt, Unique: SqlUSmallInt,
-                    Reserved: SqlUSmallInt): TSqlSmallInt {.
-                    dynlib: odbclib, importc.}
-proc SQLErr*(henv: SqlHEnv, hdbc: SqlHDBC, hstmt: SqlHStmt,
-              szSqlState, pfNativeError, szErrorMsg: PSQLCHAR,
-              cbErrorMsgMax: TSqlSmallInt,
-              pcbErrorMsg: PSQLSMALLINT): TSqlSmallInt {.
-                    dynlib: odbclib, importc: "SQLError".}
-
-{.pop.}
-when defined(nimHasStyleChecks):
-  {.pop.}
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index 70fed664e..9921b7ffd 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -10,20 +10,25 @@
 ## OpenSSL wrapper. Supports OpenSSL >= 1.1.0 dynamically (as default) or statically linked
 ## using `--dynlibOverride:ssl`.
 ##
-## To use openSSL 3, either set `-d:sslVersion=3` or `-d:useOpenssl3`.
+## `-d:sslVersion=1.2.3` can be used to force an SSL version.
+## This version must be included in the library name.
+## `-d:useOpenssl3` may be set for OpenSSL 3 instead.
+##
+## There is also limited support for OpenSSL 1.0.x which may require `-d:openssl10`.
 ##
 ## Build and test examples:
 ##
-## .. code-block::
+##   ```cmd
 ##   ./bin/nim c -d:ssl -p:. -r tests/stdlib/tssl.nim
 ##   ./bin/nim c -d:ssl --threads:on -p:. -r tests/stdlib/thttpclient_ssl.nim
 ##   ./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
 ##   ./bin/nim r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:ssl -p:testament/lib --threads:on tests/untestable/thttpclient_ssl_remotenetwork.nim
+##   ```
 
 # https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-openssl.html
 #
-from strutils import startsWith
+from std/strutils import startsWith
 
 when defined(nimPreviewSlimSystem):
   import std/syncio
@@ -46,20 +51,20 @@ when sslVersion != "":
     const
       DLLSSLName* = "libssl." & sslVersion & ".dylib"
       DLLUtilName* = "libcrypto." & sslVersion & ".dylib"
-    from posix import SocketHandle
+    from std/posix import SocketHandle
   elif defined(windows):
     const
       DLLSSLName* = "libssl-" & sslVersion & ".dll"
       DLLUtilName* =  "libcrypto-" & sslVersion & ".dll"
-    from winlean import SocketHandle
+    from std/winlean import SocketHandle
   else:
     const
       DLLSSLName* = "libssl.so." & sslVersion
       DLLUtilName* = "libcrypto.so." & sslVersion
-    from posix import SocketHandle
+    from std/posix import SocketHandle
 
 elif useWinVersion:
-  when defined(nimOldDlls):
+  when defined(openssl10) or defined(nimOldDlls):
     when defined(cpu64):
       const
         DLLSSLName* = "(ssleay32|ssleay64).dll"
@@ -77,7 +82,7 @@ elif useWinVersion:
       DLLSSLName* = "(libssl-1_1|ssleay32|libssl32).dll"
       DLLUtilName* = "(libcrypto-1_1|libeay32).dll"
 
-  from winlean import SocketHandle
+  from std/winlean import SocketHandle
 else:
   # same list of versions but ordered differently?
   when defined(osx):
@@ -97,9 +102,9 @@ else:
     const
       DLLSSLName* = "libssl.so" & versions
       DLLUtilName* = "libcrypto.so" & versions
-  from posix import SocketHandle
+  from std/posix import SocketHandle
 
-import dynlib
+import std/dynlib
 
 {.pragma: lcrypto, cdecl, dynlib: DLLUtilName, importc.}
 {.pragma: lssl, cdecl, dynlib: DLLSSLName, importc.}
@@ -276,40 +281,63 @@ proc TLSv1_method*(): PSSL_METHOD{.cdecl, dynlib: DLLSSLName, importc.}
 # and support SSLv3, TLSv1, TLSv1.1 and TLSv1.2
 # SSLv23_method(), SSLv23_server_method(), SSLv23_client_method() are removed in 1.1.0
 
-when compileOption("dynlibOverride", "ssl"):
+const useStaticLink = compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks)
+
+when useStaticLink:
   # Static linking
-  when not useOpenssl3:
+
+  when defined(openssl10):
+    proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
+    proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSLeay(): culong {.cdecl, dynlib: DLLUtilName, importc.}
+
+    proc getOpenSSLVersion*(): culong =
+      SSLeay()
+
+    proc ERR_load_BIO_strings*() {.cdecl, dynlib: DLLUtilName, importc.}
+  else:
     proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.}
     proc SSL_library_init*(): cint {.discardable.} =
       ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0
       return OPENSSL_init_ssl(0.uint64, 0.uint8)
 
-  proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+    proc TLS_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+    proc SSLv23_method*(): PSSL_METHOD =
+      TLS_method()
 
-  proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.}
+    proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.}
 
-  proc getOpenSSLVersion*(): culong =
-    ## Return OpenSSL version as unsigned long
-    OpenSSL_version_num()
+    proc getOpenSSLVersion*(): culong =
+      ## Return OpenSSL version as unsigned long
+      OpenSSL_version_num()
 
-  proc SSL_load_error_strings*() =
-    ## Removed from OpenSSL 1.1.0
-    # This proc prevents breaking existing code calling SslLoadErrorStrings
-    # Static linking against OpenSSL < 1.1.0 is not supported
-    discard
+    proc SSL_load_error_strings*() =
+      ## Removed from OpenSSL 1.1.0
+      # This proc prevents breaking existing code calling SslLoadErrorStrings
+      # Static linking against OpenSSL < 1.1.0 is not supported
+      discard
+
+    proc ERR_load_BIO_strings*() =
+      discard
 
-  when defined(libressl):
+  when defined(libressl) or defined(openssl10):
     proc SSL_state(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
     proc SSL_in_init*(ssl: SslPtr): cint {.inline.} =
-      SSl_state(ssl) and SSL_ST_INIT
+      SSL_state(ssl) and SSL_ST_INIT
   else:
     proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.}
     proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.}
 
   template OpenSSL_add_all_algorithms*() = discard
 
+  proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc SSLv2_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc SSLv3_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
+  proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl, dynlib: DLLUtilName, importc.}
+
 else:
-  # Here we're trying to stay compatible with openssl 1.1.*. Some
+  # Here we're trying to stay compatible between openssl versions. Some
   # symbols are loaded dynamically and we don't use them if not found.
   proc thisModule(): LibHandle {.inline.} =
     var thisMod {.global.}: LibHandle
@@ -367,29 +395,51 @@ else:
     let method2Proc = cast[proc(): PSSL_METHOD {.cdecl, gcsafe, raises: [].}](methodSym)
     return method2Proc()
 
-  when not useOpenssl3:
-    proc SSL_library_init*(): cint {.discardable.} =
-      ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
-      ## SSL_library_init
-      let newInitSym = sslSymNullable("OPENSSL_init_ssl")
-      if not newInitSym.isNil:
-        let newInitProc =
-          cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym)
-        return newInitProc(0, 0)
-      let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init"))
-      if not olderProc.isNil: result = olderProc()
+  proc CRYPTO_set_mem_functions(a,b,c: pointer) =
+    let theProc = cast[proc(a,b,c: pointer) {.cdecl.}](utilModule().symNullable("CRYPTO_set_mem_functions"))
+    if not theProc.isNil: theProc(a, b, c)
+
+  proc SSL_library_init*(): cint {.discardable.} =
+    ## Initialize SSL using OPENSSL_init_ssl for OpenSSL >= 1.1.0 otherwise
+    ## SSL_library_init
+    let newInitSym = sslSymNullable("OPENSSL_init_ssl")
+    if not newInitSym.isNil:
+      let newInitProc =
+        cast[proc(opts: uint64, settings: uint8): cint {.cdecl.}](newInitSym)
+      return newInitProc(0, 0)
+    let olderProc = cast[proc(): cint {.cdecl.}](sslSymThrows("SSL_library_init"))
+    if not olderProc.isNil: result = olderProc()
 
   proc SSL_load_error_strings*() =
     # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
     let theProc = cast[proc() {.cdecl.}](sslSymNullable("SSL_load_error_strings"))
     if not theProc.isNil: theProc()
 
+  proc ERR_load_BIO_strings*() =
+    let theProc = cast[proc() {.cdecl.}](utilModule().symNullable("ERR_load_BIO_strings"))
+    if not theProc.isNil: theProc()
+
+  proc SSLv23_client_method*(): PSSL_METHOD =
+    loadPSSLMethod("SSLv23_client_method", "TLS_client_method")
+
+  proc SSLv23_method*(): PSSL_METHOD =
+    loadPSSLMethod("SSLv23_method", "TLS_method")
+
+  proc SSLv2_method*(): PSSL_METHOD =
+    loadPSSLMethod("SSLv2_method", "TLS_method")
+
   proc SSLv3_method*(): PSSL_METHOD =
     loadPSSLMethod("SSLv3_method", "TLS_method")
 
   proc TLS_method*(): PSSL_METHOD =
     loadPSSLMethod("TLS_method", "SSLv23_method")
 
+  proc TLS_client_method*(): PSSL_METHOD =
+    loadPSSLMethod("TLS_client_method", "SSLv23_client_method")
+
+  proc TLS_server_method*(): PSSL_METHOD =
+    loadPSSLMethod("TLS_server_method", "SSLv23_server_method")
+
   proc OpenSSL_add_all_algorithms*() =
     # TODO: Are we ignoring this on purpose? SSL GitHub CI fails otherwise.
     let theProc = cast[proc() {.cdecl.}](sslSymNullable("OPENSSL_add_all_algorithms_conf"))
@@ -418,15 +468,10 @@ else:
       raiseInvalidLibrary MainProc
 
   proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint =
-    var theProc {.global.}: proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.}
+    var theProc {.global.}: proc(ctx: SslCtx, str: cstring): cint {.cdecl, gcsafe.}
     if theProc.isNil:
       theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites"))
-    theProc(ctx, str)
-
-proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.}
-
-proc TLS_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.}
-
+    result = theProc(ctx, str)
 
 proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.}
 proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.}
@@ -485,11 +530,8 @@ proc BIO_do_handshake*(bio: BIO): int =
 proc BIO_do_connect*(bio: BIO): int =
   return BIO_do_handshake(bio)
 
-when not defined(nimfix):
-  proc BIO_read*(b: BIO, data: cstring, length: cint): cint{.cdecl,
-      dynlib: DLLUtilName, importc.}
-  proc BIO_write*(b: BIO, data: cstring, length: cint): cint{.cdecl,
-      dynlib: DLLUtilName, importc.}
+proc BIO_read*(b: BIO, data: cstring, length: cint): cint{.cdecl, dynlib: DLLUtilName, importc.}
+proc BIO_write*(b: BIO, data: cstring, length: cint): cint{.cdecl, dynlib: DLLUtilName, importc.}
 
 proc BIO_free*(b: BIO): cint{.cdecl, dynlib: DLLUtilName, importc.}
 
@@ -531,10 +573,10 @@ proc i2d_X509*(cert: PX509): string =
   if length.int <= 0:
     raise newException(Exception, "X.509 certificate encoding failed")
 
-when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
-  proc CRYPTO_set_mem_functions(a,b,c: pointer){.cdecl,
-    dynlib: DLLUtilName, importc.}
+const
+  useNimsAlloc = not defined(nimNoAllocForSSL) and not defined(gcDestructors)
 
+when not useWinVersion and not defined(macosx) and not defined(android) and useNimsAlloc:
   proc allocWrapper(size: int): pointer {.cdecl.} = allocShared(size)
   proc reallocWrapper(p: pointer; newSize: int): pointer {.cdecl.} =
     if p == nil:
@@ -544,9 +586,11 @@ when not useWinVersion and not defined(macosx) and not defined(android) and not
   proc deallocWrapper(p: pointer) {.cdecl.} =
     if p != nil: deallocShared(p)
 
-proc CRYPTO_malloc_init*() =
-  when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL):
-    CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper)
+  proc CRYPTO_malloc_init*() =
+    CRYPTO_set_mem_functions(cast[pointer](allocWrapper), cast[pointer](reallocWrapper), cast[pointer](deallocWrapper))
+else:
+  proc CRYPTO_malloc_init*() =
+    discard
 
 proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: clong, parg: pointer): clong{.
   cdecl, dynlib: DLLSSLName, importc.}
@@ -735,7 +779,7 @@ proc md5*(d: ptr uint8; n: csize_t; md: ptr uint8): ptr uint8{.importc: "MD5".}
 proc md5_Transform*(c: var MD5_CTX; b: ptr uint8){.importc: "MD5_Transform".}
 {.pop.}
 
-from strutils import toHex, toLowerAscii
+from std/strutils import toHex, toLowerAscii
 
 proc hexStr(buf: cstring): string =
   # turn md5s output into a nice hex str
@@ -758,10 +802,10 @@ proc md5_File*(file: string): string {.raises: [IOError,Exception].} =
   while (let bytes = f.readChars(buf); bytes > 0):
     discard md5_Update(ctx, buf[0].addr, cast[csize_t](bytes))
 
-  discard md5_Final(buf[0].addr, ctx)
+  discard md5_Final(cast[cstring](buf[0].addr), ctx)
   f.close
 
-  result = hexStr(addr buf)
+  result = hexStr(cast[cstring](addr buf))
 
 proc md5_Str*(str: string): string =
   ## Generate MD5 hash for a string. Result is a 32 character
@@ -778,8 +822,8 @@ proc md5_Str*(str: string): string =
     discard md5_Update(ctx, input[i].addr, cast[csize_t](L))
     i += L
 
-  discard md5_Final(addr res, ctx)
-  result = hexStr(addr res)
+  discard md5_Final(cast[cstring](addr res), ctx)
+  result = hexStr(cast[cstring](addr res))
 
 when defined(nimHasStyleChecks):
   {.pop.}
@@ -789,17 +833,21 @@ when defined(nimHasStyleChecks):
 # On old openSSL version some of these symbols are not available
 when not defined(nimDisableCertificateValidation) and not defined(windows):
 
-  # proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
-  #  loadPSSLMethod("SSL_get_peer_certificate", "SSL_get1_peer_certificate")
-
+  # SSL_get_peer_certificate removed in 3.0
+  # SSL_get1_peer_certificate added in 3.0
   when useOpenssl3:
     proc SSL_get1_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
     proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
       SSL_get1_peer_certificate(ssl)
-
-  else:
+  elif useStaticLink:
     proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 {.cdecl, dynlib: DLLSSLName, importc.}
-
+  else:
+    proc SSL_get_peer_certificate*(ssl: SslCtx): PX509 =
+      let methodSym = sslSymNullable("SSL_get_peer_certificate", "SSL_get1_peer_certificate")
+      if methodSym.isNil:
+        raise newException(LibraryError, "Could not load SSL_get_peer_certificate or SSL_get1_peer_certificate")
+      let method2Proc = cast[proc(ssl: SslCtx): PX509 {.cdecl, gcsafe, raises: [].}](methodSym)
+      return method2Proc(ssl)
 
   proc X509_get_subject_name*(a: PX509): PX509_NAME{.cdecl, dynlib: DLLSSLName, importc.}
 
@@ -838,6 +886,8 @@ when not defined(nimDisableCertificateValidation) and not defined(windows):
   {.pop.}
 
   when isMainModule:
+    when defined(nimPreviewSlimSystem):
+      import std/assertions
     # A simple certificate test
     let certbytes = readFile("certificate.der")
     let cert = d2i_X509(certbytes)
diff --git a/lib/wrappers/postgres.nim b/lib/wrappers/postgres.nim
deleted file mode 100644
index 0bde3f1e6..000000000
--- a/lib/wrappers/postgres.nim
+++ /dev/null
@@ -1,378 +0,0 @@
-#
-#
-#            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 contains the definitions for structures and externs for
-# functions used by frontend postgres applications. It is based on
-# Postgresql's libpq-fe.h.
-#
-# It is for postgreSQL version 7.4 and higher with support for the v3.0
-# connection-protocol.
-#
-
-when defined(nimHasStyleChecks):
-  {.push styleChecks: off.}
-
-when defined(windows):
-  const
-    dllName = "libpq.dll"
-elif defined(macosx):
-  const
-    dllName = "libpq.dylib"
-else:
-  const
-    dllName = "libpq.so(.5|)"
-type
-  POid* = ptr Oid
-  Oid* = int32
-
-const
-  ERROR_MSG_LENGTH* = 4096
-  CMDSTATUS_LEN* = 40
-
-type
-  SockAddr* = array[1..112, int8]
-  PGresAttDesc*{.pure, final.} = object
-    name*: cstring
-    adtid*: Oid
-    adtsize*: int
-
-  PPGresAttDesc* = ptr PGresAttDesc
-  PPPGresAttDesc* = ptr PPGresAttDesc
-  PGresAttValue*{.pure, final.} = object
-    length*: int32
-    value*: cstring
-
-  PPGresAttValue* = ptr PGresAttValue
-  PPPGresAttValue* = ptr PPGresAttValue
-  PExecStatusType* = ptr ExecStatusType
-  ExecStatusType* = enum
-    PGRES_EMPTY_QUERY = 0, PGRES_COMMAND_OK, PGRES_TUPLES_OK, PGRES_COPY_OUT,
-    PGRES_COPY_IN, PGRES_BAD_RESPONSE, PGRES_NONFATAL_ERROR, PGRES_FATAL_ERROR,
-    PGRES_COPY_BOTH, PGRES_SINGLE_TUPLE
-  PGlobjfuncs*{.pure, final.} = object
-    fn_lo_open*: Oid
-    fn_lo_close*: Oid
-    fn_lo_creat*: Oid
-    fn_lo_unlink*: Oid
-    fn_lo_lseek*: Oid
-    fn_lo_tell*: Oid
-    fn_lo_read*: Oid
-    fn_lo_write*: Oid
-
-  PPGlobjfuncs* = ptr PGlobjfuncs
-  PConnStatusType* = ptr ConnStatusType
-  ConnStatusType* = enum
-    CONNECTION_OK, CONNECTION_BAD, CONNECTION_STARTED, CONNECTION_MADE,
-    CONNECTION_AWAITING_RESPONSE, CONNECTION_AUTH_OK, CONNECTION_SETENV,
-    CONNECTION_SSL_STARTUP, CONNECTION_NEEDED, CONNECTION_CHECK_WRITABLE,
-    CONNECTION_CONSUME, CONNECTION_GSS_STARTUP, CONNECTION_CHECK_TARGET
-  PGconn*{.pure, final.} = object
-    pghost*: cstring
-    pgtty*: cstring
-    pgport*: cstring
-    pgoptions*: cstring
-    dbName*: cstring
-    status*: ConnStatusType
-    errorMessage*: array[0..(ERROR_MSG_LENGTH) - 1, char]
-    Pfin*: File
-    Pfout*: File
-    Pfdebug*: File
-    sock*: int32
-    laddr*: SockAddr
-    raddr*: SockAddr
-    salt*: array[0..(2) - 1, char]
-    asyncNotifyWaiting*: int32
-    notifyList*: pointer
-    pguser*: cstring
-    pgpass*: cstring
-    lobjfuncs*: PPGlobjfuncs
-
-  PPGconn* = ptr PGconn
-  PGresult*{.pure, final.} = object
-    ntups*: int32
-    numAttributes*: int32
-    attDescs*: PPGresAttDesc
-    tuples*: PPPGresAttValue
-    tupArrSize*: int32
-    resultStatus*: ExecStatusType
-    cmdStatus*: array[0..(CMDSTATUS_LEN) - 1, char]
-    binary*: int32
-    conn*: PPGconn
-
-  PPGresult* = ptr PGresult
-  PPostgresPollingStatusType* = ptr PostgresPollingStatusType
-  PostgresPollingStatusType* = enum
-    PGRES_POLLING_FAILED = 0, PGRES_POLLING_READING, PGRES_POLLING_WRITING,
-    PGRES_POLLING_OK, PGRES_POLLING_ACTIVE
-  PPGTransactionStatusType* = ptr PGTransactionStatusType
-  PGTransactionStatusType* = enum
-    PQTRANS_IDLE, PQTRANS_ACTIVE, PQTRANS_INTRANS, PQTRANS_INERROR,
-    PQTRANS_UNKNOWN
-  PPGVerbosity* = ptr PGVerbosity
-  PGVerbosity* = enum
-    PQERRORS_TERSE, PQERRORS_DEFAULT, PQERRORS_VERBOSE, PQERRORS_SQLSTATE
-  PPGNotify* = ptr pgNotify
-  pgNotify*{.pure, final.} = object
-    relname*: cstring
-    be_pid*: int32
-    extra*: cstring
-
-  PQnoticeReceiver* = proc (arg: pointer, res: PPGresult){.cdecl.}
-  PQnoticeProcessor* = proc (arg: pointer, message: cstring){.cdecl.}
-  Ppqbool* = ptr pqbool
-  pqbool* = char
-  PPQprintOpt* = ptr PQprintOpt
-  PQprintOpt*{.pure, final.} = object
-    header*: pqbool
-    align*: pqbool
-    standard*: pqbool
-    html3*: pqbool
-    expanded*: pqbool
-    pager*: pqbool
-    fieldSep*: cstring
-    tableOpt*: cstring
-    caption*: cstring
-    fieldName*: ptr cstring
-
-  PPQconninfoOption* = ptr PQconninfoOption
-  PQconninfoOption*{.pure, final.} = object
-    keyword*: cstring
-    envvar*: cstring
-    compiled*: cstring
-    val*: cstring
-    label*: cstring
-    dispchar*: cstring
-    dispsize*: int32
-
-  PPQArgBlock* = ptr PQArgBlock
-  PQArgBlock*{.pure, final.} = object
-    length*: int32
-    isint*: int32
-    p*: pointer
-
-proc pqinitOpenSSL*(do_ssl: int32, do_crypto: int32) {.cdecl, dynlib: dllName,
-    importc: "PQinitOpenSSL".}
-proc pqconnectStart*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName,
-    importc: "PQconnectStart".}
-proc pqconnectPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl,
-    dynlib: dllName, importc: "PQconnectPoll".}
-proc pqconnectdb*(conninfo: cstring): PPGconn{.cdecl, dynlib: dllName,
-    importc: "PQconnectdb".}
-proc pqsetdbLogin*(pghost: cstring, pgport: cstring, pgoptions: cstring,
-                   pgtty: cstring, dbName: cstring, login: cstring, pwd: cstring): PPGconn{.
-    cdecl, dynlib: dllName, importc: "PQsetdbLogin".}
-proc pqsetdb*(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn
-proc pqfinish*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQfinish".}
-proc pqconndefaults*(): PPQconninfoOption{.cdecl, dynlib: dllName,
-    importc: "PQconndefaults".}
-proc pqconninfoFree*(connOptions: PPQconninfoOption){.cdecl, dynlib: dllName,
-    importc: "PQconninfoFree".}
-proc pqresetStart*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQresetStart".}
-proc pqresetPoll*(conn: PPGconn): PostgresPollingStatusType{.cdecl,
-    dynlib: dllName, importc: "PQresetPoll".}
-proc pqreset*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQreset".}
-proc pqrequestCancel*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQrequestCancel".}
-proc pqdb*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQdb".}
-proc pquser*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQuser".}
-proc pqpass*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQpass".}
-proc pqhost*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQhost".}
-proc pqport*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQport".}
-proc pqtty*(conn: PPGconn): cstring{.cdecl, dynlib: dllName, importc: "PQtty".}
-proc pqoptions*(conn: PPGconn): cstring{.cdecl, dynlib: dllName,
-    importc: "PQoptions".}
-proc pqstatus*(conn: PPGconn): ConnStatusType{.cdecl, dynlib: dllName,
-    importc: "PQstatus".}
-proc pqtransactionStatus*(conn: PPGconn): PGTransactionStatusType{.cdecl,
-    dynlib: dllName, importc: "PQtransactionStatus".}
-proc pqparameterStatus*(conn: PPGconn, paramName: cstring): cstring{.cdecl,
-    dynlib: dllName, importc: "PQparameterStatus".}
-proc pqserverVersion*(conn: PPGconn): int32{.cdecl,
-    dynlib: dllName, importc: "PQserverVersion".}
-proc pqprotocolVersion*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQprotocolVersion".}
-proc pqerrorMessage*(conn: PPGconn): cstring{.cdecl, dynlib: dllName,
-    importc: "PQerrorMessage".}
-proc pqsocket*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                      importc: "PQsocket".}
-proc pqbackendPID*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQbackendPID".}
-proc pqconnectionNeedsPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQconnectionNeedsPassword".}
-proc pqconnectionUsedPassword*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQconnectionUsedPassword".}
-proc pqclientEncoding*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQclientEncoding".}
-proc pqsetClientEncoding*(conn: PPGconn, encoding: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQsetClientEncoding".}
-when defined(USE_SSL):
-  # Get the SSL structure associated with a connection
-  proc pqgetssl*(conn: PPGconn): PSSL{.cdecl, dynlib: dllName,
-                                       importc: "PQgetssl".}
-proc pqsetErrorVerbosity*(conn: PPGconn, verbosity: PGVerbosity): PGVerbosity{.
-    cdecl, dynlib: dllName, importc: "PQsetErrorVerbosity".}
-proc pqtrace*(conn: PPGconn, debug_port: File){.cdecl, dynlib: dllName,
-    importc: "PQtrace".}
-proc pquntrace*(conn: PPGconn){.cdecl, dynlib: dllName, importc: "PQuntrace".}
-proc pqsetNoticeReceiver*(conn: PPGconn, theProc: PQnoticeReceiver, arg: pointer): PQnoticeReceiver{.
-    cdecl, dynlib: dllName, importc: "PQsetNoticeReceiver".}
-proc pqsetNoticeProcessor*(conn: PPGconn, theProc: PQnoticeProcessor,
-                           arg: pointer): PQnoticeProcessor{.cdecl,
-    dynlib: dllName, importc: "PQsetNoticeProcessor".}
-proc pqexec*(conn: PPGconn, query: cstring): PPGresult{.cdecl, dynlib: dllName,
-    importc: "PQexec".}
-proc pqexecParams*(conn: PPGconn, command: cstring, nParams: int32,
-                   paramTypes: POid, paramValues: cstringArray,
-                   paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQexecParams".}
-proc pqprepare*(conn: PPGconn, stmtName, query: cstring, nParams: int32,
-    paramTypes: POid): PPGresult{.cdecl, dynlib: dllName, importc: "PQprepare".}
-proc pqexecPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32,
-                     paramValues: cstringArray,
-                     paramLengths, paramFormats: ptr int32, resultFormat: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQexecPrepared".}
-proc pqsendQuery*(conn: PPGconn, query: cstring): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQuery".}
-  ## See also https://www.postgresql.org/docs/current/libpq-async.html
-proc pqsendQueryParams*(conn: PPGconn, command: cstring, nParams: int32,
-                        paramTypes: POid, paramValues: cstringArray,
-                        paramLengths, paramFormats: ptr int32,
-                        resultFormat: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQueryParams".}
-proc pqsendQueryPrepared*(conn: PPGconn, stmtName: cstring, nParams: int32,
-                          paramValues: cstringArray,
-                          paramLengths, paramFormats: ptr int32,
-                          resultFormat: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQsendQueryPrepared".}
-proc pqSetSingleRowMode*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQsetSingleRowMode".}
-  ## See also https://www.postgresql.org/docs/current/libpq-single-row-mode.html
-proc pqgetResult*(conn: PPGconn): PPGresult{.cdecl, dynlib: dllName,
-    importc: "PQgetResult".}
-proc pqisBusy*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                      importc: "PQisBusy".}
-proc pqconsumeInput*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQconsumeInput".}
-proc pqnotifies*(conn: PPGconn): PPGNotify{.cdecl, dynlib: dllName,
-    importc: "PQnotifies".}
-proc pqputCopyData*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQputCopyData".}
-proc pqputCopyEnd*(conn: PPGconn, errormsg: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQputCopyEnd".}
-proc pqgetCopyData*(conn: PPGconn, buffer: cstringArray, async: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetCopyData".}
-proc pqgetline*(conn: PPGconn, str: cstring, len: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQgetline".}
-proc pqputline*(conn: PPGconn, str: cstring): int32{.cdecl, dynlib: dllName,
-    importc: "PQputline".}
-proc pqgetlineAsync*(conn: PPGconn, buffer: cstring, bufsize: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetlineAsync".}
-proc pqputnbytes*(conn: PPGconn, buffer: cstring, nbytes: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQputnbytes".}
-proc pqendcopy*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-                                       importc: "PQendcopy".}
-proc pqsetnonblocking*(conn: PPGconn, arg: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQsetnonblocking".}
-proc pqisnonblocking*(conn: PPGconn): int32{.cdecl, dynlib: dllName,
-    importc: "PQisnonblocking".}
-proc pqflush*(conn: PPGconn): int32{.cdecl, dynlib: dllName, importc: "PQflush".}
-proc pqfn*(conn: PPGconn, fnid: int32, result_buf, result_len: ptr int32,
-           result_is_int: int32, args: PPQArgBlock, nargs: int32): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQfn".}
-proc pqresultStatus*(res: PPGresult): ExecStatusType{.cdecl, dynlib: dllName,
-    importc: "PQresultStatus".}
-proc pqresStatus*(status: ExecStatusType): cstring{.cdecl, dynlib: dllName,
-    importc: "PQresStatus".}
-proc pqresultErrorMessage*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQresultErrorMessage".}
-proc pqresultErrorField*(res: PPGresult, fieldcode: int32): cstring{.cdecl,
-    dynlib: dllName, importc: "PQresultErrorField".}
-proc pqntuples*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-                                        importc: "PQntuples".}
-proc pqnfields*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-                                        importc: "PQnfields".}
-proc pqbinaryTuples*(res: PPGresult): int32{.cdecl, dynlib: dllName,
-    importc: "PQbinaryTuples".}
-proc pqfname*(res: PPGresult, field_num: int32): cstring{.cdecl,
-    dynlib: dllName, importc: "PQfname".}
-proc pqfnumber*(res: PPGresult, field_name: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "PQfnumber".}
-proc pqftable*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "PQftable".}
-proc pqftablecol*(res: PPGresult, field_num: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQftablecol".}
-proc pqfformat*(res: PPGresult, field_num: int32): int32{.cdecl,
-    dynlib: dllName, importc: "PQfformat".}
-proc pqftype*(res: PPGresult, field_num: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "PQftype".}
-proc pqfsize*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQfsize".}
-proc pqfmod*(res: PPGresult, field_num: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQfmod".}
-proc pqcmdStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQcmdStatus".}
-proc pqoidStatus*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQoidStatus".}
-proc pqoidValue*(res: PPGresult): Oid{.cdecl, dynlib: dllName,
-                                       importc: "PQoidValue".}
-proc pqcmdTuples*(res: PPGresult): cstring{.cdecl, dynlib: dllName,
-    importc: "PQcmdTuples".}
-proc pqgetvalue*(res: PPGresult, tup_num: int32, field_num: int32): cstring{.
-    cdecl, dynlib: dllName, importc: "PQgetvalue".}
-proc pqgetlength*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetlength".}
-proc pqgetisnull*(res: PPGresult, tup_num: int32, field_num: int32): int32{.
-    cdecl, dynlib: dllName, importc: "PQgetisnull".}
-proc pqclear*(res: PPGresult){.cdecl, dynlib: dllName, importc: "PQclear".}
-proc pqfreemem*(p: pointer){.cdecl, dynlib: dllName, importc: "PQfreemem".}
-proc pqmakeEmptyPGresult*(conn: PPGconn, status: ExecStatusType): PPGresult{.
-    cdecl, dynlib: dllName, importc: "PQmakeEmptyPGresult".}
-proc pqescapeString*(till, `from`: cstring, len: int): int{.cdecl,
-    dynlib: dllName, importc: "PQescapeString".}
-proc pqescapeBytea*(bintext: cstring, binlen: int, bytealen: var int): cstring{.
-    cdecl, dynlib: dllName, importc: "PQescapeBytea".}
-proc pqunescapeBytea*(strtext: cstring, retbuflen: var int): cstring{.cdecl,
-    dynlib: dllName, importc: "PQunescapeBytea".}
-proc pqprint*(fout: File, res: PPGresult, ps: PPQprintOpt){.cdecl,
-    dynlib: dllName, importc: "PQprint".}
-proc pqdisplayTuples*(res: PPGresult, fp: File, fillAlign: int32,
-                      fieldSep: cstring, printHeader: int32, quiet: int32){.
-    cdecl, dynlib: dllName, importc: "PQdisplayTuples".}
-proc pqprintTuples*(res: PPGresult, fout: File, printAttName: int32,
-                    terseOutput: int32, width: int32){.cdecl, dynlib: dllName,
-    importc: "PQprintTuples".}
-proc lo_open*(conn: PPGconn, lobjId: Oid, mode: int32): int32{.cdecl,
-    dynlib: dllName, importc: "lo_open".}
-proc lo_close*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName,
-    importc: "lo_close".}
-proc lo_read*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
-    cdecl, dynlib: dllName, importc: "lo_read".}
-proc lo_write*(conn: PPGconn, fd: int32, buf: cstring, length: int): int32{.
-    cdecl, dynlib: dllName, importc: "lo_write".}
-proc lo_lseek*(conn: PPGconn, fd: int32, offset: int32, whence: int32): int32{.
-    cdecl, dynlib: dllName, importc: "lo_lseek".}
-proc lo_creat*(conn: PPGconn, mode: int32): Oid{.cdecl, dynlib: dllName,
-    importc: "lo_creat".}
-proc lo_tell*(conn: PPGconn, fd: int32): int32{.cdecl, dynlib: dllName,
-    importc: "lo_tell".}
-proc lo_unlink*(conn: PPGconn, lobjId: Oid): int32{.cdecl, dynlib: dllName,
-    importc: "lo_unlink".}
-proc lo_import*(conn: PPGconn, filename: cstring): Oid{.cdecl, dynlib: dllName,
-    importc: "lo_import".}
-proc lo_export*(conn: PPGconn, lobjId: Oid, filename: cstring): int32{.cdecl,
-    dynlib: dllName, importc: "lo_export".}
-proc pqmblen*(s: cstring, encoding: int32): int32{.cdecl, dynlib: dllName,
-    importc: "PQmblen".}
-proc pqenv2encoding*(): int32{.cdecl, dynlib: dllName, importc: "PQenv2encoding".}
-proc pqsetdb(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME: cstring): PPGconn =
-  result = pqsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, "", "")
-
-when defined(nimHasStyleChecks):
-  {.pop.}
diff --git a/lib/wrappers/sqlite3.nim b/lib/wrappers/sqlite3.nim
deleted file mode 100644
index 71921c10b..000000000
--- a/lib/wrappers/sqlite3.nim
+++ /dev/null
@@ -1,392 +0,0 @@
-#
-#
-#            Nim's Runtime Library
-#        (c) Copyright 2012 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-when defined(nimHasStyleChecks):
-  {.push styleChecks: off.}
-
-when defined(windows):
-  when defined(nimOldDlls):
-    const Lib = "sqlite3.dll"
-  elif defined(cpu64):
-    const Lib = "sqlite3_64.dll"
-  else:
-    const Lib = "sqlite3_32.dll"
-elif defined(macosx):
-  const
-    Lib = "libsqlite3(|.0).dylib"
-else:
-  const
-    Lib = "libsqlite3.so(|.0)"
-
-when defined(staticSqlite):
-  {.pragma: mylib.}
-  {.compile("sqlite3.c", "-O3").}
-else:
-  {.pragma: mylib, dynlib: Lib.}
-
-const
-  SQLITE_INTEGER* = 1
-  SQLITE_FLOAT* = 2
-  SQLITE_BLOB* = 4
-  SQLITE_NULL* = 5
-  SQLITE_TEXT* = 3
-  SQLITE_UTF8* = 1
-  SQLITE_UTF16LE* = 2
-  SQLITE_UTF16BE* = 3         # Use native byte order
-  SQLITE_UTF16* = 4           # sqlite3_create_function only
-  SQLITE_ANY* = 5             #sqlite_exec return values
-  SQLITE_OK* = 0
-  SQLITE_ERROR* = 1           # SQL error or missing database
-  SQLITE_INTERNAL* = 2        # An internal logic error in SQLite
-  SQLITE_PERM* = 3            # Access permission denied
-  SQLITE_ABORT* = 4           # Callback routine requested an abort
-  SQLITE_BUSY* = 5            # The database file is locked
-  SQLITE_LOCKED* = 6          # A table in the database is locked
-  SQLITE_NOMEM* = 7           # A malloc() failed
-  SQLITE_READONLY* = 8        # Attempt to write a readonly database
-  SQLITE_INTERRUPT* = 9       # Operation terminated by sqlite3_interrupt()
-  SQLITE_IOERR* = 10          # Some kind of disk I/O error occurred
-  SQLITE_CORRUPT* = 11        # The database disk image is malformed
-  SQLITE_NOTFOUND* = 12       # (Internal Only) Table or record not found
-  SQLITE_FULL* = 13           # Insertion failed because database is full
-  SQLITE_CANTOPEN* = 14       # Unable to open the database file
-  SQLITE_PROTOCOL* = 15       # Database lock protocol error
-  SQLITE_EMPTY* = 16          # Database is empty
-  SQLITE_SCHEMA* = 17         # The database schema changed
-  SQLITE_TOOBIG* = 18         # Too much data for one row of a table
-  SQLITE_CONSTRAINT* = 19     # Abort due to constraint violation
-  SQLITE_MISMATCH* = 20       # Data type mismatch
-  SQLITE_MISUSE* = 21         # Library used incorrectly
-  SQLITE_NOLFS* = 22          # Uses OS features not supported on host
-  SQLITE_AUTH* = 23           # Authorization denied
-  SQLITE_FORMAT* = 24         # Auxiliary database format error
-  SQLITE_RANGE* = 25          # 2nd parameter to sqlite3_bind out of range
-  SQLITE_NOTADB* = 26         # File opened that is not a database file
-  SQLITE_ROW* = 100           # sqlite3_step() has another row ready
-  SQLITE_DONE* = 101          # sqlite3_step() has finished executing
-  SQLITE_COPY* = 0
-  SQLITE_CREATE_INDEX* = 1
-  SQLITE_CREATE_TABLE* = 2
-  SQLITE_CREATE_TEMP_INDEX* = 3
-  SQLITE_CREATE_TEMP_TABLE* = 4
-  SQLITE_CREATE_TEMP_TRIGGER* = 5
-  SQLITE_CREATE_TEMP_VIEW* = 6
-  SQLITE_CREATE_TRIGGER* = 7
-  SQLITE_CREATE_VIEW* = 8
-  SQLITE_DELETE* = 9
-  SQLITE_DROP_INDEX* = 10
-  SQLITE_DROP_TABLE* = 11
-  SQLITE_DROP_TEMP_INDEX* = 12
-  SQLITE_DROP_TEMP_TABLE* = 13
-  SQLITE_DROP_TEMP_TRIGGER* = 14
-  SQLITE_DROP_TEMP_VIEW* = 15
-  SQLITE_DROP_TRIGGER* = 16
-  SQLITE_DROP_VIEW* = 17
-  SQLITE_INSERT* = 18
-  SQLITE_PRAGMA* = 19
-  SQLITE_READ* = 20
-  SQLITE_SELECT* = 21
-  SQLITE_TRANSACTION* = 22
-  SQLITE_UPDATE* = 23
-  SQLITE_ATTACH* = 24
-  SQLITE_DETACH* = 25
-  SQLITE_ALTER_TABLE* = 26
-  SQLITE_REINDEX* = 27
-  SQLITE_DENY* = 1
-  SQLITE_IGNORE* = 2          # Original from sqlite3.h:
-                              #define SQLITE_STATIC      ((void(*)(void *))0)
-                              #define SQLITE_TRANSIENT   ((void(*)(void *))-1)
-  SQLITE_DETERMINISTIC* = 0x800
-
-type
-  Sqlite3 {.pure, final.} = object
-  PSqlite3* = ptr Sqlite3
-  PPSqlite3* = ptr PSqlite3
-  Sqlite3_Backup {.pure, final.} = object
-  PSqlite3_Backup* = ptr Sqlite3_Backup
-  PPSqlite3_Backup* = ptr PSqlite3_Backup
-  Context{.pure, final.} = object
-  Pcontext* = ptr Context
-  TStmt{.pure, final.} = object
-  PStmt* = ptr TStmt
-  Value{.pure, final.} = object
-  PValue* = ptr Value
-  PValueArg* = array[0..127, PValue]
-
-  Callback* = proc (para1: pointer, para2: int32, para3,
-                     para4: cstringArray): int32{.cdecl.}
-  Tbind_destructor_func* = proc (para1: pointer){.cdecl, locks: 0, tags: [], gcsafe.}
-  Create_function_step_func* = proc (para1: Pcontext, para2: int32,
-                                      para3: PValueArg){.cdecl.}
-  Create_function_func_func* = proc (para1: Pcontext, para2: int32,
-                                      para3: PValueArg){.cdecl.}
-  Create_function_final_func* = proc (para1: Pcontext){.cdecl.}
-  Result_func* = proc (para1: pointer){.cdecl.}
-  Create_collation_func* = proc (para1: pointer, para2: int32, para3: pointer,
-                                  para4: int32, para5: pointer): int32{.cdecl.}
-  Collation_needed_func* = proc (para1: pointer, para2: PSqlite3, eTextRep: int32,
-                                  para4: cstring){.cdecl.}
-
-const
-  SQLITE_STATIC* = nil
-  SQLITE_TRANSIENT* = cast[Tbind_destructor_func](-1)
-
-proc close*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_close".}
-proc exec*(para1: PSqlite3, sql: cstring, para3: Callback, para4: pointer,
-           errmsg: var cstring): int32{.cdecl, mylib,
-                                        importc: "sqlite3_exec".}
-proc last_insert_rowid*(para1: PSqlite3): int64{.cdecl, mylib,
-    importc: "sqlite3_last_insert_rowid".}
-proc changes*(para1: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_changes".}
-proc total_changes*(para1: PSqlite3): int32{.cdecl, mylib,
-                                      importc: "sqlite3_total_changes".}
-proc interrupt*(para1: PSqlite3){.cdecl, mylib, importc: "sqlite3_interrupt".}
-proc complete*(sql: cstring): int32{.cdecl, mylib,
-                                     importc: "sqlite3_complete".}
-proc complete16*(sql: pointer): int32{.cdecl, mylib,
-                                       importc: "sqlite3_complete16".}
-proc busy_handler*(para1: PSqlite3,
-                   para2: proc (para1: pointer, para2: int32): int32{.cdecl.},
-                   para3: pointer): int32{.cdecl, mylib,
-    importc: "sqlite3_busy_handler".}
-proc busy_timeout*(para1: PSqlite3, ms: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_busy_timeout".}
-proc get_table*(para1: PSqlite3, sql: cstring, resultp: var cstringArray,
-                nrow, ncolumn: var cint, errmsg: ptr cstring): int32{.cdecl,
-    mylib, importc: "sqlite3_get_table".}
-proc free_table*(result: cstringArray){.cdecl, mylib,
-                                        importc: "sqlite3_free_table".}
-  # Todo: see how translate sqlite3_mprintf, sqlite3_vmprintf, sqlite3_snprintf
-  # function sqlite3_mprintf(_para1:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_mprintf';
-proc mprintf*(para1: cstring): cstring{.cdecl, varargs, mylib,
-                                        importc: "sqlite3_mprintf".}
-  #function sqlite3_vmprintf(_para1:Pchar; _para2:va_list):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_vmprintf';
-proc free*(z: cstring){.cdecl, mylib, importc: "sqlite3_free".}
-  #function sqlite3_snprintf(_para1:longint; _para2:Pchar; _para3:Pchar; args:array of const):Pchar;cdecl; external Sqlite3Lib name 'sqlite3_snprintf';
-proc snprintf*(para1: int32, para2: cstring, para3: cstring): cstring{.cdecl,
-    mylib, varargs, importc: "sqlite3_snprintf".}
-proc set_authorizer*(para1: PSqlite3, xAuth: proc (para1: pointer, para2: int32,
-    para3: cstring, para4: cstring, para5: cstring, para6: cstring): int32{.
-    cdecl.}, pUserData: pointer): int32{.cdecl, mylib,
-    importc: "sqlite3_set_authorizer".}
-proc trace*(para1: PSqlite3, xTrace: proc (para1: pointer, para2: cstring){.cdecl.},
-            para3: pointer): pointer{.cdecl, mylib,
-                                      importc: "sqlite3_trace".}
-proc progress_handler*(para1: PSqlite3, para2: int32,
-                       para3: proc (para1: pointer): int32{.cdecl.},
-                       para4: pointer){.cdecl, mylib,
-                                        importc: "sqlite3_progress_handler".}
-proc commit_hook*(para1: PSqlite3, para2: proc (para1: pointer): int32{.cdecl.},
-                  para3: pointer): pointer{.cdecl, mylib,
-    importc: "sqlite3_commit_hook".}
-proc open*(filename: cstring, ppDb: var PSqlite3): int32{.cdecl, mylib,
-    importc: "sqlite3_open".}
-proc open16*(filename: pointer, ppDb: var PSqlite3): int32{.cdecl, mylib,
-    importc: "sqlite3_open16".}
-proc errcode*(db: PSqlite3): int32{.cdecl, mylib, importc: "sqlite3_errcode".}
-proc errmsg*(para1: PSqlite3): cstring{.cdecl, mylib, importc: "sqlite3_errmsg".}
-proc errmsg16*(para1: PSqlite3): pointer{.cdecl, mylib,
-                                   importc: "sqlite3_errmsg16".}
-proc prepare*(db: PSqlite3, zSql: cstring, nBytes: int32, ppStmt: var PStmt,
-              pzTail: ptr cstring): int32{.cdecl, mylib,
-    importc: "sqlite3_prepare".}
-
-proc prepare_v2*(db: PSqlite3, zSql: cstring, nByte: cint, ppStmt: var PStmt,
-                pzTail: ptr cstring): cint {.
-                importc: "sqlite3_prepare_v2", cdecl, mylib.}
-
-proc prepare16*(db: PSqlite3, zSql: pointer, nBytes: int32, ppStmt: var PStmt,
-                pzTail: var pointer): int32{.cdecl, mylib,
-    importc: "sqlite3_prepare16".}
-proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32,
-                para5: Tbind_destructor_func): int32{.cdecl, mylib,
-    importc: "sqlite3_bind_blob".}
-proc bind_double*(para1: PStmt, para2: int32, para3: float64): int32{.cdecl,
-    mylib, importc: "sqlite3_bind_double".}
-proc bind_int*(para1: PStmt, para2: int32, para3: int32): int32{.cdecl,
-    mylib, importc: "sqlite3_bind_int".}
-proc bind_int64*(para1: PStmt, para2: int32, para3: int64): int32{.cdecl,
-    mylib, importc: "sqlite3_bind_int64".}
-proc bind_null*(para1: PStmt, para2: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_bind_null".}
-proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32,
-                para5: Tbind_destructor_func): int32{.cdecl, mylib,
-    importc: "sqlite3_bind_text".}
-proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32,
-                  para5: Tbind_destructor_func): int32{.cdecl, mylib,
-    importc: "sqlite3_bind_text16".}
-  #function sqlite3_bind_value(_para1:Psqlite3_stmt; _para2:longint; _para3:Psqlite3_value):longint;cdecl; external Sqlite3Lib name 'sqlite3_bind_value';
-  #These overloaded functions were introduced to allow the use of SQLITE_STATIC and SQLITE_TRANSIENT
-  #It's the c world man ;-)
-proc bind_blob*(para1: PStmt, para2: int32, para3: pointer, n: int32,
-                para5: int32): int32{.cdecl, mylib,
-                                      importc: "sqlite3_bind_blob".}
-proc bind_text*(para1: PStmt, para2: int32, para3: cstring, n: int32,
-                para5: int32): int32{.cdecl, mylib,
-                                      importc: "sqlite3_bind_text".}
-proc bind_text16*(para1: PStmt, para2: int32, para3: pointer, para4: int32,
-                  para5: int32): int32{.cdecl, mylib,
-                                        importc: "sqlite3_bind_text16".}
-proc bind_parameter_count*(para1: PStmt): int32{.cdecl, mylib,
-    importc: "sqlite3_bind_parameter_count".}
-proc bind_parameter_name*(para1: PStmt, para2: int32): cstring{.cdecl,
-    mylib, importc: "sqlite3_bind_parameter_name".}
-proc bind_parameter_index*(para1: PStmt, zName: cstring): int32{.cdecl,
-    mylib, importc: "sqlite3_bind_parameter_index".}
-proc clear_bindings*(para1: PStmt): int32 {.cdecl,
-    mylib, importc: "sqlite3_clear_bindings".}
-proc column_count*(PStmt: PStmt): int32{.cdecl, mylib,
-    importc: "sqlite3_column_count".}
-proc column_name*(para1: PStmt, para2: int32): cstring{.cdecl, mylib,
-    importc: "sqlite3_column_name".}
-proc column_table_name*(para1: PStmt; para2: int32): cstring{.cdecl, mylib,
-    importc: "sqlite3_column_table_name".}
-proc column_name16*(para1: PStmt, para2: int32): pointer{.cdecl, mylib,
-    importc: "sqlite3_column_name16".}
-proc column_decltype*(para1: PStmt, i: int32): cstring{.cdecl, mylib,
-    importc: "sqlite3_column_decltype".}
-proc column_decltype16*(para1: PStmt, para2: int32): pointer{.cdecl,
-    mylib, importc: "sqlite3_column_decltype16".}
-proc step*(para1: PStmt): int32{.cdecl, mylib, importc: "sqlite3_step".}
-proc data_count*(PStmt: PStmt): int32{.cdecl, mylib,
-                                       importc: "sqlite3_data_count".}
-proc column_blob*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib,
-    importc: "sqlite3_column_blob".}
-proc column_bytes*(para1: PStmt, iCol: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_column_bytes".}
-proc column_bytes16*(para1: PStmt, iCol: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_column_bytes16".}
-proc column_double*(para1: PStmt, iCol: int32): float64{.cdecl, mylib,
-    importc: "sqlite3_column_double".}
-proc column_int*(para1: PStmt, iCol: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_column_int".}
-proc column_int64*(para1: PStmt, iCol: int32): int64{.cdecl, mylib,
-    importc: "sqlite3_column_int64".}
-proc column_text*(para1: PStmt, iCol: int32): cstring{.cdecl, mylib,
-    importc: "sqlite3_column_text".}
-proc column_text16*(para1: PStmt, iCol: int32): pointer{.cdecl, mylib,
-    importc: "sqlite3_column_text16".}
-proc column_type*(para1: PStmt, iCol: int32): int32{.cdecl, mylib,
-    importc: "sqlite3_column_type".}
-proc finalize*(PStmt: PStmt): int32{.cdecl, mylib,
-                                     importc: "sqlite3_finalize".}
-proc reset*(PStmt: PStmt): int32{.cdecl, mylib, importc: "sqlite3_reset".}
-proc create_function*(para1: PSqlite3, zFunctionName: cstring, nArg: int32,
-                      eTextRep: int32, para5: pointer,
-                      xFunc: Create_function_func_func,
-                      xStep: Create_function_step_func,
-                      xFinal: Create_function_final_func): int32{.cdecl,
-    mylib, importc: "sqlite3_create_function".}
-proc create_function16*(para1: PSqlite3, zFunctionName: pointer, nArg: int32,
-                        eTextRep: int32, para5: pointer,
-                        xFunc: Create_function_func_func,
-                        xStep: Create_function_step_func,
-                        xFinal: Create_function_final_func): int32{.cdecl,
-    mylib, importc: "sqlite3_create_function16".}
-proc aggregate_count*(para1: Pcontext): int32{.cdecl, mylib,
-    importc: "sqlite3_aggregate_count".}
-proc value_blob*(para1: PValue): pointer{.cdecl, mylib,
-    importc: "sqlite3_value_blob".}
-proc value_bytes*(para1: PValue): int32{.cdecl, mylib,
-    importc: "sqlite3_value_bytes".}
-proc value_bytes16*(para1: PValue): int32{.cdecl, mylib,
-    importc: "sqlite3_value_bytes16".}
-proc value_double*(para1: PValue): float64{.cdecl, mylib,
-    importc: "sqlite3_value_double".}
-proc value_int*(para1: PValue): int32{.cdecl, mylib,
-                                       importc: "sqlite3_value_int".}
-proc value_int64*(para1: PValue): int64{.cdecl, mylib,
-    importc: "sqlite3_value_int64".}
-proc value_text*(para1: PValue): cstring{.cdecl, mylib,
-    importc: "sqlite3_value_text".}
-proc value_text16*(para1: PValue): pointer{.cdecl, mylib,
-    importc: "sqlite3_value_text16".}
-proc value_text16le*(para1: PValue): pointer{.cdecl, mylib,
-    importc: "sqlite3_value_text16le".}
-proc value_text16be*(para1: PValue): pointer{.cdecl, mylib,
-    importc: "sqlite3_value_text16be".}
-proc value_type*(para1: PValue): int32{.cdecl, mylib,
-                                        importc: "sqlite3_value_type".}
-proc aggregate_context*(para1: Pcontext, nBytes: int32): pointer{.cdecl,
-    mylib, importc: "sqlite3_aggregate_context".}
-proc user_data*(para1: Pcontext): pointer{.cdecl, mylib,
-    importc: "sqlite3_user_data".}
-proc get_auxdata*(para1: Pcontext, para2: int32): pointer{.cdecl, mylib,
-    importc: "sqlite3_get_auxdata".}
-proc set_auxdata*(para1: Pcontext, para2: int32, para3: pointer,
-                  para4: proc (para1: pointer){.cdecl.}){.cdecl, mylib,
-    importc: "sqlite3_set_auxdata".}
-proc result_blob*(para1: Pcontext, para2: pointer, para3: int32,
-                  para4: Result_func){.cdecl, mylib,
-                                        importc: "sqlite3_result_blob".}
-proc result_double*(para1: Pcontext, para2: float64){.cdecl, mylib,
-    importc: "sqlite3_result_double".}
-proc result_error*(para1: Pcontext, para2: cstring, para3: int32){.cdecl,
-    mylib, importc: "sqlite3_result_error".}
-proc result_error16*(para1: Pcontext, para2: pointer, para3: int32){.cdecl,
-    mylib, importc: "sqlite3_result_error16".}
-proc result_int*(para1: Pcontext, para2: int32){.cdecl, mylib,
-    importc: "sqlite3_result_int".}
-proc result_int64*(para1: Pcontext, para2: int64){.cdecl, mylib,
-    importc: "sqlite3_result_int64".}
-proc result_null*(para1: Pcontext){.cdecl, mylib,
-                                    importc: "sqlite3_result_null".}
-proc result_text*(para1: Pcontext, para2: cstring, para3: int32,
-                  para4: Result_func){.cdecl, mylib,
-                                        importc: "sqlite3_result_text".}
-proc result_text16*(para1: Pcontext, para2: pointer, para3: int32,
-                    para4: Result_func){.cdecl, mylib,
-    importc: "sqlite3_result_text16".}
-proc result_text16le*(para1: Pcontext, para2: pointer, para3: int32,
-                      para4: Result_func){.cdecl, mylib,
-    importc: "sqlite3_result_text16le".}
-proc result_text16be*(para1: Pcontext, para2: pointer, para3: int32,
-                      para4: Result_func){.cdecl, mylib,
-    importc: "sqlite3_result_text16be".}
-proc result_value*(para1: Pcontext, para2: PValue){.cdecl, mylib,
-    importc: "sqlite3_result_value".}
-proc create_collation*(para1: PSqlite3, zName: cstring, eTextRep: int32,
-                       para4: pointer, xCompare: Create_collation_func): int32{.
-    cdecl, mylib, importc: "sqlite3_create_collation".}
-proc create_collation16*(para1: PSqlite3, zName: cstring, eTextRep: int32,
-                         para4: pointer, xCompare: Create_collation_func): int32{.
-    cdecl, mylib, importc: "sqlite3_create_collation16".}
-proc collation_needed*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
-    cdecl, mylib, importc: "sqlite3_collation_needed".}
-proc collation_needed16*(para1: PSqlite3, para2: pointer, para3: Collation_needed_func): int32{.
-    cdecl, mylib, importc: "sqlite3_collation_needed16".}
-proc libversion*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".}
-  #Alias for allowing better code portability (win32 is not working with external variables)
-proc version*(): cstring{.cdecl, mylib, importc: "sqlite3_libversion".}
-  # Not published functions
-proc libversion_number*(): int32{.cdecl, mylib,
-                                  importc: "sqlite3_libversion_number".}
-
-proc backup_init*(pDest: PSqlite3, zDestName: cstring, pSource: PSqlite3, zSourceName: cstring): PSqlite3_Backup {.
-    cdecl, mylib, importc: "sqlite3_backup_init".}
-
-proc backup_step*(pBackup: PSqlite3_Backup, nPage: int32): int32 {.cdecl, mylib, importc: "sqlite3_backup_step".}
-
-proc backup_finish*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_finish".}
-
-proc backup_pagecount*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_pagecount".}
-
-proc backup_remaining*(pBackup: PSqlite3_Backup): int32 {.cdecl, mylib, importc: "sqlite3_backup_remaining".}
-
-proc sqlite3_sleep*(t: int64): int64 {.cdecl, mylib, importc: "sqlite3_sleep".}
-
-  #function sqlite3_key(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_key';
-  #function sqlite3_rekey(db:Psqlite3; pKey:pointer; nKey:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_rekey';
-  #function sqlite3_sleep(_para1:longint):longint;cdecl; external Sqlite3Lib name 'sqlite3_sleep';
-  #function sqlite3_expired(_para1:Psqlite3_stmt):longint;cdecl; external Sqlite3Lib name 'sqlite3_expired';
-  #function sqlite3_global_recover:longint;cdecl; external Sqlite3Lib name 'sqlite3_global_recover';
-# implementation
-
-when defined(nimHasStyleChecks):
-  {.pop.}
diff --git a/nim.nimble b/nim.nimble
index a629c0e04..bf195b0fa 100644
--- a/nim.nimble
+++ b/nim.nimble
@@ -1,14 +1,17 @@
-version = system.NimVersion
+include "lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
 author = "Andreas Rumpf"
-description = "Compiler package providing the compiler sources as a library."
+description = "Nim package providing the compiler binary"
 license = "MIT"
 
 bin = @["compiler/nim", "nimsuggest/nimsuggest"]
 skipFiles = @["azure-pipelines.yml" , "build_all.bat" , "build_all.sh" , "build_nimble.bat" , "build_nimble.sh" , "changelog.md" , "koch.nim.cfg" , "nimblemeta.json" , "readme.md" , "security.md" ]
-skipDirs = @["build" , "changelogs" , "ci" , "csources_v1" , "drnim" , "nimdoc", "testament"]
+skipDirs = @["build" , "changelogs" , "ci" , "csources_v2" , "drnim" , "nimdoc", "testament"]
 
 before install:
   when defined(windows):
-    exec "./build_all.bat"
+    if not "bin\nim.exe".fileExists:
+      exec "build_all.bat"
   else:
-    exec "./build_all.sh"
+    if not "bin/nim".fileExists:
+      exec "./build_all.sh"
diff --git a/nimdoc/extlinks/project/doc/manual.md b/nimdoc/extlinks/project/doc/manual.md
new file mode 100644
index 000000000..d44b5ca39
--- /dev/null
+++ b/nimdoc/extlinks/project/doc/manual.md
@@ -0,0 +1,17 @@
+===================
+Nothing User Manual
+===================
+
+.. importdoc:: ../main.nim, .. / sub / submodule.nim, ../../util.nim
+
+First section
+=============
+
+Second *section* &
+==================
+
+Ref. [`</a>`] or [submoduleInt] from [module nimdoc/extlinks/project/sub/submodule].
+
+Ref. [proc mainfunction*(): int].
+
+Ref. [utilfunction(x: int)].
diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html
new file mode 100644
index 000000000..32dab9216
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/_._/util.html
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/util</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/util</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="../theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">utilfunction
+  <li><a class="reference" href="#utilfunction%2Cint" title="utilfunction(x: int): int">utilfunction(x: int): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"></p>
+    <div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="utilfunction-procs-all">
+  <div id="utilfunction,int">
+  <dt><pre><span class="Keyword">proc</span> <a href="#utilfunction%2Cint"><span class="Identifier">utilfunction</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/_._/util.idx b/nimdoc/extlinks/project/expected/_._/util.idx
new file mode 100644
index 000000000..d83d8c97d
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/_._/util.idx
@@ -0,0 +1,2 @@
+nimTitle	util	_._/util.html	module nimdoc/extlinks/util		0
+nim	utilfunction	_._/util.html#utilfunction,int	proc utilfunction(x: int): int		1
diff --git a/nimdoc/extlinks/project/expected/doc/manual.html b/nimdoc/extlinks/project/expected/doc/manual.html
new file mode 100644
index 000000000..2946f803a
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/doc/manual.html
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Nothing User Manual</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Nothing User Manual</h1>
+      
+<h1 id="first-section">First section</h1>
+<h1 id="second-section-amp">Second <em>section</em> &amp;</h1><p>Ref. <a class="reference external" href="../sub/submodule.html#&lt;/a&gt;,int,int">submodule: `&lt;/a&gt;`</a> or <a class="reference external" href="../sub/submodule.html#submoduleInt">submodule: submoduleInt</a> from <a class="reference external" href="../sub/submodule.html">module nimdoc/extlinks/project/sub/submodule</a>.</p>
+<p>Ref. <a class="reference external" href="../main.html#mainfunction">main: proc mainfunction*(): int</a>.</p>
+<p>Ref. <a class="reference external" href="../_._/util.html#utilfunction,int">util: utilfunction(x: int)</a>. </p>
+
+
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/doc/manual.idx b/nimdoc/extlinks/project/expected/doc/manual.idx
new file mode 100644
index 000000000..158a758f0
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/doc/manual.idx
@@ -0,0 +1,3 @@
+markupTitle	Nothing User Manual	doc/manual.html	Nothing User Manual		0
+heading	First section	doc/manual.html#first-section	 First section		0
+heading	Second section &	doc/manual.html#second-section-amp	 Second <em>section</em> &amp;		0
diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html
new file mode 100644
index 000000000..1e7c9c126
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/main.html
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/project/main</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/project/main</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li><a class="reference" id="my-heading_toc" href="#my-heading">my heading</a></li>
+<li>
+  <a class="reference reference-toplevel" href="#6" id="56">Imports</a>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#A" title="A = object">A</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section">mainfunction
+  <li><a class="reference" href="#mainfunction" title="mainfunction(): int">mainfunction(): int</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc">
+<h1><a class="toc-backref" id="my-heading" href="#my-heading">my heading</a></h1><p>See also <a class="reference external" href="_._/util.html">module nimdoc/extlinks/util</a> or <a class="reference external" href="sub/submodule.html">nimdoc/extlinks/project/sub/submodule module</a>.</p>
+<p>Ref. <a class="reference external" href="sub/submodule.html#&lt;/a&gt;,int,int">submodule: `&lt;/a&gt;` proc</a>.</p>
+<p>Ref. <a class="reference external" href="doc/manual.html#first-section">Nothing User Manual: First section</a> or <a class="reference external" href="doc/manual.html#second-section-amp">Nothing User Manual: Second section &amp;</a> from <a class="reference external" href="doc/manual.html">Nothing User Manual</a>.</p>
+</p>
+    <div class="section" id="6">
+  <h1><a class="toc-backref" href="#6">Imports</a></h1>
+  <dl class="item">
+    <a class="reference external" href="_._/util.html">../util</a>, <a class="reference external" href="sub/submodule.html">sub/submodule</a>
+  </dl>
+</div>
+<div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="A">
+  <dt><pre><a href="main.html#A"><span class="Identifier">A</span></a> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="mainfunction-procs-all">
+  <div id="mainfunction">
+  <dt><pre><span class="Keyword">proc</span> <a href="#mainfunction"><span class="Identifier">mainfunction</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/main.idx b/nimdoc/extlinks/project/expected/main.idx
new file mode 100644
index 000000000..d01f2e4c5
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/main.idx
@@ -0,0 +1,4 @@
+nimTitle	main	main.html	module nimdoc/extlinks/project/main		0
+nim	A	main.html#A	object A		17
+nim	mainfunction	main.html#mainfunction	proc mainfunction(): int		20
+heading	my heading	main.html#my-heading	 my heading		0
diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html
new file mode 100644
index 000000000..408ce2060
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/sub/submodule.html
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>nimdoc/extlinks/project/sub/submodule</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="../nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="../dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">nimdoc/extlinks/project/sub/submodule</h1>
+      <div class="row">
+  <div class="three columns">
+    <div class="theme-select-wrapper">
+      <label for="theme-select">Theme:&nbsp;</label>
+      <select id="theme-select" onchange="setTheme(this.value)">
+        <option value="auto">🌗 Match OS</option>
+        <option value="dark">🌑 Dark</option>
+        <option value="light">🌕 Light</option>
+      </select>
+    </div>
+    <div id="global-links">
+      <ul class="simple">
+        <li><a id="indexLink" href="../theindex.html">Index</a></li>
+      </ul>
+    </div>
+    <div id="searchInputDiv">
+      Search: <input type="search" id="searchInput" oninput="search()"/>
+    </div>
+    <div>
+      Group by:
+      <select onchange="groupBy(this.value)">
+        <option value="section">Section</option>
+        <option value="type">Type</option>
+      </select>
+    </div>
+    <ul class="simple simple-toc" id="toc-list">
+  <li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
+    <ul class="simple simple-toc-section">
+      <li><a class="reference" href="#submoduleInt" title="submoduleInt = distinct int">submoduleInt</a></li>
+
+    </ul>
+  </details>
+</li>
+<li>
+  <details open>
+    <summary><a class="reference reference-toplevel" href="#12" id="62">Procs</a></summary>
+    <ul class="simple simple-toc-section">
+      <ul class="simple nested-toc-section"></a>
+  <li><a class="reference" href="#%3C%2Fa%3E%2Cint%2Cint" title="`&lt;/a&gt;`(x, y: int): bool">`&lt;/a&gt;`(x, y: int): bool</a></li>
+
+</ul>
+
+    </ul>
+  </details>
+</li>
+
+</ul>
+
+  </div>
+  <div class="nine columns" id="content">
+    
+    <div id="tocRoot"></div>
+    
+    <p class="module-desc"><p>Ref. <a class="reference external" href="../main.html#A">main: object A</a> from <a class="reference external" href="../main.html">module nimdoc/extlinks/project/main</a>.</p>
+<p>Ref. <a class="reference external" href="../_._/util.html#utilfunction,int">util: utilfunction(x: int)</a>.</p>
+<p>Ref. <a class="reference external" href="../doc/manual.html#second-section-amp">Nothing User Manual: Second section &amp;</a> from <a class="reference external" href="../doc/manual.html">Nothing User Manual</a>.</p>
+</p>
+    <div class="section" id="7">
+  <h1><a class="toc-backref" href="#7">Types</a></h1>
+  <dl class="item">
+    <div id="submoduleInt">
+  <dt><pre><a href="submodule.html#submoduleInt"><span class="Identifier">submoduleInt</span></a> <span class="Other">=</span> <span class="Keyword">distinct</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+
+  </dl>
+</div>
+<div class="section" id="12">
+  <h1><a class="toc-backref" href="#12">Procs</a></h1>
+  <dl class="item">
+    <div id="&lt;/a&gt;-procs-all">
+  <div id="</a>,int,int">
+  <dt><pre><span class="Keyword">proc</span> <a href="#%3C%2Fa%3E%2Cint%2Cint"><span class="Identifier">`&lt;/a&gt;`</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">,</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">bool</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    Attempt to break HTML formatting.
+    
+  </dd>
+</div>
+
+</div>
+
+  </dl>
+</div>
+
+  </div>
+</div>
+
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/expected/sub/submodule.idx b/nimdoc/extlinks/project/expected/sub/submodule.idx
new file mode 100644
index 000000000..2b02c889e
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/sub/submodule.idx
@@ -0,0 +1,3 @@
+nimTitle	submodule	sub/submodule.html	module nimdoc/extlinks/project/sub/submodule		0
+nim	`</a>`	sub/submodule.html#</a>,int,int	proc `</a>`(x, y: int): bool		9
+nim	submoduleInt	sub/submodule.html#submoduleInt	type submoduleInt		13
diff --git a/nimdoc/extlinks/project/expected/theindex.html b/nimdoc/extlinks/project/expected/theindex.html
new file mode 100644
index 000000000..cf250edd1
--- /dev/null
+++ b/nimdoc/extlinks/project/expected/theindex.html
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<!--  This file is generated by Nim. -->
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Index</title>
+
+<!-- Google fonts -->
+<link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
+<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
+
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
+<!-- CSS -->
+<link rel="stylesheet" type="text/css" href="nimdoc.out.css">
+
+<!-- JS -->
+<script type="text/javascript" src="dochack.js"></script>
+</head>
+<body>
+  <div class="document" id="documentId">
+    <div class="container">
+      <h1 class="title">Index</h1>
+      Documents: <a href="doc/manual.html">Nothing User Manual</a>.<br/><p />Modules: <a href="_._/util.html">../util</a>, <a href="main.html">main</a>, <a href="sub/submodule.html">sub/submodule</a>.<br/><p /><h2>API symbols</h2>
+<dl><dt><a name="%60%26lt%3B/a%26gt%3B%60" href="#%60%26lt%3B/a%26gt%3B%60"><span>`&lt;/a&gt;`:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="submodule: proc `&lt;/a&gt;`(x, y: int): bool" href="sub/submodule.html#%3C/a%3E%2Cint%2Cint">submodule: proc `&lt;/a&gt;`(x, y: int): bool</a></li>
+          </ul></dd>
+<dt><a name="A" href="#A"><span>A:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="main: object A" href="main.html#A">main: object A</a></li>
+          </ul></dd>
+<dt><a name="mainfunction" href="#mainfunction"><span>mainfunction:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="main: proc mainfunction(): int" href="main.html#mainfunction">main: proc mainfunction(): int</a></li>
+          </ul></dd>
+<dt><a name="submoduleInt" href="#submoduleInt"><span>submoduleInt:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="submodule: type submoduleInt" href="sub/submodule.html#submoduleInt">submodule: type submoduleInt</a></li>
+          </ul></dd>
+<dt><a name="utilfunction" href="#utilfunction"><span>utilfunction:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="util: proc utilfunction(x: int): int" href="_._/util.html#utilfunction%2Cint">util: proc utilfunction(x: int): int</a></li>
+          </ul></dd>
+</dl>
+      <div class="twelve-columns footer">
+        <span class="nim-sprite"></span>
+        <br>
+        <small style="color: var(--hint);">Made with Nim. Generated: 1970-01-02 03:46:40 UTC</small>
+      </div>
+    </div>
+  </div>
+  
+</body>
+</html>
diff --git a/nimdoc/extlinks/project/main.nim b/nimdoc/extlinks/project/main.nim
new file mode 100644
index 000000000..36b778af6
--- /dev/null
+++ b/nimdoc/extlinks/project/main.nim
@@ -0,0 +1,23 @@
+## my heading
+## ==========
+##
+## .. importdoc:: sub/submodule.nim, ../util.nim, doc/manual.md
+##
+## .. See also [Second&&&] and particularly [first section] and [Second section &].
+## 
+## See also [module nimdoc/extlinks/util] or [nimdoc/extlinks/project/sub/submodule module].
+##
+## Ref. [`</a>` proc].
+##
+## Ref. [First section] or [Second section &] from [Nothing User Manual].
+
+
+import ../util, sub/submodule
+
+type A* = object
+  x: int
+
+proc mainfunction*(): int =
+  # just to suppress "not used" warnings:
+  if `</a>`(1, 2):
+    result = utilfunction(0)
diff --git a/nimdoc/extlinks/project/sub/submodule.nim b/nimdoc/extlinks/project/sub/submodule.nim
new file mode 100644
index 000000000..876e00684
--- /dev/null
+++ b/nimdoc/extlinks/project/sub/submodule.nim
@@ -0,0 +1,13 @@
+## .. importdoc:: ../../util.nim, ../main.nim, ../doc/manual.md
+##
+## Ref. [object A] from [module nimdoc/extlinks/project/main].
+##
+## Ref. [utilfunction(x: int)].
+##
+## Ref. [Second section &] from [Nothing User Manual].
+
+proc `</a>`*(x, y: int): bool =
+  ## Attempt to break HTML formatting.
+  result = x < y
+
+type submoduleInt* = distinct int
diff --git a/nimdoc/extlinks/util.nim b/nimdoc/extlinks/util.nim
new file mode 100644
index 000000000..f208f98c1
--- /dev/null
+++ b/nimdoc/extlinks/util.nim
@@ -0,0 +1,2 @@
+proc utilfunction*(x: int): int =
+  x + 42
diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html
index 9debd16ac..a26704133 100644
--- a/nimdoc/rst2html/expected/rst_examples.html
+++ b/nimdoc/rst2html/expected/rst_examples.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Not a Nim Manual</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -37,11 +37,11 @@
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="theindex.html">Index</a></li>
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim
index a0bdfca1e..be2b56c67 100644
--- a/nimdoc/rsttester.nim
+++ b/nimdoc/rsttester.nim
@@ -24,11 +24,13 @@ proc testRst2Html(fixup = false) =
     let sourceFile = expectedHtml.replace('\\', '/').replace("/expected/", "/source/").replace(".html", ".rst")
     exec("$1 rst2html $2" % [nimExe, sourceFile])
     let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/")
-    if readFile(expectedHtml) != readFile(producedHtml):
+    let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch
+    let producedFile = readFile(producedHtml).replace(versionCacheParam,"") #remove version cache param used for cache invalidation
+    if readFile(expectedHtml) != producedFile:
       echo diffFiles(expectedHtml, producedHtml).output
       inc failures
       if fixup:
-        copyFile(producedHtml, expectedHtml)
+        writeFile(expectedHtml, producedFile)
     else:
       echo "SUCCESS: files identical: ", producedHtml
     if failures == 0:
diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html
index 416541d4d..2cbf6ec0f 100644
--- a/nimdoc/test_doctype/expected/test_doctype.html
+++ b/nimdoc/test_doctype/expected/test_doctype.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>nimdoc/test_doctype/test_doctype</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -37,11 +37,11 @@
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="theindex.html">Index</a></li>
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
@@ -62,8 +62,7 @@
     <div id="tocRoot"></div>
     
     <p class="module-desc"><p>Check</p>
-<p><pre class="listing">
-<span class="Identifier">text</span></pre></p>
+<p><pre class="listing"><span class="Identifier">text</span></pre></p>
 
 <h1><a class="toc-backref" id="check" href="#check">Check</a></h1>
 <h1><a class="toc-backref" id="text" href="#text">text</a></h1></p>
diff --git a/nimdoc/test_out_index_dot_html/expected/foo.idx b/nimdoc/test_out_index_dot_html/expected/foo.idx
index a8dabb67e..ac76aa532 100644
--- a/nimdoc/test_out_index_dot_html/expected/foo.idx
+++ b/nimdoc/test_out_index_dot_html/expected/foo.idx
@@ -1 +1,2 @@
-foo	index.html#foo	foo: foo()	
+nimTitle	foo	index.html	module nimdoc/test_out_index_dot_html/foo		0
+nim	foo	index.html#foo	proc foo()		1
diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html
index 50a18bdae..4370f0df8 100644
--- a/nimdoc/test_out_index_dot_html/expected/index.html
+++ b/nimdoc/test_out_index_dot_html/expected/index.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>nimdoc/test_out_index_dot_html/foo</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -37,11 +37,11 @@
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="theindex.html">Index</a></li>
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
@@ -77,7 +77,7 @@
   <dl class="item">
     <div id="foo-procs-all">
   <div id="foo">
-  <dt><pre><span class="Keyword">proc</span> <a href="#foo"><span class="Identifier">foo</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#foo"><span class="Identifier">foo</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     I do foo
diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html
index 00c81189e..ca7c2d7af 100644
--- a/nimdoc/test_out_index_dot_html/expected/theindex.html
+++ b/nimdoc/test_out_index_dot_html/expected/theindex.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Index</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -28,7 +28,7 @@
       Modules: <a href="index.html">index</a>.<br/><p /><h2>API symbols</h2>
 <dl><dt><a name="foo" href="#foo"><span>foo:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="foo: foo()" href="index.html#foo">foo: foo()</a></li>
+          data-doc-search-tag="foo: proc foo()" href="index.html#foo">foo: proc foo()</a></li>
           </ul></dd>
 </dl>
       <div class="twelve-columns footer">
diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim
index ef82ae1b9..0c0be3699 100644
--- a/nimdoc/tester.nim
+++ b/nimdoc/tester.nim
@@ -12,11 +12,16 @@ var
 
 const
   baseDir = "nimdoc"
+let
+  baseDirAbs = getCurrentDir() / baseDir
 
 type
   NimSwitches = object
     doc: seq[string]
+    docStage2: seq[string]
     buildIndex: seq[string]
+    md2html: seq[string]
+    md2htmlStage2: seq[string]
 
 proc exec(cmd: string) =
   if execShellCmd(cmd) != 0:
@@ -25,6 +30,9 @@ proc exec(cmd: string) =
 proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) =
   let
     nimDocSwitches = switches.doc.join(" ")
+    nimDocStage2Switches = switches.docStage2.join(" ")
+    nimMd2HtmlSwitches = switches.md2html.join(" ")
+    nimMd2HtmlStage2Switches = switches.md2htmlStage2.join(" ")
     nimBuildIndexSwitches = switches.buildIndex.join(" ")
 
   putEnv("SOURCE_DATE_EPOCH", "100000")
@@ -32,21 +40,37 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) =
 
   if nimDocSwitches != "":
     exec("$1 doc $2" % [nimExe, nimDocSwitches])
+    echo("$1 doc $2" % [nimExe, nimDocSwitches])
+
+  if nimMd2HtmlSwitches != "":
+    exec("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches])
+    echo("$1 md2html $2" % [nimExe, nimMd2HtmlSwitches])
+
+  if nimDocStage2Switches != "":
+    exec("$1 doc $2" % [nimExe, nimDocStage2Switches])
+    echo("$1 doc $2" % [nimExe, nimDocStage2Switches])
+
+  if nimMd2HtmlStage2Switches != "":
+    exec("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches])
+    echo("$1 md2html $2" % [nimExe, nimMd2HtmlStage2Switches])
 
   if nimBuildIndexSwitches != "":
     exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches])
+    echo("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches])
 
   for expected in walkDirRec(prjDir / "expected/", checkDir=true):
+    let versionCacheParam = "?v=" & $NimMajor & "." & $NimMinor & "." & $NimPatch
     let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir])
     if not fileExists(produced):
       echo "FAILURE: files not found: ", produced
       inc failures
-    elif readFile(expected) != readFile(produced):
+    let producedFile = readFile(produced).replace(versionCacheParam,"") #remove version cache param used for cache invalidation
+    if readFile(expected) != producedFile:
       echo "FAILURE: files differ: ", produced
       echo diffFiles(expected, produced).output
       inc failures
       if fixup:
-        copyFile(produced, expected)
+        writeFile(expected, producedFile)
     else:
       echo "SUCCESS: files identical: ", produced
 
@@ -88,5 +112,46 @@ let
   test3Switches = NimSwitches(doc: @["$1/$2.nim" % [test3Dir, test3PrjName]])
 testNimDoc(test3Dir, test3DocsDir, test3Switches, fixup)
 
+
+# Test concise external links (RFC#125) that work with `.idx` files.
+# extlinks
+# ├── project
+# │   ├── main.nim
+# │   ├── manual.md
+# │   └── sub
+# │       └── submodule.nim
+# └── util.nim
+#
+# `main.nim` imports `submodule.nim` and `../utils.nim`.
+# `main.nim`, `submodule.nim`, `manual.md` do importdoc and reference each other.
+let
+  test4PrjName = "extlinks/project"
+  test4Dir = baseDir / test4PrjName
+  test4DirAbs = baseDirAbs / test4PrjName
+  test4MainModule = "main"
+  test4MarkupDoc = "doc" / "manual.md"
+  test4DocsDir = "htmldocs"
+  # 1st stage is with --index:only, 2nd is final
+  test4Switches = NimSwitches(
+      doc: @["--project",
+             "--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--index:only",
+             "$1/$2.nim" % [test4Dir, test4MainModule]],
+      md2html:
+             @["--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--docroot:$1" % [test4DirAbs],
+             "--index:only",
+             "$1/$2" % [test4Dir, test4MarkupDoc]],
+      docStage2:
+           @["--project",
+             "--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "$1/$2.nim" % [test4Dir, test4MainModule]],
+      md2htmlStage2:
+             @["--outdir:$1/$2" % [test4Dir, test4DocsDir],
+             "--docroot:$1" % [test4DirAbs],
+             "$1/$2" % [test4Dir, test4MarkupDoc]],
+  )
+testNimDoc(test4Dir, test4DocsDir, test4Switches, fixup)
+
 if failures > 0:
   quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" %  $failures
diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css
index f924f5a36..0c399e4c1 100644
--- a/nimdoc/testproject/expected/nimdoc.out.css
+++ b/nimdoc/testproject/expected/nimdoc.out.css
@@ -147,6 +147,15 @@ body {
   box-sizing: border-box;

   margin-left: 1%; }

 

+@media print {

+  #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {

+    display:none;

+  }

+  .columns {

+    width:100% !important;

+  }

+}

+

 .column:first-child, .columns:first-child {

   margin-left: 0; }

 

@@ -614,8 +623,8 @@ pre {
 

 table.line-nums-table {

   border-radius: 4px;

-  border: 1px solid #cccccc;

-  background-color: ghostwhite;

+  border: 1px solid var(--border);

+  background-color: var(--secondary-background);

   border-collapse: separate;

   margin-top: 15px;

   margin-bottom: 25px; }

@@ -651,6 +660,9 @@ table {
   border-collapse: collapse;

   border-color: var(--third-background);

   border-spacing: 0;

+}

+

+table:not(.line-nums-table) {

   font-size: 0.9em;

 }

 

@@ -669,7 +681,7 @@ table th.docinfo-name {
   text-align: right;

 }

 

-table tr:hover {

+table:not(.line-nums-table) tr:hover {

   background-color: var(--third-background); }

 

 

@@ -758,9 +770,16 @@ div.topic {
 

 div.search_results {

   background-color: var(--third-background);

-  margin: 3em;

+  margin: 3vh 5vw;

   padding: 1em;

-  border: 1px solid #4d4d4d; }

+  border: 1px solid #4d4d4d;

+  position: fixed;

+  top: 10px;

+  isolation: isolate;

+  max-width: calc(100vw - 6em);

+  z-index: 1;

+  max-height: calc(100vh - 6em);

+  overflow-y: scroll;}

 

 div#global-links ul {

   margin-left: 0;

diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
index d888a4609..6decf79a3 100644
--- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html
+++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>subdir/subdir_b/utils</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="../../nimdoc.out.css">
 
@@ -37,11 +37,11 @@
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="../../theindex.html">Index</a></li>
+        <li><a id="indexLink" href="../../theindex.html">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
@@ -63,8 +63,7 @@
   <details open>
     <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
     <ul class="simple simple-toc-section">
-      <li><a class="reference" href="#G" title="G[T] = object
-  val: T">G</a></li>
+      <li><a class="reference" href="#G" title="G[T] = object">G</a></li>
 <li><a class="reference" href="#SomeType" title="SomeType = enum
   enumValueA, enumValueB, enumValueC">SomeType</a></li>
 
@@ -111,6 +110,7 @@
   <li><a class="reference" href="#fn2" title="fn2()">fn2()</a></li>
 <li><a class="reference" href="#fn2%2Cint" title="fn2(x: int)">fn2(x: int)</a></li>
 <li><a class="reference" href="#fn2%2Cint%2Cfloat" title="fn2(x: int; y: float)">fn2(x: int; y: float)</a></li>
+<li><a class="reference" href="#fn2%2Cint%2Cfloat%2Cfloat" title="fn2(x: int; y: float; z: float)">fn2(x: int; y: float; z: float)</a></li>
 
 </ul>
 <ul class="simple nested-toc-section">fn3
@@ -220,7 +220,7 @@
 <ol class="simple"><li>Other case value</li>
 <li>Second case.</li>
 </ol>
-<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (3 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2( int )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
+<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (4 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2( int )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
 <p>Ref generics like this: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch(openArray[T], K, proc (T, K))</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
@@ -230,11 +230,11 @@
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binary search</a>.</p>
 <p>Note that <tt class="docutils literal"><span class="pre"><span class="Keyword">proc</span></span></tt> can be used in postfix form: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
-<p>Ref. type like <a class="reference internal nimdoc" title="type G" href="#G">G</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G</a> and <a class="reference internal nimdoc" title="type G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G*[T]</a>.</p>
+<p>Ref. type like <a class="reference internal nimdoc" title="object G" href="#G">G</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G</a> and <a class="reference internal nimdoc" title="object G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G*[T]</a>.</p>
 <p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a> </p>
 Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">[]</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a>Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">[]=</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>.Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>.Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>.Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>.Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>.Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.
 <h1><a class="toc-backref" id="pandoc-markdown" href="#pandoc-markdown">Pandoc Markdown</a></h1><p>Now repeat all the auto links of above in Pandoc Markdown Syntax.</p>
-<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (3 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2(  int  )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
+<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (4 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2(  int  )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
 <p>Ref generics like this: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch(openArray[T], K, proc (T, K))</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
@@ -244,11 +244,11 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binary search</a>.</p>
 <p>Note that <tt class="docutils literal"><span class="pre"><span class="Keyword">proc</span></span></tt> can be used in postfix form: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
-<p>Ref. type like <a class="reference internal nimdoc" title="type G" href="#G">G</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G</a> and <a class="reference internal nimdoc" title="type G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G*[T]</a>.</p>
+<p>Ref. type like <a class="reference internal nimdoc" title="object G" href="#G">G</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G</a> and <a class="reference internal nimdoc" title="object G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="object G" href="#G">type G*[T]</a>.</p>
 <p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a></p>
 <p>Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">`[]`</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a> Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>. Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>. Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>. Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>. Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>. Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.</p>
 
-<h2><a class="toc-backref" id="pandoc-markdown-link-name-syntax" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></h2><p>Pandoc Markdown has synax for changing text of links: Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">this proc</a> or <a class="reference internal nimdoc" title="type G" href="#G">another symbol</a>.</p>
+<h2><a class="toc-backref" id="pandoc-markdown-link-name-syntax" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></h2><p>Pandoc Markdown has synax for changing text of links: Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">this proc</a> or <a class="reference internal nimdoc" title="object G" href="#G">another symbol</a>.</p>
 
 <h2><a class="toc-backref" id="pandoc-markdown-symbols-documentation" href="#pandoc-markdown-symbols-documentation">Symbols documentation</a></h2><p>Let us repeat auto links from symbols section below:</p>
 <p>There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>. See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.</p>
@@ -257,9 +257,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   <h1><a class="toc-backref" href="#7">Types</a></h1>
   <dl class="item">
     <div id="G">
-  <dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span>
-  <span class="Identifier">val</span><span class="Other">:</span> <span class="Identifier">T</span>
-</pre></dt>
+  <dt><pre><a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span> <span class="Other">=</span> <span class="Keyword">object</span></pre></dt>
   <dd>
     
     
@@ -302,7 +300,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="'big-procs-all">
   <div id="'big,string">
-  <dt><pre><span class="Keyword">func</span> <a href="#%27big%2Cstring"><span class="Identifier">`'big`</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#%27big%2Cstring"><span class="Identifier">`'big`</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -347,7 +345,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="f-procs-all">
   <div id="f,G[int]">
-  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bint%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bint%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>
@@ -355,7 +353,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   </dd>
 </div>
 <div id="f,G[string]">
-  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bstring%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#f%2CG%5Bstring%5D"><span class="Identifier">f</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <a href="utils.html#G"><span class="Identifier">G</span></a><span class="Other">[</span><span class="Identifier">string</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.
@@ -377,7 +375,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn2-procs-all">
   <div id="fn2">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn2"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -385,7 +383,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   </dd>
 </div>
 <div id="fn2,int">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     fn2 comment
@@ -393,7 +391,15 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   </dd>
 </div>
 <div id="fn2,int,float">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint%2Cfloat"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint%2Cfloat"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="fn2,int,float,float">
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn2%2Cint%2Cfloat%2Cfloat"><span class="Identifier">fn2</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">;</span> <span class="Identifier">y</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">;</span> <span class="Identifier">z</span><span class="Other">:</span> <span class="Identifier">float</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -404,7 +410,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn3-procs-all">
   <div id="fn3">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn3"><span class="Identifier">fn3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn3"><span class="Identifier">fn3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -415,7 +421,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn4-procs-all">
   <div id="fn4">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn4"><span class="Identifier">fn4</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn4"><span class="Identifier">fn4</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -426,7 +432,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn5-procs-all">
   <div id="fn5">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn5"><span class="Identifier">fn5</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn5"><span class="Identifier">fn5</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -437,7 +443,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn6-procs-all">
   <div id="fn6">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn6"><span class="Identifier">fn6</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn6"><span class="Identifier">fn6</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -448,7 +454,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn7-procs-all">
   <div id="fn7">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn7"><span class="Identifier">fn7</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn7"><span class="Identifier">fn7</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -459,7 +465,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn8-procs-all">
   <div id="fn8">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fn8"><span class="Identifier">fn8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fn8"><span class="Identifier">fn8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">auto</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -470,7 +476,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn9-procs-all">
   <div id="fn9,int">
-  <dt><pre><span class="Keyword">func</span> <a href="#fn9%2Cint"><span class="Identifier">fn9</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#fn9%2Cint"><span class="Identifier">fn9</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -481,7 +487,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fn10-procs-all">
   <div id="fn10,int">
-  <dt><pre><span class="Keyword">func</span> <a href="#fn10%2Cint"><span class="Identifier">fn10</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#fn10%2Cint"><span class="Identifier">fn10</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     comment
@@ -492,7 +498,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="fN11-procs-all">
   <div id="fN11">
-  <dt><pre><span class="Keyword">func</span> <a href="#fN11"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#fN11"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -500,7 +506,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   </dd>
 </div>
 <div id="fN11,int">
-  <dt><pre><span class="Keyword">func</span> <a href="#fN11%2Cint"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#fN11%2Cint"><span class="Identifier">fN11</span></a><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -522,7 +528,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
 </div>
 <div id="someType-procs-all">
   <div id="someType_2">
-  <dt><pre><span class="Keyword">proc</span> <a href="#someType_2"><span class="Identifier">someType</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#someType_2"><span class="Identifier">someType</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     constructor.
@@ -539,7 +545,7 @@ Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href=
   <dl class="item">
     <div id="fooBar-iterators-all">
   <div id="fooBar.i,seq[SomeType]">
-  <dt><pre><span class="Keyword">iterator</span> <a href="#fooBar.i%2Cseq%5BSomeType%5D"><span class="Identifier">fooBar</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">iterator</span> <a href="#fooBar.i%2Cseq%5BSomeType%5D"><span class="Identifier">fooBar</span></a><span class="Other">(</span><span class="Identifier">a</span><span class="Other">:</span> <span class="Identifier">seq</span><span class="Other">[</span><a href="utils.html#SomeType"><span class="Identifier">SomeType</span></a><span class="Other">]</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx
index 007101b37..81b27bcb9 100644
--- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx
+++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx
@@ -1,41 +1,47 @@
-funWithGenerics	subdir/subdir_b/utils.html#funWithGenerics,T,U	utils: funWithGenerics[T, U: SomeFloat](a: T; b: U)	
-enumValueA	subdir/subdir_b/utils.html#enumValueA	SomeType.enumValueA	
-enumValueB	subdir/subdir_b/utils.html#enumValueB	SomeType.enumValueB	
-enumValueC	subdir/subdir_b/utils.html#enumValueC	SomeType.enumValueC	
-SomeType	subdir/subdir_b/utils.html#SomeType	utils: SomeType	
-G	subdir/subdir_b/utils.html#G	utils: G	
-someType	subdir/subdir_b/utils.html#someType_2	utils: someType(): SomeType	
-fn2	subdir/subdir_b/utils.html#fn2	utils: fn2()	
-fn2	subdir/subdir_b/utils.html#fn2,int	utils: fn2(x: int)	
-fn2	subdir/subdir_b/utils.html#fn2,int,float	utils: fn2(x: int; y: float)	
-binarySearch	subdir/subdir_b/utils.html#binarySearch,openArray[T],K,proc(T,K)	utils: binarySearch[T, K](a: openArray[T]; key: K;\n                   cmp: proc (x: T; y: K): int {.closure.}): int	
-fn3	subdir/subdir_b/utils.html#fn3	utils: fn3(): auto	
-fn4	subdir/subdir_b/utils.html#fn4	utils: fn4(): auto	
-fn5	subdir/subdir_b/utils.html#fn5	utils: fn5()	
-fn6	subdir/subdir_b/utils.html#fn6	utils: fn6()	
-fn7	subdir/subdir_b/utils.html#fn7	utils: fn7()	
-fn8	subdir/subdir_b/utils.html#fn8	utils: fn8(): auto	
-fn9	subdir/subdir_b/utils.html#fn9,int	utils: fn9(a: int): int	
-fn10	subdir/subdir_b/utils.html#fn10,int	utils: fn10(a: int): int	
-fN11	subdir/subdir_b/utils.html#fN11	utils: fN11()	
-fN11	subdir/subdir_b/utils.html#fN11,int	utils: fN11(x: int)	
-aEnum	subdir/subdir_b/utils.html#aEnum.t	utils: aEnum(): untyped	
-bEnum	subdir/subdir_b/utils.html#bEnum.t	utils: bEnum(): untyped	
-fromUtilsGen	subdir/subdir_b/utils.html#fromUtilsGen.t	utils: fromUtilsGen(): untyped	
-f	subdir/subdir_b/utils.html#f,G[int]	utils: f(x: G[int])	
-f	subdir/subdir_b/utils.html#f,G[string]	utils: f(x: G[string])	
-`[]`	subdir/subdir_b/utils.html#[],G[T]	utils: `[]`[T](x: G[T]): T	
-`[]=`	subdir/subdir_b/utils.html#[]=,G[T],int,T	utils: `[]=`[T](a: var G[T]; index: int; value: T)	
-`$`	subdir/subdir_b/utils.html#$,G[T]	utils: `$`[T](a: G[T]): string	
-`$`	subdir/subdir_b/utils.html#$,ref.SomeType	utils: `$`[T](a: ref SomeType): string	
-fooBar	subdir/subdir_b/utils.html#fooBar.i,seq[SomeType]	utils: fooBar(a: seq[SomeType]): int	
-fn	subdir/subdir_b/utils.html#fn	utils: fn[T; U, V: SomeFloat]()	
-`'big`	subdir/subdir_b/utils.html#'big,string	utils: `&apos;big`(a: string): SomeType	
-This is now a header	subdir/subdir_b/utils.html#this-is-now-a-header	 This is now a header	
-Next header	subdir/subdir_b/utils.html#this-is-now-a-header-next-header	  Next header	
-And so on	subdir/subdir_b/utils.html#next-header-and-so-on	   And so on	
-More headers	subdir/subdir_b/utils.html#more-headers	 More headers	
-Up to level 6	subdir/subdir_b/utils.html#more-headers-up-to-level-6	      Up to level 6	
-Pandoc Markdown	subdir/subdir_b/utils.html#pandoc-markdown	 Pandoc Markdown	
-Link name syntax	subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax	  Link name syntax	
-Symbols documentation	subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation	  Symbols documentation	
+nimTitle	utils	subdir/subdir_b/utils.html	module subdir/subdir_b/utils		0
+nim	funWithGenerics	subdir/subdir_b/utils.html#funWithGenerics,T,U	proc funWithGenerics[T, U: SomeFloat](a: T; b: U)		1
+nim	fn2	subdir/subdir_b/utils.html#fn2,int,float,float	proc fn2(x: int; y: float; z: float)		5
+nim	enumValueA	subdir/subdir_b/utils.html#enumValueA	SomeType.enumValueA		45
+nim	enumValueB	subdir/subdir_b/utils.html#enumValueB	SomeType.enumValueB		45
+nim	enumValueC	subdir/subdir_b/utils.html#enumValueC	SomeType.enumValueC		45
+nim	SomeType	subdir/subdir_b/utils.html#SomeType	enum SomeType		45
+nim	G	subdir/subdir_b/utils.html#G	object G		49
+nim	someType	subdir/subdir_b/utils.html#someType_2	proc someType(): SomeType		52
+nim	fn2	subdir/subdir_b/utils.html#fn2	proc fn2()		57
+nim	fn2	subdir/subdir_b/utils.html#fn2,int	proc fn2(x: int)		58
+nim	fn2	subdir/subdir_b/utils.html#fn2,int,float	proc fn2(x: int; y: float)		61
+nim	binarySearch	subdir/subdir_b/utils.html#binarySearch,openArray[T],K,proc(T,K)	proc binarySearch[T, K](a: openArray[T]; key: K;\n                   cmp: proc (x: T; y: K): int {.closure.}): int		63
+nim	fn3	subdir/subdir_b/utils.html#fn3	proc fn3(): auto		66
+nim	fn4	subdir/subdir_b/utils.html#fn4	proc fn4(): auto		67
+nim	fn5	subdir/subdir_b/utils.html#fn5	proc fn5()		68
+nim	fn6	subdir/subdir_b/utils.html#fn6	proc fn6()		70
+nim	fn7	subdir/subdir_b/utils.html#fn7	proc fn7()		72
+nim	fn8	subdir/subdir_b/utils.html#fn8	proc fn8(): auto		75
+nim	fn9	subdir/subdir_b/utils.html#fn9,int	proc fn9(a: int): int		78
+nim	fn10	subdir/subdir_b/utils.html#fn10,int	proc fn10(a: int): int		79
+nim	fN11	subdir/subdir_b/utils.html#fN11	proc fN11()		85
+nim	fN11	subdir/subdir_b/utils.html#fN11,int	proc fN11(x: int)		86
+nim	aEnum	subdir/subdir_b/utils.html#aEnum.t	template aEnum(): untyped		90
+nim	bEnum	subdir/subdir_b/utils.html#bEnum.t	template bEnum(): untyped		95
+nim	fromUtilsGen	subdir/subdir_b/utils.html#fromUtilsGen.t	template fromUtilsGen(): untyped		106
+nim	f	subdir/subdir_b/utils.html#f,G[int]	proc f(x: G[int])		130
+nim	f	subdir/subdir_b/utils.html#f,G[string]	proc f(x: G[string])		133
+nim	`[]`	subdir/subdir_b/utils.html#[],G[T]	proc `[]`[T](x: G[T]): T		140
+nim	`[]=`	subdir/subdir_b/utils.html#[]=,G[T],int,T	proc `[]=`[T](a: var G[T]; index: int; value: T)		144
+nim	`$`	subdir/subdir_b/utils.html#$,G[T]	proc `$`[T](a: G[T]): string		148
+nim	`$`	subdir/subdir_b/utils.html#$,ref.SomeType	proc `$`[T](a: ref SomeType): string		152
+nim	fooBar	subdir/subdir_b/utils.html#fooBar.i,seq[SomeType]	iterator fooBar(a: seq[SomeType]): int		156
+nim	fn	subdir/subdir_b/utils.html#fn	proc fn[T; U, V: SomeFloat]()		160
+nim	`'big`	subdir/subdir_b/utils.html#'big,string	proc `'big`(a: string): SomeType		164
+nimgrp	$	subdir/subdir_b/utils.html#$-procs-all	proc		148
+nimgrp	fn11	subdir/subdir_b/utils.html#fN11-procs-all	proc		85
+nimgrp	fn2	subdir/subdir_b/utils.html#fn2-procs-all	proc		5
+nimgrp	f	subdir/subdir_b/utils.html#f-procs-all	proc		130
+heading	This is now a header	subdir/subdir_b/utils.html#this-is-now-a-header	 This is now a header		0
+heading	Next header	subdir/subdir_b/utils.html#this-is-now-a-header-next-header	  Next header		0
+heading	And so on	subdir/subdir_b/utils.html#next-header-and-so-on	   And so on		0
+heading	More headers	subdir/subdir_b/utils.html#more-headers	 More headers		0
+heading	Up to level 6	subdir/subdir_b/utils.html#more-headers-up-to-level-6	      Up to level 6		0
+heading	Pandoc Markdown	subdir/subdir_b/utils.html#pandoc-markdown	 Pandoc Markdown		0
+heading	Link name syntax	subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax	  Link name syntax		0
+heading	Symbols documentation	subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation	  Symbols documentation		0
diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html
index ea0269710..43a72d99d 100644
--- a/nimdoc/testproject/expected/testproject.html
+++ b/nimdoc/testproject/expected/testproject.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>testproject</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -37,11 +37,11 @@
     </div>
     <div id="global-links">
       <ul class="simple">
-        <li><a href="theindex.html">Index</a></li>
+        <li><a id="indexLink" href="theindex.html">Index</a></li>
       </ul>
     </div>
     <div id="searchInputDiv">
-      Search: <input type="search" id="searchInput" onkeyup="search()"/>
+      Search: <input type="search" id="searchInput" oninput="search()"/>
     </div>
     <div>
       Group by:
@@ -51,7 +51,10 @@
       </select>
     </div>
     <ul class="simple simple-toc" id="toc-list">
-  <li>
+  <li><a class="reference" id="basic-usage_toc" href="#basic-usage">Basic usage</a></li>
+<ul class="simple"><li><a class="reference" id="basic-usage-encoding-data_toc" href="#basic-usage-encoding-data">Encoding data</a></li>
+<li><a class="reference" id="basic-usage-decoding-data_toc" href="#basic-usage-decoding-data">Decoding data</a></li>
+</ul><li>
   <a class="reference reference-toplevel" href="#6" id="56">Imports</a>
 </li>
 <li>
@@ -60,15 +63,25 @@
     <ul class="simple simple-toc-section">
       <li><a class="reference" href="#A" title="A {.inject.} = enum
   aA">A</a></li>
+<li><a class="reference" href="#AnotherObject" title="AnotherObject = object
+  case x*: bool
+  of true:
+    y*: proc (x: string)
+  of false:">AnotherObject</a></li>
 <li><a class="reference" href="#B" title="B {.inject.} = enum
   bB">B</a></li>
 <li><a class="reference" href="#Foo" title="Foo = enum
   enumValueA2">Foo</a></li>
 <li><a class="reference" href="#FooBuzz" title="FooBuzz {.deprecated: &quot;FooBuzz msg&quot;.} = int">FooBuzz</a></li>
+<li><a class="reference" href="#MyObject" title="MyObject = object
+  someString*: string        ## This is a string
+  annotated* {.somePragma.}: string ## This is an annotated string">MyObject</a></li>
 <li><a class="reference" href="#Shapes" title="Shapes = enum
   Circle,                   ## A circle
   Triangle,                 ## A three-sided shape
   Rectangle                  ## A four-sided shape">Shapes</a></li>
+<li><a class="reference" href="#T19396" title="T19396 = object
+  a*: int">T19396</a></li>
 
     </ul>
   </details>
@@ -120,7 +133,8 @@
 
 </ul>
 <ul class="simple nested-toc-section">bar
-  <li><a class="reference" href="#bar%2CT%2CT" title="bar[T](a, b: T): T">bar[T](a, b: T): T</a></li>
+  <li><a class="reference" href="#bar" title="bar(f: FooBuzz)">bar(f: FooBuzz)</a></li>
+<li><a class="reference" href="#bar%2CT%2CT" title="bar[T](a, b: T): T">bar[T](a, b: T): T</a></li>
 
 </ul>
 <ul class="simple nested-toc-section">baz
@@ -304,6 +318,10 @@
   <li><a class="reference" href="#myfn.t" title="myfn()">myfn()</a></li>
 
 </ul>
+<ul class="simple nested-toc-section">somePragma
+  <li><a class="reference" href="#somePragma.t" title="somePragma()">somePragma()</a></li>
+
+</ul>
 <ul class="simple nested-toc-section">testNimDocTrailingExample
   <li><a class="reference" href="#testNimDocTrailingExample.t" title="testNimDocTrailingExample()">testNimDocTrailingExample()</a></li>
 
@@ -332,7 +350,11 @@
     
     <div id="tocRoot"></div>
     
-    <p class="module-desc">This is the top level module.
+    <p class="module-desc">
+<h1><a class="toc-backref" id="basic-usage" href="#basic-usage">Basic usage</a></h1>
+<h2><a class="toc-backref" id="basic-usage-encoding-data" href="#basic-usage-encoding-data">Encoding data</a></h2><p>Apart from strings you can also encode lists of integers or characters:</p>
+
+<h2><a class="toc-backref" id="basic-usage-decoding-data" href="#basic-usage-decoding-data">Decoding data</a></h2>This is the top level module.
 <p><strong class="examples_text">Example:</strong></p>
 <pre class="listing"><span class="Keyword">import</span> <span class="Identifier">testproject</span>
 <span class="Keyword">import</span> <span class="Identifier">subdir</span> <span class="Operator">/</span> <span class="Identifier">subdir_b</span> <span class="Operator">/</span> <span class="Identifier">utils</span>
@@ -364,6 +386,18 @@
     
   </dd>
 </div>
+<div id="AnotherObject">
+  <dt><pre><a href="testproject.html#AnotherObject"><span class="Identifier">AnotherObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Keyword">case</span> <span class="Identifier">x</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">bool</span>
+  <span class="Keyword">of</span> <span class="Identifier">true</span><span class="Other">:</span>
+    <span class="Identifier">y</span><span class="Operator">*</span><span class="Other">:</span> <span class="Keyword">proc</span> <span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">string</span><span class="Other">)</span>
+  <span class="Keyword">of</span> <span class="Identifier">false</span><span class="Other">:</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
 <div id="B">
   <dt><pre><a href="testproject.html#B"><span class="Identifier">B</span></a> {.<span class="Identifier">inject</span>.} <span class="Other">=</span> <span class="Keyword">enum</span>
   <span class="Identifier">bB</span></pre></dt>
@@ -393,6 +427,16 @@
     
   </dd>
 </div>
+<div id="MyObject">
+  <dt><pre><a href="testproject.html#MyObject"><span class="Identifier">MyObject</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Identifier">someString</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">string</span>        <span class="Comment">## This is a string</span>
+  <span class="Identifier">annotated</span><span class="Operator">*</span> {.<span class="Identifier">somePragma</span>.}<span class="Other">:</span> <span class="Identifier">string</span> <span class="Comment">## This is an annotated string</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
 <div id="Shapes">
   <dt><pre><a href="testproject.html#Shapes"><span class="Identifier">Shapes</span></a> <span class="Other">=</span> <span class="Keyword">enum</span>
   <span class="Identifier">Circle</span><span class="Other">,</span>                   <span class="Comment">## A circle</span>
@@ -404,6 +448,15 @@
     
   </dd>
 </div>
+<div id="T19396">
+  <dt><pre><a href="testproject.html#T19396"><span class="Identifier">T19396</span></a> <span class="Other">=</span> <span class="Keyword">object</span>
+  <span class="Identifier">a</span><span class="Operator">*</span><span class="Other">:</span> <span class="Identifier">int</span></pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
 
   </dl>
 </div>
@@ -472,7 +525,7 @@
   <dl class="item">
     <div id="addfBug14485-procs-all">
   <div id="addfBug14485">
-  <dt><pre><span class="Keyword">proc</span> <a href="#addfBug14485"><span class="Identifier">addfBug14485</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#addfBug14485"><span class="Identifier">addfBug14485</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     Some proc
@@ -495,7 +548,7 @@
 </div>
 <div id="anything-procs-all">
   <div id="anything">
-  <dt><pre><span class="Keyword">proc</span> <a href="#anything"><span class="Identifier">anything</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#anything"><span class="Identifier">anything</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     There is no block quote after blank lines at the beginning.
@@ -506,8 +559,9 @@
 </div>
 <div id="asyncFun1-procs-all">
   <div id="asyncFun1">
-  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
-                                <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun1"><span class="Identifier">asyncFun1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">int</span><span class="Other">]</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span>
+                                <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">,</span> <span class="Identifier">ValueError</span><span class="Other">]</span><span class="Other">,</span>
+                                <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     ok1
@@ -518,8 +572,8 @@
 </div>
 <div id="asyncFun2-procs-all">
   <div id="asyncFun2">
-  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span>
-                                        </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun2"><span class="Identifier">asyncFun2</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
+                                        <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -530,8 +584,8 @@
 </div>
 <div id="asyncFun3-procs-all">
   <div id="asyncFun3">
-  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span>
-                                        </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#asyncFun3"><span class="Identifier">asyncFun3</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">owned</span><span class="Other">(</span><span class="Identifier">Future</span><span class="Other">[</span><span class="Identifier">void</span><span class="Other">]</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">stackTrace</span><span class="Other">:</span> <span class="DecNumber">false</span><span class="Other">,</span> <span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">Exception</span><span class="Other">]</span><span class="Other">,</span>
+                                        <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Identifier">RootEffect</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -543,7 +597,15 @@
 
 </div>
 <div id="bar-procs-all">
-  <div id="bar,T,T">
+  <div id="bar">
+  <dt><pre><span class="Keyword">proc</span> <a href="#bar"><span class="Identifier">bar</span></a><span class="Other">(</span><span class="Identifier">f</span><span class="Other">:</span> <a href="testproject.html#FooBuzz"><span class="Identifier">FooBuzz</span></a><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
+  <dd>
+    
+    
+    
+  </dd>
+</div>
+<div id="bar,T,T">
   <dt><pre><span class="Keyword">proc</span> <a href="#bar%2CT%2CT"><span class="Identifier">bar</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">a</span><span class="Other">,</span> <span class="Identifier">b</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span></pre></dt>
   <dd>
     
@@ -555,7 +617,7 @@
 </div>
 <div id="baz-procs-all">
   <div id="baz">
-  <dt><pre><span class="Keyword">proc</span> <a href="#baz"><span class="Identifier">baz</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#baz"><span class="Identifier">baz</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -592,7 +654,7 @@
 <div id="c_nonexistent-procs-all">
   <div id="c_nonexistent,cstring">
   <dt><pre><span class="Keyword">proc</span> <a href="#c_nonexistent%2Ccstring"><span class="Identifier">c_nonexistent</span></a><span class="Other">(</span><span class="Identifier">frmt</span><span class="Other">:</span> <span class="Identifier">cstring</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">cint</span> {.<span class="Identifier">importc</span><span class="Other">:</span> <span class="StringLit">&quot;nonexistent&quot;</span><span class="Other">,</span>
-    <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdio.h&gt;&quot;</span><span class="Other">,</span> <span class="Identifier">varargs</span><span class="Other">,</span> <span class="Identifier">discardable</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+    <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdio.h&gt;&quot;</span><span class="Other">,</span> <span class="Identifier">varargs</span><span class="Other">,</span> <span class="Identifier">discardable</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -605,7 +667,7 @@
   <div id="c_printf,cstring">
   <dt><pre><span class="Keyword">proc</span> <a href="#c_printf%2Ccstring"><span class="Identifier">c_printf</span></a><span class="Other">(</span><span class="Identifier">frmt</span><span class="Other">:</span> <span class="Identifier">cstring</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">cint</span> {.<span class="Identifier">importc</span><span class="Other">:</span> <span class="StringLit">&quot;printf&quot;</span><span class="Other">,</span> <span class="Identifier">header</span><span class="Other">:</span> <span class="StringLit">&quot;&lt;stdio.h&gt;&quot;</span><span class="Other">,</span>
                                      <span class="Identifier">varargs</span><span class="Other">,</span> <span class="Identifier">discardable</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span>
-                                     </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+                                     <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     the c printf. etc.
@@ -616,7 +678,7 @@
 </div>
 <div id="fromUtils3-procs-all">
   <div id="fromUtils3">
-  <dt><pre><span class="Keyword">proc</span> <a href="#fromUtils3"><span class="Identifier">fromUtils3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#fromUtils3"><span class="Identifier">fromUtils3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     came form utils but should be shown where <tt class="docutils literal"><span class="pre"><span class="Identifier">fromUtilsGen</span></span></tt> is called
@@ -642,7 +704,7 @@
 <div id="low-procs-all">
   <div id="low,T">
   <dt><pre><span class="Keyword">proc</span> <a href="#low%2CT"><span class="Identifier">low</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">:</span> <span class="Identifier">Ordinal</span> <span class="Operator">|</span> <span class="Keyword">enum</span> <span class="Operator">|</span> <span class="Identifier">range</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span class="Identifier">magic</span><span class="Other">:</span> <span class="StringLit">&quot;Low&quot;</span><span class="Other">,</span> <span class="Identifier">noSideEffect</span><span class="Other">,</span>
-    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     <p>Returns the lowest possible value of an ordinal value <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt>. As a special semantic rule, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> may also be a type identifier.</p>
@@ -658,7 +720,7 @@
 <div id="low2-procs-all">
   <div id="low2,T">
   <dt><pre><span class="Keyword">proc</span> <a href="#low2%2CT"><span class="Identifier">low2</span></a><span class="Other">[</span><span class="Identifier">T</span><span class="Other">:</span> <span class="Identifier">Ordinal</span> <span class="Operator">|</span> <span class="Keyword">enum</span> <span class="Operator">|</span> <span class="Identifier">range</span><span class="Other">]</span><span class="Other">(</span><span class="Identifier">x</span><span class="Other">:</span> <span class="Identifier">T</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">T</span> {.<span class="Identifier">magic</span><span class="Other">:</span> <span class="StringLit">&quot;Low&quot;</span><span class="Other">,</span> <span class="Identifier">noSideEffect</span><span class="Other">,</span>
-    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+    <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     <p>Returns the lowest possible value of an ordinal value <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt>. As a special semantic rule, <tt class="docutils literal"><span class="pre"><span class="Identifier">x</span></span></tt> may also be a type identifier.</p>
@@ -675,7 +737,7 @@
 </div>
 <div id="p1-procs-all">
   <div id="p1">
-  <dt><pre><span class="Keyword">proc</span> <a href="#p1"><span class="Identifier">p1</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#p1"><span class="Identifier">p1</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cp1
@@ -702,7 +764,7 @@ this is a nested doc comment
 </div>
 <div id="someFunc-procs-all">
   <div id="someFunc">
-  <dt><pre><span class="Keyword">func</span> <a href="#someFunc"><span class="Identifier">someFunc</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">func</span> <a href="#someFunc"><span class="Identifier">someFunc</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     My someFunc. Stuff in <tt class="docutils literal"><span class="pre"><span class="Identifier">quotes</span></span></tt> here. <a class="reference external" href="https://nim-lang.org">Some link</a>
@@ -713,7 +775,7 @@ this is a nested doc comment
 </div>
 <div id="tripleStrLitTest-procs-all">
   <div id="tripleStrLitTest">
-  <dt><pre><span class="Keyword">proc</span> <a href="#tripleStrLitTest"><span class="Identifier">tripleStrLitTest</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#tripleStrLitTest"><span class="Identifier">tripleStrLitTest</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -760,7 +822,7 @@ at indent 0
 </div>
 <div id="z1-procs-all">
   <div id="z1">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z1"><span class="Identifier">z1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z1"><span class="Identifier">z1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <a href="testproject.html#Foo"><span class="Identifier">Foo</span></a> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz1
@@ -771,7 +833,7 @@ at indent 0
 </div>
 <div id="z2-procs-all">
   <div id="z2">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z2"><span class="Identifier">z2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z2"><span class="Identifier">z2</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz2
@@ -784,7 +846,7 @@ at indent 0
 </div>
 <div id="z3-procs-all">
   <div id="z3">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z3"><span class="Identifier">z3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z3"><span class="Identifier">z3</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz3
@@ -795,7 +857,7 @@ at indent 0
 </div>
 <div id="z4-procs-all">
   <div id="z4">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z4"><span class="Identifier">z4</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z4"><span class="Identifier">z4</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz4
@@ -806,7 +868,7 @@ at indent 0
 </div>
 <div id="z5-procs-all">
   <div id="z5">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z5"><span class="Identifier">z5</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z5"><span class="Identifier">z5</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz5
@@ -817,7 +879,7 @@ at indent 0
 </div>
 <div id="z6-procs-all">
   <div id="z6">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z6"><span class="Identifier">z6</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z6"><span class="Identifier">z6</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz6
@@ -828,7 +890,7 @@ at indent 0
 </div>
 <div id="z7-procs-all">
   <div id="z7">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z7"><span class="Identifier">z7</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z7"><span class="Identifier">z7</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz7
@@ -839,7 +901,7 @@ at indent 0
 </div>
 <div id="z8-procs-all">
   <div id="z8">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z8"><span class="Identifier">z8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z8"><span class="Identifier">z8</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz8
@@ -850,7 +912,7 @@ at indent 0
 </div>
 <div id="z9-procs-all">
   <div id="z9">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z9"><span class="Identifier">z9</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z9"><span class="Identifier">z9</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -863,7 +925,7 @@ at indent 0
 </div>
 <div id="z10-procs-all">
   <div id="z10">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z10"><span class="Identifier">z10</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z10"><span class="Identifier">z10</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -876,7 +938,7 @@ at indent 0
 </div>
 <div id="z11-procs-all">
   <div id="z11">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z11"><span class="Identifier">z11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z11"><span class="Identifier">z11</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -889,7 +951,7 @@ at indent 0
 </div>
 <div id="z12-procs-all">
   <div id="z12">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z12"><span class="Identifier">z12</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z12"><span class="Identifier">z12</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -902,7 +964,7 @@ at indent 0
 </div>
 <div id="z13-procs-all">
   <div id="z13">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z13"><span class="Identifier">z13</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z13"><span class="Identifier">z13</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz13
@@ -915,7 +977,7 @@ at indent 0
 </div>
 <div id="z17-procs-all">
   <div id="z17">
-  <dt><pre><span class="Keyword">proc</span> <a href="#z17"><span class="Identifier">z17</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">proc</span> <a href="#z17"><span class="Identifier">z17</span></a><span class="Other">(</span><span class="Other">)</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     cz17 rest
@@ -934,7 +996,7 @@ at indent 0
   <dl class="item">
     <div id="method1-methods-all">
   <div id="method1.e,Moo">
-  <dt><pre><span class="Keyword">method</span> <a href="#method1.e%2CMoo"><span class="Identifier">method1</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">method</span> <a href="#method1.e%2CMoo"><span class="Identifier">method1</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     foo1
@@ -945,7 +1007,7 @@ at indent 0
 </div>
 <div id="method2-methods-all">
   <div id="method2.e,Moo">
-  <dt><pre><span class="Keyword">method</span> <a href="#method2.e%2CMoo"><span class="Identifier">method2</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">method</span> <a href="#method2.e%2CMoo"><span class="Identifier">method2</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     foo2
@@ -956,7 +1018,7 @@ at indent 0
 </div>
 <div id="method3-methods-all">
   <div id="method3.e,Moo">
-  <dt><pre><span class="Keyword">method</span> <a href="#method3.e%2CMoo"><span class="Identifier">method3</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">method</span> <a href="#method3.e%2CMoo"><span class="Identifier">method3</span></a><span class="Other">(</span><span class="Identifier">self</span><span class="Other">:</span> <span class="Identifier">Moo</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span class="Identifier">base</span><span class="Other">,</span> <span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     foo3
@@ -973,7 +1035,7 @@ at indent 0
   <dl class="item">
     <div id="fromUtils1-iterators-all">
   <div id="fromUtils1.i">
-  <dt><pre><span class="Keyword">iterator</span> <a href="#fromUtils1.i"><span class="Identifier">fromUtils1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">iterator</span> <a href="#fromUtils1.i"><span class="Identifier">fromUtils1</span></a><span class="Other">(</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     
@@ -988,7 +1050,7 @@ at indent 0
 </div>
 <div id="iter1-iterators-all">
   <div id="iter1.i,int">
-  <dt><pre><span class="Keyword">iterator</span> <a href="#iter1.i%2Cint"><span class="Identifier">iter1</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">iterator</span> <a href="#iter1.i%2Cint"><span class="Identifier">iter1</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     foo1
@@ -999,7 +1061,7 @@ at indent 0
 </div>
 <div id="iter2-iterators-all">
   <div id="iter2.i,int">
-  <dt><pre><span class="Keyword">iterator</span> <a href="#iter2.i%2Cint"><span class="Identifier">iter2</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> </span><span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span>.}</pre></dt>
+  <dt><pre><span class="Keyword">iterator</span> <a href="#iter2.i%2Cint"><span class="Identifier">iter2</span></a><span class="Other">(</span><span class="Identifier">n</span><span class="Other">:</span> <span class="Identifier">int</span><span class="Other">)</span><span class="Other">:</span> <span class="Identifier">int</span> {.<span><span class="Other pragmadots">...</span></span><span class="pragmawrap"><span class="Identifier">raises</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">tags</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span><span class="Other">,</span> <span class="Identifier">forbids</span><span class="Other">:</span> <span class="Other">[</span><span class="Other">]</span></span>.}</pre></dt>
   <dd>
     
     foo2
@@ -1110,6 +1172,17 @@ bar
 </div>
 
 </div>
+<div id="somePragma-templates-all">
+  <div id="somePragma.t">
+  <dt><pre><span class="Keyword">template</span> <a href="#somePragma.t"><span class="Identifier">somePragma</span></a><span class="Other">(</span><span class="Other">)</span> {.<span class="Identifier">pragma</span>.}</pre></dt>
+  <dd>
+    
+    Just some annotation
+    
+  </dd>
+</div>
+
+</div>
 <div id="testNimDocTrailingExample-templates-all">
   <div id="testNimDocTrailingExample.t">
   <dt><pre><span class="Keyword">template</span> <a href="#testNimDocTrailingExample.t"><span class="Identifier">testNimDocTrailingExample</span></a><span class="Other">(</span><span class="Other">)</span></pre></dt>
diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx
index 4d0e63c0c..81d65a05a 100644
--- a/nimdoc/testproject/expected/testproject.idx
+++ b/nimdoc/testproject/expected/testproject.idx
@@ -1,63 +1,74 @@
-someVariable	testproject.html#someVariable	testproject: someVariable	
-C_A	testproject.html#C_A	testproject: C_A	
-C_B	testproject.html#C_B	testproject: C_B	
-C_C	testproject.html#C_C	testproject: C_C	
-C_D	testproject.html#C_D	testproject: C_D	
-bar	testproject.html#bar,T,T	testproject: bar[T](a, b: T): T	
-baz	testproject.html#baz,T,T	testproject: baz[T](a, b: T): T	
-buzz	testproject.html#buzz,T,T	testproject: buzz[T](a, b: T): T	
-FooBuzz	testproject.html#FooBuzz	testproject: FooBuzz	
-aVariable	testproject.html#aVariable	testproject: aVariable	
-A	testproject.html#A	testproject: A	
-B	testproject.html#B	testproject: B	
-someFunc	testproject.html#someFunc	testproject: someFunc()	
-fromUtils1	testproject.html#fromUtils1.i	testproject: fromUtils1(): int	
-fromUtils2	testproject.html#fromUtils2.t	testproject: fromUtils2()	
-fromUtils3	testproject.html#fromUtils3	testproject: fromUtils3()	
-isValid	testproject.html#isValid,T	testproject: isValid[T](x: T): bool	
-enumValueA2	testproject.html#enumValueA2	Foo.enumValueA2	
-Foo	testproject.html#Foo	testproject: Foo	
-z1	testproject.html#z1	testproject: z1(): Foo	
-z2	testproject.html#z2	testproject: z2()	
-z3	testproject.html#z3	testproject: z3()	
-z4	testproject.html#z4	testproject: z4()	
-z5	testproject.html#z5	testproject: z5(): int	
-z6	testproject.html#z6	testproject: z6(): int	
-z6t	testproject.html#z6t.t	testproject: z6t(): int	
-z7	testproject.html#z7	testproject: z7(): int	
-z8	testproject.html#z8	testproject: z8(): int	
-z9	testproject.html#z9	testproject: z9()	
-z10	testproject.html#z10	testproject: z10()	
-z11	testproject.html#z11	testproject: z11()	
-z12	testproject.html#z12	testproject: z12(): int	
-z13	testproject.html#z13	testproject: z13()	
-baz	testproject.html#baz	testproject: baz()	
-z17	testproject.html#z17	testproject: z17()	
-p1	testproject.html#p1	testproject: p1()	
-addfBug14485	testproject.html#addfBug14485	testproject: addfBug14485()	
-c_printf	testproject.html#c_printf,cstring	testproject: c_printf(frmt: cstring): cint	
-c_nonexistent	testproject.html#c_nonexistent,cstring	testproject: c_nonexistent(frmt: cstring): cint	
-low	testproject.html#low,T	testproject: low[T: Ordinal | enum | range](x: T): T	
-low2	testproject.html#low2,T	testproject: low2[T: Ordinal | enum | range](x: T): T	
-tripleStrLitTest	testproject.html#tripleStrLitTest	testproject: tripleStrLitTest()	
-method1	testproject.html#method1.e,Moo	testproject: method1(self: Moo)	
-method2	testproject.html#method2.e,Moo	testproject: method2(self: Moo): int	
-method3	testproject.html#method3.e,Moo	testproject: method3(self: Moo): int	
-iter1	testproject.html#iter1.i,int	testproject: iter1(n: int): int	
-iter2	testproject.html#iter2.i,int	testproject: iter2(n: int): int	
-bar	testproject.html#bar.m	testproject: bar(): untyped	
-z16	testproject.html#z16.m	testproject: z16()	
-z18	testproject.html#z18.m	testproject: z18(): int	
-foo	testproject.html#foo.t,SomeType,SomeType	testproject: foo(a, b: SomeType)	
-myfn	testproject.html#myfn.t	testproject: myfn()	
-z14	testproject.html#z14.t	testproject: z14()	
-z15	testproject.html#z15.t	testproject: z15()	
-asyncFun1	testproject.html#asyncFun1	testproject: asyncFun1(): Future[int]	
-asyncFun2	testproject.html#asyncFun2	testproject: asyncFun2(): owned(Future[void])	
-asyncFun3	testproject.html#asyncFun3	testproject: asyncFun3(): owned(Future[void])	
-testNimDocTrailingExample	testproject.html#testNimDocTrailingExample.t	testproject: testNimDocTrailingExample()	
-Circle	testproject.html#Circle	Shapes.Circle	
-Triangle	testproject.html#Triangle	Shapes.Triangle	
-Rectangle	testproject.html#Rectangle	Shapes.Rectangle	
-Shapes	testproject.html#Shapes	testproject: Shapes	
-anything	testproject.html#anything	testproject: anything()	
+nimTitle	testproject	testproject.html	module testproject		0
+nim	someVariable	testproject.html#someVariable	var someVariable		25
+nim	C_A	testproject.html#C_A	const C_A		38
+nim	C_B	testproject.html#C_B	const C_B		39
+nim	C_C	testproject.html#C_C	const C_C		40
+nim	C_D	testproject.html#C_D	const C_D		41
+nim	bar	testproject.html#bar,T,T	proc bar[T](a, b: T): T		43
+nim	baz	testproject.html#baz,T,T	proc baz[T](a, b: T): T		46
+nim	buzz	testproject.html#buzz,T,T	proc buzz[T](a, b: T): T		50
+nim	FooBuzz	testproject.html#FooBuzz	type FooBuzz		55
+nim	bar	testproject.html#bar	proc bar(f: FooBuzz)		59
+nim	aVariable	testproject.html#aVariable	var aVariable		64
+nim	A	testproject.html#A	enum A		92
+nim	B	testproject.html#B	enum B		97
+nim	someFunc	testproject.html#someFunc	proc someFunc()		68
+nim	fromUtils1	testproject.html#fromUtils1.i	iterator fromUtils1(): int		112
+nim	fromUtils2	testproject.html#fromUtils2.t	template fromUtils2()		119
+nim	fromUtils3	testproject.html#fromUtils3	proc fromUtils3()		69
+nim	isValid	testproject.html#isValid,T	proc isValid[T](x: T): bool		71
+nim	enumValueA2	testproject.html#enumValueA2	Foo.enumValueA2		78
+nim	Foo	testproject.html#Foo	enum Foo		78
+nim	z1	testproject.html#z1	proc z1(): Foo		81
+nim	z2	testproject.html#z2	proc z2()		85
+nim	z3	testproject.html#z3	proc z3()		90
+nim	z4	testproject.html#z4	proc z4()		93
+nim	z5	testproject.html#z5	proc z5(): int		99
+nim	z6	testproject.html#z6	proc z6(): int		103
+nim	z6t	testproject.html#z6t.t	template z6t(): int		107
+nim	z7	testproject.html#z7	proc z7(): int		111
+nim	z8	testproject.html#z8	proc z8(): int		115
+nim	z9	testproject.html#z9	proc z9()		123
+nim	z10	testproject.html#z10	proc z10()		126
+nim	z11	testproject.html#z11	proc z11()		131
+nim	z12	testproject.html#z12	proc z12(): int		136
+nim	z13	testproject.html#z13	proc z13()		141
+nim	baz	testproject.html#baz	proc baz()		146
+nim	z17	testproject.html#z17	proc z17()		156
+nim	p1	testproject.html#p1	proc p1()		168
+nim	addfBug14485	testproject.html#addfBug14485	proc addfBug14485()		189
+nim	c_printf	testproject.html#c_printf,cstring	proc c_printf(frmt: cstring): cint		205
+nim	c_nonexistent	testproject.html#c_nonexistent,cstring	proc c_nonexistent(frmt: cstring): cint		209
+nim	low	testproject.html#low,T	proc low[T: Ordinal | enum | range](x: T): T		212
+nim	low2	testproject.html#low2,T	proc low2[T: Ordinal | enum | range](x: T): T		222
+nim	tripleStrLitTest	testproject.html#tripleStrLitTest	proc tripleStrLitTest()		235
+nim	method1	testproject.html#method1.e,Moo	method method1(self: Moo)		276
+nim	method2	testproject.html#method2.e,Moo	method method2(self: Moo): int		278
+nim	method3	testproject.html#method3.e,Moo	method method3(self: Moo): int		281
+nim	iter1	testproject.html#iter1.i,int	iterator iter1(n: int): int		286
+nim	iter2	testproject.html#iter2.i,int	iterator iter2(n: int): int		290
+nim	bar	testproject.html#bar.m	macro bar(): untyped		297
+nim	z16	testproject.html#z16.m	macro z16()		300
+nim	z18	testproject.html#z18.m	macro z18(): int		309
+nim	foo	testproject.html#foo.t,SomeType,SomeType	template foo(a, b: SomeType)		314
+nim	myfn	testproject.html#myfn.t	template myfn()		319
+nim	z14	testproject.html#z14.t	template z14()		340
+nim	z15	testproject.html#z15.t	template z15()		345
+nim	asyncFun1	testproject.html#asyncFun1	proc asyncFun1(): Future[int]		370
+nim	asyncFun2	testproject.html#asyncFun2	proc asyncFun2(): owned(Future[void])		373
+nim	asyncFun3	testproject.html#asyncFun3	proc asyncFun3(): owned(Future[void])		374
+nim	testNimDocTrailingExample	testproject.html#testNimDocTrailingExample.t	template testNimDocTrailingExample()		383
+nim	Circle	testproject.html#Circle	Shapes.Circle		392
+nim	Triangle	testproject.html#Triangle	Shapes.Triangle		392
+nim	Rectangle	testproject.html#Rectangle	Shapes.Rectangle		392
+nim	Shapes	testproject.html#Shapes	enum Shapes		392
+nim	anything	testproject.html#anything	proc anything()		399
+nim	T19396	testproject.html#T19396	object T19396		404
+nim	somePragma	testproject.html#somePragma.t	template somePragma()		408
+nim	MyObject	testproject.html#MyObject	object MyObject		412
+nim	AnotherObject	testproject.html#AnotherObject	object AnotherObject		417
+nimgrp	bar	testproject.html#bar-procs-all	proc		43
+nimgrp	baz	testproject.html#baz-procs-all	proc		46
+heading	Basic usage	testproject.html#basic-usage	 Basic usage		0
+heading	Encoding data	testproject.html#basic-usage-encoding-data	  Encoding data		0
+heading	Decoding data	testproject.html#basic-usage-decoding-data	  Decoding data		0
diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html
index c62b4c7db..71a487e0c 100644
--- a/nimdoc/testproject/expected/theindex.html
+++ b/nimdoc/testproject/expected/theindex.html
@@ -1,20 +1,20 @@
 <?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 <!--  This file is generated by Nim. -->
-<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<html xmlns="https://www.w3.org/1999/xhtml" xml:lang="en" lang="en" data-theme="auto">
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Index</title>
 
-<!-- Favicon -->
-<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
-<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
-
 <!-- Google fonts -->
 <link href='https://fonts.googleapis.com/css?family=Lato:400,600,900' rel='stylesheet' type='text/css'/>
 <link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:400,500,600' rel='stylesheet' type='text/css'/>
 
+<!-- Favicon -->
+<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
+<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4QQQEwksSS9ZWwAAAk1JREFUWMPtll2ITVEUx39nn/O7Y5qR8f05wtCUUr6ZIS++8pEnkZInPImneaCQ5METNdOkeFBKUhMPRIkHKfEuUZSUlGlKPN2TrgfncpvmnntnmlEyq1Z7t89/rf9a6+y99oZxGZf/XeIq61EdtgKXgdXA0xrYAvBjOIF1AI9zvjcC74BSpndrJPkBWDScTF8Aa4E3wDlgHbASaANmVqlcCnwHvgDvgVfAJ+AikAAvgfVZwLnSVZHZaOuKoQi3ZOMi4NkYkpe1p4J7A8BpYAD49hfIy/oqG0+hLomiKP2L5L+1ubn5115S+3OAn4EnwBlgMzCjyt6ZAnQCJ4A7wOs88iRJHvw50HoujuPBoCKwHWiosy8MdfZnAdcHk8dxXFJ3VQbQlCTJvRBCGdRbD4M6uc5glpY3eAihpN5S5w12diSEcCCEcKUO4ljdr15T76ur1FDDLIQQ3qv71EdDOe3Kxj3leRXyk+pxdWnFWod6Wt2bY3de3aSuUHcPBVimHs7mK9WrmeOF6lR1o9qnzskh2ar2qm1qizpfXaPeVGdlmGN5pb09qMxz1Xb1kLqgzn1RyH7JUXW52lr5e/Kqi9qpto7V1atuUzfnARrV7jEib1T76gG2qxdGmXyiekkt1GswPTtek0aBfJp6YySGBfWg2tPQ0FAYgf1stUfdmdcjarbYJEniKIq6gY/Aw+zWHAC+p2labGpqiorFYgGYCEzN7oQdQClN07O1/EfDyGgC0ALMBdYAi4FyK+4H3gLPsxfR1zRNi+NP7nH5J+QntnXe5B5mpfQAAAAASUVORK5CYII=">
+
 <!-- CSS -->
 <link rel="stylesheet" type="text/css" href="nimdoc.out.css">
 
@@ -28,375 +28,395 @@
       Modules: <a href="subdir/subdir_b/utils.html">subdir/subdir_b/utils</a>, <a href="testproject.html">testproject</a>.<br/><p /><h2>API symbols</h2>
 <dl><dt><a name="%60%24%60" href="#%60%24%60"><span>`$`:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: `$`[T](a: G[T]): string" href="subdir/subdir_b/utils.html#%24%2CG%5BT%5D">utils: `$`[T](a: G[T]): string</a></li>
+          data-doc-search-tag="utils: proc `$`[T](a: G[T]): string" href="subdir/subdir_b/utils.html#%24%2CG%5BT%5D">utils: proc `$`[T](a: G[T]): string</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="utils: `$`[T](a: ref SomeType): string" href="subdir/subdir_b/utils.html#%24%2Cref.SomeType">utils: `$`[T](a: ref SomeType): string</a></li>
+          data-doc-search-tag="utils: proc `$`[T](a: ref SomeType): string" href="subdir/subdir_b/utils.html#%24%2Cref.SomeType">utils: proc `$`[T](a: ref SomeType): string</a></li>
           </ul></dd>
 <dt><a name="%60%27big%60" href="#%60%27big%60"><span>`'big`:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: `&apos;big`(a: string): SomeType" href="subdir/subdir_b/utils.html#%27big%2Cstring">utils: `&apos;big`(a: string): SomeType</a></li>
+          data-doc-search-tag="utils: proc `'big`(a: string): SomeType" href="subdir/subdir_b/utils.html#%27big%2Cstring">utils: proc `'big`(a: string): SomeType</a></li>
           </ul></dd>
 <dt><a name="%60%5B%5D%3D%60" href="#%60%5B%5D%3D%60"><span>`[]=`:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: `[]=`[T](a: var G[T]; index: int; value: T)" href="subdir/subdir_b/utils.html#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT">utils: `[]=`[T](a: var G[T]; index: int; value: T)</a></li>
+          data-doc-search-tag="utils: proc `[]=`[T](a: var G[T]; index: int; value: T)" href="subdir/subdir_b/utils.html#%5B%5D%3D%2CG%5BT%5D%2Cint%2CT">utils: proc `[]=`[T](a: var G[T]; index: int; value: T)</a></li>
           </ul></dd>
 <dt><a name="%60%5B%5D%60" href="#%60%5B%5D%60"><span>`[]`:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: `[]`[T](x: G[T]): T" href="subdir/subdir_b/utils.html#%5B%5D%2CG%5BT%5D">utils: `[]`[T](x: G[T]): T</a></li>
+          data-doc-search-tag="utils: proc `[]`[T](x: G[T]): T" href="subdir/subdir_b/utils.html#%5B%5D%2CG%5BT%5D">utils: proc `[]`[T](x: G[T]): T</a></li>
           </ul></dd>
 <dt><a name="A" href="#A"><span>A:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: A" href="testproject.html#A">testproject: A</a></li>
+          data-doc-search-tag="testproject: enum A" href="testproject.html#A">testproject: enum A</a></li>
           </ul></dd>
 <dt><a name="addfBug14485" href="#addfBug14485"><span>addfBug14485:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: addfBug14485()" href="testproject.html#addfBug14485">testproject: addfBug14485()</a></li>
+          data-doc-search-tag="testproject: proc addfBug14485()" href="testproject.html#addfBug14485">testproject: proc addfBug14485()</a></li>
           </ul></dd>
 <dt><a name="aEnum" href="#aEnum"><span>aEnum:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: aEnum(): untyped" href="subdir/subdir_b/utils.html#aEnum.t">utils: aEnum(): untyped</a></li>
+          data-doc-search-tag="utils: template aEnum(): untyped" href="subdir/subdir_b/utils.html#aEnum.t">utils: template aEnum(): untyped</a></li>
+          </ul></dd>
+<dt><a name="AnotherObject" href="#AnotherObject"><span>AnotherObject:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object AnotherObject" href="testproject.html#AnotherObject">testproject: object AnotherObject</a></li>
           </ul></dd>
 <dt><a name="anything" href="#anything"><span>anything:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: anything()" href="testproject.html#anything">testproject: anything()</a></li>
+          data-doc-search-tag="testproject: proc anything()" href="testproject.html#anything">testproject: proc anything()</a></li>
           </ul></dd>
 <dt><a name="asyncFun1" href="#asyncFun1"><span>asyncFun1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: asyncFun1(): Future[int]" href="testproject.html#asyncFun1">testproject: asyncFun1(): Future[int]</a></li>
+          data-doc-search-tag="testproject: proc asyncFun1(): Future[int]" href="testproject.html#asyncFun1">testproject: proc asyncFun1(): Future[int]</a></li>
           </ul></dd>
 <dt><a name="asyncFun2" href="#asyncFun2"><span>asyncFun2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: asyncFun2(): owned(Future[void])" href="testproject.html#asyncFun2">testproject: asyncFun2(): owned(Future[void])</a></li>
+          data-doc-search-tag="testproject: proc asyncFun2(): owned(Future[void])" href="testproject.html#asyncFun2">testproject: proc asyncFun2(): owned(Future[void])</a></li>
           </ul></dd>
 <dt><a name="asyncFun3" href="#asyncFun3"><span>asyncFun3:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: asyncFun3(): owned(Future[void])" href="testproject.html#asyncFun3">testproject: asyncFun3(): owned(Future[void])</a></li>
+          data-doc-search-tag="testproject: proc asyncFun3(): owned(Future[void])" href="testproject.html#asyncFun3">testproject: proc asyncFun3(): owned(Future[void])</a></li>
           </ul></dd>
 <dt><a name="aVariable" href="#aVariable"><span>aVariable:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: aVariable" href="testproject.html#aVariable">testproject: aVariable</a></li>
+          data-doc-search-tag="testproject: var aVariable" href="testproject.html#aVariable">testproject: var aVariable</a></li>
           </ul></dd>
 <dt><a name="B" href="#B"><span>B:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: B" href="testproject.html#B">testproject: B</a></li>
+          data-doc-search-tag="testproject: enum B" href="testproject.html#B">testproject: enum B</a></li>
           </ul></dd>
 <dt><a name="bar" href="#bar"><span>bar:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: bar[T](a, b: T): T" href="testproject.html#bar%2CT%2CT">testproject: bar[T](a, b: T): T</a></li>
+          data-doc-search-tag="testproject: proc bar(f: FooBuzz)" href="testproject.html#bar">testproject: proc bar(f: FooBuzz)</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="testproject: proc bar[T](a, b: T): T" href="testproject.html#bar%2CT%2CT">testproject: proc bar[T](a, b: T): T</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="testproject: bar(): untyped" href="testproject.html#bar.m">testproject: bar(): untyped</a></li>
+          data-doc-search-tag="testproject: macro bar(): untyped" href="testproject.html#bar.m">testproject: macro bar(): untyped</a></li>
           </ul></dd>
 <dt><a name="baz" href="#baz"><span>baz:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: baz()" href="testproject.html#baz">testproject: baz()</a></li>
+          data-doc-search-tag="testproject: proc baz()" href="testproject.html#baz">testproject: proc baz()</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="testproject: baz[T](a, b: T): T" href="testproject.html#baz%2CT%2CT">testproject: baz[T](a, b: T): T</a></li>
+          data-doc-search-tag="testproject: proc baz[T](a, b: T): T" href="testproject.html#baz%2CT%2CT">testproject: proc baz[T](a, b: T): T</a></li>
           </ul></dd>
 <dt><a name="bEnum" href="#bEnum"><span>bEnum:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: bEnum(): untyped" href="subdir/subdir_b/utils.html#bEnum.t">utils: bEnum(): untyped</a></li>
+          data-doc-search-tag="utils: template bEnum(): untyped" href="subdir/subdir_b/utils.html#bEnum.t">utils: template bEnum(): untyped</a></li>
           </ul></dd>
 <dt><a name="binarySearch" href="#binarySearch"><span>binarySearch:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: binarySearch[T, K](a: openArray[T]; key: K;
-                   cmp: proc (x: T; y: K): int {.closure.}): int" href="subdir/subdir_b/utils.html#binarySearch%2CopenArray%5BT%5D%2CK%2Cproc%28T%2CK%29">utils: binarySearch[T, K](a: openArray[T]; key: K;
+          data-doc-search-tag="utils: proc binarySearch[T, K](a: openArray[T]; key: K;
+                   cmp: proc (x: T; y: K): int {.closure.}): int" href="subdir/subdir_b/utils.html#binarySearch%2CopenArray%5BT%5D%2CK%2Cproc%28T%2CK%29">utils: proc binarySearch[T, K](a: openArray[T]; key: K;
                    cmp: proc (x: T; y: K): int {.closure.}): int</a></li>
           </ul></dd>
 <dt><a name="buzz" href="#buzz"><span>buzz:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: buzz[T](a, b: T): T" href="testproject.html#buzz%2CT%2CT">testproject: buzz[T](a, b: T): T</a></li>
+          data-doc-search-tag="testproject: proc buzz[T](a, b: T): T" href="testproject.html#buzz%2CT%2CT">testproject: proc buzz[T](a, b: T): T</a></li>
           </ul></dd>
 <dt><a name="C_A" href="#C_A"><span>C_A:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: C_A" href="testproject.html#C_A">testproject: C_A</a></li>
+          data-doc-search-tag="testproject: const C_A" href="testproject.html#C_A">testproject: const C_A</a></li>
           </ul></dd>
 <dt><a name="C_B" href="#C_B"><span>C_B:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: C_B" href="testproject.html#C_B">testproject: C_B</a></li>
+          data-doc-search-tag="testproject: const C_B" href="testproject.html#C_B">testproject: const C_B</a></li>
           </ul></dd>
 <dt><a name="C_C" href="#C_C"><span>C_C:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: C_C" href="testproject.html#C_C">testproject: C_C</a></li>
+          data-doc-search-tag="testproject: const C_C" href="testproject.html#C_C">testproject: const C_C</a></li>
           </ul></dd>
 <dt><a name="C_D" href="#C_D"><span>C_D:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: C_D" href="testproject.html#C_D">testproject: C_D</a></li>
+          data-doc-search-tag="testproject: const C_D" href="testproject.html#C_D">testproject: const C_D</a></li>
           </ul></dd>
 <dt><a name="Circle" href="#Circle"><span>Circle:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="Shapes.Circle" href="testproject.html#Circle">Shapes.Circle</a></li>
+          data-doc-search-tag="testproject: Shapes.Circle" href="testproject.html#Circle">testproject: Shapes.Circle</a></li>
           </ul></dd>
 <dt><a name="c_nonexistent" href="#c_nonexistent"><span>c_nonexistent:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: c_nonexistent(frmt: cstring): cint" href="testproject.html#c_nonexistent%2Ccstring">testproject: c_nonexistent(frmt: cstring): cint</a></li>
+          data-doc-search-tag="testproject: proc c_nonexistent(frmt: cstring): cint" href="testproject.html#c_nonexistent%2Ccstring">testproject: proc c_nonexistent(frmt: cstring): cint</a></li>
           </ul></dd>
 <dt><a name="c_printf" href="#c_printf"><span>c_printf:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: c_printf(frmt: cstring): cint" href="testproject.html#c_printf%2Ccstring">testproject: c_printf(frmt: cstring): cint</a></li>
+          data-doc-search-tag="testproject: proc c_printf(frmt: cstring): cint" href="testproject.html#c_printf%2Ccstring">testproject: proc c_printf(frmt: cstring): cint</a></li>
           </ul></dd>
 <dt><a name="enumValueA" href="#enumValueA"><span>enumValueA:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">SomeType.enumValueA</a></li>
+          data-doc-search-tag="utils: SomeType.enumValueA" href="subdir/subdir_b/utils.html#enumValueA">utils: SomeType.enumValueA</a></li>
           </ul></dd>
 <dt><a name="enumValueA2" href="#enumValueA2"><span>enumValueA2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="Foo.enumValueA2" href="testproject.html#enumValueA2">Foo.enumValueA2</a></li>
+          data-doc-search-tag="testproject: Foo.enumValueA2" href="testproject.html#enumValueA2">testproject: Foo.enumValueA2</a></li>
           </ul></dd>
 <dt><a name="enumValueB" href="#enumValueB"><span>enumValueB:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="SomeType.enumValueB" href="subdir/subdir_b/utils.html#enumValueB">SomeType.enumValueB</a></li>
+          data-doc-search-tag="utils: SomeType.enumValueB" href="subdir/subdir_b/utils.html#enumValueB">utils: SomeType.enumValueB</a></li>
           </ul></dd>
 <dt><a name="enumValueC" href="#enumValueC"><span>enumValueC:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="SomeType.enumValueC" href="subdir/subdir_b/utils.html#enumValueC">SomeType.enumValueC</a></li>
+          data-doc-search-tag="utils: SomeType.enumValueC" href="subdir/subdir_b/utils.html#enumValueC">utils: SomeType.enumValueC</a></li>
           </ul></dd>
 <dt><a name="f" href="#f"><span>f:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: f(x: G[int])" href="subdir/subdir_b/utils.html#f%2CG%5Bint%5D">utils: f(x: G[int])</a></li>
+          data-doc-search-tag="utils: proc f(x: G[int])" href="subdir/subdir_b/utils.html#f%2CG%5Bint%5D">utils: proc f(x: G[int])</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="utils: f(x: G[string])" href="subdir/subdir_b/utils.html#f%2CG%5Bstring%5D">utils: f(x: G[string])</a></li>
+          data-doc-search-tag="utils: proc f(x: G[string])" href="subdir/subdir_b/utils.html#f%2CG%5Bstring%5D">utils: proc f(x: G[string])</a></li>
           </ul></dd>
 <dt><a name="fn" href="#fn"><span>fn:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn[T; U, V: SomeFloat]()" href="subdir/subdir_b/utils.html#fn">utils: fn[T; U, V: SomeFloat]()</a></li>
+          data-doc-search-tag="utils: proc fn[T; U, V: SomeFloat]()" href="subdir/subdir_b/utils.html#fn">utils: proc fn[T; U, V: SomeFloat]()</a></li>
           </ul></dd>
 <dt><a name="fn10" href="#fn10"><span>fn10:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn10(a: int): int" href="subdir/subdir_b/utils.html#fn10%2Cint">utils: fn10(a: int): int</a></li>
+          data-doc-search-tag="utils: proc fn10(a: int): int" href="subdir/subdir_b/utils.html#fn10%2Cint">utils: proc fn10(a: int): int</a></li>
           </ul></dd>
 <dt><a name="fN11" href="#fN11"><span>fN11:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fN11()" href="subdir/subdir_b/utils.html#fN11">utils: fN11()</a></li>
+          data-doc-search-tag="utils: proc fN11()" href="subdir/subdir_b/utils.html#fN11">utils: proc fN11()</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="utils: fN11(x: int)" href="subdir/subdir_b/utils.html#fN11%2Cint">utils: fN11(x: int)</a></li>
+          data-doc-search-tag="utils: proc fN11(x: int)" href="subdir/subdir_b/utils.html#fN11%2Cint">utils: proc fN11(x: int)</a></li>
           </ul></dd>
 <dt><a name="fn2" href="#fn2"><span>fn2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn2()" href="subdir/subdir_b/utils.html#fn2">utils: fn2()</a></li>
+          data-doc-search-tag="utils: proc fn2()" href="subdir/subdir_b/utils.html#fn2">utils: proc fn2()</a></li>
+          <li><a class="reference external"
+          data-doc-search-tag="utils: proc fn2(x: int)" href="subdir/subdir_b/utils.html#fn2%2Cint">utils: proc fn2(x: int)</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="utils: fn2(x: int)" href="subdir/subdir_b/utils.html#fn2%2Cint">utils: fn2(x: int)</a></li>
+          data-doc-search-tag="utils: proc fn2(x: int; y: float)" href="subdir/subdir_b/utils.html#fn2%2Cint%2Cfloat">utils: proc fn2(x: int; y: float)</a></li>
           <li><a class="reference external"
-          data-doc-search-tag="utils: fn2(x: int; y: float)" href="subdir/subdir_b/utils.html#fn2%2Cint%2Cfloat">utils: fn2(x: int; y: float)</a></li>
+          data-doc-search-tag="utils: proc fn2(x: int; y: float; z: float)" href="subdir/subdir_b/utils.html#fn2%2Cint%2Cfloat%2Cfloat">utils: proc fn2(x: int; y: float; z: float)</a></li>
           </ul></dd>
 <dt><a name="fn3" href="#fn3"><span>fn3:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn3(): auto" href="subdir/subdir_b/utils.html#fn3">utils: fn3(): auto</a></li>
+          data-doc-search-tag="utils: proc fn3(): auto" href="subdir/subdir_b/utils.html#fn3">utils: proc fn3(): auto</a></li>
           </ul></dd>
 <dt><a name="fn4" href="#fn4"><span>fn4:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn4(): auto" href="subdir/subdir_b/utils.html#fn4">utils: fn4(): auto</a></li>
+          data-doc-search-tag="utils: proc fn4(): auto" href="subdir/subdir_b/utils.html#fn4">utils: proc fn4(): auto</a></li>
           </ul></dd>
 <dt><a name="fn5" href="#fn5"><span>fn5:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn5()" href="subdir/subdir_b/utils.html#fn5">utils: fn5()</a></li>
+          data-doc-search-tag="utils: proc fn5()" href="subdir/subdir_b/utils.html#fn5">utils: proc fn5()</a></li>
           </ul></dd>
 <dt><a name="fn6" href="#fn6"><span>fn6:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn6()" href="subdir/subdir_b/utils.html#fn6">utils: fn6()</a></li>
+          data-doc-search-tag="utils: proc fn6()" href="subdir/subdir_b/utils.html#fn6">utils: proc fn6()</a></li>
           </ul></dd>
 <dt><a name="fn7" href="#fn7"><span>fn7:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn7()" href="subdir/subdir_b/utils.html#fn7">utils: fn7()</a></li>
+          data-doc-search-tag="utils: proc fn7()" href="subdir/subdir_b/utils.html#fn7">utils: proc fn7()</a></li>
           </ul></dd>
 <dt><a name="fn8" href="#fn8"><span>fn8:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn8(): auto" href="subdir/subdir_b/utils.html#fn8">utils: fn8(): auto</a></li>
+          data-doc-search-tag="utils: proc fn8(): auto" href="subdir/subdir_b/utils.html#fn8">utils: proc fn8(): auto</a></li>
           </ul></dd>
 <dt><a name="fn9" href="#fn9"><span>fn9:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fn9(a: int): int" href="subdir/subdir_b/utils.html#fn9%2Cint">utils: fn9(a: int): int</a></li>
+          data-doc-search-tag="utils: proc fn9(a: int): int" href="subdir/subdir_b/utils.html#fn9%2Cint">utils: proc fn9(a: int): int</a></li>
           </ul></dd>
 <dt><a name="Foo" href="#Foo"><span>Foo:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: Foo" href="testproject.html#Foo">testproject: Foo</a></li>
+          data-doc-search-tag="testproject: enum Foo" href="testproject.html#Foo">testproject: enum Foo</a></li>
           </ul></dd>
 <dt><a name="foo" href="#foo"><span>foo:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: foo(a, b: SomeType)" href="testproject.html#foo.t%2CSomeType%2CSomeType">testproject: foo(a, b: SomeType)</a></li>
+          data-doc-search-tag="testproject: template foo(a, b: SomeType)" href="testproject.html#foo.t%2CSomeType%2CSomeType">testproject: template foo(a, b: SomeType)</a></li>
           </ul></dd>
 <dt><a name="fooBar" href="#fooBar"><span>fooBar:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fooBar(a: seq[SomeType]): int" href="subdir/subdir_b/utils.html#fooBar.i%2Cseq%5BSomeType%5D">utils: fooBar(a: seq[SomeType]): int</a></li>
+          data-doc-search-tag="utils: iterator fooBar(a: seq[SomeType]): int" href="subdir/subdir_b/utils.html#fooBar.i%2Cseq%5BSomeType%5D">utils: iterator fooBar(a: seq[SomeType]): int</a></li>
           </ul></dd>
 <dt><a name="FooBuzz" href="#FooBuzz"><span>FooBuzz:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: FooBuzz" href="testproject.html#FooBuzz">testproject: FooBuzz</a></li>
+          data-doc-search-tag="testproject: type FooBuzz" href="testproject.html#FooBuzz">testproject: type FooBuzz</a></li>
           </ul></dd>
 <dt><a name="fromUtils1" href="#fromUtils1"><span>fromUtils1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: fromUtils1(): int" href="testproject.html#fromUtils1.i">testproject: fromUtils1(): int</a></li>
+          data-doc-search-tag="testproject: iterator fromUtils1(): int" href="testproject.html#fromUtils1.i">testproject: iterator fromUtils1(): int</a></li>
           </ul></dd>
 <dt><a name="fromUtils2" href="#fromUtils2"><span>fromUtils2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: fromUtils2()" href="testproject.html#fromUtils2.t">testproject: fromUtils2()</a></li>
+          data-doc-search-tag="testproject: template fromUtils2()" href="testproject.html#fromUtils2.t">testproject: template fromUtils2()</a></li>
           </ul></dd>
 <dt><a name="fromUtils3" href="#fromUtils3"><span>fromUtils3:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: fromUtils3()" href="testproject.html#fromUtils3">testproject: fromUtils3()</a></li>
+          data-doc-search-tag="testproject: proc fromUtils3()" href="testproject.html#fromUtils3">testproject: proc fromUtils3()</a></li>
           </ul></dd>
 <dt><a name="fromUtilsGen" href="#fromUtilsGen"><span>fromUtilsGen:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: fromUtilsGen(): untyped" href="subdir/subdir_b/utils.html#fromUtilsGen.t">utils: fromUtilsGen(): untyped</a></li>
+          data-doc-search-tag="utils: template fromUtilsGen(): untyped" href="subdir/subdir_b/utils.html#fromUtilsGen.t">utils: template fromUtilsGen(): untyped</a></li>
           </ul></dd>
 <dt><a name="funWithGenerics" href="#funWithGenerics"><span>funWithGenerics:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: funWithGenerics[T, U: SomeFloat](a: T; b: U)" href="subdir/subdir_b/utils.html#funWithGenerics%2CT%2CU">utils: funWithGenerics[T, U: SomeFloat](a: T; b: U)</a></li>
+          data-doc-search-tag="utils: proc funWithGenerics[T, U: SomeFloat](a: T; b: U)" href="subdir/subdir_b/utils.html#funWithGenerics%2CT%2CU">utils: proc funWithGenerics[T, U: SomeFloat](a: T; b: U)</a></li>
           </ul></dd>
 <dt><a name="G" href="#G"><span>G:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: G" href="subdir/subdir_b/utils.html#G">utils: G</a></li>
+          data-doc-search-tag="utils: object G" href="subdir/subdir_b/utils.html#G">utils: object G</a></li>
           </ul></dd>
 <dt><a name="isValid" href="#isValid"><span>isValid:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: isValid[T](x: T): bool" href="testproject.html#isValid%2CT">testproject: isValid[T](x: T): bool</a></li>
+          data-doc-search-tag="testproject: proc isValid[T](x: T): bool" href="testproject.html#isValid%2CT">testproject: proc isValid[T](x: T): bool</a></li>
           </ul></dd>
 <dt><a name="iter1" href="#iter1"><span>iter1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: iter1(n: int): int" href="testproject.html#iter1.i%2Cint">testproject: iter1(n: int): int</a></li>
+          data-doc-search-tag="testproject: iterator iter1(n: int): int" href="testproject.html#iter1.i%2Cint">testproject: iterator iter1(n: int): int</a></li>
           </ul></dd>
 <dt><a name="iter2" href="#iter2"><span>iter2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: iter2(n: int): int" href="testproject.html#iter2.i%2Cint">testproject: iter2(n: int): int</a></li>
+          data-doc-search-tag="testproject: iterator iter2(n: int): int" href="testproject.html#iter2.i%2Cint">testproject: iterator iter2(n: int): int</a></li>
           </ul></dd>
 <dt><a name="low" href="#low"><span>low:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: low[T: Ordinal | enum | range](x: T): T" href="testproject.html#low%2CT">testproject: low[T: Ordinal | enum | range](x: T): T</a></li>
+          data-doc-search-tag="testproject: proc low[T: Ordinal | enum | range](x: T): T" href="testproject.html#low%2CT">testproject: proc low[T: Ordinal | enum | range](x: T): T</a></li>
           </ul></dd>
 <dt><a name="low2" href="#low2"><span>low2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: low2[T: Ordinal | enum | range](x: T): T" href="testproject.html#low2%2CT">testproject: low2[T: Ordinal | enum | range](x: T): T</a></li>
+          data-doc-search-tag="testproject: proc low2[T: Ordinal | enum | range](x: T): T" href="testproject.html#low2%2CT">testproject: proc low2[T: Ordinal | enum | range](x: T): T</a></li>
           </ul></dd>
 <dt><a name="method1" href="#method1"><span>method1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: method1(self: Moo)" href="testproject.html#method1.e%2CMoo">testproject: method1(self: Moo)</a></li>
+          data-doc-search-tag="testproject: method method1(self: Moo)" href="testproject.html#method1.e%2CMoo">testproject: method method1(self: Moo)</a></li>
           </ul></dd>
 <dt><a name="method2" href="#method2"><span>method2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: method2(self: Moo): int" href="testproject.html#method2.e%2CMoo">testproject: method2(self: Moo): int</a></li>
+          data-doc-search-tag="testproject: method method2(self: Moo): int" href="testproject.html#method2.e%2CMoo">testproject: method method2(self: Moo): int</a></li>
           </ul></dd>
 <dt><a name="method3" href="#method3"><span>method3:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: method3(self: Moo): int" href="testproject.html#method3.e%2CMoo">testproject: method3(self: Moo): int</a></li>
+          data-doc-search-tag="testproject: method method3(self: Moo): int" href="testproject.html#method3.e%2CMoo">testproject: method method3(self: Moo): int</a></li>
           </ul></dd>
 <dt><a name="myfn" href="#myfn"><span>myfn:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: myfn()" href="testproject.html#myfn.t">testproject: myfn()</a></li>
+          data-doc-search-tag="testproject: template myfn()" href="testproject.html#myfn.t">testproject: template myfn()</a></li>
+          </ul></dd>
+<dt><a name="MyObject" href="#MyObject"><span>MyObject:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object MyObject" href="testproject.html#MyObject">testproject: object MyObject</a></li>
           </ul></dd>
 <dt><a name="p1" href="#p1"><span>p1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: p1()" href="testproject.html#p1">testproject: p1()</a></li>
+          data-doc-search-tag="testproject: proc p1()" href="testproject.html#p1">testproject: proc p1()</a></li>
           </ul></dd>
 <dt><a name="Rectangle" href="#Rectangle"><span>Rectangle:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="Shapes.Rectangle" href="testproject.html#Rectangle">Shapes.Rectangle</a></li>
+          data-doc-search-tag="testproject: Shapes.Rectangle" href="testproject.html#Rectangle">testproject: Shapes.Rectangle</a></li>
           </ul></dd>
 <dt><a name="Shapes" href="#Shapes"><span>Shapes:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: Shapes" href="testproject.html#Shapes">testproject: Shapes</a></li>
+          data-doc-search-tag="testproject: enum Shapes" href="testproject.html#Shapes">testproject: enum Shapes</a></li>
           </ul></dd>
 <dt><a name="someFunc" href="#someFunc"><span>someFunc:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: someFunc()" href="testproject.html#someFunc">testproject: someFunc()</a></li>
+          data-doc-search-tag="testproject: proc someFunc()" href="testproject.html#someFunc">testproject: proc someFunc()</a></li>
+          </ul></dd>
+<dt><a name="somePragma" href="#somePragma"><span>somePragma:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: template somePragma()" href="testproject.html#somePragma.t">testproject: template somePragma()</a></li>
           </ul></dd>
 <dt><a name="SomeType" href="#SomeType"><span>SomeType:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: SomeType" href="subdir/subdir_b/utils.html#SomeType">utils: SomeType</a></li>
+          data-doc-search-tag="utils: enum SomeType" href="subdir/subdir_b/utils.html#SomeType">utils: enum SomeType</a></li>
           </ul></dd>
 <dt><a name="someType" href="#someType"><span>someType:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="utils: someType(): SomeType" href="subdir/subdir_b/utils.html#someType_2">utils: someType(): SomeType</a></li>
+          data-doc-search-tag="utils: proc someType(): SomeType" href="subdir/subdir_b/utils.html#someType_2">utils: proc someType(): SomeType</a></li>
           </ul></dd>
 <dt><a name="someVariable" href="#someVariable"><span>someVariable:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: someVariable" href="testproject.html#someVariable">testproject: someVariable</a></li>
+          data-doc-search-tag="testproject: var someVariable" href="testproject.html#someVariable">testproject: var someVariable</a></li>
+          </ul></dd>
+<dt><a name="T19396" href="#T19396"><span>T19396:</span></a></dt><dd><ul class="simple">
+<li><a class="reference external"
+          data-doc-search-tag="testproject: object T19396" href="testproject.html#T19396">testproject: object T19396</a></li>
           </ul></dd>
 <dt><a name="testNimDocTrailingExample" href="#testNimDocTrailingExample"><span>testNimDocTrailingExample:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: testNimDocTrailingExample()" href="testproject.html#testNimDocTrailingExample.t">testproject: testNimDocTrailingExample()</a></li>
+          data-doc-search-tag="testproject: template testNimDocTrailingExample()" href="testproject.html#testNimDocTrailingExample.t">testproject: template testNimDocTrailingExample()</a></li>
           </ul></dd>
 <dt><a name="Triangle" href="#Triangle"><span>Triangle:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="Shapes.Triangle" href="testproject.html#Triangle">Shapes.Triangle</a></li>
+          data-doc-search-tag="testproject: Shapes.Triangle" href="testproject.html#Triangle">testproject: Shapes.Triangle</a></li>
           </ul></dd>
 <dt><a name="tripleStrLitTest" href="#tripleStrLitTest"><span>tripleStrLitTest:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: tripleStrLitTest()" href="testproject.html#tripleStrLitTest">testproject: tripleStrLitTest()</a></li>
+          data-doc-search-tag="testproject: proc tripleStrLitTest()" href="testproject.html#tripleStrLitTest">testproject: proc tripleStrLitTest()</a></li>
           </ul></dd>
 <dt><a name="z1" href="#z1"><span>z1:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z1(): Foo" href="testproject.html#z1">testproject: z1(): Foo</a></li>
+          data-doc-search-tag="testproject: proc z1(): Foo" href="testproject.html#z1">testproject: proc z1(): Foo</a></li>
           </ul></dd>
 <dt><a name="z10" href="#z10"><span>z10:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z10()" href="testproject.html#z10">testproject: z10()</a></li>
+          data-doc-search-tag="testproject: proc z10()" href="testproject.html#z10">testproject: proc z10()</a></li>
           </ul></dd>
 <dt><a name="z11" href="#z11"><span>z11:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z11()" href="testproject.html#z11">testproject: z11()</a></li>
+          data-doc-search-tag="testproject: proc z11()" href="testproject.html#z11">testproject: proc z11()</a></li>
           </ul></dd>
 <dt><a name="z12" href="#z12"><span>z12:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z12(): int" href="testproject.html#z12">testproject: z12(): int</a></li>
+          data-doc-search-tag="testproject: proc z12(): int" href="testproject.html#z12">testproject: proc z12(): int</a></li>
           </ul></dd>
 <dt><a name="z13" href="#z13"><span>z13:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z13()" href="testproject.html#z13">testproject: z13()</a></li>
+          data-doc-search-tag="testproject: proc z13()" href="testproject.html#z13">testproject: proc z13()</a></li>
           </ul></dd>
 <dt><a name="z14" href="#z14"><span>z14:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z14()" href="testproject.html#z14.t">testproject: z14()</a></li>
+          data-doc-search-tag="testproject: template z14()" href="testproject.html#z14.t">testproject: template z14()</a></li>
           </ul></dd>
 <dt><a name="z15" href="#z15"><span>z15:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z15()" href="testproject.html#z15.t">testproject: z15()</a></li>
+          data-doc-search-tag="testproject: template z15()" href="testproject.html#z15.t">testproject: template z15()</a></li>
           </ul></dd>
 <dt><a name="z16" href="#z16"><span>z16:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z16()" href="testproject.html#z16.m">testproject: z16()</a></li>
+          data-doc-search-tag="testproject: macro z16()" href="testproject.html#z16.m">testproject: macro z16()</a></li>
           </ul></dd>
 <dt><a name="z17" href="#z17"><span>z17:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z17()" href="testproject.html#z17">testproject: z17()</a></li>
+          data-doc-search-tag="testproject: proc z17()" href="testproject.html#z17">testproject: proc z17()</a></li>
           </ul></dd>
 <dt><a name="z18" href="#z18"><span>z18:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z18(): int" href="testproject.html#z18.m">testproject: z18(): int</a></li>
+          data-doc-search-tag="testproject: macro z18(): int" href="testproject.html#z18.m">testproject: macro z18(): int</a></li>
           </ul></dd>
 <dt><a name="z2" href="#z2"><span>z2:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z2()" href="testproject.html#z2">testproject: z2()</a></li>
+          data-doc-search-tag="testproject: proc z2()" href="testproject.html#z2">testproject: proc z2()</a></li>
           </ul></dd>
 <dt><a name="z3" href="#z3"><span>z3:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z3()" href="testproject.html#z3">testproject: z3()</a></li>
+          data-doc-search-tag="testproject: proc z3()" href="testproject.html#z3">testproject: proc z3()</a></li>
           </ul></dd>
 <dt><a name="z4" href="#z4"><span>z4:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z4()" href="testproject.html#z4">testproject: z4()</a></li>
+          data-doc-search-tag="testproject: proc z4()" href="testproject.html#z4">testproject: proc z4()</a></li>
           </ul></dd>
 <dt><a name="z5" href="#z5"><span>z5:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z5(): int" href="testproject.html#z5">testproject: z5(): int</a></li>
+          data-doc-search-tag="testproject: proc z5(): int" href="testproject.html#z5">testproject: proc z5(): int</a></li>
           </ul></dd>
 <dt><a name="z6" href="#z6"><span>z6:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z6(): int" href="testproject.html#z6">testproject: z6(): int</a></li>
+          data-doc-search-tag="testproject: proc z6(): int" href="testproject.html#z6">testproject: proc z6(): int</a></li>
           </ul></dd>
 <dt><a name="z6t" href="#z6t"><span>z6t:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z6t(): int" href="testproject.html#z6t.t">testproject: z6t(): int</a></li>
+          data-doc-search-tag="testproject: template z6t(): int" href="testproject.html#z6t.t">testproject: template z6t(): int</a></li>
           </ul></dd>
 <dt><a name="z7" href="#z7"><span>z7:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z7(): int" href="testproject.html#z7">testproject: z7(): int</a></li>
+          data-doc-search-tag="testproject: proc z7(): int" href="testproject.html#z7">testproject: proc z7(): int</a></li>
           </ul></dd>
 <dt><a name="z8" href="#z8"><span>z8:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z8(): int" href="testproject.html#z8">testproject: z8(): int</a></li>
+          data-doc-search-tag="testproject: proc z8(): int" href="testproject.html#z8">testproject: proc z8(): int</a></li>
           </ul></dd>
 <dt><a name="z9" href="#z9"><span>z9:</span></a></dt><dd><ul class="simple">
 <li><a class="reference external"
-          data-doc-search-tag="testproject: z9()" href="testproject.html#z9">testproject: z9()</a></li>
+          data-doc-search-tag="testproject: proc z9()" href="testproject.html#z9">testproject: proc z9()</a></li>
           </ul></dd>
 </dl>
       <div class="twelve-columns footer">
diff --git a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim
index 2c45ffb83..d3f5edd29 100644
--- a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim
+++ b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim
@@ -1 +1,6 @@
 proc funWithGenerics*[T, U: SomeFloat](a: T, b: U) = discard
+
+# We check that presence of overloaded `fn2` here does not break
+# referencing in the "parent" file (the one that includes this one)
+proc fn2*(x: int, y: float, z: float) =
+  discard
diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim
index 57960c69f..383c4c827 100644
--- a/nimdoc/testproject/testproject.nim
+++ b/nimdoc/testproject/testproject.nim
@@ -1,3 +1,15 @@
+## Basic usage
+## ===========
+##
+## Encoding data
+## -------------
+##
+## Apart from strings you can also encode lists of integers or characters:
+
+## Decoding data
+## -------------
+##
+
 
 import subdir / subdir_b / utils
 
@@ -42,6 +54,11 @@ proc buzz*[T](a, b: T): T {.deprecated: "since v0.20".} =
 type
   FooBuzz* {.deprecated: "FooBuzz msg".} = int
 
+using f: FooBuzz
+
+proc bar*(f) = # `f` should be expanded to `f: FooBuzz`
+  discard
+
 import std/macros
 
 var aVariable*: array[1, int]
@@ -383,3 +400,23 @@ when true: # issue #15184
     ##
     ##  There is no block quote after blank lines at the beginning.
   discard
+
+type T19396* = object # bug #19396
+   a*: int
+   b: float
+
+template somePragma*() {.pragma.}
+  ## Just some annotation
+
+type # bug #21483
+   MyObject* = object
+      someString*: string ## This is a string
+      annotated* {.somePragma.}: string ## This is an annotated string
+
+type
+  AnotherObject* = object
+    case x*: bool
+    of true:
+      y*: proc (x: string)
+    of false:
+      hidden: string
diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim
index e3b4c9a3a..e5abf0d2d 100644
--- a/nimpretty/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -12,7 +12,7 @@
 when not defined(nimpretty):
   {.error: "This needs to be compiled with --define:nimPretty".}
 
-import ../compiler / [idents, msgs, syntaxes, options, pathutils, layouter]
+import ../compiler / [idents, llstream, ast, msgs, syntaxes, options, pathutils, layouter]
 
 import parseopt, strutils, os, sequtils
 
@@ -48,6 +48,42 @@ type
     indWidth*: Natural
     maxLineLen*: Positive
 
+proc goodEnough(a, b: PNode): bool =
+  if a.kind == b.kind and a.safeLen == b.safeLen:
+    case a.kind
+    of nkNone, nkEmpty, nkNilLit: result = true
+    of nkIdent: result = a.ident.id == b.ident.id
+    of nkSym: result = a.sym == b.sym
+    of nkType: result = true
+    of nkCharLit, nkIntLit..nkInt64Lit, nkUIntLit..nkUInt64Lit:
+      result = a.intVal == b.intVal
+    of nkFloatLit..nkFloat128Lit:
+      result = a.floatVal == b.floatVal
+    of nkStrLit, nkRStrLit, nkTripleStrLit:
+      result = a.strVal == b.strVal
+    else:
+      for i in 0 ..< a.len:
+        if not goodEnough(a[i], b[i]): return false
+      return true
+  elif a.kind == nkStmtList and a.len == 1:
+    result = goodEnough(a[0], b)
+  elif b.kind == nkStmtList and b.len == 1:
+    result = goodEnough(a, b[0])
+  else:
+    result = false
+
+proc finalCheck(content: string; origAst: PNode): bool {.nimcall.} =
+  var conf = newConfigRef()
+  let oldErrors = conf.errorCounter
+  var parser: Parser
+  parser.em.indWidth = 2
+  let fileIdx = fileInfoIdx(conf, AbsoluteFile "nimpretty_bug.nim")
+
+  openParser(parser, fileIdx, llStreamOpen(content), newIdentCache(), conf)
+  let newAst = parseAll(parser)
+  closeParser(parser)
+  result = conf.errorCounter == oldErrors # and goodEnough(newAst, origAst)
+
 proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) =
   var conf = newConfigRef()
   let fileIdx = fileInfoIdx(conf, AbsoluteFile infile)
@@ -58,8 +94,10 @@ proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) =
   parser.em.indWidth = opt.indWidth
   if setupParser(parser, fileIdx, newIdentCache(), conf):
     parser.em.maxLineLen = opt.maxLineLen
-    discard parseAll(parser)
+    let fullAst = parseAll(parser)
     closeParser(parser)
+    when defined(nimpretty):
+      closeEmitter(parser.em, fullAst, finalCheck)
 
 proc main =
   var outfile, outdir: string
@@ -78,7 +116,13 @@ proc main =
   for kind, key, val in getopt():
     case kind
     of cmdArgument:
-      infiles.add(key.addFileExt(".nim"))
+      if dirExists(key):
+        for file in walkDirRec(key, skipSpecial = true):
+          if file.endsWith(".nim") or file.endsWith(".nimble"):
+            infiles.add(file)
+      else:
+        infiles.add(key.addFileExt(".nim"))
+
     of cmdLongOption, cmdShortOption:
       case normalize(key)
       of "help", "h": writeHelp()
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
index 30cfc47a9..e5a73305b 100644
--- a/nimpretty/tests/exhaustive.nim
+++ b/nimpretty/tests/exhaustive.nim
@@ -267,7 +267,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if not em.endsInWhite: wr(" ")
     wr(tok.ident.s)
     template isUnary(tok): bool =
-      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+      tok.spacing == {tsLeading}
 
     if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
       wr(" ")
@@ -693,11 +693,11 @@ proc newRecordGen(ctx: Context; typ: TypRef): PNode =
 String `interpolation`:idx: / `format`:idx: inspired by
 Python's ``f``-strings.
 
-.. code-block:: nim
-
-    import strformat
-    let msg = "hello"
-    doAssert fmt"{msg}\n" == "hello\\n"
+  ```nim
+  import strformat
+  let msg = "hello"
+  doAssert fmt"{msg}\n" == "hello\\n"
+  ```
 
 Because the literal is a raw string literal, the ``\n`` is not interpreted as
 an escape sequence.
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
index 82927a11c..7f78b7e56 100644
--- a/nimpretty/tests/expected/exhaustive.nim
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -272,7 +272,7 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     if not em.endsInWhite: wr(" ")
     wr(tok.ident.s)
     template isUnary(tok): bool =
-      tok.strongSpaceB == 0 and tok.strongSpaceA > 0
+      tok.spacing == {tsLeading}
 
     if not isUnary(tok) or em.lastTok in {tkOpr, tkDotDot}:
       wr(" ")
@@ -699,11 +699,11 @@ proc newRecordGen(ctx: Context; typ: TypRef): PNode =
 String `interpolation`:idx: / `format`:idx: inspired by
 Python's ``f``-strings.
 
-.. code-block:: nim
-
-    import strformat
-    let msg = "hello"
-    doAssert fmt"{msg}\n" == "hello\\n"
+  ```nim
+  import strformat
+  let msg = "hello"
+  doAssert fmt"{msg}\n" == "hello\\n"
+  ```
 
 Because the literal is a raw string literal, the ``\n`` is not interpreted as
 an escape sequence.
diff --git a/nimpretty/tests/expected/simple.nim b/nimpretty/tests/expected/simple.nim
index d13558621..e711eb3b6 100644
--- a/nimpretty/tests/expected/simple.nim
+++ b/nimpretty/tests/expected/simple.nim
@@ -16,3 +16,14 @@ proc a() =
 discard ## comment 3
 
 discard # comment 4
+
+
+# bug #20553
+
+let `'hello` = 12
+echo `'hello`
+
+
+proc `'u4`(n: string) =
+  # The leading ' is required.
+  discard
diff --git a/nimpretty/tests/simple.nim b/nimpretty/tests/simple.nim
index 435bd6bd2..2a01a176e 100644
--- a/nimpretty/tests/simple.nim
+++ b/nimpretty/tests/simple.nim
@@ -16,3 +16,14 @@ proc a() =
 discard## comment 3
 
 discard # comment 4
+
+
+# bug #20553
+
+let `'hello` = 12
+echo `'hello`
+
+
+proc `'u4`(n: string) =
+  # The leading ' is required.
+  discard
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index 4fee8b2a2..04bae08c1 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -8,10 +8,22 @@
 #
 
 import compiler/renderer
+import compiler/types
+import compiler/trees
+import compiler/wordrecg
+import compiler/sempass2
 import strformat
+import algorithm
 import tables
-import std/sha1
 import times
+import procmonitor
+
+template tryImport(module) = import module
+
+when compiles tryImport ../dist/checksums/src/checksums/sha1:
+  import ../dist/checksums/src/checksums/sha1
+else:
+  import checksums/sha1
 
 ## Nimsuggest is a tool that helps to give editors IDE like capabilities.
 
@@ -22,18 +34,21 @@ import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp
 # Do NOT import suggest. It will lead to weird bugs with
 # suggestionResultHook, because suggest.nim is included by sigmatch.
 # So we import that one instead.
-import compiler / [options, commands, modules, sem,
+import compiler / [options, commands, modules,
   passes, passaux, msgs,
   sigmatch, ast,
   idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper,
-  pathutils]
+  pathutils, condsyms, syntaxes, suggestsymdb]
 
+when defined(nimPreviewSlimSystem):
+  import std/typedthreads
 
 when defined(windows):
   import winlean
 else:
   import posix
 
+const HighestSuggestProtocolVersion = 4
 const DummyEof = "!EOF!"
 const Usage = """
 Nimsuggest - Tool to give every editor IDE like capabilities for Nim
@@ -46,17 +61,25 @@ Options:
   --address:HOST          binds to that address, by default ""
   --stdin                 read commands from stdin and write results to
                           stdout instead of using sockets
+  --clientProcessId:PID   shutdown nimsuggest in case this process dies
   --epc                   use emacs epc mode
   --debug                 enable debug output
   --log                   enable verbose logging to nimsuggest.log file
   --v1                    use version 1 of the protocol; for backwards compatibility
   --v2                    use version 2(default) of the protocol
   --v3                    use version 3 of the protocol
+  --v4                    use version 4 of the protocol
+  --info:X                information
+    --info:nimVer           return the Nim compiler version that nimsuggest uses internally
+    --info:protocolVer      return the newest protocol version that is supported
+    --info:capabilities     return the capabilities supported by nimsuggest
   --refresh               perform automatic refreshes to keep the analysis precise
   --maxresults:N          limit the number of suggestions to N
   --tester                implies --stdin and outputs a line
                           '""" & DummyEof & """' for the tester
   --find                  attempts to find the project file of the current project
+  --exceptionInlayHints:on|off
+                          globally turn exception inlay hints on|off
 
 The server then listens to the connection and takes line-based commands.
 
@@ -75,7 +98,7 @@ type
 
 var
   gPort = 6000.Port
-  gAddress = ""
+  gAddress = "127.0.0.1"
   gMode: Mode
   gEmitEof: bool # whether we write '!EOF!' dummy lines
   gLogging = defined(logging)
@@ -85,7 +108,7 @@ var
   requests: Channel[string]
   results: Channel[Suggest]
 
-proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int;
+proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
   graph: ModuleGraph);
 
 proc writelnToChannel(line: string) =
@@ -108,6 +131,12 @@ const
          "type 'quit' to quit\n" &
          "type 'debug' to toggle debug mode on/off\n" &
          "type 'terse' to toggle terse mode on/off"
+  #List of currently supported capabilities. So lang servers/ides can iterate over and check for what's enabled
+  Capabilities = [
+    "con", #current NimSuggest supports the `con` commmand
+    "exceptionInlayHints",
+    "unknownFile", #current NimSuggest can handle unknown files
+  ]
 
 proc parseQuoted(cmd: string; outp: var string; start: int): int =
   var i = start
@@ -137,6 +166,9 @@ proc sexp(s: Suggest): SexpNode =
   ])
   if s.section == ideSug:
     result.add convertSexp(s.prefix)
+  if s.section in {ideOutline, ideExpand} and s.version == 3:
+    result.add convertSexp(s.endLine.int)
+    result.add convertSexp(s.endCol)
 
 proc sexp(s: seq[Suggest]): SexpNode =
   result = newSList()
@@ -149,7 +181,7 @@ proc listEpc(): SexpNode =
     argspecs = sexp("file line column dirtyfile".split(" ").map(newSSymbol))
     docstring = sexp("line starts at 1, column at 0, dirtyfile is optional")
   result = newSList()
-  for command in ["sug", "con", "def", "use", "dus", "chk", "mod", "globalSymbols", "recompile", "saved", "chkFile"]:
+  for command in ["sug", "con", "def", "use", "dus", "chk", "mod", "globalSymbols", "recompile", "saved", "chkFile", "declaration", "inlayHints"]:
     let
       cmd = sexp(command)
       methodDesc = newSList()
@@ -172,12 +204,49 @@ proc symFromInfo(graph: ModuleGraph; trackPos: TLineInfo): PSym =
   if m != nil and m.ast != nil:
     result = findNode(m.ast, trackPos)
 
-proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
+template benchmark(benchmarkName: untyped, code: untyped) =
+  block:
+    myLog "Started [" & benchmarkName & "]..."
+    let t0 = epochTime()
+    code
+    let elapsed = epochTime() - t0
+    let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
+    myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
+
+proc clearInstCache(graph: ModuleGraph, projectFileIdx: FileIndex) =
+  if projectFileIdx == InvalidFileIdx:
+    graph.typeInstCache.clear()
+    graph.procInstCache.clear()
+    return
+  var typeIdsToDelete = newSeq[ItemId]()
+  for id in graph.typeInstCache.keys:
+    if id.module == projectFileIdx.int:
+      typeIdsToDelete.add id
+  for id in typeIdsToDelete:
+    graph.typeInstCache.del id
+  var procIdsToDelete = newSeq[ItemId]()
+  for id in graph.procInstCache.keys:
+    if id.module == projectFileIdx.int:
+      procIdsToDelete.add id
+  for id in procIdsToDelete:
+    graph.procInstCache.del id
+
+  for tbl in mitems(graph.attachedOps):
+    var attachedOpsToDelete = newSeq[ItemId]()
+    for id in tbl.keys:
+      if id.module == projectFileIdx.int and sfOverridden in resolveAttachedOp(graph, tbl[id]).flags:
+        attachedOpsToDelete.add id
+    for id in attachedOpsToDelete:
+      tbl.del id
+
+proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, tag: string,
              graph: ModuleGraph) =
   let conf = graph.config
 
-  if conf.suggestVersion == 3:
-    executeNoHooksV3(cmd, file, dirtyfile, line, col, graph)
+  if conf.suggestVersion >= 3:
+    let command = fmt "cmd = {cmd} {file}:{line}:{col}"
+    benchmark command:
+      executeNoHooksV3(cmd, file, dirtyfile, line, col, tag, graph)
     return
 
   myLog("cmd: " & $cmd & ", file: " & file.string &
@@ -198,6 +267,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
   if conf.suggestVersion == 1:
     graph.usageSym = nil
   if not isKnownFile:
+    graph.clearInstCache(dirtyIdx)
     graph.compileProject(dirtyIdx)
   if conf.suggestVersion == 0 and conf.ideCmd in {ideUse, ideDus} and
       dirtyfile.isEmpty:
@@ -208,6 +278,7 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
     graph.markClientsDirty dirtyIdx
     if conf.ideCmd != ideMod:
       if isKnownFile:
+        graph.clearInstCache(modIdx)
         graph.compileProject(modIdx)
   if conf.ideCmd in {ideUse, ideDus}:
     let u = if conf.suggestVersion != 1: graph.symFromInfo(conf.m.trackPos) else: graph.usageSym
@@ -216,7 +287,10 @@ proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
     else:
       localError(conf, conf.m.trackPos, "found no symbol at this position " & (conf $ conf.m.trackPos))
 
-proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
+proc executeNoHooks(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int, graph: ModuleGraph) =
+  executeNoHooks(cmd, file, dirtyfile, line, col, "", graph)
+
+proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int; tag: string,
              graph: ModuleGraph) =
   if cmd == ideChk:
     graph.config.structuredErrorHook = errorHook
@@ -224,7 +298,7 @@ proc execute(cmd: IdeCmd, file, dirtyfile: AbsoluteFile, line, col: int;
   else:
     graph.config.structuredErrorHook = nil
     graph.config.writelnHook = myLog
-  executeNoHooks(cmd, file, dirtyfile, line, col, graph)
+  executeNoHooks(cmd, file, dirtyfile, line, col, tag, graph)
 
 proc executeEpc(cmd: IdeCmd, args: SexpNode;
                 graph: ModuleGraph) =
@@ -235,7 +309,7 @@ proc executeEpc(cmd: IdeCmd, args: SexpNode;
   var dirtyfile = AbsoluteFile""
   if len(args) > 3:
     dirtyfile = AbsoluteFile args[3].getStr("")
-  execute(cmd, file, dirtyfile, int(line), int(column), graph)
+  execute(cmd, file, dirtyfile, int(line), int(column), args[3].getStr, graph)
 
 proc returnEpc(socket: Socket, uid: BiggestInt, s: SexpNode|string,
                returnSymbol = "return") =
@@ -455,9 +529,16 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   of "project": conf.ideCmd = ideProject
   of "changed": conf.ideCmd = ideChanged
   of "globalsymbols": conf.ideCmd = ideGlobalSymbols
+  of "declaration": conf.ideCmd = ideDeclaration
+  of "expand": conf.ideCmd = ideExpand
   of "chkfile": conf.ideCmd = ideChkFile
   of "recompile": conf.ideCmd = ideRecompile
   of "type": conf.ideCmd = ideType
+  of "inlayhints":
+    if conf.suggestVersion >= 4:
+      conf.ideCmd = ideInlayHints
+    else:
+      err()
   else: err()
   var dirtyfile = ""
   var orig = ""
@@ -474,6 +555,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   i += parseInt(cmd, line, i)
   i += skipWhile(cmd, seps, i)
   i += parseInt(cmd, col, i)
+  let tag = substr(cmd, i)
 
   if conf.ideCmd == ideKnown:
     results.send(Suggest(section: ideKnown, quality: ord(fileInfoKnown(conf, AbsoluteFile orig))))
@@ -482,18 +564,9 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) =
   else:
     if conf.ideCmd == ideChk:
       for cm in cachedMsgs: errorHook(conf, cm.info, cm.msg, cm.sev)
-    execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, graph)
+    execute(conf.ideCmd, AbsoluteFile orig, AbsoluteFile dirtyfile, line, col, tag, graph)
   sentinel()
 
-template benchmark(benchmarkName: string, code: untyped) =
-  block:
-    myLog "Started [" & benchmarkName & "]..."
-    let t0 = epochTime()
-    code
-    let elapsed = epochTime() - t0
-    let elapsedStr = elapsed.formatFloat(format = ffDecimal, precision = 3)
-    myLog "CPU Time [" & benchmarkName & "] " & elapsedStr & "s"
-
 proc recompileFullProject(graph: ModuleGraph) =
   benchmark "Recompilation(clean)":
     graph.resetForBackend()
@@ -505,9 +578,9 @@ proc recompileFullProject(graph: ModuleGraph) =
 
 proc mainThread(graph: ModuleGraph) =
   let conf = graph.config
-  if gLogging:
-    for it in conf.searchPaths:
-      log(it.string)
+  myLog "searchPaths: "
+  for it in conf.searchPaths:
+    myLog("  " & it.string)
 
   proc wrHook(line: string) {.closure.} =
     if gMode == mepc:
@@ -530,7 +603,7 @@ proc mainThread(graph: ModuleGraph) =
     else:
       os.sleep 250
       idle += 1
-    if idle == 20 and gRefresh and conf.suggestVersion != 3:
+    if idle == 20 and gRefresh and conf.suggestVersion < 3:
       # we use some nimsuggest activity to enable a lazy recompile:
       conf.ideCmd = ideChk
       conf.writelnHook = proc (s: string) = discard
@@ -549,6 +622,7 @@ proc mainCommand(graph: ModuleGraph) =
   registerPass graph, verbosePass
   registerPass graph, semPass
   conf.setCmd cmdIdeTools
+  defineSymbol(conf.symbols, $conf.backend)
   wantMainModule(conf)
 
   if not fileExists(conf.projectFull):
@@ -560,7 +634,7 @@ proc mainCommand(graph: ModuleGraph) =
   # do not print errors, but log them
   conf.writelnHook = proc (msg: string) = discard
 
-  if graph.config.suggestVersion == 3:
+  if graph.config.suggestVersion >= 3:
     graph.config.structuredErrorHook = proc (conf: ConfigRef; info: TLineInfo; msg: string; sev: Severity) =
       let suggest = Suggest(section: ideChk, filePath: toFullPath(conf, info),
         line: toLinenumber(info), column: toColumn(info), doc: msg, forth: $sev)
@@ -574,6 +648,9 @@ proc mainCommand(graph: ModuleGraph) =
   open(requests)
   open(results)
 
+  if graph.config.clientProcessId != 0:
+    hookProcMonitor(graph.config.clientProcessId)
+
   case gMode
   of mstdin: createThread(inputThread, replStdin, (gPort, gAddress))
   of mtcp: createThread(inputThread, replTcp, (gPort, gAddress))
@@ -624,6 +701,25 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
       of "v1": conf.suggestVersion = 1
       of "v2": conf.suggestVersion = 0
       of "v3": conf.suggestVersion = 3
+      of "v4": conf.suggestVersion = 4
+      of "info":
+        case p.val.normalize
+        of "protocolver":
+          stdout.writeLine(HighestSuggestProtocolVersion)
+          quit 0
+        of "nimver":
+          stdout.writeLine(system.NimVersion)
+          quit 0
+        of "capabilities":
+          stdout.writeLine(Capabilities.toSeq.mapIt($it).join(" "))
+          quit 0
+        else:
+          processSwitch(pass, p, conf)
+      of "exceptioninlayhints":
+        case p.val.normalize
+        of "", "on": incl(conf.globalOptions, optIdeExceptionInlayHints)
+        of "off": excl(conf.globalOptions, optIdeExceptionInlayHints)
+        else: processSwitch(pass, p, conf)
       of "tester":
         gMode = mstdin
         gEmitEof = true
@@ -638,6 +734,8 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) =
         conf.suggestMaxResults = parseInt(p.val)
       of "find":
         findProject = true
+      of "clientprocessid":
+        conf.clientProcessId = parseInt(p.val)
       else: processSwitch(pass, p, conf)
     of cmdArgument:
       let a = unixToNativePath(p.key)
@@ -669,20 +767,16 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) =
 
   if gMode != mstdin:
     conf.writelnHook = proc (msg: string) = discard
-  # Find Nim's prefix dir.
-  let binaryPath = findExe("nim")
-  if binaryPath == "":
-    raise newException(IOError,
-        "Cannot find Nim standard library: Nim compiler not in PATH")
-  conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir()
-  if not dirExists(conf.prefixDir / RelativeDir"lib"):
-    conf.prefixDir = AbsoluteDir""
-
+  conf.prefixDir = conf.getPrefixDir()
   #msgs.writelnHook = proc (line: string) = log(line)
   myLog("START " & conf.projectFull.string)
 
   var graph = newModuleGraph(cache, conf)
   if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
+
+    if conf.selectedGC == gcUnselected and
+          conf.backend != backendJs:
+      initOrcDefines(conf)
     mainCommand(graph)
 
 # v3 start
@@ -695,9 +789,7 @@ proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) =
 
   # inst caches are breaking incremental compilation when the cache caches stuff
   # from dirty buffer
-  # TODO: investigate more efficient way to achieve the same
-  # graph.typeInstCache.clear()
-  # graph.procInstCache.clear()
+  graph.clearInstCache(projectFileIdx)
 
   GC_fullCollect()
 
@@ -711,27 +803,100 @@ proc recompilePartially(graph: ModuleGraph, projectFileIdx = InvalidFileIdx) =
     except Exception as e:
       myLog fmt "Failed clean recompilation:\n {e.msg} \n\n {e.getStackTrace()}"
 
+func deduplicateSymInfoPair[SymInfoPair](xs: seq[SymInfoPair]): seq[SymInfoPair] =
+  # xs contains duplicate items and we want to filter them by range because the
+  # sym may not match. This can happen when xs contains the same definition but
+  # with different signature because suggestSym might be called multiple times
+  # for the same symbol (e. g. including/excluding the pragma)
+  result = newSeqOfCap[SymInfoPair](xs.len)
+  for itm in xs.reversed:
+    var found = false
+    for res in result:
+      if res.info.exactEquals(itm.info):
+        found = true
+        break
+    if not found:
+      result.add(itm)
+  result.reverse()
+
+func deduplicateSymInfoPair(xs: SuggestFileSymbolDatabase): SuggestFileSymbolDatabase =
+  # xs contains duplicate items and we want to filter them by range because the
+  # sym may not match. This can happen when xs contains the same definition but
+  # with different signature because suggestSym might be called multiple times
+  # for the same symbol (e. g. including/excluding the pragma)
+  result = SuggestFileSymbolDatabase(
+    lineInfo: newSeqOfCap[TinyLineInfo](xs.lineInfo.len),
+    sym: newSeqOfCap[PSym](xs.sym.len),
+    isDecl: newPackedBoolArray(),
+    caughtExceptions: newSeqOfCap[seq[PType]](xs.caughtExceptions.len),
+    caughtExceptionsSet: newPackedBoolArray(),
+    fileIndex: xs.fileIndex,
+    trackCaughtExceptions: xs.trackCaughtExceptions,
+    isSorted: false
+  )
+  var i = xs.lineInfo.high
+  while i >= 0:
+    let itm = xs.lineInfo[i]
+    var found = false
+    for res in result.lineInfo:
+      if res.exactEquals(itm):
+        found = true
+        break
+    if not found:
+      result.add(xs.getSymInfoPair(i))
+    dec i
+  result.reverse()
+
+proc findSymData(graph: ModuleGraph, trackPos: TLineInfo):
+    ref SymInfoPair =
+  let db = graph.fileSymbols(trackPos.fileIndex).deduplicateSymInfoPair
+  doAssert(db.fileIndex == trackPos.fileIndex)
+  for i in db.lineInfo.low..db.lineInfo.high:
+    if isTracked(db.lineInfo[i], TinyLineInfo(line: trackPos.line, col: trackPos.col), db.sym[i].name.s.len):
+      var res = db.getSymInfoPair(i)
+      new(result)
+      result[] = res
+      break
+
+func isInRange*(current, startPos, endPos: TinyLineInfo, tokenLen: int): bool =
+  result =
+    (current.line > startPos.line or (current.line == startPos.line and current.col>=startPos.col)) and
+    (current.line < endPos.line or (current.line == endPos.line and current.col <= endPos.col))
+
+proc findSymDataInRange(graph: ModuleGraph, startPos, endPos: TLineInfo):
+    seq[SymInfoPair] =
+  result = newSeq[SymInfoPair]()
+  let db = graph.fileSymbols(startPos.fileIndex).deduplicateSymInfoPair
+  for i in db.lineInfo.low..db.lineInfo.high:
+    if isInRange(db.lineInfo[i], TinyLineInfo(line: startPos.line, col: startPos.col), TinyLineInfo(line: endPos.line, col: endPos.col), db.sym[i].name.s.len):
+      result.add(db.getSymInfoPair(i))
+
 proc findSymData(graph: ModuleGraph, file: AbsoluteFile; line, col: int):
     ref SymInfoPair =
   let
     fileIdx = fileInfoIdx(graph.config, file)
     trackPos = newLineInfo(fileIdx, line, col)
-  for s in graph.fileSymbols(fileIdx):
-    if isTracked(s.info, trackPos, s.sym.name.s.len):
-      new(result)
-      result[] = s
-      break
+  result = findSymData(graph, trackPos)
+
+proc findSymDataInRange(graph: ModuleGraph, file: AbsoluteFile; startLine, startCol, endLine, endCol: int):
+    seq[SymInfoPair] =
+  let
+    fileIdx = fileInfoIdx(graph.config, file)
+    startPos = newLineInfo(fileIdx, startLine, startCol)
+    endPos = newLineInfo(fileIdx, endLine, endCol)
+  result = findSymDataInRange(graph, startPos, endPos)
 
 proc markDirtyIfNeeded(graph: ModuleGraph, file: string, originalFileIdx: FileIndex) =
   let sha = $sha1.secureHashFile(file)
-  if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd == ideSug:
+  if graph.config.m.fileInfos[originalFileIdx.int32].hash != sha or graph.config.ideCmd in {ideSug, ideCon}:
     myLog fmt "{file} changed compared to last compilation"
     graph.markDirty originalFileIdx
     graph.markClientsDirty originalFileIdx
   else:
     myLog fmt "No changes in file {file} compared to last compilation"
 
-proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSection = ideNone) =
+proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
   let section = if defaultSection != ideNone:
                   defaultSection
                 elif sym.info.exactEquals(info):
@@ -739,14 +904,156 @@ proc suggestResult(graph: ModuleGraph, sym: PSym, info: TLineInfo, defaultSectio
                 else:
                   ideUse
   let suggest = symToSuggest(graph, sym, isLocal=false, section,
-                             info, 100, PrefixMatch.None, false, 0)
+                             info, 100, PrefixMatch.None, false, 0,
+                             endLine = endLine, endCol = endCol)
   suggestResult(graph.config, suggest)
 
+proc suggestInlayHintResultType(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, endLine: uint16 = 0, endCol = 0) =
+  let section = if defaultSection != ideNone:
+                  defaultSection
+                elif sym.info.exactEquals(info):
+                  ideDef
+                else:
+                  ideUse
+  var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
+                                info, 100, PrefixMatch.None, false, 0, true,
+                                endLine = endLine, endCol = endCol)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayTypeHint(suggestDef)
+  suggestDef.section = ideInlayHints
+  if sym.kind == skForVar:
+    suggestDef.inlayHintInfo.allowInsert = false
+  suggestResult(graph.config, suggestDef)
+
+proc suggestInlayHintResultException(graph: ModuleGraph, sym: PSym, info: TLineInfo,
+                   defaultSection = ideNone, caughtExceptions: seq[PType], caughtExceptionsSet: bool, endLine: uint16 = 0, endCol = 0) =
+  if not caughtExceptionsSet:
+    return
+
+  if sym.kind == skParam and sfEffectsDelayed in sym.flags:
+    return
+
+  var raisesList: seq[PType] = @[getEbase(graph, info)]
+
+  let t = sym.typ
+  if not isNil(t) and not isNil(t.n) and t.n.len > 0 and t.n[0].len > exceptionEffects:
+    let effects = t.n[0]
+    if effects.kind == nkEffectList and effects.len == effectListLen:
+      let effs = effects[exceptionEffects]
+      if not isNil(effs):
+        raisesList = @[]
+        for eff in items(effs):
+          if not isNil(eff):
+            raisesList.add(eff.typ)
+
+  var propagatedExceptionList: seq[PType] = @[]
+  for re in raisesList:
+    var exceptionIsPropagated = true
+    for ce in caughtExceptions:
+      if isNil(ce) or safeInheritanceDiff(re, ce) <= 0:
+        exceptionIsPropagated = false
+        break
+    if exceptionIsPropagated:
+      propagatedExceptionList.add(re)
+
+  if propagatedExceptionList.len == 0:
+    return
+
+  let section = if defaultSection != ideNone:
+                  defaultSection
+                elif sym.info.exactEquals(info):
+                  ideDef
+                else:
+                  ideUse
+  var suggestDef = symToSuggest(graph, sym, isLocal=false, section,
+                                info, 100, PrefixMatch.None, false, 0, true,
+                                endLine = endLine, endCol = endCol)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintLeft(suggestDef, propagatedExceptionList)
+  suggestDef.section = ideInlayHints
+  suggestResult(graph.config, suggestDef)
+  suggestDef.inlayHintInfo = suggestToSuggestInlayExceptionHintRight(suggestDef, propagatedExceptionList)
+  suggestResult(graph.config, suggestDef)
+
 const
   # kinds for ideOutline and ideGlobalSymbols
   searchableSymKinds = {skField, skEnumField, skIterator, skMethod, skFunc, skProc, skConverter, skTemplate}
 
-proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int;
+proc symbolEqual(left, right: PSym): bool =
+  # More relaxed symbol comparison
+  return left.info.exactEquals(right.info) and left.name == right.name
+
+proc findDef(n: PNode, line: uint16, col: int16): PNode =
+  if n.kind in {nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkMacroDef}:
+    if n.info.line == line:
+      return n
+  else:
+    for i in 0 ..< safeLen(n):
+      let res = findDef(n[i], line, col)
+      if res != nil: return res
+
+proc findByTLineInfo(trackPos: TLineInfo, infoPairs: SuggestFileSymbolDatabase):
+    ref SymInfoPair =
+  result = nil
+  if infoPairs.fileIndex == trackPos.fileIndex:
+    for i in infoPairs.lineInfo.low..infoPairs.lineInfo.high:
+      let s = infoPairs.getSymInfoPair(i)
+      if s.info.exactEquals trackPos:
+        new(result)
+        result[] = s
+        break
+
+proc outlineNode(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
+  proc checkSymbol(sym: PSym, info: TLineInfo): bool =
+    result = (sym.owner.kind in {skModule, skType} or sym.kind in {skProc, skMethod, skIterator, skTemplate, skType})
+
+  if n.kind == nkSym and n.sym.checkSymbol(n.info):
+    graph.suggestResult(n.sym, n.sym.info, ideOutline, endInfo.line, endInfo.col)
+    return true
+  elif n.kind == nkIdent:
+    let symData = findByTLineInfo(n.info, infoPairs)
+    if symData != nil and symData.sym.checkSymbol(symData.info):
+       let sym = symData.sym
+       graph.suggestResult(sym, sym.info, ideOutline, endInfo.line, endInfo.col)
+       return true
+
+proc handleIdentOrSym(graph: ModuleGraph, n: PNode, endInfo: TLineInfo, infoPairs: SuggestFileSymbolDatabase): bool =
+  for child in n:
+    if child.kind in {nkIdent, nkSym}:
+      if graph.outlineNode(child, endInfo, infoPairs):
+        return true
+    elif child.kind == nkPostfix:
+      if graph.handleIdentOrSym(child, endInfo, infoPairs):
+        return true
+
+proc iterateOutlineNodes(graph: ModuleGraph, n: PNode, infoPairs: SuggestFileSymbolDatabase) =
+  var matched = true
+  if n.kind == nkIdent:
+    let symData = findByTLineInfo(n.info, infoPairs)
+    if symData != nil and symData.sym.kind == skEnumField and symData.info.exactEquals(symData.sym.info):
+       let sym = symData.sym
+       graph.suggestResult(sym, sym.info, ideOutline, n.endInfo.line, n.endInfo.col)
+  elif (n.kind in {nkFuncDef, nkProcDef, nkTypeDef, nkMacroDef, nkTemplateDef, nkConverterDef, nkEnumFieldDef, nkConstDef}):
+    matched = handleIdentOrSym(graph, n, n.endInfo, infoPairs)
+  else:
+    matched = false
+
+  if n.kind != nkFormalParams:
+    for child in n:
+      graph.iterateOutlineNodes(child, infoPairs)
+
+proc calculateExpandRange(n: PNode, info: TLineInfo): TLineInfo =
+  if ((n.kind in {nkFuncDef, nkProcDef, nkIteratorDef, nkTemplateDef, nkMethodDef, nkConverterDef} and
+          n.info.exactEquals(info)) or
+         (n.kind in {nkCall, nkCommand} and n[0].info.exactEquals(info))):
+    result = n.endInfo
+  else:
+    for child in n:
+      result = child.calculateExpandRange(info)
+      if result != unknownLineInfo:
+        return result
+    result = unknownLineInfo
+
+proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, line, col: int; tag: string,
     graph: ModuleGraph) =
   let conf = graph.config
   conf.writelnHook = proc (s: string) = discard
@@ -758,15 +1065,11 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
 
   conf.ideCmd = cmd
 
-  myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}"
+  myLog fmt "cmd: {cmd}, file: {file}[{line}:{col}], dirtyFile: {dirtyfile}, tag: {tag}"
 
   var fileIndex: FileIndex
 
   if not (cmd in {ideRecompile, ideGlobalSymbols}):
-    if not fileInfoKnown(conf, file):
-      myLog fmt "{file} is unknown, returning no results"
-      return
-
     fileIndex = fileInfoIdx(conf, file)
     msgs.setDirtyFile(
       conf,
@@ -777,7 +1080,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
       graph.markDirtyIfNeeded(dirtyFile.string, fileInfoIdx(conf, file))
 
   # these commands require fully compiled project
-  if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk} and graph.needsCompilation():
+  if cmd in {ideUse, ideDus, ideGlobalSymbols, ideChk, ideInlayHints} and graph.needsCompilation():
     graph.recompilePartially()
     # when doing incremental build for the project root we should make sure that
     # everything is unmarked as no longer beeing dirty in case there is no
@@ -786,16 +1089,15 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
     graph.unmarkAllDirty()
 
   # these commands require partially compiled project
-  elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType} and
-       (graph.needsCompilation(fileIndex) or cmd == ideSug):
+  elif cmd in {ideSug, ideCon, ideOutline, ideHighlight, ideDef, ideChkFile, ideType, ideDeclaration, ideExpand} and
+       (graph.needsCompilation(fileIndex) or cmd in {ideSug, ideCon}):
     # for ideSug use v2 implementation
-    if cmd == ideSug:
+    if cmd in {ideSug, ideCon}:
       conf.m.trackPos = newLineInfo(fileIndex, line, col)
       conf.m.trackPosAttached = false
     else:
       conf.m.trackPos = default(TLineInfo)
-
-    graph.recompilePartially(fileIndex)
+      graph.recompilePartially(fileIndex)
 
   case cmd
   of ideDef:
@@ -814,13 +1116,20 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
   of ideUse, ideDus:
     let symbol = graph.findSymData(file, line, col)
     if not symbol.isNil:
+      var res: seq[SymInfoPair] = @[]
       for s in graph.suggestSymbolsIter:
-        if s.sym == symbol.sym:
-          graph.suggestResult(s.sym, s.info)
+        if s.sym.symbolEqual(symbol.sym):
+          res.add(s)
+      for s in res.deduplicateSymInfoPair():
+        graph.suggestResult(s.sym, s.info)
   of ideHighlight:
     let sym = graph.findSymData(file, line, col)
     if not sym.isNil:
-      let usages = graph.fileSymbols(fileIndex).filterIt(it.sym == sym.sym)
+      let fs = graph.fileSymbols(fileIndex)
+      var usages: seq[SymInfoPair] = @[]
+      for i in fs.lineInfo.low..fs.lineInfo.high:
+        if fs.sym[i] == sym.sym:
+          usages.add(fs.getSymInfoPair(i))
       myLog fmt "Found {usages.len} usages in {file.string}"
       for s in usages:
         graph.suggestResult(s.sym, s.info)
@@ -828,20 +1137,16 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
     graph.recompileFullProject()
   of ideChanged:
     graph.markDirtyIfNeeded(file.string, fileIndex)
-  of ideSug:
-    # ideSug performs partial build of the file, thus mark it dirty for the
+  of ideSug, ideCon:
+    # ideSug/ideCon performs partial build of the file, thus mark it dirty for the
     # future calls.
     graph.markDirtyIfNeeded(file.string, fileIndex)
+    graph.recompilePartially(fileIndex) 
+    let m = graph.getModule fileIndex
+    incl m.flags, sfDirty 
   of ideOutline:
-    let
-      module = graph.getModule fileIndex
-      symbols = graph.fileSymbols(fileIndex)
-        .filterIt(it.sym.info.exactEquals(it.info) and
-                    (it.sym.owner == module or
-                     it.sym.kind in searchableSymKinds))
-
-    for s in symbols:
-      graph.suggestResult(s.sym, s.info, ideOutline)
+    let n = parseFile(fileIndex, graph.cache, graph.config)
+    graph.iterateOutlineNodes(n, graph.fileSymbols(fileIndex).deduplicateSymInfoPair)
   of ideChk:
     myLog fmt "Reporting errors for {graph.suggestErrors.len} file(s)"
     for sug in graph.suggestErrorsIter:
@@ -852,16 +1157,126 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile,
     for error in errors:
       suggestResult(graph.config, error)
   of ideGlobalSymbols:
-    var counter = 0
+    var
+      counter = 0
+      res: seq[SymInfoPair] = @[]
+
     for s in graph.suggestSymbolsIter:
       if (sfGlobal in s.sym.flags or s.sym.kind in searchableSymKinds) and
           s.sym.info == s.info:
         if contains(s.sym.name.s, file.string):
           inc counter
-          graph.suggestResult(s.sym, s.info)
-        # stop after first 100 results
-        if counter > 100:
+          res = res.filterIt(not it.info.exactEquals(s.info))
+          res.add s
+          # stop after first 1000 matches...
+          if counter > 1000:
+            break
+
+    # ... then sort them by weight ...
+    res.sort() do (left, right: SymInfoPair) -> int:
+      let
+        leftString = left.sym.name.s
+        rightString = right.sym.name.s
+        leftIndex = leftString.find(file.string)
+        rightIndex = rightString.find(file.string)
+
+      if leftIndex == rightIndex:
+        result = cmp(toLowerAscii(leftString),
+                     toLowerAscii(rightString))
+      else:
+        result = cmp(leftIndex, rightIndex)
+
+    # ... and send first 100 results
+    if res.len > 0:
+      for i in 0 .. min(100, res.len - 1):
+        let s = res[i]
+        graph.suggestResult(s.sym, s.info)
+
+  of ideDeclaration:
+    let s = graph.findSymData(file, line, col)
+    if not s.isNil:
+      # find first mention of the symbol in the file containing the definition.
+      # It is either the definition or the declaration.
+      var first: SymInfoPair
+      let db = graph.fileSymbols(s.sym.info.fileIndex).deduplicateSymInfoPair
+      for i in db.lineInfo.low..db.lineInfo.high:
+        if s.sym.symbolEqual(db.sym[i]):
+          first = db.getSymInfoPair(i)
           break
+
+      if s.info.exactEquals(first.info):
+        # we are on declaration, go to definition
+        graph.suggestResult(first.sym, first.sym.info, ideDeclaration)
+      else:
+        # we are on definition or usage, look for declaration
+        graph.suggestResult(first.sym, first.info, ideDeclaration)
+  of ideExpand:
+    var level: int = high(int)
+    let index = skipWhitespace(tag, 0);
+    let trimmed = substr(tag, index)
+    if not (trimmed == "" or trimmed == "all"):
+      discard parseInt(trimmed, level, 0)
+
+    conf.expandPosition = newLineInfo(fileIndex, line, col)
+    conf.expandLevels = level
+    conf.expandProgress = false
+    conf.expandNodeResult = ""
+
+    graph.markDirty fileIndex
+    graph.markClientsDirty fileIndex
+    graph.recompilePartially()
+    var suggest = Suggest()
+    suggest.section = ideExpand
+    suggest.version = 3
+    suggest.line = line
+    suggest.column = col
+    suggest.doc = graph.config.expandNodeResult
+    if suggest.doc != "":
+      let
+        n = parseFile(fileIndex, graph.cache, graph.config)
+        endInfo = n.calculateExpandRange(conf.expandPosition)
+
+      suggest.endLine = endInfo.line
+      suggest.endCol = endInfo.col
+
+    suggestResult(graph.config, suggest)
+
+    graph.markDirty fileIndex
+    graph.markClientsDirty fileIndex
+  of ideInlayHints:
+    myLog fmt "Executing inlayHints"
+    var endLine = 0
+    var endCol = -1
+    var i = 0
+    i += skipWhile(tag, seps, i)
+    i += parseInt(tag, endLine, i)
+    i += skipWhile(tag, seps, i)
+    i += parseInt(tag, endCol, i)
+    i += skipWhile(tag, seps, i)
+    var typeHints = true
+    var exceptionHints = false
+    while i <= tag.high:
+      var token: string
+      i += parseUntil(tag, token, seps, i)
+      i += skipWhile(tag, seps, i)
+      case token:
+      of "+typeHints":
+        typeHints = true
+      of "-typeHints":
+        typeHints = false
+      of "+exceptionHints":
+        exceptionHints = true
+      of "-exceptionHints":
+        exceptionHints = false
+      else:
+        myLog fmt "Discarding unknown inlay hint parameter {token}"
+
+    let s = graph.findSymDataInRange(file, line, col, endLine, endCol)
+    for q in s:
+      if typeHints and q.sym.kind in {skLet, skVar, skForVar, skConst} and q.isDecl and not q.sym.hasUserSpecifiedType:
+        graph.suggestInlayHintResultType(q.sym, q.info, ideInlayHints)
+      if exceptionHints and q.sym.kind in {skProc, skFunc, skMethod, skVar, skLet, skParam} and not q.isDecl:
+        graph.suggestInlayHintResultException(q.sym, q.info, ideInlayHints, caughtExceptions = q.caughtExceptions, caughtExceptionsSet = q.caughtExceptionsSet)
   else:
     myLog fmt "Discarding {cmd}"
 
@@ -882,10 +1297,12 @@ else:
     proc mockCommand(graph: ModuleGraph) =
       retval = graph
       let conf = graph.config
+      conf.setCmd cmdIdeTools
+      defineSymbol(conf.symbols, $conf.backend)
       clearPasses(graph)
       registerPass graph, verbosePass
       registerPass graph, semPass
-      conf.setCmd cmdIdeTools
+
       wantMainModule(conf)
 
       if not fileExists(conf.projectFull):
@@ -928,13 +1345,7 @@ else:
       conf.writelnHook = proc (msg: string) = discard
     # Find Nim's prefix dir.
     if nimPath == "":
-      let binaryPath = findExe("nim")
-      if binaryPath == "":
-        raise newException(IOError,
-            "Cannot find Nim standard library: Nim compiler not in PATH")
-      conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir()
-      if not dirExists(conf.prefixDir / RelativeDir"lib"):
-        conf.prefixDir = AbsoluteDir""
+      conf.prefixDir = conf.getPrefixDir()
     else:
       conf.prefixDir = AbsoluteDir nimPath
 
@@ -945,9 +1356,9 @@ else:
     if self.loadConfigsAndProcessCmdLine(cache, conf, graph):
       mockCommand(graph)
     if gLogging:
-      log("Search paths:")
+      myLog("Search paths:")
       for it in conf.searchPaths:
-        log(" " & it.string)
+        myLog(" " & it.string)
 
     retval.doStopCompile = proc (): bool = false
     return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[])
diff --git a/nimsuggest/nimsuggest.nimble b/nimsuggest/nimsuggest.nimble
index 53b5d1d6f..b3790e116 100644
--- a/nimsuggest/nimsuggest.nimble
+++ b/nimsuggest/nimsuggest.nimble
@@ -1,8 +1,8 @@
-version       = "0.1.0"
-author        = "Andreas Rumpf"
-description   = "Tool for providing auto completion data for Nim source code."
-license       = "MIT"
-
+include "../lib/system/compilation.nim"
+version = $NimMajor & "." & $NimMinor & "." & $NimPatch
+author = "Andreas Rumpf"
+description = "Tool for providing auto completion data for Nim source code."
+license = "MIT"
 bin = @["nimsuggest"]
 
-requires "nim >= 1.1.1"
+requires "compiler >= 1.9.0" , "checksums"
diff --git a/nimsuggest/procmonitor.nim b/nimsuggest/procmonitor.nim
new file mode 100644
index 000000000..0f1ba1e0d
--- /dev/null
+++ b/nimsuggest/procmonitor.nim
@@ -0,0 +1,34 @@
+# Monitor a client process and shutdown the current process, if the client
+# process is found to be dead
+
+import os
+
+when defined(posix):
+  import posix_utils
+  import posix
+
+when defined(windows):
+  import winlean
+
+when defined(posix):
+  proc monitorClientProcessIdThreadProc(pid: int) {.thread.} =
+    while true:
+      sleep(1000)
+      try:
+        sendSignal(Pid(pid), 0)
+      except:
+        discard kill(Pid(getCurrentProcessId()), cint(SIGTERM))
+
+when defined(windows):
+  proc monitorClientProcessIdThreadProc(pid: int) {.thread.} =
+    var process = openProcess(SYNCHRONIZE, 0, DWORD(pid))
+    if process != 0:
+      discard waitForSingleObject(process, INFINITE)
+      discard closeHandle(process)
+    quit(0)
+
+var tid: Thread[int]
+
+proc hookProcMonitor*(pid: int) =
+  when defined(posix) or defined(windows):
+    createThread(tid, monitorClientProcessIdThreadProc, pid)
diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim
index 467f922cc..03369ccb7 100644
--- a/nimsuggest/sexp.nim
+++ b/nimsuggest/sexp.nim
@@ -14,6 +14,9 @@ import
 
 import std/private/decode_helpers
 
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, formatfloat]
+
 type
   SexpEventKind* = enum  ## enumeration of all events that may occur when parsing
     sexpError,           ## an error occurred during parsing
@@ -401,7 +404,7 @@ macro convertSexp*(x: untyped): untyped =
   ## `%` for every element.
   result = toSexp(x)
 
-proc `==`* (a, b: SexpNode): bool {.noSideEffect.} =
+func `==`* (a, b: SexpNode): bool =
   ## Check two nodes for equality
   if a.isNil:
     if b.isNil: return true
diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim
index 6e068e067..9b9488348 100644
--- a/nimsuggest/tester.nim
+++ b/nimsuggest/tester.nim
@@ -66,7 +66,7 @@ proc parseTest(filename: string; epcMode=false): Test =
       elif x.startsWith(">"):
         # since 'markers' here are not complete yet, we do the $substitutions
         # afterwards
-        result.script.add((x.substr(1).replaceWord("$path", tpath), ""))
+        result.script.add((x.substr(1).replaceWord("$path", tpath).replaceWord("$file", filename), ""))
       elif x.len > 0:
         # expected output line:
         let x = x % ["file", filename, "lib", libpath]
@@ -218,7 +218,12 @@ proc sexpToAnswer(s: SexpNode): string =
       result.add doc
       result.add '\t'
       result.addInt a[8].getNum
-      if a.len >= 10:
+      if a.len >= 11:
+        result.add '\t'
+        result.addInt a[9].getNum
+        result.add '\t'
+        result.addInt a[10].getNum
+      elif a.len >= 10:
         result.add '\t'
         result.add a[9].getStr
     result.add '\L'
@@ -229,8 +234,8 @@ proc doReport(filename, answer, resp: string; report: var string) =
     var hasDiff = false
     for i in 0..min(resp.len-1, answer.len-1):
       if resp[i] != answer[i]:
-        report.add "\n  Expected:  " & resp.substr(i, i+200)
-        report.add "\n  But got:   " & answer.substr(i, i+200)
+        report.add "\n  Expected:\n" & resp
+        report.add "\n  But got:\n" & answer
         hasDiff = true
         break
     if not hasDiff:
@@ -274,7 +279,13 @@ proc runEpcTest(filename: string): int =
         os.sleep(50)
         inc i
       let a = outp.readAll().strip()
-    let port = parseInt(a)
+    var port: int
+    try:
+      port = parseInt(a)
+    except ValueError:
+      echo "Error parsing port number: " & a
+      echo outp.readAll()
+      quit 1
     socket.connect("localhost", Port(port))
 
     for req, resp in items(s.script):
@@ -342,8 +353,8 @@ proc main() =
   if os.paramCount() > 0:
     let x = os.paramStr(1)
     let xx = expandFilename x
+    # run only stdio when running single test
     failures += runTest(xx)
-    failures += runEpcTest(xx)
   else:
     let files = toSeq(walkFiles(tpath / "t*.nim"))
     for i, x in files:
diff --git a/nimsuggest/tests/module_20265.nim b/nimsuggest/tests/module_20265.nim
new file mode 100644
index 000000000..24b7d10c9
--- /dev/null
+++ b/nimsuggest/tests/module_20265.nim
@@ -0,0 +1,6 @@
+type A* = tuple
+  a: int
+  b: int
+
+var x*: A = (a: 2, b: 10)
+var y* = (a: 2, b: 10)
diff --git a/nimsuggest/tests/t20265_1.nim b/nimsuggest/tests/t20265_1.nim
new file mode 100644
index 000000000..553b3d545
--- /dev/null
+++ b/nimsuggest/tests/t20265_1.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skField;;a;;int;;*module_20265.nim;;6;;10;;"";;100;;None
+sug;;skField;;b;;int;;*module_20265.nim;;6;;16;;"";;100;;None
+"""
+import module_20265
+y.#[!]#
diff --git a/nimsuggest/tests/t20265_2.nim b/nimsuggest/tests/t20265_2.nim
new file mode 100644
index 000000000..33edf2d9a
--- /dev/null
+++ b/nimsuggest/tests/t20265_2.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skField;;a;;int;;*module_20265.nim;;2;;2;;"";;100;;None
+sug;;skField;;b;;int;;*module_20265.nim;;3;;2;;"";;100;;None
+"""
+import module_20265
+x.#[!]#
diff --git a/nimsuggest/tests/t20440.nim b/nimsuggest/tests/t20440.nim
new file mode 100644
index 000000000..0456aa074
--- /dev/null
+++ b/nimsuggest/tests/t20440.nim
@@ -0,0 +1,7 @@
+when not defined(js):
+  {.fatal: "Crash".}
+echo 4
+
+discard """
+$nimsuggest --v3 --tester $file
+"""
diff --git a/nimsuggest/tests/t20440.nims b/nimsuggest/tests/t20440.nims
new file mode 100644
index 000000000..1336be3d4
--- /dev/null
+++ b/nimsuggest/tests/t20440.nims
@@ -0,0 +1 @@
+switch("backend", "js")
diff --git a/nimsuggest/tests/t21185.nim b/nimsuggest/tests/t21185.nim
new file mode 100644
index 000000000..bf5a0e3cc
--- /dev/null
+++ b/nimsuggest/tests/t21185.nim
@@ -0,0 +1,18 @@
+
+# Reduced case of 21185. Issue was first parameter being static
+proc foo(x: static[int]) = discard
+
+type
+  Person = object
+    name: string
+    age: int
+
+let p = Person()
+p.#[!]#
+
+discard """
+$nimsuggest --tester --v3 --maxresults:2 $file
+>sug $1
+sug;;skField;;age;;int;;$file;;8;;4;;"";;100;;None
+sug;;skField;;name;;string;;$file;;7;;4;;"";;100;;None
+"""
diff --git a/nimsuggest/tests/t22448.nim b/nimsuggest/tests/t22448.nim
new file mode 100644
index 000000000..8664bbbc3
--- /dev/null
+++ b/nimsuggest/tests/t22448.nim
@@ -0,0 +1,11 @@
+proc fn(a: static float) = discard
+proc fn(a: int) = discard
+
+let x = 1
+fn(x)
+
+discard """
+$nimsuggest --tester --v3 $file
+>chk $file
+chk;;skUnknown;;;;Hint;;*
+"""
diff --git a/nimsuggest/tests/tarrowcrash.nim b/nimsuggest/tests/tarrowcrash.nim
new file mode 100644
index 000000000..a303e88f5
--- /dev/null
+++ b/nimsuggest/tests/tarrowcrash.nim
@@ -0,0 +1,20 @@
+# issue #24179
+
+import sugar
+
+type
+    Parser[T] = object
+    
+proc eatWhile[T](p: Parser[T], predicate: T -> bool): seq[T] =
+    return @[]
+
+proc skipWs(p: Parser[char]) =
+    discard p.eatWhile((c: char) => c == 'a')
+#[!]#
+    
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tarrowcrash.nim [Processing]";;0
+chk;;skUnknown;;;;Hint;;$file;;11;;5;;"\'skipWs\' is declared but not used [XDeclaredButNotUsed]";;0
+"""
diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim
index f8e2989c5..be6115c1c 100644
--- a/nimsuggest/tests/tchk1.nim
+++ b/nimsuggest/tests/tchk1.nim
@@ -20,8 +20,8 @@ $nimsuggest --tester $file
 chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk1.nim [Processing]";;0
 chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0
 chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0
-chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0
 chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0
 chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0
 chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'main\' is declared but not used [XDeclaredButNotUsed]";;0
 """
diff --git a/nimsuggest/tests/tchk2.nim b/nimsuggest/tests/tchk2.nim
new file mode 100644
index 000000000..f5404368d
--- /dev/null
+++ b/nimsuggest/tests/tchk2.nim
@@ -0,0 +1,35 @@
+# bug #22794
+type O = object
+
+proc `=destroy`(x: O) = discard
+proc `=trace`(x: var O; env: pointer) = discard
+proc `=copy`(a: var O; b: O) = discard
+proc `=dup`(a: O): O {.nodestroy.} = a
+proc `=sink`(a: var O; b: O) = discard
+
+
+# bug #23316
+type SomeSturct = object
+
+proc `=destroy`(x: SomeSturct) =
+  echo "SomeSturct destroyed"
+
+# bug #23867
+type ObjStr = object
+  s: string
+
+let ostr = ObjStr() # <-- nimsuggest crashes
+discard ostr
+
+type ObjSeq = object
+  s: seq[int]
+
+let oseq = ObjSeq() # <-- nimsuggest crashes
+discard oseq
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk2.nim [Processing]";;0
+"""
diff --git a/nimsuggest/tests/tconcept1.nim b/nimsuggest/tests/tconcept1.nim
new file mode 100644
index 000000000..d81cd8120
--- /dev/null
+++ b/nimsuggest/tests/tconcept1.nim
@@ -0,0 +1,12 @@
+SomeNumber = concept
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept1.nim [Processing]";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"the \'concept\' keyword is only valid in \'type\' sections";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;13;;"expression expected, but found \'keyword concept\'";;0
+chk;;skUnknown;;;;Error;;$file;;1;;0;;"\'SomeNumber\' cannot be assigned to";;0
+"""
diff --git a/nimsuggest/tests/tconcept2.nim b/nimsuggest/tests/tconcept2.nim
new file mode 100644
index 000000000..7f7d147f5
--- /dev/null
+++ b/nimsuggest/tests/tconcept2.nim
@@ -0,0 +1,15 @@
+  SomeNumber = concept a, type T
+    a.int is int
+    int.to(T) is type(a)
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tconcept2.nim [Processing]";;0
+chk;;skUnknown;;;;Error;;$file;;1;;2;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"the \'concept\' keyword is only valid in \'type\' sections";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"invalid indentation";;0
+chk;;skUnknown;;;;Error;;$file;;1;;15;;"expression expected, but found \'keyword concept\'";;0
+chk;;skUnknown;;;;Error;;$file;;1;;2;;"\'SomeNumber\' cannot be assigned to";;0
+"""
diff --git a/nimsuggest/tests/tdef1.nim b/nimsuggest/tests/tdef1.nim
index 2cd040ea1..49265bbc1 100644
--- a/nimsuggest/tests/tdef1.nim
+++ b/nimsuggest/tests/tdef1.nim
@@ -1,12 +1,14 @@
 discard """
 $nimsuggest --tester $file
 >def $1
-def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100
->def $1
-def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"Return hello";;100
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
+>def $2
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
+>def $2
+def;;skProc;;tdef1.hello;;proc (): string{.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;11;;5;;"Return hello";;100
 """
 
-proc hello(): string =
+proc hel#[!]#lo(): string =
   ## Return hello
   "Hello"
 
diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim
index e1ff96553..f2c6c765f 100644
--- a/nimsuggest/tests/tdot4.nim
+++ b/nimsuggest/tests/tdot4.nim
@@ -15,7 +15,7 @@ discard """
 $nimsuggest --tester --maxresults:2 $file
 >sug $1
 sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;6;;5;;"";;100;;None
-sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None
+sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None
 """
 
 # TODO - determine appropriate behaviour for further suggest output and test it
diff --git a/nimsuggest/tests/tenum_field.nim b/nimsuggest/tests/tenum_field.nim
new file mode 100644
index 000000000..4ceb3e021
--- /dev/null
+++ b/nimsuggest/tests/tenum_field.nim
@@ -0,0 +1,17 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+>sug $2
+sug;;skConst;;tenum_field.BarFoo;;int literal(1);;$file;;10;;6;;"";;100;;Prefix
+"""
+
+proc something() = discard
+
+const BarFoo = 1
+
+type
+  Foo = enum
+    # Test that typing the name doesn't give suggestions
+    somethi#[!]#
+    # Test that the right hand side still gets suggestions
+    another = BarFo#[!]#
diff --git a/nimsuggest/tests/tfatal1.nim b/nimsuggest/tests/tfatal1.nim
new file mode 100644
index 000000000..19778f22e
--- /dev/null
+++ b/nimsuggest/tests/tfatal1.nim
@@ -0,0 +1,15 @@
+{.warning: "I'm a warning!".}
+{.error: "I'm an error!".}
+{.fatal: "I'm a fatal error!".}
+{.error: "I'm an error after fatal error!".}
+
+#[!]#
+discard """
+$nimsuggest --tester $file
+>chk $1
+chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tfatal1.nim [Processing]";;0
+chk;;skUnknown;;;;Warning;;$file;;1;;9;;"I\'m a warning! [User]";;0
+chk;;skUnknown;;;;Error;;$file;;2;;7;;"I\'m an error!";;0
+chk;;skUnknown;;;;Error;;$file;;3;;7;;"fatal error: I\'m a fatal error!";;0
+chk;;skUnknown;;;;Error;;$file;;4;;7;;"I\'m an error after fatal error!";;0
+"""
diff --git a/nimsuggest/tests/tgeneric_highlight.nim b/nimsuggest/tests/tgeneric_highlight.nim
index 334323613..c7291d08b 100644
--- a/nimsuggest/tests/tgeneric_highlight.nim
+++ b/nimsuggest/tests/tgeneric_highlight.nim
@@ -7,14 +7,7 @@ $nimsuggest --tester $file
 >highlight $1
 highlight;;skType;;1;;7;;3
 highlight;;skProc;;1;;0;;6
-highlight;;skProc;;1;;0;;6
-highlight;;skType;;1;;7;;3
-highlight;;skProc;;1;;0;;6
-highlight;;skType;;2;;14;;3
-highlight;;skProc;;2;;7;;6
-highlight;;skProc;;2;;7;;6
 highlight;;skType;;2;;14;;3
 highlight;;skProc;;2;;7;;6
-highlight;;skTemplate;;3;;0;;8
 highlight;;skType;;3;;9;;3
 """
diff --git a/nimsuggest/tests/tgenerics.nim b/nimsuggest/tests/tgenerics.nim
new file mode 100644
index 000000000..7f490321c
--- /dev/null
+++ b/nimsuggest/tests/tgenerics.nim
@@ -0,0 +1,18 @@
+type
+  Hello[T] = object
+    value: T
+
+proc printHelloValue[T](hello: Hello[T]) =
+  echo hello.value
+
+proc main() =
+  let a = Hello[float]()
+  p#[!]#rintHelloValue(a)
+
+main()
+
+discard """
+$nimsuggest --tester $file
+>def $1
+def;;skProc;;tgenerics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tic.nim b/nimsuggest/tests/tic.nim
new file mode 100644
index 000000000..26e644f83
--- /dev/null
+++ b/nimsuggest/tests/tic.nim
@@ -0,0 +1,20 @@
+import std/[appdirs, assertions, cmdline, compilesettings, decls, 
+  dirs, editdistance, effecttraits, enumerate, enumutils, envvars, 
+  exitprocs, files, formatfloat, genasts, importutils, 
+  isolation, jsonutils, logic, monotimes, objectdollar, 
+  oserrors, outparams, packedsets, paths, private, setutils, sha1, 
+  socketstreams, stackframes, staticos, strbasics, symlinks, syncio, 
+  sysatomics, sysrand, tasks, tempfiles, time_t, typedthreads, varints, 
+  vmutils, widestrs, with, wordwrap, wrapnils]
+
+proc test(a: string, b:string) = discard
+proc test(a: int) = discard
+
+test(#[!]#
+
+discard """
+$nimsuggest --v3 --ic:off --tester $file 
+>con $1
+con;;skProc;;tic.test;;proc (a: string, b: string);;$file;;10;;5;;"";;100
+con;;skProc;;tic.test;;proc (a: int);;$file;;11;;5;;"";;100
+"""
\ No newline at end of file
diff --git a/nimsuggest/tests/timport_highlight.nim b/nimsuggest/tests/timport_highlight.nim
new file mode 100644
index 000000000..043f87d98
--- /dev/null
+++ b/nimsuggest/tests/timport_highlight.nim
@@ -0,0 +1,12 @@
+import std/paths
+import json as J
+import std/[os,streams]#[!]#
+
+discard """
+$nimsuggest --tester $file
+>highlight $1
+highlight;;skModule;;1;;11;;5
+highlight;;skModule;;2;;7;;4
+highlight;;skModule;;3;;12;;2
+highlight;;skModule;;3;;15;;7
+"""
diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim
index b67440b9e..f5cbabf05 100644
--- a/nimsuggest/tests/tinclude.nim
+++ b/nimsuggest/tests/tinclude.nim
@@ -11,7 +11,7 @@ go()
 discard """
 $nimsuggest --tester $file
 >def $path/tinclude.nim:7:14
-def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, locks: 0.};;*fixtures/minclude_include.nim;;3;;5;;"";;100
+def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, raises: <inferred> [].};;*fixtures/minclude_include.nim;;3;;5;;"";;100
 >def $path/fixtures/minclude_include.nim:3:71
 def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100
 >def $path/fixtures/minclude_include.nim:3:71
diff --git a/nimsuggest/tests/tsug_pragmas.nim b/nimsuggest/tests/tsug_pragmas.nim
new file mode 100644
index 000000000..ce9c4e8f8
--- /dev/null
+++ b/nimsuggest/tests/tsug_pragmas.nim
@@ -0,0 +1,40 @@
+template fooBar1() {.pragma.}
+proc fooBar2() = discard
+macro fooBar3(x: untyped) = discard
+{.pragma: fooBar4 fooBar3.}
+
+proc test1() {.fooBar#[!]#.} = discard
+
+var test2 {.fooBar#[!]#.} = 9
+
+type
+  Person {.fooBar#[!]#.} = object
+    hello {.fooBar#[!]#.}: string
+  Callback = proc () {.fooBar#[!]#.}
+
+# Check only macros/templates/pragmas are suggested
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $2
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $3
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $4
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+>sug $5
+sug;;skTemplate;;fooBar4;;;;$file;;4;;8;;"";;100;;Prefix
+sug;;skTemplate;;tsug_pragmas.fooBar1;;template ();;$file;;1;;9;;"";;100;;Prefix
+sug;;skMacro;;tsug_pragmas.fooBar3;;macro (x: untyped){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;3;;6;;"";;50;;Prefix
+"""
+
+
diff --git a/nimsuggest/tests/tsug_recursive.nim b/nimsuggest/tests/tsug_recursive.nim
new file mode 100644
index 000000000..97ee5ca01
--- /dev/null
+++ b/nimsuggest/tests/tsug_recursive.nim
@@ -0,0 +1,8 @@
+discard """
+$nimsuggest --tester $file
+>sug $1
+sug;;skProc;;tsug_recursive.fooBar;;proc ();;$file;;7;;5;;"";;100;;Prefix
+"""
+
+proc fooBar() =
+  fooBa#[!]#
diff --git a/nimsuggest/tests/tsug_template.nim b/nimsuggest/tests/tsug_template.nim
index 15615f0af..da494d279 100644
--- a/nimsuggest/tests/tsug_template.nim
+++ b/nimsuggest/tests/tsug_template.nim
@@ -6,7 +6,7 @@ tmp#[!]#
 discard """
 $nimsuggest --tester $file
 >sug $1
-sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe, locks: 0.};;$file;;2;;6;;"";;100;;Prefix
+sug;;skMacro;;tsug_template.tmpb;;macro (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;2;;6;;"";;100;;Prefix
 sug;;skConverter;;tsug_template.tmpc;;converter ();;$file;;3;;10;;"";;100;;Prefix
 sug;;skTemplate;;tsug_template.tmpa;;template ();;$file;;1;;9;;"";;100;;Prefix
 """
diff --git a/nimsuggest/tests/tsug_typedecl.nim b/nimsuggest/tests/tsug_typedecl.nim
index 833043d31..2a510929d 100644
--- a/nimsuggest/tests/tsug_typedecl.nim
+++ b/nimsuggest/tests/tsug_typedecl.nim
@@ -21,6 +21,6 @@ $nimsuggest --tester $file
 >sug $1
 sug;;skType;;tsug_typedecl.someType;;someType;;*nimsuggest/tests/tsug_typedecl.nim;;7;;2;;"";;100;;Prefix
 sug;;skType;;tsug_typedecl.super;;super;;*nimsuggest/tests/tsug_typedecl.nim;;6;;2;;"";;100;;Prefix
-sug;;skType;;system.string;;string;;*lib/system.nim;;*;;*;;*;;100;;Prefix
+sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;*;;*;;*;;100;;Prefix
 sug;;skType;;system.seq;;seq;;*lib/system.nim;;*;;*;;*;;100;;Prefix
-"""
\ No newline at end of file
+"""
diff --git a/nimsuggest/tests/ttype_decl.nim b/nimsuggest/tests/ttype_decl.nim
index 6022392d0..61d8c26cd 100644
--- a/nimsuggest/tests/ttype_decl.nim
+++ b/nimsuggest/tests/ttype_decl.nim
@@ -2,8 +2,8 @@ discard """
 $nimsuggest --tester --maxresults:3 $file
 >sug $1
 sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;100;;None
-sug;;skType;;system.int;;int;;*/lib/system/basic_types.nim;;2;;2;;"";;100;;None
-sug;;skType;;system.string;;string;;*/lib/system.nim;;34;;2;;"";;100;;None
+sug;;skType;;system.int;;int;;*lib/system/basic_types.nim;;2;;2;;"";;100;;None
+sug;;skType;;system.string;;string;;*lib/system/basic_types.nim;;23;;2;;"";;100;;None
 """
 import strutils
 type
diff --git a/nimsuggest/tests/tuse.nim b/nimsuggest/tests/tuse.nim
index 89a9c151a..7c1d1ad0c 100644
--- a/nimsuggest/tests/tuse.nim
+++ b/nimsuggest/tests/tuse.nim
@@ -14,9 +14,9 @@ proc #[!]#someProc*() =
 discard """
 $nimsuggest --tester $file
 >use $1
-def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"";;100
-use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, locks: 0.};;$file;;12;;0;;"";;100
+def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100
+use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100
 >use $2
-def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, locks: 0.};;$file;;9;;5;;"";;100
-use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, locks: 0.};;$file;;12;;0;;"";;100
+def;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;9;;5;;"";;100
+use;;skProc;;tuse.someProc;;proc (){.noSideEffect, gcsafe, raises: <inferred> [].};;$file;;12;;0;;"";;100
 """
diff --git a/nimsuggest/tests/tuse_enum.nim b/nimsuggest/tests/tuse_enum.nim
new file mode 100644
index 000000000..8a40a8348
--- /dev/null
+++ b/nimsuggest/tests/tuse_enum.nim
@@ -0,0 +1,15 @@
+discard """
+$nimsuggest --tester $file
+>use $1
+def;;skEnumField;;tuse_enum.Colour.Red;;Colour;;$file;;10;;4;;"";;100
+use;;skEnumField;;tuse_enum.Colour.Red;;Colour;;$file;;14;;8;;"";;100
+"""
+
+type
+  Colour = enum
+    Red
+    Green
+    Blue
+
+discard #[!]#Red
+
diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim
index 57ad86e4d..80e51e364 100644
--- a/nimsuggest/tests/tv3.nim
+++ b/nimsuggest/tests/tv3.nim
@@ -16,14 +16,10 @@ def	skField	tv3.Foo.bar	string	$file	5	4	""	100
 use	skField	tv3.Foo.bar	string	$file	8	9	""	100
 >def $1
 def	skField	tv3.Foo.bar	string	$file	5	4	""	100
->outline $1
-outline	skType	tv3.Foo	Foo	$file	4	2	""	100
-outline	skField	tv3.Foo.bar	string	$file	5	4	""	100
-outline	skProc	tv3.test	proc (f: Foo){.gcsafe, locks: 0.}	$file	7	5	""	100
 >sug $1
 sug	skField	bar	string	$file	5	4	""	100	Prefix
 >globalSymbols test
-def	skProc	tv3.test	proc (f: Foo){.gcsafe, locks: 0.}	$file	7	5	""	100
+def	skProc	tv3.test	proc (f: Foo){.gcsafe, raises: <inferred> [].}	$file	7	5	""	100
 >globalSymbols Foo
 def	skType	tv3.Foo	Foo	$file	4	2	""	100
 >def $2
diff --git a/nimsuggest/tests/tv3_con.nim b/nimsuggest/tests/tv3_con.nim
new file mode 100644
index 000000000..4714c366b
--- /dev/null
+++ b/nimsuggest/tests/tv3_con.nim
@@ -0,0 +1,13 @@
+# tests v3
+
+proc test(a: string, b:string) = discard
+proc test(a: int) = discard
+
+test(#[!]#
+
+discard """
+$nimsuggest --v3 --tester $file
+>con $1
+con;;skProc;;tv3_con.test;;proc (a: string, b: string);;$file;;3;;5;;"";;100
+con;;skProc;;tv3_con.test;;proc (a: int);;$file;;4;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tv3_forward_definition.nim b/nimsuggest/tests/tv3_forward_definition.nim
new file mode 100644
index 000000000..7a16ea331
--- /dev/null
+++ b/nimsuggest/tests/tv3_forward_definition.nim
@@ -0,0 +1,23 @@
+proc de#[!]#mo(): int
+
+proc de#[!]#mo(): int = 5
+
+let a = de#[!]#mo()
+
+discard """
+$nimsuggest --v3 --tester $file
+>use $1
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+def	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	8	""	100
+>use $2
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+def	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+use	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	8	""	100
+>declaration $1
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+>declaration $2
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+>declaration $3
+declaration	skProc	tv3_forward_definition.demo	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	1	5	""	100
+"""
diff --git a/nimsuggest/tests/tv3_generics.nim b/nimsuggest/tests/tv3_generics.nim
new file mode 100644
index 000000000..2bfb2ca1d
--- /dev/null
+++ b/nimsuggest/tests/tv3_generics.nim
@@ -0,0 +1,18 @@
+type
+  Hello[T] = object
+    value: T
+
+proc printHelloValue[T](hello: Hello[T]) =
+  echo hello.value
+
+proc main() =
+  let a = Hello[float]()
+  p#[!]#rintHelloValue(a)
+
+main()
+
+discard """
+$nimsuggest --v3 --tester $file
+>def $1
+def;;skProc;;tv3_generics.printHelloValue;;proc (hello: Hello[printHelloValue.T]);;$file;;5;;5;;"";;100
+"""
diff --git a/nimsuggest/tests/tv3_globalSymbols.nim b/nimsuggest/tests/tv3_globalSymbols.nim
new file mode 100644
index 000000000..c3bb9933b
--- /dev/null
+++ b/nimsuggest/tests/tv3_globalSymbols.nim
@@ -0,0 +1,14 @@
+# Tests the order of the matches
+proc Btoken(): int = 5
+proc tokenA(): int = 5
+proc token(): int = 5
+proc BBtokenA(): int = 5
+
+discard """
+$nimsuggest --v3 --tester $file
+>globalSymbols token
+def	skProc	tv3_globalSymbols.token	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	4	5	""	100
+def	skProc	tv3_globalSymbols.tokenA	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	3	5	""	100
+def	skProc	tv3_globalSymbols.Btoken	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	2	5	""	100
+def	skProc	tv3_globalSymbols.BBtokenA	proc (): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	5	5	""	100
+"""
diff --git a/nimsuggest/tests/tv3_outline.nim b/nimsuggest/tests/tv3_outline.nim
new file mode 100644
index 000000000..518620c87
--- /dev/null
+++ b/nimsuggest/tests/tv3_outline.nim
@@ -0,0 +1,45 @@
+# tests v3 outline
+
+type
+  Foo* = ref object of RootObj
+    bar*: string
+  FooEnum = enum value1, value2
+  FooPrivate = ref object of RootObj
+    barPrivate: string
+
+macro m(arg: untyped): untyped = discard
+template t(arg: untyped): untyped = discard
+proc p(): void = discard
+iterator i(): int = discard
+converter c(s: string): int = discard
+method m(f: Foo): void = discard
+func f(): void = discard
+
+let a = 1
+var b = 2
+const con = 2
+
+proc outer(): void =
+  proc inner() = discard
+
+proc procWithLocal(): void =
+  let local = 10
+
+discard """
+$nimsuggest --v3 --tester $file
+>outline $file
+outline	skType	tv3_outline.Foo	Foo	$file	4	2	""	100	5	16
+outline	skType	tv3_outline.FooEnum	FooEnum	$file	6	2	""	100	6	31
+outline	skEnumField	tv3_outline.FooEnum.value1	FooEnum	$file	6	17	""	100	6	23
+outline	skEnumField	tv3_outline.FooEnum.value2	FooEnum	$file	6	25	""	100	6	31
+outline	skType	tv3_outline.FooPrivate	FooPrivate	$file	7	2	""	100	8	22
+outline	skMacro	tv3_outline.m	macro (arg: untyped): untyped{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	10	6	""	100	10	40
+outline	skTemplate	tv3_outline.t	template (arg: untyped): untyped	$file	11	9	""	100	11	43
+outline	skProc	tv3_outline.p	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	12	5	""	100	12	24
+outline	skConverter	tv3_outline.c	converter (s: string): int{.noSideEffect, gcsafe, raises: <inferred> [].}	$file	14	10	""	100	14	37
+outline	skFunc	tv3_outline.f	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	16	5	""	100	16	24
+outline	skConst	tv3_outline.con	int literal(2)	$file	20	6	""	100	20	13
+outline	skProc	tv3_outline.outer	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	22	5	""	100	23	24
+outline	skProc	tv3_outline.outer.inner	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	23	7	""	100	23	24
+outline	skProc	tv3_outline.procWithLocal	proc (){.noSideEffect, gcsafe, raises: <inferred> [].}	$file	25	5	""	100	26	16
+"""
diff --git a/nimsuggest/tests/twithin_macro.nim b/nimsuggest/tests/twithin_macro.nim
index d79dae1be..98d58381f 100644
--- a/nimsuggest/tests/twithin_macro.nim
+++ b/nimsuggest/tests/twithin_macro.nim
@@ -45,7 +45,7 @@ $nimsuggest --tester --maxresults:5 $file
 >sug $1
 sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;None
 sug;;skField;;name;;string;;$file;;5;;6;;"";;100;;None
-sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;None
-sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;7;;9;;"";;100;;None
+sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;None
+sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string{.raises: <inferred> [].};;$file;;7;;9;;"";;100;;None
 sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;23;;9;;"";;100;;None
 """
diff --git a/nimsuggest/tests/twithin_macro_prefix.nim b/nimsuggest/tests/twithin_macro_prefix.nim
index dd3810818..e89c8b942 100644
--- a/nimsuggest/tests/twithin_macro_prefix.nim
+++ b/nimsuggest/tests/twithin_macro_prefix.nim
@@ -44,5 +44,5 @@ discard """
 $nimsuggest --tester $file
 >sug $1
 sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;Prefix
-sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;Prefix
+sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int{.raises: <inferred> [].};;$file;;8;;9;;"";;100;;Prefix
 """
diff --git a/readme.md b/readme.md
index 3d32bcf87..da54a8d0d 100644
--- a/readme.md
+++ b/readme.md
@@ -35,9 +35,11 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs](
 The compiler currently officially supports the following platform and
 architecture combinations:
 
-  * Windows (Windows XP or greater) - x86 and x86_64
-  * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l
-  * Mac OS X (10.04 or greater) - x86, x86_64, ppc64 and Apple Silicon (based on the ARM64 architecture)
+| Operating System               | Architectures Supported                          |
+|--------------------------------|----------------------------------------|
+| Windows (Windows XP or greater) | x86 and x86_64                             |
+| Linux (most distributions)     | x86, x86_64, ppc64, and armv6l             |
+| Mac OS X (10.04 or greater)    | x86, x86_64, ppc64, and Apple Silicon (ARM64) |
 
 More platforms are supported, however, they are not tested regularly and they
 may not be as stable as the above-listed platforms.
@@ -47,7 +49,7 @@ Compiling the Nim compiler is quite straightforward if you follow these steps:
 First, the C source of an older version of the Nim compiler is needed to
 bootstrap the latest version because the Nim compiler itself is written in the
 Nim programming language. Those C sources are available within the
-[``nim-lang/csources_v1``][csources-v1-repo] repository.
+[``nim-lang/csources_v2``][csources-v2-repo] repository.
 
 Next, to build from source you will need:
 
@@ -119,7 +121,6 @@ This project exists thanks to all the people who contribute.
 ## Contributing
 
 [![Backers on Open Collective](https://opencollective.com/nim/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/nim/sponsors/badge.svg)](#sponsors)
-[![Setup a bounty via Bountysource][badge-nim-bountysource]][nim-bountysource]
 [![Donate Bitcoins][badge-nim-bitcoin]][nim-bitcoin]
 [![Open Source Helpers](https://www.codetriage.com/nim-lang/nim/badges/users.svg)](https://www.codetriage.com/nim-lang/nim)
 
@@ -130,8 +131,7 @@ standard library are welcomed and appreciated. Before you start contributing,
 you should familiarize yourself with the following repository structure:
 
 * ``bin/``, ``build/`` - these directories are empty, but are used when Nim is built.
-* ``compiler/`` - the compiler source code. Also includes nimfix, and plugins within
-  ``compiler/nimfix`` and ``compiler/plugins`` respectively.
+* ``compiler/`` - the compiler source code. Also includes plugins within ``compiler/plugins``.
 * ``nimsuggest`` - the nimsuggest tool that previously lived in the [``nim-lang/nimsuggest``][nimsuggest-repo] repository.
 * ``config/`` - the configuration for the compiler and documentation generator.
 * ``doc/`` - the documentation files in reStructuredText format.
@@ -164,7 +164,6 @@ You can also help with the development of Nim by making donations. Donations can
 made using:
 
 * [Open Collective](https://opencollective.com/nim)
-* [Bountysource][nim-bountysource]
 * [Bitcoin][nim-bitcoin]
 
 If you have any questions feel free to submit a question on the
@@ -203,7 +202,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim.
 
 Please read the [copying.txt](copying.txt) file for more details.
 
-Copyright © 2006-2022 Andreas Rumpf, all rights reserved.
+Copyright © 2006-2024 Andreas Rumpf, all rights reserved.
 
 [nim-site]: https://nim-lang.org
 [nim-forum]: https://forum.nim-lang.org
@@ -218,19 +217,17 @@ Copyright © 2006-2022 Andreas Rumpf, all rights reserved.
 [nim-matrix]: https://matrix.to/#/#nim-lang:matrix.org
 [nim-matrix-space]: https://matrix.to/#/#nim:envs.net
 [nim-telegram]: https://t.me/nim_lang
-[nim-bountysource]: https://www.bountysource.com/teams/nim
 [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ
 [nimble-repo]: https://github.com/nim-lang/nimble
 [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest
 [csources-repo-deprecated]: https://github.com/nim-lang/csources
-[csources-v1-repo]: https://github.com/nim-lang/csources_v1
+[csources-v2-repo]: https://github.com/nim-lang/csources_v2
 [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square
 [badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square
 [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square
 [badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square
 [badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social
 [badge-nim-stackoverflow]: https://img.shields.io/badge/stackoverflow-nim_tag-yellow.svg?style=flat-square
-[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square
 [badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square
 [pull-request-instructions]: https://help.github.com/articles/using-pull-requests/
 [nim-wiki]: https://github.com/nim-lang/Nim/wiki
diff --git a/testament/categories.nim b/testament/categories.nim
index e8b13746a..843bef3f9 100644
--- a/testament/categories.nim
+++ b/testament/categories.nim
@@ -47,7 +47,7 @@ proc isTestFile*(file: string): bool =
 
 # --------------------- DLL generation tests ----------------------------------
 
-proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
+proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string, isOrc = false) =
   const rpath = when defined(macosx):
       " --passL:-rpath --passL:@loader_path"
     else:
@@ -59,6 +59,7 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
   var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat)
   test2.spec.action = actionCompile
   testSpec c, test2
+
   var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat)
   test3.spec.action = actionCompile
   testSpec c, test3
@@ -81,6 +82,13 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) =
   testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat)
 
   if "boehm" notin options:
+    # hcr tests
+    
+    var basicHcrTest = makeTest("tests/dll/nimhcr_basic.nim", options & " --threads:off --forceBuild --hotCodeReloading:on " & rpath, cat)
+    # test segfaults for now but compiles:
+    if isOrc: basicHcrTest.spec.action = actionCompile
+    testSpec r, basicHcrTest
+
     # force build required - see the comments in the .nim file for more details
     var hcri = makeTest("tests/dll/nimhcr_integration.nim",
                                    options & " --threads:off --forceBuild --hotCodeReloading:on" & rpath, cat)
@@ -96,6 +104,8 @@ proc dllTests(r: var TResults, cat: Category, options: string) =
 
   runBasicDLLTest c, r, cat, options & " --mm:refc"
   runBasicDLLTest c, r, cat, options & " -d:release --mm:refc"
+  runBasicDLLTest c, r, cat, options, isOrc = true
+  runBasicDLLTest c, r, cat, options & " -d:release", isOrc = true
   when not defined(windows):
     # still cannot find a recent Windows version of boehm.dll:
     runBasicDLLTest c, r, cat, options & " --gc:boehm"
@@ -208,9 +218,9 @@ proc jsTests(r: var TResults, cat: Category, options: string) =
   for testfile in ["exception/texceptions", "exception/texcpt1",
                    "exception/texcsub", "exception/tfinally",
                    "exception/tfinally2", "exception/tfinally3",
-                   "actiontable/tactiontable", "method/tmultimjs",
+                   "collections/tactiontable", "method/tmultimjs",
                    "varres/tvarres0", "varres/tvarres3", "varres/tvarres4",
-                   "varres/tvartup", "misc/tints", "misc/tunsignedinc",
+                   "varres/tvartup", "int/tints", "int/tunsignedinc",
                    "async/tjsandnativeasync"]:
     test "tests/" & testfile & ".nim"
 
@@ -680,7 +690,7 @@ proc processCategory(r: var TResults, cat: Category,
       else:
         jsTests(r, cat, options)
     of "dll":
-      dllTests(r, cat, options)
+      dllTests(r, cat, options & " -d:nimDebugDlOpen")
     of "gc":
       gcTests(r, cat, options)
     of "debugger":
@@ -718,6 +728,8 @@ proc processCategory(r: var TResults, cat: Category,
     case cat2
     of "megatest":
       runJoinedTest(r, cat, testsDir, options)
+      if isNimRepoTests():
+        runJoinedTest(r, cat, testsDir, options & " --mm:refc")
     else:
       var testsRun = 0
       var files: seq[string]
diff --git a/testament/important_packages.nim b/testament/important_packages.nim
index 392587e7b..efec04b3c 100644
--- a/testament/important_packages.nim
+++ b/testament/important_packages.nim
@@ -33,36 +33,44 @@ var packages*: seq[NimblePackage]
 proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailure = false) =
   packages.add NimblePackage(name: name, cmd: cmd, url: url, useHead: useHead, allowFailure: allowFailure)
 
-pkg "alea", allowFailure = true
+pkg "alea"
 pkg "argparse"
 pkg "arraymancer", "nim c tests/tests_cpu.nim"
-pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true
+pkg "ast_pattern_matching", "nim c -r tests/test1.nim"
+pkg "asyncftpclient", "nimble compileExample"
 pkg "asyncthreadpool", "nimble test --mm:refc"
 pkg "awk"
 pkg "bigints"
 pkg "binaryheap", "nim c -r binaryheap.nim"
 pkg "BipBuffer"
-pkg "blscurve", allowFailure = true # pending https://github.com/status-im/nim-blscurve/issues/39
+pkg "blscurve", allowFailure = true
 pkg "bncurve"
 pkg "brainfuck", "nim c -d:release -r tests/compile.nim"
-pkg "bump", "nim c --gc:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump"
+pkg "bump", "nim c --mm:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump", allowFailure = true
 pkg "c2nim", "nim c testsuite/tester.nim"
 pkg "cascade"
 pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true
+pkg "checksums"
 pkg "chroma"
 pkg "chronicles", "nim c -o:chr -r chronicles.nim"
 pkg "chronos", "nim c -r -d:release tests/testall"
 pkg "cligen", "nim c --path:. -r cligen.nim"
-pkg "combparser", "nimble test --gc:orc"
+pkg "combparser", "nimble test --mm:orc"
 pkg "compactdict"
 pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension"
-pkg "criterion", allowFailure = true # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed)
+pkg "constantine", "nimble make_lib"
+pkg "cowstrings"
+pkg "criterion", allowFailure = true # needs testing binary
 pkg "datamancer"
 pkg "dashing", "nim c tests/functional.nim"
 pkg "delaunay"
+pkg "dnsclient", allowFailure = true # super fragile
 pkg "docopt"
+pkg "dotenv"
+# when defined(linux): pkg "drchaos"
 pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl"
 pkg "elvis"
+pkg "faststreams"
 pkg "fidget"
 pkg "fragments", "nim c -r fragments/dsl.nim", allowFailure = true # pending https://github.com/nim-lang/packages/issues/2115 
 pkg "fusion"
@@ -71,14 +79,17 @@ pkg "glob"
 pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim"
 pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup", allowFailure = true
 pkg "gnuplot", "nim c gnuplot.nim"
-# pkg "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram"
+# pkg "gram", "nim c -r --mm:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram"
   # pending https://github.com/nim-lang/Nim/issues/16509
 pkg "hts", "nim c -o:htss src/hts.nim"
 pkg "httpauth"
+pkg "httputils"
 pkg "illwill", "nimble examples"
 pkg "inim"
 pkg "itertools", "nim doc src/itertools.nim"
 pkg "iterutils"
+pkg "json_rpc"
+pkg "json_serialization"
 pkg "jstin"
 pkg "karax", "nim c -r tests/tester.nim"
 pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree"
@@ -87,35 +98,39 @@ pkg "lockfreequeues"
 pkg "macroutils"
 pkg "manu"
 pkg "markdown"
+pkg "measuremancer", "nimble testDeps; nimble -y test"
 pkg "memo"
 pkg "msgpack4nim", "nim c -r tests/test_spec.nim"
 pkg "nake", "nim c nakefile.nim"
 pkg "neo", "nim c -d:blas=openblas --mm:refc tests/all.nim"
-pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true
+pkg "nesm", "nimble tests", "https://github.com/nim-lang/NESM", useHead = true, allowFailure = true
+  # inactive, tests not adapted to #23096
 pkg "netty"
 pkg "nico", allowFailure = true
 pkg "nicy", "nim c -r src/nicy.nim"
 pkg "nigui", "nim c -o:niguii -r src/nigui.nim"
 pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround needed, see D20210308T165435
 pkg "NimData", "nim c -o:nimdataa src/nimdata.nim"
-pkg "nimes", "nimble install -y sdl2@#HEAD;nim c src/nimes.nim"
+pkg "nimes", "nim c src/nimes.nim"
 pkg "nimfp", "nim c -o:nfp -r src/fp.nim"
 pkg "nimgame2", "nim c --mm:refc nimgame2/nimgame.nim"
-  # XXX Doesn't work with deprecated 'randomize', will create a PR.
 pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim"
-pkg "nimlsp", allowFailure = true # dependency on ast_pattern_matching
+pkg "nimib"
+pkg "nimlsp"
 pkg "nimly", "nim c -r tests/test_readme_example.nim"
 pkg "nimongo", "nimble test_ci", allowFailure = true
 pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true
+pkg "nimPNG", useHead = true
 pkg "nimpy", "nim c -r tests/nimfrompy.nim"
 pkg "nimquery"
-pkg "nimsl", "nimble install -y variant@#HEAD;nimble test", "https://github.com/nim-lang/nimsl", useHead = true
+pkg "nimsl"
 pkg "nimsvg"
-pkg "nimterop", "nimble minitest"
+pkg "nimterop", "nimble minitest", url = "https://github.com/nim-lang/nimterop"
 pkg "nimwc", "nim c nimwc.nim"
-pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true
+pkg "nimx", "nim c test/main.nim", allowFailure = true
 pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter"
-pkg "norm", "testament r tests/sqlite/trows.nim"
+pkg "norm", "testament r tests/common/tmodel.nim"
+pkg "normalize"
 pkg "npeg", "nimble testarc"
 pkg "numericalnim", "nimble nimCI"
 pkg "optionsutils"
@@ -125,43 +140,54 @@ pkg "patty"
 pkg "pixie"
 pkg "plotly", "nim c examples/all.nim"
 pkg "pnm"
-pkg "polypbren", allowFailure = true
+pkg "polypbren"
+pkg "presto"
 pkg "prologue", "nimble tcompile"
-pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim"
-pkg "pylib"
+# remove fork after https://github.com/PMunch/combparser/pull/7 is merged:
+pkg "protobuf", "nimble install -y https://github.com/metagn/combparser@#HEAD; nim c -o:protobuff -r src/protobuf.nim"
 pkg "rbtree"
 pkg "react", "nimble example"
 pkg "regex", "nim c src/regex"
-pkg "result", "nim c -r result.nim"
+pkg "results", "nim c -r results.nim"
 pkg "RollingHash", "nim c -r tests/test_cyclichash.nim"
 pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim"
 pkg "sdl1", "nim c -r src/sdl.nim"
 pkg "sdl2_nim", "nim c -r sdl2/sdl.nim"
-pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4"
+pkg "serialization"
+pkg "sigv4", "nim c --mm:arc -r sigv4.nim", "https://github.com/disruptek/sigv4"
 pkg "sim"
+pkg "smtp", "nimble compileExample"
 pkg "snip", "nimble test", "https://github.com/genotrance/snip"
-pkg "stint", "nim r stint.nim"
+pkg "ssostrings"
+pkg "stew"
+pkg "stint", "nim c stint.nim"
 pkg "strslice"
 pkg "strunicode", "nim c -r --mm:refc src/strunicode.nim"
 pkg "supersnappy"
 pkg "synthesis"
+pkg "taskpools"
 pkg "telebot", "nim c -o:tbot -r src/telebot.nim"
 pkg "tempdir"
 pkg "templates"
 pkg "tensordsl", "nim c -r --mm:refc tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git"
 pkg "terminaltables", "nim c src/terminaltables.nim"
 pkg "termstyle", "nim c -r termstyle.nim"
+pkg "testutils"
 pkg "timeit"
 pkg "timezones"
 pkg "tiny_sqlite"
 pkg "unicodedb", "nim c -d:release -r tests/tests.nim"
 pkg "unicodeplus", "nim c -d:release -r tests/tests.nim"
+pkg "union", "nim c -r tests/treadme.nim", url = "https://github.com/alaviss/union"
+pkg "unittest2"
 pkg "unpack"
-pkg "weave", "nimble install -y cligen synthesis;nimble test_gc_arc"
+pkg "weave", "nimble install -y cligen@#HEAD; nimble test_gc_arc", useHead = true
+pkg "websock"
 pkg "websocket", "nim c websocket.nim"
-pkg "winim", "nim c winim.nim"
+# pkg "winim", allowFailure = true
 pkg "with"
 pkg "ws", allowFailure = true
-pkg "yaml", "nim c -r test/tserialization.nim"
-pkg "zero_functional", "nim c -r -d:nimNoLentIterators test.nim"
+pkg "yaml"
+pkg "zero_functional", "nim c -r test.nim"
 pkg "zippy"
+pkg "zxcvbn"
diff --git a/testament/lib/stdtest/specialpaths.nim b/testament/lib/stdtest/specialpaths.nim
index 7df63666f..e214d113d 100644
--- a/testament/lib/stdtest/specialpaths.nim
+++ b/testament/lib/stdtest/specialpaths.nim
@@ -48,7 +48,7 @@ proc splitTestFile*(file: string): tuple[cat: string, path: string] =
       else:
         result.path = file
       return result
-  doAssert false, "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'"
+  raiseAssert "file must match this pattern: '/pathto/tests/dir/**/tfile.nim', got: '" & file & "'"
 
 static:
   # sanity check
diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim
index 12e6a56ab..a490b17c8 100644
--- a/testament/lib/stdtest/testutils.nim
+++ b/testament/lib/stdtest/testutils.nim
@@ -40,7 +40,7 @@ when not defined(js) and not defined(nimscript):
         yield line
     template isMatch(lhsi, rhsi): bool =
       if allowPrefixMatch:
-        startsWith(rhsi, lhsi):
+        startsWith(rhsi, lhsi)
       else:
         lhsi == rhsi
 
@@ -66,6 +66,11 @@ template enableRemoteNetworking*: bool =
   ## a `nim` invocation (possibly via additional intermediate processes).
   getEnv("NIM_TESTAMENT_REMOTE_NETWORKING") == "1"
 
+template disableSSLTesting*: bool =
+  ## TODO: workaround for GitHub Action gcc 14 matrix; remove this
+  ## matrix and the flag after Azure agent supports ubuntu 24.04
+  getEnv("NIM_TESTAMENT_DISABLE_SSL") == "1"
+
 template whenRuntimeJs*(bodyIf, bodyElse) =
   ##[
   Behaves as `when defined(js) and not nimvm` (which isn't legal yet).
diff --git a/testament/specs.nim b/testament/specs.nim
index cbb73a5f2..c3040c1d8 100644
--- a/testament/specs.nim
+++ b/testament/specs.nim
@@ -105,7 +105,7 @@ type
 
 proc getCmd*(s: TSpec): string =
   if s.cmd.len == 0:
-    result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file"
+    result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:build/deps/pkgs2 $options $file"
   else:
     result = s.cmd
 
@@ -143,27 +143,26 @@ proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var T
   ##
   ## Can parse a single message for a line:
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
   ##        ^ 'generic_proc' should be: 'genericProc' [Name] ]#
+  ##   ```
   ##
   ## Can parse multiple messages for a line when they are separated by ';':
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
   ##        ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Error
   ##                           ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Error
   ##                                       ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
+  ##   ```
   ##
-  ## .. code-block:: nim
-  ##
+  ##   ```nim
   ##   proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Error
   ##        ^ 'generic_proc' should be: 'genericProc' [Name];
   ##     tt.Error              ^ 'no_destroy' should be: 'nodestroy' [Name];
   ##     tt.Error                          ^ 'userPragma' should be: 'user_pragma' [template declared in mstyleCheck.nim(10, 9)] [Name] ]#
-  ##
+  ##   ```
   result = i + len(inlineErrorMarker)
   inc col, len(inlineErrorMarker)
   let msgLine = line
@@ -222,7 +221,10 @@ proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var T
 
   while result < s.len-1:
     if s[result] == '\n':
-      msg.add '\n'
+      if result > 0 and s[result - 1] == '\r':
+        msg[^1] = '\n'
+      else:
+        msg.add '\n'
       inc result
       inc line
       col = 1
@@ -511,7 +513,7 @@ proc parseSpec*(filename: string): TSpec =
     try:
       msg % ["/", $DirSep, "file", result.filename]
     except ValueError:
-      result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unitests-output-message-variable-interpolation')"
+      result.parseErrors.addLine "invalid variable interpolation (see 'https://nim-lang.github.io/Nim/testament.html#writing-unit-tests-output-message-variable-interpolation')"
       msg
   result.nimout = result.nimout.varSub
   result.msg = result.msg.varSub
diff --git a/testament/testament.nim b/testament/testament.nim
index a63194eba..1e892e636 100644
--- a/testament/testament.nim
+++ b/testament/testament.nim
@@ -10,15 +10,20 @@
 ## This program verifies Nim against the testcases.
 
 import
-  strutils, pegs, os, osproc, streams, json, std/exitprocs,
-  backend, parseopt, specs, htmlgen, browsers, terminal,
-  algorithm, times, md5, azure, intsets, macros
+  std/[strutils, pegs, os, osproc, streams, json,
+    parseopt, browsers, terminal, exitprocs,
+    algorithm, times, intsets, macros]
+
+import backend, specs, azure, htmlgen
+
 from std/sugar import dup
 import compiler/nodejs
 import lib/stdtest/testutils
 from lib/stdtest/specialpaths import splitTestFile
 from std/private/gitutils import diffStrings
 
+import ../dist/checksums/src/checksums/md5
+
 proc trimUnitSep(x: var string) =
   let L = x.len
   if L > 0 and x[^1] == '\31':
@@ -29,6 +34,7 @@ var backendLogging = true
 var simulate = false
 var optVerbose = false
 var useMegatest = true
+var valgrindEnabled = true
 
 proc verboseCmd(cmd: string) =
   if optVerbose:
@@ -43,7 +49,7 @@ const
 
 Command:
   p|pat|pattern <glob>        run all the tests matching the given pattern
-  all                         run all tests
+  all                         run all tests in category folders
   c|cat|category <category>   run all the tests of a certain category
   r|run <test>                run single test file
   html                        generate $1 from the database
@@ -60,6 +66,7 @@ Options:
   --colors:on|off           Turn messages coloring on|off.
   --backendLogging:on|off   Disable or enable backend logging. By default turned on.
   --megatest:on|off         Enable or disable megatest. Default is on.
+  --valgrind:on|off         Enable or disable valgrind support. Default is on.
   --skipFrom:file           Read tests to skip from `file` - one test per line, # comments ignored
 
 On Azure Pipelines, testament will also publish test results via Azure Pipelines' Test Management API
@@ -189,7 +196,6 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
         foundSuccessMsg = true
     elif not running(p):
       break
-  close(p)
   result.msg = ""
   result.file = ""
   result.output = ""
@@ -197,8 +203,9 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
   result.column = 0
 
   result.err = reNimcCrash
-  let exitCode = p.peekExitCode
-  case exitCode
+  result.exitCode = p.peekExitCode
+  close p
+  case result.exitCode
   of 0:
     if foundErrorMsg:
       result.debugInfo.add " compiler exit code was 0 but some Error's were found."
@@ -210,7 +217,7 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string,
     if foundSuccessMsg:
       result.debugInfo.add " compiler exit code was 1 but no `isSuccess` was true."
   else:
-    result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $exitCode
+    result.debugInfo.add " expected compiler exit code 0 or 1, got $1." % $result.exitCode
 
   if err =~ pegLineError:
     result.file = extractFilename(matches[0])
@@ -376,7 +383,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest,
     r.addResult(test, target, extraOptions, expected.nimout, given.nimout, reMsgsDiffer)
   elif extractFilename(expected.file) != extractFilename(given.file) and
       "internal error:" notin expected.msg:
-    r.addResult(test, target, extraOptions, expected.filename, given.file, reFilesDiffer)
+    r.addResult(test, target, extraOptions, expected.file, given.file, reFilesDiffer)
   elif expected.line != given.line and expected.line != 0 or
        expected.column != given.column and expected.column != 0:
     r.addResult(test, target, extraOptions, $expected.line & ':' & $expected.column,
@@ -487,7 +494,7 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec,
             args = @["--unhandled-rejections=strict", exeFile] & args
           else:
             exeCmd = exeFile.dup(normalizeExe)
-            if expected.useValgrind != disabled:
+            if valgrindEnabled and expected.useValgrind != disabled:
               var valgrindOptions = @["--error-exitcode=1"]
               if expected.useValgrind != leaking:
                 valgrindOptions.add "--leak-check=yes"
@@ -507,6 +514,7 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec,
             else:
               buf
           if exitCode != expected.exitCode:
+            given.err = reExitcodesDiffer
             r.addResult(test, target, extraOptions, "exitcode: " & $expected.exitCode,
                               "exitcode: " & $exitCode & "\n\nOutput:\n" &
                               bufB, reExitcodesDiffer)
@@ -516,7 +524,29 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec,
             r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer)
           compilerOutputTests(test, target, extraOptions, given, expected, r)
   of actionReject:
+    # Make sure its the compiler rejecting and not the system (e.g. segfault)
     cmpMsgs(r, expected, given, test, target, extraOptions)
+    if given.exitCode != QuitFailure:
+      r.addResult(test, target, extraOptions, "exitcode: " & $QuitFailure,
+                        "exitcode: " & $given.exitCode & "\n\nOutput:\n" &
+                        given.nimout, reExitcodesDiffer)
+
+
+
+proc changeTarget(extraOptions: string; defaultTarget: TTarget): TTarget =
+  result = defaultTarget
+  var p = parseopt.initOptParser(extraOptions)
+
+  while true:
+    parseopt.next(p)
+    case p.kind
+    of cmdEnd: break
+    of cmdLongOption, cmdShortOption:
+      if p.key == "b" or p.key == "backend":
+        result = parseEnum[TTarget](p.val.normalize)
+        # chooses the last one
+    else:
+      discard
 
 proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) =
   for target in expected.targets:
@@ -530,6 +560,7 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: s
     else:
       let nimcache = nimcacheDir(test.name, test.options, target)
       var testClone = test
+      let target = changeTarget(extraOptions, target)
       testSpecHelper(r, testClone, expected, target, extraOptions, nimcache)
 
 proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) =
@@ -663,6 +694,14 @@ proc main() =
         useMegatest = false
       else:
         quit Usage
+    of "valgrind":
+      case p.val:
+      of "on":
+        valgrindEnabled = true
+      of "off":
+        valgrindEnabled = false
+      else:
+        quit Usage
     of "backendlogging":
       case p.val:
       of "on":
diff --git a/tests/alias/t19349.nim b/tests/alias/t19349.nim
new file mode 100644
index 000000000..1e1e58264
--- /dev/null
+++ b/tests/alias/t19349.nim
@@ -0,0 +1,19 @@
+discard """
+  action: "compile"
+"""
+
+type
+  Vec3[T: SomeNumber] = object
+    arr: array[3, T]
+
+var 
+  cfloatArr: array[3, array[3, cfloat]]
+  cfloatSeq = newSeq[Vec3[cfloat]]()
+for row in cfloatArr:
+  cfloatSeq.add(Vec3[float32](arr: [row[0], row[1], row[2]]))
+
+var 
+  cuintArr: array[3, array[3, cuint]]
+  cuintSeq = newSeq[Vec3[cuint]]()
+for row in cuintArr:
+  cuintSeq.add(Vec3[uint32](arr: [row[0], row[1], row[2]]))
diff --git a/tests/align/talign.nim b/tests/align/talign.nim
index 3b8f6b4df..08373ee49 100644
--- a/tests/align/talign.nim
+++ b/tests/align/talign.nim
@@ -51,3 +51,19 @@ type Bug[T] = object
 
 var bug: Bug[int]
 doAssert sizeof(bug) == 128, "Oops my size is " & $sizeof(bug) # 16
+
+
+block: # bug #22419
+  type
+    ValidatorPubKey = object
+      blob: array[96, byte]
+
+  proc f(): auto =
+    return iterator() =
+      var pad: int8 = 0
+      var y {.align: 16.}: ValidatorPubKey
+      let value = cast[uint64](addr y)
+      doAssert value mod 16 == 0
+
+  f()()
+
diff --git a/tests/alloc/tmembug.nim b/tests/alloc/tmembug.nim
new file mode 100644
index 000000000..63b51ec5d
--- /dev/null
+++ b/tests/alloc/tmembug.nim
@@ -0,0 +1,54 @@
+discard """
+  joinable: false
+"""
+
+import std / [atomics, strutils, sequtils]
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+var
+  chan1: Channel[BackendMessage]
+  chan2: Channel[BackendMessage]
+
+chan1.open()
+chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  discard chan2.trySend(msg)
+
+var
+  recv: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    let resp = chan1.tryRecv()
+    if resp.dataAvailable:
+      routeMessage(resp.msg)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](recv, recvMsg)
+
+const MESSAGE_COUNT = 100
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..500).toSeq())
+  for j in 0..0: #100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(recv)
+
+main()
diff --git a/tests/alloc/tmembug2.nim b/tests/alloc/tmembug2.nim
new file mode 100644
index 000000000..01bce6f14
--- /dev/null
+++ b/tests/alloc/tmembug2.nim
@@ -0,0 +1,58 @@
+discard """
+  disabled: "true"
+"""
+
+import std / [atomics, strutils, sequtils, isolation]
+
+import threading / channels
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+const MESSAGE_COUNT = 100
+
+var
+  chan1 = newChan[BackendMessage](MESSAGE_COUNT*2)
+  chan2 = newChan[BackendMessage](MESSAGE_COUNT*2)
+
+#chan1.open()
+#chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  var m = isolate(msg)
+  discard chan2.trySend(m)
+
+var
+  thr: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    var resp: BackendMessage
+    if chan1.tryRecv(resp):
+      #if resp.dataAvailable:
+      routeMessage(resp)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](thr, recvMsg)
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..5).toSeq())
+  for j in 0..100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(thr)
+
+main()
diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim
index bcd5f3a93..dd8ac89b0 100644
--- a/tests/arc/t17812.nim
+++ b/tests/arc/t17812.nim
@@ -27,3 +27,15 @@ block: # bug #17812
     proc `$`(o: MyObj): string = o.repr
 
     doAssert ($MyObj()).len > 0
+
+# bug #22175
+
+type Xxx = object
+  value: string
+
+proc complete(xxx: ref Xxx, v: sink string) =
+  xxx.value = move(v)
+
+let yyy = (ref Xxx)()
+
+yyy.complete("test")
diff --git a/tests/arc/t18645.nim b/tests/arc/t18645.nim
new file mode 100644
index 000000000..c5fddd4bb
--- /dev/null
+++ b/tests/arc/t18645.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--gc:arc; --gc:refc"
+  output: '''
+1
+2
+3
+'''
+"""
+
+proc bitTypeIdUnion() =
+  var bitId {.global.} = block:
+    0
+  inc bitId
+  echo bitId
+
+bitTypeIdUnion()
+bitTypeIdUnion()
+bitTypeIdUnion()
diff --git a/tests/arc/t18977.nim b/tests/arc/t18977.nim
new file mode 100644
index 000000000..c775551a4
--- /dev/null
+++ b/tests/arc/t18977.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type
+  E = enum
+    a, b, c, d
+  X = object
+    v: int
+  O = object
+    case kind: E
+    of a:
+      a: int
+    of {b, c}:
+      b: float
+    else:
+      d: X
+
+proc `=destroy`(x: var X) =
+  echo "x destroyed"
+
+var o = O(kind: d, d: X(v: 12345))
+doAssert o.d.v == 12345
+
+doAssertRaises(FieldDefect):
+  o.kind = a
diff --git a/tests/arc/t19231.nim b/tests/arc/t19231.nim
new file mode 100644
index 000000000..40fcf277c
--- /dev/null
+++ b/tests/arc/t19231.nim
@@ -0,0 +1,18 @@
+discard """
+  matrix: "--mm:orc"
+  targets: "c cpp"
+"""
+
+type
+  Game* = ref object
+
+proc free*(game: Game) =
+  var mixNumOpened:cint = 0
+  for i in 0..<mixNumOpened:
+    mixNumOpened -= 1
+
+proc newGame*(): Game =
+  new result, free
+
+var
+  game*: Game
diff --git a/tests/arc/t19364.nim b/tests/arc/t19364.nim
new file mode 100644
index 000000000..f520f3291
--- /dev/null
+++ b/tests/arc/t19364.nim
@@ -0,0 +1,30 @@
+discard """
+  cmd: '''nim c --gc:arc --expandArc:fooLeaks $file'''
+  nimout: '''
+--expandArc: fooLeaks
+
+var
+  tmpTuple_cursor
+  a_cursor
+  b_cursor
+  c_cursor
+tmpTuple_cursor = refTuple
+a_cursor = tmpTuple_cursor[0]
+b_cursor = tmpTuple_cursor[1]
+c_cursor = tmpTuple_cursor[2]
+-- end of expandArc ------------------------
+'''
+"""
+
+func fooLeaks(refTuple: tuple[a,
+                              b,
+                              c: seq[float]]): float =
+  let (a, b, c) = refTuple
+
+let refset = (a: newSeq[float](25_000_000),
+              b: newSeq[float](25_000_000),
+              c: newSeq[float](25_000_000))
+
+var res = newSeq[float](1_000_000)
+for i in 0 .. res.high:
+  res[i] = fooLeaks(refset)
diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim
index f7146ec26..6d3f57692 100644
--- a/tests/arc/t19862.nim
+++ b/tests/arc/t19862.nim
@@ -2,6 +2,8 @@ discard """
   matrix: "--gc:refc; --gc:arc"
 """
 
+import std/widestrs
+
 # bug #19862
 type NewString = object
 
diff --git a/tests/arc/t20456.nim b/tests/arc/t20456.nim
new file mode 100644
index 000000000..ace79255a
--- /dev/null
+++ b/tests/arc/t20456.nim
@@ -0,0 +1,7 @@
+discard """
+  cmd: "nim check $file"
+  action: "compile"
+"""
+
+when not defined(gcOrc):
+  {.error: "orc".}
diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim
new file mode 100644
index 000000000..008bd1dcd
--- /dev/null
+++ b/tests/arc/t20588.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check --warnings:off --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t20588.nim(20, 12) Error: illegal type conversion to 'auto'
+t20588.nim(21, 14) Error: illegal type conversion to 'typed'
+t20588.nim(22, 16) Error: illegal type conversion to 'untyped'
+t20588.nim(24, 7) Error: illegal type conversion to 'any'
+'''
+"""
+
+
+
+
+
+
+
+
+
+discard 0.0.auto
+discard typed("abc")
+discard untyped(4)
+var a = newSeq[bool](1000)
+if any(a):
+  echo "ok?"
\ No newline at end of file
diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim
new file mode 100644
index 000000000..91d0c42c7
--- /dev/null
+++ b/tests/arc/t21184.nim
@@ -0,0 +1,77 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+import std/[with]
+
+type
+  Node* {.acyclic.} = ref object of RootObj
+    name: string
+    data: pointer
+    children: seq[Node]
+  TextNode = ref object of Node
+    text: string
+
+proc fakeEcho(s: string) =
+  if s.len < 0:
+    echo s
+
+proc newNode[T: Node](parent: Node): T =
+  new result
+  result.data = alloc0(250)
+  parent.children.add(result)
+
+proc newRootNode(): Node =
+  new result
+  result.data = alloc0(250)
+
+method printNode(node: Node) {.base.} =
+  fakeEcho node.name
+
+method printNode(node: TextNode) =
+  procCall printNode(Node(node))
+  fakeEcho node.text
+
+proc printChildren(node: Node) =
+  for child in node.children:
+    child.printNode()
+    printChildren(child)
+
+proc free(node: Node) =
+  for child in node.children:
+    free(child)
+  dealloc(node.data)
+
+template node(parent: Node, body: untyped): untyped =
+  var node = newNode[Node](parent)
+  with node:
+    body
+
+proc textNode(parent: Node, text: string) =
+  var node = newNode[TextNode](parent)
+  node.text = text
+
+template withRootNode(body: untyped): untyped =
+  var root = newRootNode()
+  root.name = "root"
+  with root:
+    body
+  root.printNode()
+  printChildren(root)
+  root.free()
+
+proc doTest() =
+  withRootNode:
+    node:
+      name = "child1"
+      node:
+        name = "child2"
+        node:
+          name = "child3"
+          textNode "Hello, world!"
+
+
+# bug #21171
+if isMainModule:
+  for i in 0..100000:
+    doTest()
diff --git a/tests/arc/t22218.nim b/tests/arc/t22218.nim
new file mode 100644
index 000000000..7837ed1d0
--- /dev/null
+++ b/tests/arc/t22218.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim c --mm:arc $file"
+  errormsg: "'=copy' is not available for type <Obj>; requires a copy because it's not the last read of 'chan[]'; routine: test"
+"""
+
+# bug #22218
+type Obj[T] = object
+  v: T
+
+proc `=copy`[T](
+    dest: var Obj[T],
+    source: Obj[T]
+  ) {.error: "A channel cannot be copied".}
+
+from system/ansi_c import c_calloc
+
+proc test() =
+    var v: bool = true
+    var chan = cast[ptr Obj[int]](c_calloc(1, csize_t sizeof(Obj[int])))
+    var copy = chan[]
+
+    echo chan.v
+    echo v
+
+test()
\ No newline at end of file
diff --git a/tests/arc/t22237.nim b/tests/arc/t22237.nim
new file mode 100644
index 000000000..c6dbf768a
--- /dev/null
+++ b/tests/arc/t22237.nim
@@ -0,0 +1,55 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+import std/macros
+import std/streams
+
+# bug #22237
+
+proc iterlines_closure2(f: File | Stream): iterator (): string =
+  result = iterator(): string =
+    for line in f.lines:
+      if line.len == 0:
+        break
+      yield line
+
+proc test() =
+  let f = newStringStream("""
+    1
+    2
+
+    3
+    4
+
+    5
+    6
+    7
+
+    8
+""")
+  while not f.atEnd():
+    let iterator_inst = iterlines_closure2(f)
+    for item in iterator_inst(): # Fails with "SIGSEGV: Illegal storage access. (Attempt to read from nil?)"
+      discard
+
+test()
+
+# bug #21160
+import sequtils
+
+iterator allMoves(fls: seq[int]): seq[int] =
+  yield fls
+
+proc neighbors(flrs: seq[int]): iterator: seq[int] =
+  return iterator(): seq[int] =
+    for flrs2 in allMoves(flrs):
+      yield flrs2
+      for flrs3 in allMoves(flrs2):
+        yield flrs3
+
+let f = @[1]
+for _ in neighbors(f):
+  discard
+for _ in neighbors(f):
+  discard
diff --git a/tests/arc/t22478.nim b/tests/arc/t22478.nim
new file mode 100644
index 000000000..5373fa161
--- /dev/null
+++ b/tests/arc/t22478.nim
@@ -0,0 +1,46 @@
+discard """
+  matrix: "-d:nimNoLentIterators --mm:arc"
+  output: '''PUSH DATA: {"test.message":{"test":{"nested":"v1"}}}'''
+  joinable: false
+"""
+
+# bug #22748
+import std/[json, typetraits, times]
+
+# publish
+
+proc publish*[T](payload: T) =
+  discard
+
+type MetricsPoint* = JsonNode
+
+proc push*(stat: string, data: JsonNode, usec: int64 = 0) =
+  let payload = newJObject()
+
+  # this results in a infinite recursion unless we deepCopy()
+  payload[stat] = data #.deepCopy
+
+  echo "PUSH DATA: ", payload
+
+  publish[MetricsPoint](payload)
+
+var scopes {.threadvar.}: seq[JsonNode]
+
+type WithTimeCallback*[T] = proc(data: var JsonNode): T
+
+proc pushScoped*[T](metric: string, blk: WithTimeCallback[T]): T {.gcsafe.} =
+  scopes.add newJObject()
+  defer: discard scopes.pop()
+
+  let stc = (cpuTime() * 1000_000).int64
+  result = blk(scopes[^1])
+  let dfc = (cpuTime() * 1000_000).int64 - stc
+
+  push(metric, scopes[^1], dfc)
+
+# demo code
+
+discard pushScoped[int]("test.message") do (data: var JsonNode) -> int:
+  data["test"] = %*{
+    "nested": "v1"
+  }
\ No newline at end of file
diff --git a/tests/arc/t22787.nim b/tests/arc/t22787.nim
new file mode 100644
index 000000000..5840a984b
--- /dev/null
+++ b/tests/arc/t22787.nim
@@ -0,0 +1,37 @@
+discard """
+  joinable: false
+"""
+
+import std/assertions
+
+proc foo =
+  var s:seq[string]
+  var res = ""
+
+  for i in 0..3:
+    s.add ("test" & $i)
+    s.add ("test" & $i)
+
+  var lastname:string
+
+  for i in s:
+    var name = i[0..4]
+
+    if name != lastname:
+      res.add "NEW:" & name & "\n"
+    else:
+      res.add name & ">" & lastname & "\n"
+
+    lastname = name
+
+  doAssert res == """
+NEW:test0
+test0>test0
+NEW:test1
+test1>test1
+NEW:test2
+test2>test2
+NEW:test3
+test3>test3
+"""
+foo()
\ No newline at end of file
diff --git a/tests/arc/t23247.nim b/tests/arc/t23247.nim
new file mode 100644
index 000000000..0fadc50cd
--- /dev/null
+++ b/tests/arc/t23247.nim
@@ -0,0 +1,52 @@
+discard """
+  matrix: ";-d:useMalloc"
+"""
+
+# bug #23247
+import std/hashes
+
+func baseAddr[T](x: openArray[T]): ptr T =
+  # Return the address of the zero:th element of x or `nil` if x is empty
+  if x.len == 0: nil else: cast[ptr T](x)
+
+func makeUncheckedArray[T](p: ptr T): ptr UncheckedArray[T] =
+  cast[ptr UncheckedArray[T]](p)
+
+type
+  LabelKey = object
+    data: seq[string]
+    refs: ptr UncheckedArray[string]
+    refslen: int
+
+  Gauge = ref object
+    metrics: seq[seq[seq[string]]]
+
+template values(key: LabelKey): openArray[string] =
+  if key.refslen > 0:
+    key.refs.toOpenArray(0, key.refslen - 1)
+  else:
+    key.data
+
+proc hash(key: LabelKey): Hash =
+  hash(key.values)
+
+proc view(T: type LabelKey, values: openArray[string]): T =
+  # TODO some day, we might get view types - until then..
+  LabelKey(refs: baseAddr(values).makeUncheckedArray(), refslen: values.len())
+
+template withValue2(k: untyped) =
+  discard hash(k)
+
+proc setGauge(
+    collector: Gauge,
+    labelValues: openArray[string],
+) =
+  let v = LabelKey.view(labelValues)
+  withValue2(v)
+  collector.metrics.add @[@labelValues, @labelValues]
+  discard @labelValues
+
+var nim_gc_mem_bytes = Gauge()
+let threadID = $getThreadId()
+setGauge(nim_gc_mem_bytes, @[threadID])
+setGauge(nim_gc_mem_bytes, @[threadID])
\ No newline at end of file
diff --git a/tests/arc/taliased_reassign.nim b/tests/arc/taliased_reassign.nim
new file mode 100644
index 000000000..5563fae8c
--- /dev/null
+++ b/tests/arc/taliased_reassign.nim
@@ -0,0 +1,41 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+# bug #20993
+
+type
+  Dual[int] = object # must be generic (even if fully specified)
+    p: int
+proc D(p: int): Dual[int] = Dual[int](p: p)
+proc `+`(x: Dual[int], y: Dual[int]): Dual[int] = D(x.p + y.p)
+
+type
+  Tensor[T] = object
+    buf: seq[T]
+proc newTensor*[T](s: int): Tensor[T] = Tensor[T](buf: newSeq[T](s))
+proc `[]`*[T](t: Tensor[T], idx: int): T = t.buf[idx]
+proc `[]=`*[T](t: var Tensor[T], idx: int, val: T) = t.buf[idx] = val
+
+proc `+.`[T](t1, t2: Tensor[T]): Tensor[T] =
+  let n = t1.buf.len
+  result = newTensor[T](n)
+  for i in 0 ..< n:
+    result[i] = t1[i] + t2[i]
+
+proc toTensor*[T](a: sink seq[T]): Tensor[T] =
+  ## This breaks it: Using `T` instead makes it work
+  type U = typeof(a[0])
+  var t: Tensor[U] # Tensor[T] works
+  t.buf = a
+  result = t
+
+proc loss() =
+  var B = toTensor(@[D(123)])
+  let a = toTensor(@[D(-10)])
+  B = B +. a
+  doAssert B[0].p == 113, "I want to be 113, but I am " & $B[0].p
+
+loss()
+
+
diff --git a/tests/arc/tarc_macro.nim b/tests/arc/tarc_macro.nim
new file mode 100644
index 000000000..33ade1da4
--- /dev/null
+++ b/tests/arc/tarc_macro.nim
@@ -0,0 +1,57 @@
+import macros
+
+var destroyCalled = false
+macro bar() =
+  let s = newTree(nnkAccQuoted, ident"=destroy")
+  # let s = ident"`=destroy`" # this would not work
+  result = quote do:
+    type Foo = object
+    # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work
+    proc `s`(a: var Foo) = destroyCalled = true
+    block:
+      let a = Foo()
+bar()
+doAssert destroyCalled
+
+# custom `op`
+var destroyCalled2 = false
+macro bar(ident) =
+  var x = 1.5
+  result = quote("@") do:
+    type Foo = object
+    let `@ident` = 0 # custom op interpolated symbols need quoted (``)
+    proc `=destroy`(a: var Foo) =
+      doAssert @x == 1.5
+      doAssert compiles(@x == 1.5)
+      let b1 = @[1,2]
+      let b2 = @@[1,2]
+      doAssert $b1 == "[1, 2]"
+      doAssert $b2 == "@[1, 2]"
+      destroyCalled2 = true
+    block:
+      let a = Foo()
+bar(someident)
+doAssert destroyCalled2
+
+proc `&%`(x: int): int = 1
+proc `&%`(x, y: int): int = 2
+
+macro bar2() =
+  var x = 3
+  result = quote("&%") do:
+    var y = &%x # quoting operator
+    doAssert &%&%y == 1 # unary operator => need to escape
+    doAssert y &% y == 2 # binary operator => no need to escape
+    doAssert y == 3
+bar2()
+
+block:
+  macro foo(a: openArray[string] = []): string =
+    echo a # Segfault doesn't happen if this is removed
+    newLit ""
+
+  proc bar(a: static[openArray[string]] = []) =
+    const tmp = foo(a)
+
+  # bug #22909
+  doAssert not compiles(bar())
diff --git a/tests/arc/tarc_orc.nim b/tests/arc/tarc_orc.nim
index c6a7845b0..f2c7de2fc 100644
--- a/tests/arc/tarc_orc.nim
+++ b/tests/arc/tarc_orc.nim
@@ -33,3 +33,154 @@ proc bug20303() =
   doAssert res == "are"
 
 bug20303()
+
+proc main() = # todo bug with templates
+  block: # bug #11267
+    var a: seq[char] = block: @[]
+    doAssert a == @[]
+    # 2
+    proc b: seq[string] =
+      discard
+      @[]
+    doAssert b() == @[]
+static: main()
+main()
+
+
+type Obj = tuple
+  value: int
+  arr: seq[int]
+
+proc bug(): seq[Obj] =
+  result.add (value: 0, arr: @[])
+  result[^1].value = 1
+  result[^1].arr.add 1
+
+# bug #19990
+let s = bug()
+doAssert s[0] == (value: 1, arr: @[1])
+
+block: # bug #21974
+  type Test[T] = ref object
+      values : seq[T]
+      counter: int
+
+  proc newTest[T](): Test[T] =
+      result         = new(Test[T])
+      result.values  = newSeq[T](16)
+      result.counter = 0
+
+  proc push[T](self: Test[T], value: T) =
+      self.counter += 1
+      if self.counter >= self.values.len:
+          self.values.setLen(self.values.len * 2)
+      self.values[self.counter - 1] = value
+
+  proc pop[T](self: Test[T]): T =
+      result         = self.values[0]
+      self.values[0] = self.values[self.counter - 1] # <--- This line
+      self.counter  -= 1
+
+
+  type X = tuple
+      priority: int
+      value   : string
+
+  var a = newTest[X]()
+  a.push((1, "One"))
+  doAssert a.pop.value == "One"
+
+# bug #21987
+
+type
+  EmbeddedImage* = distinct Image
+  Image = object
+    len: int
+
+proc imageCopy*(image: Image): Image {.nodestroy.}
+
+proc `=destroy`*(x: var Image) =
+  discard
+proc `=sink`*(dest: var Image; source: Image) =
+  `=destroy`(dest)
+  wasMoved(dest)
+
+proc `=dup`*(source: Image): Image {.nodestroy.} =
+  result = imageCopy(source)
+
+proc `=copy`*(dest: var Image; source: Image) =
+  dest = imageCopy(source) # calls =sink implicitly
+
+proc `=destroy`*(x: var EmbeddedImage) = discard
+
+proc `=dup`*(source: EmbeddedImage): EmbeddedImage {.nodestroy.} = source
+
+proc `=copy`*(dest: var EmbeddedImage; source: EmbeddedImage) {.nodestroy.} =
+  dest = source
+
+proc imageCopy*(image: Image): Image =
+  result = image
+
+proc main2 =
+  block:
+    var a = Image(len: 2).EmbeddedImage
+    var b = Image(len: 1).EmbeddedImage
+    b = a
+    doAssert Image(a).len == 2
+    doAssert Image(b).len == 2
+
+  block:
+    var a = Image(len: 2)
+    var b = Image(len: 1)
+    b = a
+    doAssert a.len == 2
+    doAssert b.len == 0
+
+main2()
+
+block:
+  type
+    TestObj = object of RootObj
+      name: string
+    
+    TestSubObj = object of TestObj
+      objname: string
+
+  proc `=destroy`(x: TestObj) =
+    `=destroy`(x.name)
+
+  proc `=destroy`(x: TestSubObj) =
+    `=destroy`(x.objname)
+    `=destroy`(TestObj(x))
+
+  proc testCase() =
+    let t1 {.used.} = TestSubObj(objname: "tso1", name: "to1")
+
+  proc main() =
+    testCase()
+
+  main()
+
+block: # bug #23858
+  type Object = object
+    a: int
+    b: ref int
+  var x = 0
+  proc fn(): auto {.cdecl.} =
+    inc x
+    return Object()
+  discard fn()
+  doAssert x == 1
+
+block: # bug #24147
+  type
+    O = object of RootObj
+      val: string
+    OO = object of O
+
+  proc `=copy`(dest: var O, src: O) =
+      dest.val = src.val
+
+  let oo = OO(val: "hello world")
+  var ooCopy : OO
+  `=copy`(ooCopy, oo)
diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim
index 45d1514cd..b4476ef4f 100644
--- a/tests/arc/tarcmisc.nim
+++ b/tests/arc/tarcmisc.nim
@@ -1,5 +1,7 @@
 discard """
   output: '''
+Destructor for TestTestObj
+=destroy called
 123xyzabc
 destroyed: false
 destroyed: false
@@ -26,18 +28,90 @@ new line after - @['a']
 finalizer
 aaaaa
 hello
-ok
 true
 copying
 123
 42
-closed
+@["", "d", ""]
+ok
 destroying variable: 20
 destroying variable: 10
+closed
 '''
-  cmd: "nim c --gc:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file"
+  cmd: "nim c --mm:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file"
 """
 
+block: # bug #23627
+  type
+    TestObj = object of RootObj
+
+    Test2 = object of RootObj
+      foo: TestObj
+
+    TestTestObj = object of RootObj
+      shit: TestObj
+
+  proc `=destroy`(x: TestTestObj) =
+    echo "Destructor for TestTestObj"
+    let test = Test2(foo: TestObj())
+
+  proc testCaseT() =
+    let tt1 {.used.} = TestTestObj(shit: TestObj())
+
+
+  proc main() =
+    testCaseT()
+
+  main()
+
+
+# bug #9401
+
+type
+  MyObj = object
+    len: int
+    data: ptr UncheckedArray[float]
+
+proc `=destroy`*(m: MyObj) =
+
+  echo "=destroy called"
+
+  if m.data != nil:
+    deallocShared(m.data)
+
+type
+  MyObjDistinct = distinct MyObj
+
+proc `=copy`*(m: var MyObj, m2: MyObj) =
+  if m.data == m2.data: return
+  if m.data != nil:
+    `=destroy`(m)
+  m.len = m2.len
+  if m.len > 0:
+    m.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * m.len))
+    copyMem(m.data, m2.data, sizeof(float) * m.len)
+
+
+proc `=sink`*(m: var MyObj, m2: MyObj) =
+  if m.data != m2.data:
+    if m.data != nil:
+      `=destroy`(m)
+    m.len = m2.len
+    m.data = m2.data
+
+proc newMyObj(len: int): MyObj =
+  result.len = len
+  result.data = cast[ptr UncheckedArray[float]](allocShared(sizeof(float) * len))
+
+proc newMyObjDistinct(len: int): MyObjDistinct =
+  MyObjDistinct(newMyObj(len))
+
+proc fooDistinct =
+  doAssert newMyObjDistinct(2).MyObj.len == 2
+
+fooDistinct()
+
+
 proc takeSink(x: sink string): bool = true
 
 proc b(x: sink string): string =
@@ -55,7 +129,7 @@ bbb("123")
 type Variable = ref object
   value: int
 
-proc `=destroy`(self: var typeof(Variable()[])) =
+proc `=destroy`(self: typeof(Variable()[])) =
   echo "destroying variable: ",self.value
 
 proc newVariable(value: int): Variable =
@@ -75,7 +149,7 @@ proc test(count: int) =
 test(3)
 
 proc test2(count: int) =
-  #block: #XXX: Fails with block currently
+  block: #XXX: Fails with block currently
     var v {.global.} = newVariable(20)
 
     var count = count - 1
@@ -109,7 +183,7 @@ type
   B = ref object of A
     x: int
 
-proc `=destroy`(x: var AObj) =
+proc `=destroy`(x: AObj) =
   close(x.io)
   echo "closed"
 
@@ -497,3 +571,266 @@ proc fooz(sec: var InputSectionBase) =
 var sec = create(InputSection)
 sec[] = InputSection(relocations: newSeq[int]())
 fooz sec[]
+
+block:
+  type
+    Data = ref object
+      id: int
+  proc main =
+    var x = Data(id: 99)
+    var y = x
+    x[] = Data(id: 778)[]
+    doAssert y.id == 778
+    doAssert x[].id == 778
+  main()
+
+block: # bug #19857
+  type
+    ValueKind = enum VNull, VFloat, VObject # need 3 elements. Cannot remove VNull or VObject
+
+    Value = object
+      case kind: ValueKind
+      of VFloat: fnum: float
+      of VObject: tab: Table[int, int] # OrderedTable[T, U] also makes it fail.
+                                      # "simpler" types also work though
+      else: discard # VNull can be like this, but VObject must be filled
+
+    # required. Pure proc works
+    FormulaNode = proc(c: OrderedTable[string, int]): Value
+
+  proc toF(v: Value): float =
+    doAssert v.kind == VFloat
+    case v.kind
+    of VFloat: result = v.fnum
+    else: discard
+
+
+  proc foo() =
+    let fuck = initOrderedTable[string, int]()
+    proc cb(fuck: OrderedTable[string, int]): Value =
+                            # works:
+                            #result = Value(kind: VFloat, fnum: fuck["field_that_does_not_exist"].float)
+                            # broken:
+      discard "actuall runs!"
+      let t = fuck["field_that_does_not_exist"]
+      echo "never runs, but we crash after! ", t
+
+    doAssertRaises(KeyError):
+      let fn = FormulaNode(cb)
+      let v = fn(fuck)
+      #echo v
+      let res = v.toF()
+
+  foo()
+
+import std/options
+
+# bug #21592
+type Event* = object
+  code*: string
+
+type App* = ref object of RootObj
+  id*: string
+
+method process*(self: App): Option[Event] {.base.} =
+  raise Exception.new_exception("not impl")
+
+# bug #21617
+type Test2 = ref object of RootObj
+
+method bug(t: Test2): seq[float] {.base.} = discard
+
+block: # bug #22664
+  type
+    ElementKind = enum String, Number
+    Element = object
+      case kind: ElementKind
+      of String:
+        str: string
+      of Number:
+        num: float
+    Calc = ref object
+      stack: seq[Element]
+
+  var calc = new Calc
+
+  calc.stack.add Element(kind: Number, num: 200.0)
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  let calc2 = calc
+  calc2.stack = calc.stack # This nulls out the object in the stack
+  doAssert $calc.stack == "@[(kind: Number, num: 200.0)]"
+  doAssert $calc2.stack == "@[(kind: Number, num: 200.0)]"
+
+block: # bug #19250
+  type
+    Bar[T] = object
+      err: proc(): string
+
+    Foo[T] = object
+      run: proc(): Bar[T]
+
+  proc bar[T](err: proc(): string): Bar[T] =
+    assert not err.isNil
+    Bar[T](err: err)
+
+  proc foo(): Foo[char] = 
+    result.run = proc(): Bar[char] =
+      # works
+      # result = Bar[char](err: proc(): string = "x")
+      # not work
+      result = bar[char](proc(): string = "x")
+
+  proc bug[T](fs: Foo[T]): Foo[T] =
+    result.run = proc(): Bar[T] =
+      let res = fs.run()
+      
+      # works
+      # var errors = @[res.err] 
+      
+      # not work
+      var errors: seq[proc(): string]
+      errors.add res.err
+      
+      return bar[T] do () -> string:
+        for err in errors:
+          result.add res.err()
+
+  doAssert bug(foo()).run().err() == "x"
+
+block: # bug #22259
+  type
+    ProcWrapper = tuple
+      p: proc() {.closure.}
+
+
+  proc f(wrapper: ProcWrapper) =
+    let s = @[wrapper.p]
+    let a = [wrapper.p]
+
+  proc main =
+    # let wrapper: ProcWrapper = ProcWrapper(p: proc {.closure.} = echo 10)
+    let wrapper: ProcWrapper = (p: proc {.closure.} = echo 10)
+    f(wrapper)
+
+  main()
+
+block:
+  block: # bug #22923
+    block:
+      let
+        a: int = 100
+        b: int32 = 200'i32
+
+      let
+        x = arrayWith(a, 8) # compiles
+        y = arrayWith(b, 8) # internal error
+        z = arrayWith(14, 8) # integer literal also results in a crash
+
+      doAssert x == [100, 100, 100, 100, 100, 100, 100, 100]
+      doAssert $y == "[200, 200, 200, 200, 200, 200, 200, 200]"
+      doAssert z == [14, 14, 14, 14, 14, 14, 14, 14]
+
+    block:
+      let a: string = "nim"
+      doAssert arrayWith(a, 3) == ["nim", "nim", "nim"]
+
+      let b: char = 'c'
+      doAssert arrayWith(b, 3) == ['c', 'c', 'c']
+
+      let c: uint = 300'u
+      doAssert $arrayWith(c, 3) == "[300, 300, 300]"
+
+block: # bug #23505
+  type
+    K = object
+    C = object
+      value: ptr K
+
+  proc init(T: type C): C =
+    let tmp = new K
+    C(value: addr tmp[])
+
+  discard init(C)
+
+block: # bug #23524
+  type MyType = object
+    a: int
+
+  proc `=destroy`(typ: MyType) = discard
+
+  var t1 = MyType(a: 100)
+  var t2 = t1 # Should be a copy?
+
+  proc main() =
+    t2 = t1
+    doAssert t1.a == 100
+    doAssert t2.a == 100
+
+  main()
+
+block: # bug #23907
+  type
+    Thingy = object
+      value: int
+
+    ExecProc[C] = proc(value: sink C): int {.nimcall.}
+
+  proc `=copy`(a: var Thingy, b: Thingy) {.error.}
+
+  var thingyDestroyCount = 0
+
+  proc `=destroy`(thingy: Thingy) =
+    assert(thingyDestroyCount <= 0)
+    thingyDestroyCount += 1
+
+  proc store(value: sink Thingy): int =
+    result = value.value
+
+  let callback: ExecProc[Thingy] = store
+
+  doAssert callback(Thingy(value: 123)) == 123
+
+import std/strutils
+
+block: # bug #23974
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/d/"
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()
+    echo t
+
+  n()
+
+block: # bug #23973
+  func g(e: seq[string]): lent seq[string] = result = e
+  proc k(f: string): seq[string] = f.split("/")
+  proc n() =
+    const r = "/test/empty"  # or "/test/empty/1"
+    let a = k(r).g()
+    let t =
+      if true:
+        k(r).g()
+      else:
+        k("/" & r).g()   # or raiseAssert ""
+    doAssert t == a
+
+  n()
+
+block: # bug #24141
+  func reverse(s: var openArray[char]) =
+    s[0] = 'f'
+
+  func rev(s: var string) =
+    s.reverse
+
+  proc main =
+    var abc = "abc"
+    abc.rev
+    doAssert abc == "fbc"
+
+  main()
diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim
index eb0c45213..8e3a7b3e7 100644
--- a/tests/arc/tasyncleak.nim
+++ b/tests/arc/tasyncleak.nim
@@ -1,5 +1,5 @@
 discard """
-  outputsub: "(allocCount: 4302, deallocCount: 4300)"
+  outputsub: "(allocCount: 4050, deallocCount: 4048)"
   cmd: "nim c --gc:orc -d:nimAllocStats $file"
 """
 
diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim
index d52833e4d..3499f5c1e 100644
--- a/tests/arc/tcaseobj.nim
+++ b/tests/arc/tcaseobj.nim
@@ -60,7 +60,7 @@ proc `=destroy`(o: var TMyObj) =
     o.p = nil
     echo "myobj destroyed"
 
-proc `=`(dst: var TMyObj, src: TMyObj) =
+proc `=copy`(dst: var TMyObj, src: TMyObj) =
   `=destroy`(dst)
   dst.p = alloc(src.len)
   dst.len = src.len
@@ -170,18 +170,24 @@ proc test_myobject =
   x.x1 = "x1"
   x.x2 = "x2"
   x.y1 = "ljhkjhkjh"
-  x.kind1 = true
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
   x.y2 = @["1", "2"]
-  x.kind2 = true
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
   x.z1 = "yes"
-  x.kind2 = false
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
   x.z2 = @["1", "2"]
-  x.kind2 = true
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
   x.z1 = "yes"
   x.kind2 = true # should be no effect
   doAssert(x.z1 == "yes")
-  x.kind2 = false
-  x.kind1 = x.kind2 # support self assignment with effect
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+  {.cast(uncheckedAssign).}:
+    x.kind1 = x.kind2 # support self assignment with effect
 
   try:
     x.kind1 = x.flag # flag is not accesible
@@ -207,8 +213,9 @@ type
       error*: string
 
 proc init(): RocksDBResult[string] =
-  result.ok = true
-  result.value = "ok"
+  {.cast(uncheckedAssign).}:
+    result.ok = true
+    result.value = "ok"
 
 echo init()
 
@@ -222,7 +229,8 @@ type MyObj = object
     of true: x1: string
 
 var a = MyObj(kind: false, x0: 1234)
-a.kind = true
+{.cast(uncheckedAssign).}:
+  a.kind = true
 doAssert(a.x1 == "")
 
 block:
@@ -269,3 +277,90 @@ proc bug20305 =
   echo x.pChildren
 
 bug20305()
+
+# bug #21023
+block:
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.o == true
+
+    mainSync()
+
+  block:
+    type
+      MGErrorKind = enum
+        mgeUnexpected, mgeNotFound
+
+    type Foo = object
+      kind: MGErrorKind
+      ex: Exception
+
+    type Boo = object
+      a: seq[int]
+
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: Foo
+        of true:
+          v: Boo
+          s: int
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true, s: 12)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.s == 12
+
+    mainSync()
+
+import std/sequtils
+
+# bug #23690
+type
+  SomeObj* = object of RootObj
+
+  Item* = object
+    case kind*: 0..1
+    of 0:
+      a*: int
+      b*: SomeObj
+    of 1:
+      c*: string
+
+  ItemExt* = object
+    a*: Item
+    b*: string
+
+proc do1(x: int): seq[(string, Item)] =
+  result = @[("zero", Item(kind: 1, c: "first"))]
+
+proc do2(x: int, e: ItemExt): seq[(string, ItemExt)] =
+  do1(x).map(proc(v: (string, Item)): auto = (v[0], ItemExt(a: v[1], b: e.b)))
+
+doAssert $do2(0, ItemExt(a: Item(kind: 1, c: "second"), b: "third")) == """@[("zero", (a: (kind: 1, c: "first"), b: "third"))]"""
diff --git a/tests/arc/tcaseobjcopy.nim b/tests/arc/tcaseobjcopy.nim
index ed07b404e..fb26a4973 100644
--- a/tests/arc/tcaseobjcopy.nim
+++ b/tests/arc/tcaseobjcopy.nim
@@ -169,18 +169,23 @@ proc test_myobject =
   x.x1 = "x1"
   x.x2 = "x2"
   x.y1 = "ljhkjhkjh"
-  x.kind1 = true
+  {.cast(uncheckedAssign).}:
+    x.kind1 = true
   x.y2 = @["1", "2"]
-  x.kind2 = true
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
   x.z1 = "yes"
-  x.kind2 = false
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
   x.z2 = @["1", "2"]
-  x.kind2 = true
+  {.cast(uncheckedAssign).}:
+    x.kind2 = true
   x.z1 = "yes"
   x.kind2 = true # should be no effect
   doAssert(x.z1 == "yes")
-  x.kind2 = false
-  x.kind1 = x.kind2 # support self assignment with effect
+  {.cast(uncheckedAssign).}:
+    x.kind2 = false
+    x.kind1 = x.kind2 # support self assignment with effect
 
   try:
     x.kind1 = x.flag # flag is not accesible
@@ -206,7 +211,8 @@ type
       error*: string
 
 proc init(): RocksDBResult[string] =
-  result.ok = true
+  {.cast(uncheckedAssign).}:
+    result.ok = true
   result.value = "ok"
 
 echo init()
@@ -221,7 +227,8 @@ type MyObj = object
     of true: x1: string
 
 var a = MyObj(kind: false, x0: 1234)
-a.kind = true
+{.cast(uncheckedAssign).}:
+  a.kind = true
 doAssert(a.x1 == "")
 
 block:
diff --git a/tests/arc/tcomputedgoto.nim b/tests/arc/tcomputedgoto.nim
index 541a748c6..07487684a 100644
--- a/tests/arc/tcomputedgoto.nim
+++ b/tests/arc/tcomputedgoto.nim
@@ -1,16 +1,19 @@
 discard """
-  cmd: '''nim c --gc:arc $file'''
-  output: '''2
-2'''
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
 """
 
 type
   ObjWithDestructor = object
     a: int
-proc `=destroy`(self: var ObjWithDestructor) =
+proc `=destroy`(self: ObjWithDestructor) =
   echo "destroyed"
 
-proc `=`(self: var ObjWithDestructor, other: ObjWithDestructor) =
+proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
   echo "copied"
 
 proc test(a: range[0..1], arg: ObjWithDestructor) =
@@ -38,4 +41,4 @@ proc test(a: range[0..1], arg: ObjWithDestructor) =
       if iteration == 2:
         break
 
-test(1, ObjWithDestructor())
\ No newline at end of file
+test(1, ObjWithDestructor())
diff --git a/tests/arc/tcomputedgotocopy.nim b/tests/arc/tcomputedgotocopy.nim
index 8337123ba..07487684a 100644
--- a/tests/arc/tcomputedgotocopy.nim
+++ b/tests/arc/tcomputedgotocopy.nim
@@ -1,13 +1,16 @@
 discard """
-  cmd: '''nim c --gc:arc $file'''
-  output: '''2
-2'''
+  cmd: '''nim c --mm:arc $file'''
+  output: '''
+2
+2
+destroyed
+'''
 """
 
 type
   ObjWithDestructor = object
     a: int
-proc `=destroy`(self: var ObjWithDestructor) =
+proc `=destroy`(self: ObjWithDestructor) =
   echo "destroyed"
 
 proc `=copy`(self: var ObjWithDestructor, other: ObjWithDestructor) =
diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim
index 80cc2b187..dbc115903 100644
--- a/tests/arc/tcontrolflow.nim
+++ b/tests/arc/tcontrolflow.nim
@@ -76,7 +76,7 @@ var c = Control(x: 7)
 
 run(c)
 
-proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
+proc sysFatal(exceptn: typedesc, message: string) {.inline.} =
   var buf = newStringOfCap(200)
   add(buf, "##")
   add(buf, message)
diff --git a/tests/arc/tdup.nim b/tests/arc/tdup.nim
new file mode 100644
index 000000000..61f262a68
--- /dev/null
+++ b/tests/arc/tdup.nim
@@ -0,0 +1,71 @@
+discard """
+  cmd: "nim c --mm:arc --expandArc:foo --hints:off $file"
+  nimout: '''
+--expandArc: foo
+
+var
+  x
+  :tmpD
+  s
+  :tmpD_1
+x = Ref(id: 8)
+inc:
+  :tmpD = `=dup`(x)
+  :tmpD
+inc:
+  let blitTmp = x
+  blitTmp
+var id_1 = 777
+s = RefCustom(id_2: addr(id_1))
+inc_1 :
+  :tmpD_1 = `=dup_1`(s)
+  :tmpD_1
+inc_1 :
+  let blitTmp_1 = s
+  blitTmp_1
+-- end of expandArc ------------------------
+'''
+"""
+
+type
+  Ref = ref object
+    id: int
+
+  RefCustom = object
+    id: ptr int
+
+proc `=dup`(x: RefCustom): RefCustom =
+  result.id = x.id
+
+proc inc(x: sink Ref) =
+  inc x.id
+
+proc inc(x: sink RefCustom) =
+  inc x.id[]
+
+
+proc foo =
+  var x = Ref(id: 8)
+  inc(x)
+  inc(x)
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  inc s
+
+foo()
+
+proc foo2 =
+  var x = Ref(id: 8)
+  inc(x)
+  doAssert x.id == 9
+  inc(x)
+  doAssert x.id == 10
+  var id = 777
+  var s = RefCustom(id: addr id)
+  inc s
+  doAssert s.id[] == 778
+  inc s
+  doAssert s.id[] == 779
+
+foo2()
diff --git a/tests/arc/texplicit_sink.nim b/tests/arc/texplicit_sink.nim
new file mode 100644
index 000000000..4b021ed62
--- /dev/null
+++ b/tests/arc/texplicit_sink.nim
@@ -0,0 +1,29 @@
+discard """
+  output: '''de'''
+  cmd: '''nim c --mm:arc --expandArc:main $file'''
+  nimout: '''--expandArc: main
+
+var
+  a
+  b_cursor
+try:
+  a = f "abc"
+  b_cursor = "de"
+  `=sink`(a, b_cursor)
+  echo [a]
+finally:
+  `=destroy`(a)
+-- end of expandArc ------------------------'''
+"""
+
+# bug #20572
+
+proc f(s: string): string = s
+
+proc main =
+  var a = f "abc"
+  var b = "de"
+  `=sink`(a, b)
+  echo a
+
+main()
diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim
index baa964c77..30cfddb05 100644
--- a/tests/arc/thard_alignment.nim
+++ b/tests/arc/thard_alignment.nim
@@ -1,9 +1,11 @@
 discard """
 disabled: "arm64"
-cmd: "nim c --gc:arc $file"
+cmd: "nim c --mm:arc -u:nimPreviewNonVarDestructor $file"
 output: "y"
 """
 
+# TODO: fixme: investigate why it failed with non-var destructors
+
 {.passC: "-march=native".}
 
 proc isAlignedCheck(p: pointer, alignment: int) = 
diff --git a/tests/arc/titeration_doesnt_copy.nim b/tests/arc/titeration_doesnt_copy.nim
new file mode 100644
index 000000000..e510a6eff
--- /dev/null
+++ b/tests/arc/titeration_doesnt_copy.nim
@@ -0,0 +1,67 @@
+discard """
+  output: "true"
+"""
+
+type
+  Idx = object
+    i: int
+  Node = object
+    n: int
+    next: seq[Idx]
+  FooBar = object
+    s: seq[Node]
+
+proc `=copy`(dest: var Idx; source: Idx) {.error.}
+proc `=copy`(dest: var Node; source: Node) {.error.}
+proc `=copy`(dest: var FooBar; source: FooBar) {.error.}
+
+proc doSomething(ss: var seq[int], s: FooBar) =
+  for i in 0 .. s.s.len-1:
+    for elm in items s.s[i].next:
+      ss.add s.s[elm.i].n
+
+when isMainModule:
+  const foo = FooBar(s: @[Node(n: 1, next: @[Idx(i: 0)])])
+  var ss: seq[int]
+  doSomething(ss, foo)
+  echo ss == @[1]
+
+from sequtils import mapIt
+from strutils import join
+
+proc toBinSeq*(b: uint8): seq[uint8] =
+  ## Return binary sequence from each bits of uint8.
+  runnableExamples:
+    from sequtils import repeat
+    doAssert 0'u8.toBinSeq == 0'u8.repeat(8)
+    doAssert 0b1010_1010.toBinSeq == @[1'u8, 0, 1, 0, 1, 0, 1, 0]
+  result = @[]
+  var c = b
+  for i in 1..8:
+    result.add (uint8(c and 0b1000_0000) shr 7)
+    c = c shl 1
+
+proc toBinString*(data: openArray[uint8], col: int): string =
+  ## Return binary string from each bits of uint8.
+  runnableExamples:
+    doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+    doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+  result = ""
+  for b in items data.mapIt(it.toBinSeq.mapIt(it.`$`[0].char)):
+    for i, c in b:
+      if i < col:
+        result.add c
+
+doAssert @[0b0000_1111'u8, 0b1010_1010].toBinString(8) == "0000111110101010"
+doAssert @[0b1000_0000'u8, 0b0000_0000].toBinString(1) == "10"
+
+block: # bug #23982
+  iterator `..`(a, b: ptr int16): ptr int16 = discard
+  var a: seq[int16] #; let p = a[0].addr
+  var b: seq[ptr int16]
+
+  try:
+    for x in a[0].addr .. b[1]: # `p .. b[1]` works
+      discard
+  except IndexDefect:
+    discard
diff --git a/tests/arc/tmalloc.nim b/tests/arc/tmalloc.nim
new file mode 100644
index 000000000..1d72646c8
--- /dev/null
+++ b/tests/arc/tmalloc.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--mm:arc -d:useMalloc; --mm:arc"
+"""
+
+block: # bug #22058
+  template foo(): auto =
+    {.noSideEffect.}:
+      newSeq[byte](1)
+
+  type V = object
+    v: seq[byte]
+
+  proc bar(): V =
+    V(v: foo())
+
+  doAssert bar().v == @[byte(0)]
diff --git a/tests/arc/tmove_regression.nim b/tests/arc/tmove_regression.nim
new file mode 100644
index 000000000..7d9a867c3
--- /dev/null
+++ b/tests/arc/tmove_regression.nim
@@ -0,0 +1,22 @@
+discard """
+  output: '''/1/2
+/1
+/
+'''
+""""
+
+# bug #22001
+
+import std / [os, strutils]
+
+proc finOp2(path, name: string): (string, File) = # Find & open FIRST `name`
+  var current = path
+  while true:
+    if current.isRootDir: break # <- current=="" => current.isRootDir
+    current = current.parentDir
+    let dir = current
+    echo dir.replace('\\', '/')  # Commenting out try/except below hides bug
+    try: result[0] = dir/name; result[1] = open(result[0]); return
+    except CatchableError: discard
+
+discard finOp2("/1/2/3", "4")  # All same if this->inside a proc
diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim
index 002bd6796..8ad7c955c 100644
--- a/tests/arc/tmovebug.nim
+++ b/tests/arc/tmovebug.nim
@@ -93,6 +93,7 @@ destroy
 destroy
 destroy
 sink
+sink
 destroy
 copy
 (f: 1)
@@ -767,8 +768,7 @@ proc initC(): C =
   C(o: initO())
 
 proc pair(): tuple[a: C, b: C] =
-  result.a = initC() # <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy
-  result.b = initC()
+  result = (a: initC(), b: initC())# <- when firstWrite tries to find this node to start its analysis it fails, because injectdestructors uses copyTree/shallowCopy
 
 discard pair()
 
@@ -818,3 +818,24 @@ proc atomicClosureOp =
 
 atomicClosureOp()
 
+
+template assertEq(a, b: untyped): untyped =
+  block:
+    let
+      aval = a
+      bval = b
+
+    if aval != bval:
+      quit "bug!"
+
+proc convoluted =
+  let _ = (;
+    var val1: string;
+    if true: val1 = "22"
+    true
+  )
+
+  assertEq val1, "22"
+  assertEq val1, "22"
+
+convoluted()
diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim
index 03ec7adf2..ba91666ba 100644
--- a/tests/arc/topenarray.nim
+++ b/tests/arc/topenarray.nim
@@ -6,6 +6,8 @@ Nim
 '''
   matrix: "--gc:arc -d:useMalloc; --gc:arc"
 """
+{.experimental: "views".}
+
 block: # bug 18627
   proc setPosition(params: openArray[string]) =
     for i in params.toOpenArray(0, params.len - 1):
@@ -22,3 +24,63 @@ block: # bug 18627
     for i in params.toOpenArray(0, params.len - 1):
       echo i
   uciLoop2()
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+block: # bug #20954
+  block:
+    doAssertRaises(IndexDefect):
+      var v: array[10, int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+  block:
+    doAssertRaises(IndexDefect):
+      var v: seq[int]
+
+      echo len(toOpenArray(v, 20, 30))
+
+# bug #20422
+
+proc f(a: var string) =
+  var v = a.toOpenArray(1, 3)
+  v[0] = 'a'
+
+var a = "Hello"
+f(a)
+doAssert a == "Hallo"
+
+# bug #22132
+block:
+  func foo[T](arr: openArray[T], idx: int = arr.low): string =
+    doAssert idx == 0
+    return $arr
+
+  let bug = ["0", "c", "a"]
+
+  let str = foo(bug)
+
+  const expected = """["0", "c", "a"]"""
+  doAssert str == expected
+
+  const noBugConst = ["0", "c", "a"]
+  doAssert foo(noBugConst) == expected
+  let noBugSeq = @["0", "c", "a"]
+  doAssert foo(noBugSeq) == expected
+
+block: # bug #20865
+  var p: pointer
+  var x: array[0, int]
+  # echo toOpenArray(x, 0, 1)[0] # Raises IndexDefect
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(cast[ptr array[0, int]](p)[], 0, 1)[0] # Does not raise IndexDefect
+
+block: # bug #20987
+  var v: array[1, byte]
+
+  var p = cast[ptr array[0, byte]](addr v)
+
+  doAssertRaises(IndexDefect):
+    echo toOpenArray(p[], 1, 2)
+
diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim
index 0de7f41b9..0a4984a69 100644
--- a/tests/arc/topt_no_cursor.nim
+++ b/tests/arc/topt_no_cursor.nim
@@ -1,23 +1,20 @@
 discard """
-  output: '''(package: "", ext: "meo")
-doing shady stuff...
-3
-6
-(@[1], @[2])
-192.168.0.1
-192.168.0.1
-192.168.0.1
-192.168.0.1'''
-  cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file'''
-  nimout: '''--expandArc: newTarget
+  nimoutFull: true
+  cmd: '''nim c -r --warnings:off --hints:off --mm:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file'''
+  nimout: '''
+--expandArc: newTarget
 
+var
+  splat
+  :tmp
+  :tmp_1
 splat = splitDrive do:
   let blitTmp = path
   blitTmp
 :tmp = splat.drive
-wasMoved(splat.drive)
+`=wasMoved`(splat.drive)
 :tmp_1 = splat.path_1
-wasMoved(splat.path_1)
+`=wasMoved`(splat.path_1)
 result = (
   let blitTmp_1 = :tmp
   blitTmp_1,
@@ -42,14 +39,14 @@ var
   lresult
   lvalue
   lnext
-  _
+  tmpTupleAsgn
 lresult = @[123]
-_ = (
+tmpTupleAsgn = (
   let blitTmp = lresult
   blitTmp, ";")
-lvalue = _[0]
-lnext = _[1]
-result.value = move lvalue
+lvalue = tmpTupleAsgn[0]
+lnext = tmpTupleAsgn[1]
+`=sink`(result.value, move lvalue)
 `=destroy`(lnext)
 `=destroy_1`(lvalue)
 -- end of expandArc ------------------------
@@ -64,11 +61,9 @@ var
 try:
   it_cursor = x
   a = (
-    wasMoved(:tmpD)
-    `=copy`(:tmpD, it_cursor.key)
+    :tmpD = `=dup`(it_cursor.key)
     :tmpD,
-    wasMoved(:tmpD_1)
-    `=copy`(:tmpD_1, it_cursor.val)
+    :tmpD_1 = `=dup`(it_cursor.val)
     :tmpD_1)
   echo [
     :tmpD_2 = `$$`(a)
@@ -96,11 +91,14 @@ try:
             `=copy`(lan_ip, splitted[1])
           echo [lan_ip]
           echo [splitted[1]]
+          {.push, overflowChecks: false.}
           inc(i, 1)
+          {.pop.}
         finally:
           `=destroy`(splitted)
 finally:
   `=destroy_1`(lan_ip)
+-- end of expandArc ------------------------
 --expandArc: mergeShadowScope
 
 var shadowScope
@@ -115,10 +113,11 @@ block :tmp:
       var :tmpD
       sym = shadowScope.symbols[i]
       addInterfaceDecl(c):
-        wasMoved(:tmpD)
-        `=copy_1`(:tmpD, sym)
+        :tmpD = `=dup`(sym)
         :tmpD
+      {.push, overflowChecks: false.}
       inc(i, 1)
+      {.pop.}
 `=destroy`(shadowScope)
 -- end of expandArc ------------------------
 --expandArc: check
@@ -128,19 +127,16 @@ this.isValid = fileExists(this.value)
 if dirExists(this.value):
   var :tmpD
   par = (dir:
-    wasMoved(:tmpD)
-    `=copy`(:tmpD, this.value)
+    :tmpD = `=dup`(this.value)
     :tmpD, front: "") else:
   var
     :tmpD_1
     :tmpD_2
     :tmpD_3
   par = (dir_1: parentDir(this.value), front_1:
-    wasMoved(:tmpD_1)
-    `=copy`(:tmpD_1,
+    :tmpD_1 = `=dup`(
       :tmpD_3 = splitDrive do:
-        wasMoved(:tmpD_2)
-        `=copy`(:tmpD_2, this.value)
+        :tmpD_2 = `=dup`(this.value)
         :tmpD_2
       :tmpD_3.path)
     :tmpD_1)
@@ -150,7 +146,21 @@ if dirExists(par.dir):
 else:
   `=sink`(this.matchDirs, [])
 `=destroy`(par)
--- end of expandArc ------------------------'''
+-- end of expandArc ------------------------
+--expandArc: check
+
+check(this)
+-- end of expandArc ------------------------
+(package: "", ext: "meo")
+doing shady stuff...
+3
+6
+(@[1], @[2])
+192.168.0.1
+192.168.0.1
+192.168.0.1
+192.168.0.1
+'''
 """
 
 import os, std/private/ntpath
diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim
index c13d81bad..8c638a4a1 100644
--- a/tests/arc/topt_refcursors.nim
+++ b/tests/arc/topt_refcursors.nim
@@ -1,7 +1,8 @@
 discard """
   output: ''''''
   cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file'''
-  nimout: '''--expandArc: traverse
+  nimout: '''
+--expandArc: traverse
 
 var
   it_cursor
@@ -22,12 +23,13 @@ try:
         `=copy`(ri_1, jt.ri)
         echo [jt.s]
         `=sink`(jt, ri_1)
-        wasMoved(ri_1)
+        `=wasMoved`(ri_1)
       finally:
         `=destroy`(ri_1)
 finally:
   `=destroy`(jt)
--- end of expandArc ------------------------'''
+-- end of expandArc ------------------------
+'''
 """
 
 type
diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim
index 2f971f112..c96340a30 100644
--- a/tests/arc/topt_wasmoved_destroy_pairs.nim
+++ b/tests/arc/topt_wasmoved_destroy_pairs.nim
@@ -1,7 +1,8 @@
 discard """
   output: ''''''
   cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file'''
-  nimout: '''--expandArc: main
+  nimout: '''
+--expandArc: main
 
 var
   a
@@ -29,6 +30,7 @@ try:
   x = f()
   block :tmp:
     var i_cursor
+    mixin inc
     var i_1 = 0
     block :tmp_1:
       while i_1 < 4:
@@ -37,25 +39,25 @@ try:
         if i_cursor == 2:
           return
         add(a):
-          wasMoved(:tmpD)
-          `=copy`(:tmpD, x)
+          :tmpD = `=dup`(x)
           :tmpD
         inc i_1, 1
   if cond:
     add(a):
       let blitTmp = x
-      wasMoved(x)
+      `=wasMoved`(x)
       blitTmp
   else:
     add(b):
       let blitTmp_1 = x
-      wasMoved(x)
+      `=wasMoved`(x)
       blitTmp_1
 finally:
   `=destroy`(x)
   `=destroy_1`(b)
   `=destroy_1`(a)
--- end of expandArc ------------------------'''
+-- end of expandArc ------------------------
+'''
 """
 
 proc f(): seq[int] =
diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim
index 50d433208..abf28330a 100644
--- a/tests/arc/trepr.nim
+++ b/tests/arc/trepr.nim
@@ -9,6 +9,7 @@ nil
 2
 Obj(member: ref @["hello"])
 ref (member: ref @["hello"])
+ObjUa(v: 0, a: [...])
 '''
 """
 
@@ -87,3 +88,10 @@ macro extract(): untyped =
   test(parseExpr("discard"))
   
 extract()
+
+type
+  ObjUa = ref object
+    v: int
+    a: UncheckedArray[char]
+
+echo ObjUa().repr
diff --git a/tests/arc/tstringliteral.nim b/tests/arc/tstringliteral.nim
new file mode 100644
index 000000000..c5fac22d8
--- /dev/null
+++ b/tests/arc/tstringliteral.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: "--mm:arc; --mm:orc"
+"""
+
+block: # issue #24080
+  var a = (s: "a")
+  var b = "a"
+  a.s.setLen 0
+  b = a.s
+  doAssert b == ""
+
+block: # issue #24080, longer string
+  var a = (s: "abc")
+  var b = "abc"
+  a.s.setLen 2
+  b = a.s
+  doAssert b == "ab"
diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim
index 220d65f97..1c60ac403 100644
--- a/tests/arc/tweave.nim
+++ b/tests/arc/tweave.nim
@@ -8,6 +8,9 @@ discard """
 
 import std/atomics
 
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, typedthreads]
+
 const MemBlockSize = 256
 
 type
diff --git a/tests/arc/twrong_sinkinference.nim b/tests/arc/twrong_sinkinference.nim
index 59930a9fa..ecf09d28a 100644
--- a/tests/arc/twrong_sinkinference.nim
+++ b/tests/arc/twrong_sinkinference.nim
@@ -1,6 +1,6 @@
 discard """
   cmd: "nim c --gc:arc $file"
-  errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe, locks: 0.}>"
+  errormsg: "type mismatch: got <proc (a: string, b: sink string){.noSideEffect, gcsafe.}>"
   line: 18
 """
 
diff --git a/tests/array/t15117.nim b/tests/array/t15117.nim
new file mode 100644
index 000000000..157b04bee
--- /dev/null
+++ b/tests/array/t15117.nim
@@ -0,0 +1,27 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+{.experimental: "views".}
+
+let a: array[0, byte] = []
+discard a
+
+type B = object
+  a:int
+let b: array[0, B] = []
+let c: array[0, ptr B] = []
+let d: array[0, ref B] = []
+discard b
+discard c
+discard d
+
+discard default(array[0, B])
+
+type
+  View1 = openArray[byte]
+discard default(View1)
diff --git a/tests/array/t20248.nim b/tests/array/t20248.nim
new file mode 100644
index 000000000..66142548b
--- /dev/null
+++ b/tests/array/t20248.nim
@@ -0,0 +1,14 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: "ordinal type expected; given: Error Type"
+nimout: '''
+t20248.nim(10, 36) Error: ordinal type expected; given: Error Type
+t20248.nim(14, 20) Error: ordinal type expected; given: Error Type
+'''
+"""
+
+type Vec[N: static[int]] = array[0 ..< N, float]
+
+var v: Vec[32]
+
+var stuff: array[0 ..< 16, int]
diff --git a/tests/misc/tinvalidarrayaccess.nim b/tests/array/tinvalidarrayaccess.nim
index f8bce45ef..f8bce45ef 100644
--- a/tests/misc/tinvalidarrayaccess.nim
+++ b/tests/array/tinvalidarrayaccess.nim
diff --git a/tests/misc/tinvalidarrayaccess2.nim b/tests/array/tinvalidarrayaccess2.nim
index 0a0703834..0a0703834 100644
--- a/tests/misc/tinvalidarrayaccess2.nim
+++ b/tests/array/tinvalidarrayaccess2.nim
diff --git a/tests/array/tlargeindex.nim b/tests/array/tlargeindex.nim
new file mode 100644
index 000000000..61bcbd61d
--- /dev/null
+++ b/tests/array/tlargeindex.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+# issue #17163
+var e: array[int32, byte] #[tt.Error
+             ^ index type 'int32' for array is too large]#
+var f: array[uint32, byte] #[tt.Error
+             ^ index type 'uint32' for array is too large]#
+var g: array[int64, byte] #[tt.Error
+             ^ index type 'int64' for array is too large]#
+var h: array[uint64, byte] #[tt.Error
+             ^ index type 'uint64' for array is too large]#
+
+# crash in issue #23204
+proc y[N](): array[N, int] = default(array[N, int]) #[tt.Error
+                                           ^ index type 'int' for array is too large]#
+discard y[int]()
diff --git a/tests/assert/panicoverride.nim b/tests/assert/panicoverride.nim
new file mode 100644
index 000000000..53ad64215
--- /dev/null
+++ b/tests/assert/panicoverride.nim
@@ -0,0 +1,15 @@
+# panicoverride.nim
+
+proc printf(fmt: cstring) {.varargs, importc, header:"stdio.h".}
+proc exit(code: cint) {.importc, header:"stdlib.h".}
+
+{.push stack_trace: off, profiler:off.}
+
+proc rawoutput(s: cstring) =
+  printf("RAW: %s\n", s)
+  
+proc panic(s: cstring) {.noreturn.} =
+  printf("PANIC: %s\n", s)
+  exit(0)
+
+{.pop.}
\ No newline at end of file
diff --git a/tests/assert/t21195.nim b/tests/assert/t21195.nim
new file mode 100644
index 000000000..b690d22a0
--- /dev/null
+++ b/tests/assert/t21195.nim
@@ -0,0 +1,6 @@
+discard """
+  matrix: "--verbosity:0 --os:standalone --mm:none"
+"""
+# bug #21195
+var n = 11
+assert(n == 12)
diff --git a/tests/assert/tassert2.nim b/tests/assert/tassert2.nim
index 5d849aaad..e32ab72e1 100644
--- a/tests/assert/tassert2.nim
+++ b/tests/assert/tassert2.nim
@@ -24,7 +24,7 @@ except AssertionDefect as e:
 try:
   assert false # assert test with no msg
 except AssertionDefect as e:
-  assert e.msg.endsWith "tassert2.nim(25, 10) `false` "
+  assert e.msg.endsWith "tassert2.nim(25, 3) `false` "
 
 try:
   let a = 1
@@ -100,7 +100,7 @@ block: ## checks for issue https://github.com/nim-lang/Nim/issues/9301
     doAssert 1 + 1 == 3
   except AssertionDefect as e:
     # used to const fold as false
-    assert e.msg.endsWith "tassert2.nim(100, 14) `1 + 1 == 3` "
+    assert e.msg.endsWith "tassert2.nim(100, 5) `1 + 1 == 3` "
 
 block: ## checks AST isn't transformed as it used to
   let a = 1
@@ -108,4 +108,4 @@ block: ## checks AST isn't transformed as it used to
     doAssert a > 1
   except AssertionDefect as e:
     # used to rewrite as `1 < a`
-    assert e.msg.endsWith "tassert2.nim(108, 14) `a > 1` "
+    assert e.msg.endsWith "tassert2.nim(108, 5) `a > 1` "
diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim
index 5c8f529ad..e3e3b8147 100644
--- a/tests/assert/tassert_c.nim
+++ b/tests/assert/tassert_c.nim
@@ -1,14 +1,14 @@
 discard """
-  cmd: "nim $target $options --excessiveStackTrace:off $file"
+  matrix: "-d:nimPreviewSlimSystem --stackTrace:on --excessiveStackTrace:off"
   output: '''true'''
 """
-
+import std/assertions
 const expected = """
 tassert_c.nim(35)        tassert_c
 tassert_c.nim(34)        foo
 assertions.nim(*)       failedAssertImpl
 assertions.nim(*)       raiseAssert
-fatal.nim(*)            sysFatal"""
+"""
 
 proc tmatch(x, p: string): bool =
   var i = 0
diff --git a/tests/assign/tvariantasgn.nim b/tests/assign/tvariantasgn.nim
index 1c3deeae1..4c3c38ca5 100644
--- a/tests/assign/tvariantasgn.nim
+++ b/tests/assign/tvariantasgn.nim
@@ -24,3 +24,16 @@ s = TAny(kind: nkInt, intVal: 78)
 # s = nr # works
 nr = s # fails!
 echo "came here"
+
+block: # bug #12464
+  type
+    Foo = object
+      case isFunc: bool
+      of false: nil
+      of true:
+        fun: proc(): int
+
+  const i = Foo(isFunc: false)
+
+  let j = i
+  doAssert not j.isFunc
diff --git a/tests/async/t11558.nim b/tests/async/t11558.nim
new file mode 100644
index 000000000..99961e7b6
--- /dev/null
+++ b/tests/async/t11558.nim
@@ -0,0 +1,13 @@
+discard """
+output: "Hello\n9"
+"""
+import std/asyncdispatch
+
+proc foo(): Future[string] {.async.} =
+  "Hello"
+
+proc bar(): Future[int] {.async.} =
+  result = 9
+
+echo waitFor foo()
+echo waitFor bar()
diff --git a/tests/async/t17045.nim b/tests/async/t17045.nim
new file mode 100644
index 000000000..2b5acf48a
--- /dev/null
+++ b/tests/async/t17045.nim
@@ -0,0 +1,28 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:arc"
+"""
+
+type Future = ref object
+
+iterator paths: string = 
+  # without "when nimvm" everything works
+  when nimvm:
+    yield "test.md"
+  else:
+    yield "test.md"
+
+template await(f: Future): string =
+  # need this yield, also the template has to return something
+  yield f
+  "hello world"
+
+proc generatePostContextsAsync() =
+  iterator generatePostContextsAsyncIter(): Future {.closure.} =
+    for filePath in paths():
+      var temp = await Future()
+
+  # need this line
+  var nameIterVar = generatePostContextsAsyncIter
+
+generatePostContextsAsync()
\ No newline at end of file
diff --git a/tests/async/t20111.nim b/tests/async/t20111.nim
new file mode 100644
index 000000000..0aaa7d886
--- /dev/null
+++ b/tests/async/t20111.nim
@@ -0,0 +1,19 @@
+discard """
+  action: "run"
+"""
+import asyncdispatch
+type
+    Sync = object
+    Async = object
+    SyncRes = (Sync, string)
+    AsyncRes = (Async, string)
+
+proc foo(val: Sync | Async): Future[(Async, string) | (Sync, string)] {.multisync.} =
+    return (val, "hello")
+
+let
+  myAsync = Async()
+  mySync = Sync()
+
+doAssert typeof(waitFor foo(myAsync)) is AsyncRes
+doAssert typeof(foo(mySync)) is SyncRes
diff --git a/tests/async/t21447.nim b/tests/async/t21447.nim
new file mode 100644
index 000000000..e4f7ae31f
--- /dev/null
+++ b/tests/async/t21447.nim
@@ -0,0 +1,6 @@
+discard """
+  action: "compile"
+  cmd: "nim c -d:release -d:futureLogging $file"
+"""
+
+import std/asyncdispatch
diff --git a/tests/async/t21893.nim b/tests/async/t21893.nim
new file mode 100644
index 000000000..658cb02eb
--- /dev/null
+++ b/tests/async/t21893.nim
@@ -0,0 +1,13 @@
+discard """
+output: "@[97]\ntrue"
+"""
+
+import asyncdispatch
+
+proc test(): Future[bool] {.async.} =
+  const S4 = @[byte('a')]
+  echo S4
+  return true
+
+echo waitFor test()
+
diff --git a/tests/async/t22210.nim b/tests/async/t22210.nim
new file mode 100644
index 000000000..fcf939472
--- /dev/null
+++ b/tests/async/t22210.nim
@@ -0,0 +1,41 @@
+discard """
+output: '''
+stage 1
+stage 2
+stage 3
+(status: 200, data: "SOMEDATA")
+'''
+"""
+
+import std/asyncdispatch
+
+
+# bug #22210
+type
+  ClientResponse = object
+    status*: int
+    data*: string
+
+proc subFoo1(): Future[int] {.async.} =
+  await sleepAsync(100)
+  return 200
+
+proc subFoo2(): Future[string] {.async.} =
+  await sleepAsync(100)
+  return "SOMEDATA"
+
+proc testFoo(): Future[ClientResponse] {.async.} =
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    return ClientResponse(status: status, data: data)
+  finally:
+    echo "stage 1"
+    await sleepAsync(100)
+    echo "stage 2"
+    await sleepAsync(200)
+    echo "stage 3"
+
+when isMainModule:
+  echo waitFor testFoo()
\ No newline at end of file
diff --git a/tests/async/t22210_2.nim b/tests/async/t22210_2.nim
new file mode 100644
index 000000000..9db664a32
--- /dev/null
+++ b/tests/async/t22210_2.nim
@@ -0,0 +1,73 @@
+import std/asyncdispatch
+
+
+# bug #22210
+type
+  ClientResponse = object
+    status*: int
+    data*: string
+
+proc subFoo1(): Future[int] {.async.} =
+  await sleepAsync(100)
+  return 200
+
+proc subFoo2(): Future[string] {.async.} =
+  await sleepAsync(100)
+  return "SOMEDATA"
+
+
+proc testFoo2(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    result = ClientResponse(status: status, data: data)
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo2()
+
+proc testFoo3(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    if false:
+      return ClientResponse(status: status, data: data)
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo3()
+
+
+proc testFoo4(): Future[ClientResponse] {.async.} =
+  var flag = 0
+  try:
+    let status = await subFoo1()
+    doAssert(status == 200)
+    let data = await subFoo2()
+    if status == 200:
+      return ClientResponse(status: status, data: data)
+    else:
+      return ClientResponse()
+  finally:
+    inc flag
+    await sleepAsync(100)
+    inc flag
+    await sleepAsync(200)
+    inc flag
+  doAssert flag == 3
+
+discard waitFor testFoo4()
diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim
index 00c92b109..f3e6bc691 100644
--- a/tests/async/tasync_gcunsafe.nim
+++ b/tests/async/tasync_gcunsafe.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'"
+  errormsg: "'anotherGCSafeAsyncProc (Async)' is not GC-safe as it calls 'asyncGCUnsafeProc'"
   cmd: "nim c --threads:on $file"
   file: "asyncmacro.nim"
 """
diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim
index ec67d34f9..98f71b192 100644
--- a/tests/async/tasync_traceback.nim
+++ b/tests/async/tasync_traceback.nim
@@ -67,51 +67,22 @@ import re
 const expected = """
 b failure
 Async traceback:
-  tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-  asyncmacro\.nim\(\d+?\)\s+?a
-  asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?aIter
-  asyncmacro\.nim\(\d+?\)\s+?b
-  asyncmacro\.nim\(\d+?\)\s+?bNimAsyncContinue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?bIter
-  #\[
-    tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-    asyncmacro\.nim\(\d+?\)\s+?a
-    asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue
-      ## Resumes an async procedure
-    asyncmacro\.nim\(\d+?\)\s+?aIter
-    asyncfutures\.nim\(\d+?\)\s+?read
-  \]#
+  tasync_traceback\.nim\(\d+?\) tasync_traceback
+  tasync_traceback\.nim\(\d+?\) a \(Async\)
+  tasync_traceback\.nim\(\d+?\) b \(Async\)
 Exception message: b failure
 
 
 bar failure
 Async traceback:
-  tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-  asyncdispatch\.nim\(\d+?\)\s+?waitFor
-  asyncdispatch\.nim\(\d+?\)\s+?poll
+  tasync_traceback\.nim\(\d+?\) tasync_traceback
+  asyncdispatch\.nim\(\d+?\) waitFor
+  asyncdispatch\.nim\(\d+?\) poll
     ## Processes asynchronous completion events
-  asyncdispatch\.nim\(\d+?\)\s+?runOnce
-  asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks
+  asyncdispatch\.nim\(\d+?\) runOnce
+  asyncdispatch\.nim\(\d+?\) processPendingCallbacks
     ## Executes pending callbacks
-  asyncmacro\.nim\(\d+?\)\s+?barNimAsyncContinue
-    ## Resumes an async procedure
-  tasync_traceback\.nim\(\d+?\)\s+?barIter
-  #\[
-    tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback
-    asyncdispatch\.nim\(\d+?\)\s+?waitFor
-    asyncdispatch\.nim\(\d+?\)\s+?poll
-      ## Processes asynchronous completion events
-    asyncdispatch\.nim\(\d+?\)\s+?runOnce
-    asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks
-      ## Executes pending callbacks
-    asyncmacro\.nim\(\d+?\)\s+?fooNimAsyncContinue
-      ## Resumes an async procedure
-    asyncmacro\.nim\(\d+?\)\s+?fooIter
-    asyncfutures\.nim\(\d+?\)\s+?read
-  \]#
+  tasync_traceback\.nim\(\d+?\) bar \(Async\)
 Exception message: bar failure
 
 """
diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim
index 222aaa3a1..57de3271d 100644
--- a/tests/async/tasyncssl.nim
+++ b/tests/async/tasyncssl.nim
@@ -1,5 +1,6 @@
 discard """
   cmd: "nim $target --hints:on --define:ssl $options $file"
+  disabled: osx
 """
 
 import asyncdispatch, asyncnet, net, strutils
diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim
index 77d03f8f6..f53767408 100644
--- a/tests/async/tioselectors.nim
+++ b/tests/async/tioselectors.nim
@@ -58,7 +58,11 @@ when not defined(windows):
     registerHandle(selector, client_socket, {Event.Write}, 0)
 
     freeAddrInfo(aiList)
-    discard selector.select(100)
+
+    # make sure both sockets are selected
+    var nevs = 0
+    while nevs < 2:
+      nevs += selector.select(100).len
 
     var sockAddress: SockAddr
     var addrLen = sizeof(sockAddress).Socklen
@@ -427,6 +431,20 @@ when not defined(windows):
       doAssert(res[0].fd == dirfd and
                {Event.Vnode, Event.VnodeDelete} <= res[0].events)
 
+  proc pipe_test(): bool =
+    # closing the read end of a pipe will result in it automatically
+    # being removed from the kqueue; make sure no exception is raised
+    var s = newSelector[int]()
+    var fds: array[2, cint]
+    discard pipe(fds)
+    s.registerHandle(fds[1], {Write}, 0)
+    discard close(fds[0])
+    let res = s.select(-1)
+    doAssert(res.len == 1)
+    s.unregister(fds[1])
+    discard close(fds[1])
+    return true
+
   when hasThreadSupport:
 
     var counter = 0
@@ -468,6 +486,7 @@ when not defined(windows):
   when defined(macosx) or defined(freebsd) or defined(openbsd) or
        defined(netbsd):
     processTest("File notification test...", vnode_test())
+    processTest("Pipe test...", pipe_test())
   echo("All tests passed!")
 else:
   import nativesockets, winlean, os, osproc
diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim
index ba1e4134e..68de796a0 100644
--- a/tests/async/tnewasyncudp.nim
+++ b/tests/async/tnewasyncudp.nim
@@ -58,7 +58,7 @@ proc launchSwarm(name: ptr SockAddr) {.async.} =
                                     16384, cast[ptr SockAddr](addr saddr),
                                     addr slen)
       size = 0
-      var grammString = $cstring(addr buffer)
+      var grammString = $cast[cstring](addr buffer)
       if grammString == message:
         saveSendingPort(sockport)
         inc(recvCount)
@@ -80,7 +80,7 @@ proc readMessages(server: AsyncFD) {.async.} =
                                   16384, cast[ptr SockAddr](addr(saddr)),
                                   addr(slen))
     size = 0
-    var grammString = $cstring(addr buffer)
+    var grammString = $cast[cstring](addr buffer)
     if grammString.startsWith("Message ") and
        saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32):
       await sendTo(server, addr grammString[0], len(grammString),
diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim
index ad27ca38f..f0a8f6a62 100644
--- a/tests/async/twinasyncrw.nim
+++ b/tests/async/twinasyncrw.nim
@@ -19,7 +19,7 @@ when defined(windows):
           retFuture.complete()
           return true
       else:
-          retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+          retFuture.fail(newOSError(OSErrorCode(ret)))
           return true
 
     var aiList = getAddrInfo(address, port, domain)
@@ -45,7 +45,7 @@ when defined(windows):
 
     freeAddrInfo(aiList)
     if not success:
-      retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+      retFuture.fail(newOSError(lastError))
     return retFuture
 
   proc winRecv*(socket: AsyncFD, size: int,
@@ -63,7 +63,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete("")
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       elif res == 0:
         # Disconnected
         retFuture.complete("")
@@ -88,7 +88,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete(0)
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       else:
         retFuture.complete(res)
     # TODO: The following causes a massive slowdown.
@@ -112,7 +112,7 @@ when defined(windows):
         if flags.isDisconnectionError(lastError):
           retFuture.complete()
         else:
-          retFuture.fail(newException(OSError, osErrorMsg(lastError)))
+          retFuture.fail(newOSError(lastError))
       else:
         written.inc(res)
         if res != netSize:
@@ -136,7 +136,7 @@ when defined(windows):
         var client = nativesockets.accept(sock.SocketHandle,
                                           cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
         if client == osInvalidSocket:
-          retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
+          retFuture.fail(newOSError(osLastError()))
         else:
           retFuture.complete((getAddrString(cast[ptr SockAddr](addr sockAddress)), client.AsyncFD))
 
diff --git a/tests/bind/tdatabind.nim b/tests/bind/tdatabind.nim
deleted file mode 100644
index f6455749c..000000000
--- a/tests/bind/tdatabind.nim
+++ /dev/null
@@ -1,95 +0,0 @@
-discard """
-  disabled: true
-"""
-
-import events
-type
-  TProperty*[T] = object of TObject
-    getProc: proc(property: TProperty[T]): T {.nimcall.}
-    setProc: proc(property: var TProperty[T], value: T) {.nimcall.}
-    value: T
-    ValueChanged*: TEventHandler
-    Binders: seq[TProperty[T]]
-    EEmitter: TEventEmitter
-  # Not a descriptive name but it was that or TPropertyValueChangeEventArgs.
-  TValueEventArgs[T] = object of TEventArgs
-    Property*: TProperty[T]
-
-
-proc newProperty*[T](value: T): TProperty[T] =
-  var prop: TProperty[T]
-
-  prop.EEmitter = initEventEmitter()
-  prop.Binders = @[]
-  prop.ValueChanged = initEventHandler("ValueChanged")
-  prop.value = value
-
-  proc getter(property: TProperty[T]): T =
-   return property.value
-
-  prop.getProc = getter
-
-  proc setter(property: var TProperty[T], v: T) =
-    property.value = v
-
-    # fire event here
-    var args: TValueEventArgs[T]
-    args.Property = property
-    property.EEmitter.emit(property.ValueChanged, args)
-
-  prop.setProc = setter
-
-  return prop
-
-proc `prop`[T] (p: TProperty[T]): T =
-  # I'm assuming this is trying to get a value from the property.
-  # i.e. myVar = myProperty
-  return p.getProc(p)
-
-proc `~=`[T] (p: var TProperty[T], v: T) =
-  # Assuming this is setting the value.
-  p.setProc(p, v)
-
-proc `$`[T] (p: TProperty[T]): string =
-  var value = p.getProc(p)
-  return $value
-
-proc propertyBind*[T](p1: var TProperty[T], p2: var TProperty[T]) =
-  p1.Binders.add(p2)
-
-  # make handler -> handler[T] so trigger even more generics bugs ...
-  proc handler(e: TEventArgs) =
-    type TEA = TValueEventArgs[T]
-    var args = TEA(e)
-    var val = args.Property.getProc(p1)
-    for i in countup(0, len(e.Property.ValueChanged.Binders) -1):
-      var binded = e.Property.ValueChanged.Binders[i]
-      binded.setProc(binded, val)
-
-    echo("Property 1 has changed to " & $val)
-
-  if p1.ValueChanged.containsHandler(handler) == false:
-    addHandler(p1.ValueChanged, handler)
-
-proc `->`[T](p1: var TProperty[T], p2: var TProperty[T]) =
-  propertyBind(p2,p1)
-
-when true:
-  # Initial value testing
-  var myProp = newProperty(5)
-
-  echo(myProp)
-
-  myProp ~= 7 # Temp operator until overloading of '=' is implemented.
-  echo(myProp)
-
-  # Binding testing
-
-  var prop1 = newProperty(9)
-  var prop2: TProperty[int]
-
-  prop2 -> prop1 # Binds prop2 to prop1
-
-  prop1 ~= 7
-  echo(prop2) # Output: 7
-
diff --git a/tests/borrow/tinvalidborrow.nim b/tests/borrow/tinvalidborrow.nim
deleted file mode 100644
index 08148608d..000000000
--- a/tests/borrow/tinvalidborrow.nim
+++ /dev/null
@@ -1,25 +0,0 @@
-discard """
-  cmd: "nim check --hints:off --warnings:off $file"
-  action: "reject"
-  nimout:'''
-tinvalidborrow.nim(18, 3) Error: only a 'distinct' type can borrow `.`
-tinvalidborrow.nim(19, 3) Error: only a 'distinct' type can borrow `.`
-tinvalidborrow.nim(20, 1) Error: no symbol to borrow from found
-'''
-"""
-
-# bug #516
-
-type
-  TAtom = culong
-  Test {.borrow:`.`.} = distinct int
-  Foo[T] = object
-    a: int
-  Bar[T] {.borrow:`.`.} = Foo[T]
-  OtherFoo {.borrow:`.`.} = Foo[int]
-proc `==`*(a, b: TAtom): bool {.borrow.}
-
-var
-  d, e: TAtom
-
-discard( $(d == e) )
diff --git a/tests/compilepragma/test.c b/tests/c/tcompile.c
index fc0482e40..fc0482e40 100644
--- a/tests/compilepragma/test.c
+++ b/tests/c/tcompile.c
diff --git a/tests/compilepragma/test.nim b/tests/c/tcompile.nim
index 56087fa57..cf99fd7ed 100644
--- a/tests/compilepragma/test.nim
+++ b/tests/c/tcompile.nim
@@ -3,7 +3,7 @@ discard """
   joinable: "false"
 """
 
-{.compile: "test.c".}
+{.compile: "tcompile.c".}
 
 proc foo(a, b: cint): cint {.importc: "foo", cdecl.}
 
diff --git a/tests/misc/temit.nim b/tests/c/temit.nim
index ee7455d4c..1943c94ea 100644
--- a/tests/misc/temit.nim
+++ b/tests/c/temit.nim
@@ -4,6 +4,7 @@ discard """
 # Test the new ``emit`` pragma:
 
 {.emit: """
+#include <stdio.h>
 static int cvariable = 420;
 
 """.}
diff --git a/tests/misc/treservedcidentsasfields.nim b/tests/c/treservedcidentsasfields.nim
index a9a954651..6cdf9e855 100644
--- a/tests/misc/treservedcidentsasfields.nim
+++ b/tests/c/treservedcidentsasfields.nim
@@ -1,39 +1,39 @@
-discard """

-  targets: "c cpp"

-"""

-

-import macros

-

-macro make_test_type(idents: varargs[untyped]): untyped =

-  result = nnkStmtList.newTree()

-

-  var ident_defs: seq[NimNode] = @[]

-  for i in idents:

-    ident_defs.add newIdentDefs(i, ident("int"))

-

-  result.add newTree(nnkTypeSection,

-    newTree(nnkTypeDef,

-      ident("TestType"),

-      newEmptyNode(),

-      newTree(nnkObjectTy,

-        newEmptyNode(),

-        newEmptyNode(),

-        newTree(nnkRecList,

-          ident_defs

-        )

-      )

-    )

-  )

-

-make_test_type(

-  auto, bool, catch, char, class, compl, const_cast, default, delete, double,

-  dynamic_cast, explicit, extern, false, float, friend, goto, int, long,

-  mutable, namespace, new, operator, private, protected, public, register,

-  reinterpret_cast, restrict, short, signed, sizeof, static_cast, struct, switch,

-  this, throw, true, typedef, typeid, typeof, typename, union, packed, unsigned,

-  virtual, void, volatile, wchar_t, alignas, alignof, constexpr, decltype, nullptr,

-  noexcept, thread_local, static_assert, char16_t, char32_t

-)

-

-# Make sure the type makes it to codegen.

-var test_instance: TestType

+discard """
+  targets: "c cpp"
+"""
+
+import macros
+
+macro make_test_type(idents: varargs[untyped]): untyped =
+  result = nnkStmtList.newTree()
+
+  var ident_defs: seq[NimNode] = @[]
+  for i in idents:
+    ident_defs.add newIdentDefs(i, ident("int"))
+
+  result.add newTree(nnkTypeSection,
+    newTree(nnkTypeDef,
+      ident("TestType"),
+      newEmptyNode(),
+      newTree(nnkObjectTy,
+        newEmptyNode(),
+        newEmptyNode(),
+        newTree(nnkRecList,
+          ident_defs
+        )
+      )
+    )
+  )
+
+make_test_type(
+  auto, bool, catch, char, class, compl, const_cast, default, delete, double,
+  dynamic_cast, explicit, extern, false, float, friend, goto, int, long,
+  mutable, namespace, new, operator, private, protected, public, register,
+  reinterpret_cast, restrict, short, signed, sizeof, static_cast, struct, switch,
+  this, throw, true, typedef, typeid, typeof, typename, union, packed, unsigned,
+  virtual, void, volatile, wchar_t, alignas, alignof, constexpr, decltype, nullptr,
+  noexcept, thread_local, static_assert, char16_t, char32_t
+)
+
+# Make sure the type makes it to codegen.
+var test_instance: TestType
diff --git a/tests/casestmt/t18964.nim b/tests/casestmt/t18964.nim
new file mode 100644
index 000000000..1d2de2bbc
--- /dev/null
+++ b/tests/casestmt/t18964.nim
@@ -0,0 +1,12 @@
+discard """
+errormsg: "invalid order of case branches"
+"""
+
+import macros
+
+macro genCase(val: string): untyped =
+  result = nnkCaseStmt.newTree(val,
+    nnkElse.newTree(quote do: echo "else"),
+    nnkOfBranch.newTree(newLit("miauz"), quote do: echo "first branch"))
+
+genCase("miauz")
diff --git a/tests/casestmt/tcase_issues.nim b/tests/casestmt/tcase_issues.nim
new file mode 100644
index 000000000..20a79df2c
--- /dev/null
+++ b/tests/casestmt/tcase_issues.nim
@@ -0,0 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
+block: # bug #24031
+  case 0
+  else: discard
\ No newline at end of file
diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim
index 3a4907494..66de4183d 100644
--- a/tests/casestmt/tcasestmt.nim
+++ b/tests/casestmt/tcasestmt.nim
@@ -41,6 +41,11 @@ block t8333:
   case 0
   of 'a': echo 0
   else: echo 1
+block: # issue #11422
+  var c: int = 5
+  case c
+  of 'a' .. 'c': discard
+  else: discard
 
 
 block emptyset_when:
@@ -298,3 +303,17 @@ proc main(a: uint64) =
 static:
   main(10)
 main(10)
+
+block:
+  # Just needs to compile
+  proc bar(): int {.discardable.} = discard
+
+  proc foo() {.noreturn.} = discard
+
+  case "*"
+  of "*":
+    bar()
+  else:
+    # Make sure this noreturn doesn't
+    # cause the discardable to not discard
+    foo()
diff --git a/tests/casestmt/trangeexhaustiveness.nim b/tests/casestmt/trangeexhaustiveness.nim
new file mode 100644
index 000000000..2b7f3558e
--- /dev/null
+++ b/tests/casestmt/trangeexhaustiveness.nim
@@ -0,0 +1,7 @@
+block: # issue #22661
+  template foo(a: typed) =
+    a
+    
+  foo:
+    case false
+    of false..true: discard
diff --git a/tests/cast/tcast.nim b/tests/cast/tcast.nim
new file mode 100644
index 000000000..205444ea3
--- /dev/null
+++ b/tests/cast/tcast.nim
@@ -0,0 +1,21 @@
+discard """
+  targets: "c cpp js"
+"""
+
+proc main() =
+  block: # bug #16806
+    let
+      a = 42u16
+      b = cast[int16](a)
+    doAssert a.int16 == 42
+    doAssert b in int16.low..int16.high
+
+  block: # bug #16808
+    doAssert cast[int8](cast[uint8](int8(-12))) == int8(-12)
+    doAssert cast[int16](cast[uint16](int16(-12))) == int16(-12)
+    doAssert cast[int32](cast[uint32](int32(-12))) == int32(-12)
+
+  doAssert cast[int8](int16.high) == -1
+
+static: main()
+main()
diff --git a/tests/ccgbugs/m1/defs.nim b/tests/ccgbugs/m1/defs.nim
new file mode 100644
index 000000000..ed78d8b72
--- /dev/null
+++ b/tests/ccgbugs/m1/defs.nim
@@ -0,0 +1,4 @@
+type MyObj* = object
+  field1*: int
+  s*: string
+  ch*: char
diff --git a/tests/ccgbugs/m2/defs.nim b/tests/ccgbugs/m2/defs.nim
new file mode 100644
index 000000000..798d1fea8
--- /dev/null
+++ b/tests/ccgbugs/m2/defs.nim
@@ -0,0 +1,4 @@
+type MyObj* = object
+  s*: string
+  field1*: int
+  ch*: char
diff --git a/tests/ccgbugs/t10964.nim b/tests/ccgbugs/t10964.nim
new file mode 100644
index 000000000..c19db6997
--- /dev/null
+++ b/tests/ccgbugs/t10964.nim
@@ -0,0 +1,7 @@
+func test*(input: var openArray[int32], start: int = 0, fin: int = input.len - 1) =

+    discard

+

+var someSeq = @[1'i32]

+

+test(someSeq)

+# bug with gcc 14
\ No newline at end of file
diff --git a/tests/ccgbugs/t13062.nim b/tests/ccgbugs/t13062.nim
index a8d2c1fec..cfda1da7c 100644
--- a/tests/ccgbugs/t13062.nim
+++ b/tests/ccgbugs/t13062.nim
@@ -24,7 +24,10 @@ type
     fulfilled: Atomic[bool]
 
 var x: Pledge
-when defined(gcRefc):
+when defined(cpp):
+  # TODO: fixme
+  discard "it doesn't work for refc/orc because of contrived `Atomic` in cpp"
+elif defined(gcRefc):
   doAssert x.repr == "[p = nil]"
-elif not defined(cpp): # fixme # bug #20081
+else: # fixme # bug #20081
   doAssert x.repr == "Pledge(p: nil)"
diff --git a/tests/ccgbugs/t15428.nim b/tests/ccgbugs/t15428.nim
new file mode 100644
index 000000000..d9ae8ff16
--- /dev/null
+++ b/tests/ccgbugs/t15428.nim
@@ -0,0 +1,22 @@
+discard """

+    cmd: "nim $target --mm:refc $file"

+    output: '''5

+5

+[1, 2, 3, 4, 5]

+(data: [1, 2, 3, 4, 5])

+'''

+"""

+

+proc take[T](f: openArray[T]) =

+  echo f.len

+let f = @[0,1,2,3,4]

+take(f.toOpenArray(0,4))

+

+{.experimental: "views".}

+type

+  Foo = object

+    data: openArray[int]

+let f2 = Foo(data: [1,2,3,4,5])

+echo f2.data.len

+echo f2.data

+echo f2
\ No newline at end of file
diff --git a/tests/ccgbugs/t20139.nim b/tests/ccgbugs/t20139.nim
new file mode 100644
index 000000000..4592b994d
--- /dev/null
+++ b/tests/ccgbugs/t20139.nim
@@ -0,0 +1,10 @@
+discard """
+  joinable: false
+"""
+
+# bug #20139
+import m1/defs as md1
+import m2/defs as md2
+
+doAssert $(md1.MyObj(field1: 1)) == """(field1: 1, s: "", ch: '\x00')"""
+doAssert $(md2.MyObj(field1: 1)) == """(s: "", field1: 1, ch: '\x00')"""
diff --git a/tests/ccgbugs/t20141.nim b/tests/ccgbugs/t20141.nim
index 499cd21aa..60e130690 100644
--- a/tests/ccgbugs/t20141.nim
+++ b/tests/ccgbugs/t20141.nim
@@ -16,7 +16,7 @@ template n[T, U](x: U): T =
 
 proc k() =
   var res: A
-  m(n[B](res))
+  m(n[B, A](res))
 
 proc w(mounter: U) = discard
 
@@ -24,4 +24,4 @@ proc mount(proto: U) = discard
 proc v() = mount k
 
 # This is required for failure
-w(v)
\ No newline at end of file
+w(v)
diff --git a/tests/ccgbugs/t20787.nim b/tests/ccgbugs/t20787.nim
new file mode 100644
index 000000000..c2d848c2c
--- /dev/null
+++ b/tests/ccgbugs/t20787.nim
@@ -0,0 +1,4 @@
+type
+  Obj = object
+    f: UncheckedArray[byte]
+let o = new Obj
\ No newline at end of file
diff --git a/tests/ccgbugs/t21116.nim b/tests/ccgbugs/t21116.nim
new file mode 100644
index 000000000..cc77de198
--- /dev/null
+++ b/tests/ccgbugs/t21116.nim
@@ -0,0 +1,10 @@
+discard """
+  targets: "c cpp"
+  disabled: windows
+"""
+# bug #21116
+import std/os
+
+proc p(glob: string) =
+  for _ in walkFiles(glob): discard
+p("dir/*")
diff --git a/tests/ccgbugs/t21972.nim b/tests/ccgbugs/t21972.nim
new file mode 100644
index 000000000..58d0cfc62
--- /dev/null
+++ b/tests/ccgbugs/t21972.nim
@@ -0,0 +1,33 @@
+discard """

+    targets: "c cpp"

+    outputsub: "Error: unhandled exception: Err2 [IOError]"

+    exitcode: "1"

+"""

+

+proc bar(x: var int) =

+  inc x

+  if x == 3:

+    raise newException(ValueError, "H0")

+

+  elif x == 5:

+    raise newException(IOError, "H1")

+

+  elif x > 7:

+    raise newException(IOError, "H2")

+

+

+proc foo() =

+  var i = 0

+  while true:

+    try:

+      bar(i)

+      echo i

+

+    except ValueError:

+      debugEcho("ValueError")

+

+    except IOError:

+      raise newException(IOError, "Err2")

+

+when isMainModule:

+  foo()
\ No newline at end of file
diff --git a/tests/ccgbugs/t21995.nim b/tests/ccgbugs/t21995.nim
new file mode 100644
index 000000000..0ec88aa59
--- /dev/null
+++ b/tests/ccgbugs/t21995.nim
@@ -0,0 +1,9 @@
+discard """
+    targets: "c cpp"
+    output: "Hi!"
+"""
+
+try:
+  raise
+except:
+  echo "Hi!"
\ No newline at end of file
diff --git a/tests/ccgbugs/t22462.nim b/tests/ccgbugs/t22462.nim
new file mode 100644
index 000000000..9adfbb19b
--- /dev/null
+++ b/tests/ccgbugs/t22462.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "run"
+  output: '''
+1
+1
+1
+'''
+  matrix: "--mm:refc"
+  targets: "c cpp"
+"""
+
+type Object = object
+  someComplexType: seq[int]
+  index: Natural
+
+func newObject(): Object = result.index.inc
+
+for i in 1..3:
+  let o = newObject()
+  echo o.index
diff --git a/tests/ccgbugs/t23796.nim b/tests/ccgbugs/t23796.nim
new file mode 100644
index 000000000..421ec04d8
--- /dev/null
+++ b/tests/ccgbugs/t23796.nim
@@ -0,0 +1,25 @@
+discard """
+    targets: "c cpp"
+"""
+
+# bug #23796
+
+{.emit: """
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fooArr(float data[3]) {}
+void fooIntArr(int id, float data[3]) {}
+
+#ifdef __cplusplus
+}
+#endif
+""".}
+
+proc fooArr(data: var array[3, cfloat]) {.importc.}
+proc fooIntArr(id: cint, data: var array[3, cfloat]) {.importc, nodecl.}
+
+var arr = [cfloat 1, 2, 3]
+fooArr(arr)
+fooIntArr(1, arr)
diff --git a/tests/ccgbugs/tbug21505.nim b/tests/ccgbugs/tbug21505.nim
new file mode 100644
index 000000000..0c0811ec5
--- /dev/null
+++ b/tests/ccgbugs/tbug21505.nim
@@ -0,0 +1,39 @@
+discard """
+    action: "compile"
+    targets: "cpp"
+    cmd: "nim cpp $file"
+"""
+
+# see #21505: ensure compilation of imported C++ objects with explicit constructors while retaining default initialization through codegen changes due to #21279
+
+{.emit:"""/*TYPESECTION*/
+
+struct ExplObj
+{
+  explicit ExplObj(int bar = 0) {}  
+};
+
+struct BareObj
+{
+    BareObj() {}
+};
+
+""".}
+
+type
+  ExplObj {.importcpp.} = object
+  BareObj {.importcpp.} = object
+
+type
+  Composer = object
+    explObj: ExplObj
+    bareObj: BareObj
+
+proc foo =
+  var composer1 {.used.}: Composer
+  let composer2 {.used.} = Composer()
+
+var composer1 {.used.}: Composer
+let composer2 {.used.} = Composer()
+
+foo()
\ No newline at end of file
diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim
index 0fe4b8852..2eddc6fdd 100644
--- a/tests/ccgbugs/tcgbug.nim
+++ b/tests/ccgbugs/tcgbug.nim
@@ -4,6 +4,7 @@ success
 M1 M2
 ok
 '''
+matrix: "--mm:refc;--mm:orc"
 """
 
 type
@@ -24,6 +25,7 @@ q(a)
 
 # bug #914
 when defined(windows):
+  import std/widestrs
   var x = newWideCString("Hello")
 
 echo "success"
@@ -122,3 +124,40 @@ proc bug19613 =
   doAssert x.bid.root.data[0] == 42
 
 bug19613()
+
+proc foo = # bug #23280
+  let foo = @[1,2,3,4,5,6]
+  doAssert toOpenArray(foo, 0, 5).len == 6
+  doAssert toOpenArray(foo, 0, 5).len mod 6 == 0 # this should output 0
+  doAssert toOpenArray(foo, 0, 5).max mod 6 == 0
+  let L = toOpenArray(foo, 0, 5).len
+  doAssert L mod 6 == 0 
+
+foo()
+
+block: # bug #9940
+  {.emit:"""/*TYPESECTION*/
+typedef struct { int base; } S;
+""".}
+
+  type S {.importc: "S", completeStruct.} = object
+    base: cint
+  proc init(x:ptr S) =
+    x.base = 1
+
+  type
+    Foo = object
+      a: seq[float]
+      b: seq[float]
+      c: seq[float]
+      d: seq[float]
+      s: S
+
+  proc newT(): Foo =
+    var t: Foo
+    t.s.addr.init
+    doAssert t.s.base == 1
+    t
+
+  var t = newT()
+  doAssert t.s.base == 1
diff --git a/tests/ccgbugs/tderefblock.nim b/tests/ccgbugs/tderefblock.nim
index fd21a19b8..d3ba07667 100644
--- a/tests/ccgbugs/tderefblock.nim
+++ b/tests/ccgbugs/tderefblock.nim
@@ -1,6 +1,5 @@
 discard """
-  cmd: "nim c -d:release -d:danger $file"
-  matrix: ";--gc:orc"
+  matrix: "--mm:refc -d:release -d:danger;--mm:orc -d:useMalloc -d:release -d:danger"
   output: "42"
 """
 
@@ -23,3 +22,55 @@ proc m() =
   echo $f.a
 
 m()
+
+block: # bug #21540
+  type
+    Option = object
+      val: string
+      has: bool
+
+  proc some(val: string): Option =
+    result.has = true
+    result.val = val
+
+  # Remove lent and it works
+  proc get(self: Option): lent string =
+    result = self.val
+
+  type
+    StringStream = ref object
+      data: string
+      pos: int
+
+  proc readAll(s: StringStream): string =
+    result = newString(s.data.len)
+    copyMem(addr(result[0]), addr(s.data[0]), s.data.len)
+
+  proc newStringStream(s: string = ""): StringStream =
+    new(result)
+    result.data = s
+
+  proc parseJson(s: string): string =
+    let stream = newStringStream(s)
+    result = stream.readAll()
+
+  proc main =
+    let initialFEN = block:
+      let initialFEN = some parseJson("startpos")
+      initialFEN.get
+
+    doAssert initialFEN == "startpos"
+
+  main()
+
+import std/[
+    json,
+    options
+]
+
+block: # bug #21540
+  let cheek = block:
+    let initialFEN = some("""{"initialFen": "startpos"}""".parseJson{"initialFen"}.getStr)
+    initialFEN.get
+
+  doAssert cheek == "startpos"
diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim
index 416e50eb5..b115dcbe7 100644
--- a/tests/ccgbugs/tforward_decl_only.nim
+++ b/tests/ccgbugs/tforward_decl_only.nim
@@ -1,7 +1,5 @@
 discard """
-ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')"
-ccodecheck: "\\i !@('mymoduleInit')"
-ccodecheck: "\\i @('atmmymoduledotnim_DatInit000')"
+ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' _')"
 output: "hello"
 """
 
diff --git a/tests/ccgbugs/tnoalias.nim b/tests/ccgbugs/tnoalias.nim
index f200992d7..2c3c2f0f4 100644
--- a/tests/ccgbugs/tnoalias.nim
+++ b/tests/ccgbugs/tnoalias.nim
@@ -1,5 +1,5 @@
 discard """
-  ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'"
+  ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x_p0,' @'void* NIM_NOALIAS q'"
 """
 
 type
diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim
index a69391e5c..ded18e9f8 100644
--- a/tests/ccgbugs/tsamename3.nim
+++ b/tests/ccgbugs/tsamename3.nim
@@ -109,3 +109,12 @@ block: # make sure `hashType` doesn't recurse infinitely
       a, b: PFoo
       c: int
   var a: PFoo
+
+block: # issue #22571
+  macro foo(x: typed) =
+    result = x
+
+  block: # or `proc main =`
+    foo:
+      type Foo = object
+    doAssert $Foo() == "()"
diff --git a/tests/ccgbugs/twrong_tupleconv.nim b/tests/ccgbugs/twrong_tupleconv.nim
index 7a887d183..031712dac 100644
--- a/tests/ccgbugs/twrong_tupleconv.nim
+++ b/tests/ccgbugs/twrong_tupleconv.nim
@@ -1,3 +1,8 @@
+discard """
+  targets: "c cpp"
+  matrix: "--gc:refc; --gc:arc"
+"""
+
 # bug #1833
 iterator myitems*[T](a: var seq[T]): var T {.inline.} =
   ## iterates over each item of `a` so that you can modify the yielded value.
@@ -18,3 +23,13 @@ var ys = @[(1,"a"),(2,"b"),(3,"c")]
 for y in myitems(ys):
   inc y[0]
 
+# bug #16331
+type T1 = tuple[a, b: int]
+
+proc p(b: bool): string =
+  var x: T1 = (10, 20)
+  x = if b: (x.b, x.a) else: (-x.b, -x.a)
+  $x
+
+assert p(false) == "(a: -20, b: -10)"
+assert p(true) == "(a: 20, b: 10)"
diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim
index 84cd76e2f..aac1ecaf3 100644
--- a/tests/ccgbugs2/tcodegen.nim
+++ b/tests/ccgbugs2/tcodegen.nim
@@ -1,28 +1,47 @@
-discard """
-  targets: "c cpp"
-"""
-
-# bug #19094
-type
-  X = object
-    filler: array[2048, int]
-    innerAddress: uint
-
-proc initX(): X =
-  result.innerAddress = cast[uint](result.addr)
-
-proc initXInPlace(x: var X) =
-  x.innerAddress = cast[uint](x.addr)
-
-block: # NRVO1
-  var x = initX()
-  let innerAddress = x.innerAddress
-  let outerAddress = cast[uint](x.addr)
-  doAssert(innerAddress == outerAddress) # [OK]
-
-block: # NRVO2
-  var x: X
-  initXInPlace(x)
-  let innerAddress = x.innerAddress
-  let outerAddress = cast[uint](x.addr)
-  doAssert(innerAddress == outerAddress) # [OK]
+discard """

+  targets: "c cpp"

+"""

+

+# bug #19094

+type

+  X = object

+    filler: array[2048, int]

+    innerAddress: uint

+

+proc initX(): X =

+  result.innerAddress = cast[uint](result.addr)

+

+proc initXInPlace(x: var X) =

+  x.innerAddress = cast[uint](x.addr)

+

+block: # NRVO1

+  var x = initX()

+  let innerAddress = x.innerAddress

+  let outerAddress = cast[uint](x.addr)

+  doAssert(innerAddress == outerAddress) # [OK]

+

+block: # NRVO2

+  var x: X

+  initXInPlace(x)

+  let innerAddress = x.innerAddress

+  let outerAddress = cast[uint](x.addr)

+  doAssert(innerAddress == outerAddress) # [OK]

+

+block: # bug #22354

+  type Object = object

+    foo: int

+

+  proc takeFoo(self: var Object): int =

+    result = self.foo

+    self.foo = 999

+

+  proc doSomething(self: var Object; foo: int = self.takeFoo()) =

+    discard

+

+  proc main() =

+    var obj = Object(foo: 2)

+    obj.doSomething()

+    doAssert obj.foo == 999

+

+

+  main()

diff --git a/tests/closure/t19095.nim b/tests/closure/t19095.nim
new file mode 100644
index 000000000..880456e02
--- /dev/null
+++ b/tests/closure/t19095.nim
@@ -0,0 +1,35 @@
+discard """
+  action: compile
+"""
+
+block:
+  func inCheck() =
+    discard
+
+  iterator iter(): int =
+    yield 0
+    yield 0
+
+  func search() =
+    let inCheck = 0
+
+    for i in iter():
+
+      proc hello() =
+        inCheck()
+
+  search()
+block:
+  iterator iter(): int =
+    yield 0
+    yield 0
+
+  func search() =
+    let lmrMoveCounter = 0
+
+    for i in iter():
+
+      proc hello() =
+        discard lmrMoveCounter
+
+  search()
diff --git a/tests/closure/t20152.nim b/tests/closure/t20152.nim
new file mode 100644
index 000000000..484ea0741
--- /dev/null
+++ b/tests/closure/t20152.nim
@@ -0,0 +1,20 @@
+discard """

+  action: compile

+"""

+

+proc foo() =

+  iterator it():int {.closure.} =

+    yield 1

+  proc useIter() {.nimcall.} =

+    var iii = it # <-- illegal capture

+    doAssert iii() == 1

+  useIter()

+foo()

+

+proc foo2() =

+  proc bar() = # Local function, but not a closure, because no captures

+    echo "hi"

+  proc baz() {.nimcall.} = # Calls local function

+    bar()

+  baz()

+foo2()

diff --git a/tests/closure/t8550.nim b/tests/closure/t8550.nim
index 153246f08..a07f45cdc 100644
--- a/tests/closure/t8550.nim
+++ b/tests/closure/t8550.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c js"
   output: "@[\"42\"]"
 """
 
diff --git a/tests/closure/t9334.nim b/tests/closure/t9334.nim
new file mode 100644
index 000000000..36a9a7d77
--- /dev/null
+++ b/tests/closure/t9334.nim
@@ -0,0 +1,19 @@
+discard """
+  cmd: "nim $target --hints:off $options -r $file"
+  nimout: '''@[1]
+@[1, 1]
+'''
+  nimoutFull: true
+"""
+proc p(s: var seq[int]): auto =
+  let sptr = addr s
+  return proc() = sptr[].add 1
+
+proc f =
+  var data = @[1]
+  p(data)()
+  echo repr data
+
+static:
+  f() # prints [1]
+f() # prints [1, 1]
diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim
index 8ae6c44bb..401a71d40 100644
--- a/tests/closure/tclosure.nim
+++ b/tests/closure/tclosure.nim
@@ -65,7 +65,7 @@ block tclosure:
 
   # bug #5015
 
-  type Mutator = proc(matched: string): string {.noSideEffect, gcsafe, locks: 0.}
+  type Mutator = proc(matched: string): string {.noSideEffect, gcsafe.}
 
   proc putMutated(
       MutatorCount: static[int],
@@ -239,19 +239,19 @@ block doNotation:
   b.onClick do (e: Event):
     echo "click at ", e.x, ",", e.y
 
-  b.onFocusLost:
+  b.onFocusLost do ():
     echo "lost focus 1"
 
-  b.onFocusLost do:
+  b.onFocusLost do ():
     echo "lost focus 2"
 
-  b.onUserEvent("UserEvent 1") do:
+  b.onUserEvent("UserEvent 1") do ():
     discard
 
-  b.onUserEvent "UserEvent 2":
+  onUserEvent(b, "UserEvent 2") do ():
     discard
 
-  b.onUserEvent("UserEvent 3"):
+  b.onUserEvent("UserEvent 3") do ():
     discard
 
   b.onUserEvent("UserEvent 4", () => echo "event 4")
@@ -491,3 +491,14 @@ block tnoclosure:
       row = zip(row & @[0], @[0] & row).mapIt(it[0] + it[1])
     echo row
   pascal(10)
+
+block: # bug #22297
+  iterator f: int {.closure.} =
+    try:
+      yield 12
+    finally:
+      return 14
+
+  let s = f
+  doAssert s() == 12
+  doAssert s() == 14
diff --git a/tests/closure/tclosure_issues.nim b/tests/closure/tclosure_issues.nim
index 4688834de..b1a2d7c6b 100644
--- a/tests/closure/tclosure_issues.nim
+++ b/tests/closure/tclosure_issues.nim
@@ -71,12 +71,12 @@ block tissue7104:
   proc sp(cb: proc())=
       cb()
 
-  sp:
+  sp do ():
       var i = 0
       echo "ok ", i
-      sp():
+      sp do ():
           inc i
           echo "ok ", i
-          sp do:
+          sp do ():
               inc i
               echo "ok ", i
diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim
index 47f3f105f..37d0f68a2 100644
--- a/tests/closure/tinvalidclosure.nim
+++ b/tests/closure/tinvalidclosure.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "type mismatch: got <proc (x: int){.nimcall, gcsafe, locks: 0.}>"
+  errormsg: "type mismatch: got <proc (x: int){.nimcall, gcsafe.}>"
   line: 12
 """
 
diff --git a/tests/closure/tinvalidclosure5.nim b/tests/closure/tinvalidclosure5.nim
index d03d93867..3b5f46a40 100644
--- a/tests/closure/tinvalidclosure5.nim
+++ b/tests/closure/tinvalidclosure5.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "type mismatch: got <proc (){.closure, gcsafe, locks: 0.}> but expected 'A = proc (){.nimcall.}'"
+  errormsg: "type mismatch: got <proc (){.closure, gcsafe.}> but expected 'A = proc (){.nimcall.}'"
   line: 9
 """
 
diff --git a/tests/closure/tnested.nim b/tests/closure/tnested.nim
index ca8d0e08b..ec5af9b13 100644
--- a/tests/closure/tnested.nim
+++ b/tests/closure/tnested.nim
@@ -1,4 +1,5 @@
 discard """
+targets: "c js"
 output: '''
 foo88
 23 24foo 88
@@ -33,7 +34,7 @@ py
 py
 px
 6
-proc (){.closure, gcsafe, locks: 0.}
+proc (){.closure, noSideEffect, gcsafe.}
 '''
 """
 
@@ -183,14 +184,32 @@ block tclosure2:
 
 import typetraits
 
-proc myDiscard[T](a: T) = discard
+block:
+  proc myDiscard[T](a: T) = discard
 
-proc foo() =
-  let a = 5
-  let f = (proc() =
-             myDiscard (proc() = echo a)
-          )
-  echo name(typeof(f))
+  proc foo() =
+    let a = 5
+    let f = (proc() =
+              myDiscard (proc() = echo a)
+            )
+    echo name(typeof(f))
 
-foo()
+  foo()
 
+
+block:
+  iterator foo: int {.closure.} =
+    yield 1
+    yield 2
+    yield 3
+
+  proc pork =
+    let call = foo
+    for i in call():
+      discard i
+
+    let call2 = foo
+    while not finished(call2):
+      discard call2()
+
+  pork()
diff --git a/tests/closure/tstmtlist.nim b/tests/closure/tstmtlist.nim
new file mode 100644
index 000000000..6a1390617
--- /dev/null
+++ b/tests/closure/tstmtlist.nim
@@ -0,0 +1,9 @@
+discard """
+  action: compile
+"""
+
+proc foo(x: proc()) = x()
+foo: echo "a" #[tt.Warning
+     ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]#
+foo do: echo "b" #[tt.Warning
+        ^ statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead [StmtListLambda]]#
diff --git a/tests/codegen/titaniummangle.nim b/tests/codegen/titaniummangle.nim
new file mode 100644
index 000000000..4b45e59ae
--- /dev/null
+++ b/tests/codegen/titaniummangle.nim
@@ -0,0 +1,193 @@
+discard """
+  targets: "c"
+  matrix: "--debugger:native"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE6stringN14titaniummangle3FooE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int7varargsI6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle3BooE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE8typeDescIN14titaniummangle17EnumAnotherSampleEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI14uncheckedArrayI3intEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3setIN14titaniummangle10EnumSampleEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE4procI6string6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3intN10Comparable10ComparableE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3int3int'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle10EnumSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncEN14titaniummangle17EnumAnotherSampleE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI3int3intE7cstring'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5tupleI5float5floatE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3ptrI3ptrI3intEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3refIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3varIN14titaniummangle3FooEE5int325int323refIN14titaniummangle3FooEE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE3varI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9openArrayI6stringE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE5arrayI7range013intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI3intE'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE10Container2I5int325int32E'"
+  ccodecheck: "'_ZN14titaniummangle8testFuncE9ContainerI10Container2I5int325int32EE'"
+"""
+
+#When debugging this notice that if one check fails, it can be due to any of the above.
+
+type
+  Comparable = concept x, y
+    (x < y) is bool
+
+  Foo = object
+    a: int32
+    b: int32
+
+  FooTuple = tuple
+    a: int
+    b: int
+
+  Container[T] = object
+    data: T
+      
+  Container2[T, T2] = object
+    data: T
+    data2: T2
+
+  Boo = distinct Foo
+
+  Coo = Foo
+
+  Doo = Boo | Foo 
+
+  TestProc = proc(a:string): string
+
+type EnumSample = enum
+  a, b, c
+
+type EnumAnotherSample = enum
+  a, b, c
+
+proc testFunc(a: set[EnumSample]) = 
+  echo $a
+
+proc testFunc(a: typedesc) = 
+  echo $a
+
+proc testFunc(a: ptr Foo) = 
+  echo repr a
+
+proc testFunc(s: string, a: Coo) = 
+  echo repr a
+
+proc testFunc(s: int, a: Comparable) = 
+  echo repr a
+
+proc testFunc(a: TestProc) = 
+  let b = ""
+  echo repr a("")
+
+proc testFunc(a: ref Foo) = 
+  echo repr a
+
+proc testFunc(b: Boo) = 
+  echo repr b
+
+proc testFunc(a: ptr UncheckedArray[int]) = 
+  echo repr a
+
+proc testFunc(a: ptr int) = 
+  echo repr a
+
+proc testFunc(a: ptr ptr int) = 
+  echo repr a
+
+proc testFunc(e: FooTuple, str: cstring) = 
+  echo e
+
+proc testFunc(e: (float, float)) = 
+  echo e
+
+proc testFunc(e: EnumSample) = 
+  echo e
+
+proc testFunc(e: var int) = 
+  echo e
+
+proc testFunc(e: var Foo, a, b: int32, refFoo: ref Foo) = 
+  echo e
+
+proc testFunc(xs: Container[int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: Container2[int32, int32]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: Container[Container2[int32, int32]]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: seq[int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: openArray[string]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(xs: array[2, int]) = 
+  let a = 2
+  echo xs
+
+proc testFunc(e: EnumAnotherSample) = 
+  echo e
+
+proc testFunc(a, b: int) = 
+  echo "hola"
+  discard
+
+proc testFunc(a: int, xs: varargs[string]) = 
+  let a = 10
+  for x in xs:
+    echo x
+
+proc testFunc() = 
+  var a = 2
+  var aPtr = a.addr
+  var foo = Foo()
+  let refFoo : ref Foo = new(Foo)
+  let b = Foo().Boo()
+  let d: Doo = Foo()
+  testFunc("", Coo())
+  testFunc(1, )
+  testFunc(b)
+  testFunc(EnumAnotherSample)
+  var t = [1, 2]
+  let uArr = cast[ptr UncheckedArray[int]](t.addr)
+  testFunc(uArr)
+  testFunc({})
+  testFunc(proc(s:string): string = "test")
+  testFunc(20, a.int32)
+  testFunc(20, 2)
+  testFunc(EnumSample.c)
+  testFunc(EnumAnotherSample.c)
+  testFunc((2, 1), "adios")
+  testFunc((22.1, 1.2))
+  testFunc(a.addr)
+  testFunc(foo.addr)
+  testFunc(aPtr.addr)
+  testFunc(refFoo)
+  testFunc(foo, 2, 1, refFoo)
+  testFunc(a)
+  testFunc(@[2, 1, 2])
+  testFunc(@["hola"])
+  testFunc(2, "hola", "adios")
+  let arr: array[2, int] = [2, 1]
+  testFunc(arr)
+  testFunc(Container[int](data: 10))
+  let c2 = Container2[int32, int32](data: 10, data2: 20)
+  testFunc(c2)
+  testFunc(Container[Container2[int32, int32]](data: c2))
+  
+
+testFunc()
\ No newline at end of file
diff --git a/tests/actiontable/tactiontable.nim b/tests/collections/tactiontable.nim
index 3f15a70bd..3f15a70bd 100644
--- a/tests/actiontable/tactiontable.nim
+++ b/tests/collections/tactiontable.nim
diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim
index c4dd40052..0f8084c78 100644
--- a/tests/collections/tseq.nim
+++ b/tests/collections/tseq.nim
@@ -12,6 +12,7 @@ FilterIt: [1, 3, 7]
 Concat: [1, 3, 5, 7, 2, 4, 6]
 Deduplicate: [1, 2, 3, 4, 5, 7]
 @[()]
+Minmax: (1, 7)
 2345623456
 '''
 """
@@ -156,6 +157,13 @@ block tsequtils:
   let someObjSeq = aSeq.mapIt(it.field)
   echo someObjSeq
 
+  block minmax:
+    doAssert minmax(@[0]) == (0, 0)
+    doAssert minmax(@[0, 1]) == (0, 1)
+    doAssert minmax(@[1, 0]) == (0, 1)
+    doAssert minmax(@[8,2,1,7,3,9,4,0,5]) == (0, 9)
+    echo "Minmax: ", $(minmax(concat(seq1, seq2)))
+
 
 when not defined(nimseqsv2):
   block tshallowseq:
@@ -175,12 +183,14 @@ when not defined(nimseqsv2):
       var emptySeq: seq[int] = newSeq[int]()
       block:
         var t = @[1,2,3]
-        shallow(nilSeq)
+        when defined(gcRefc):
+          shallow(nilSeq)
         t = nilSeq
         doAssert t == @[]
       block:
         var t = @[1,2,3]
-        shallow(emptySeq)
+        when defined(gcRefc):
+          shallow(emptySeq)
         t = emptySeq
         doAssert t == @[]
       block:
@@ -213,3 +223,20 @@ for i in 0..100:
   var test = newSeqOfCap[uint32](1)
   test.setLen(1)
   doAssert test[0] == 0, $(test[0], i)
+
+
+# bug #22560
+doAssert len(newSeqOfCap[int](42)) == 0
+
+block: # bug #17197
+  type Matrix = seq[seq[int]]
+
+  proc needlemanWunsch(sequence1: string, sequence2: string, gap_penal: int8, match: int8, indel_penal: int8): bool =
+    let seq2_len = sequence2.len
+
+    var grid: Matrix
+    for i in sequence1:
+      grid.add(newSeqOfCap[seq[int]](seq2_len))
+    result = true
+
+  doAssert needlemanWunsch("ABC", "DEFG", 1, 2, 3)
diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim
index 638f4241b..95f9418a0 100644
--- a/tests/collections/ttables.nim
+++ b/tests/collections/ttables.nim
@@ -177,16 +177,14 @@ block tableconstr:
 block ttables2:
   proc TestHashIntInt() =
     var tab = initTable[int,int]()
-    let n = 100_000
+    let n = 10
     for i in 1..n:
       tab[i] = i
     for i in 1..n:
       var x = tab[i]
       if x != i : echo "not found ", i
 
-  proc run1() =
-    for i in 1 .. 50:
-      TestHashIntInt()
+  TestHashIntInt()
 
   # bug #2107
 
@@ -198,7 +196,6 @@ block ttables2:
   delTab[5] = 5
 
 
-  run1()
   echo "2"
 
 block tablesref:
diff --git a/tests/compileoption/texperimental.nim b/tests/compileoption/texperimental.nim
new file mode 100644
index 000000000..be637e58a
--- /dev/null
+++ b/tests/compileoption/texperimental.nim
@@ -0,0 +1,20 @@
+static:
+  doAssert compileOption("experimental", "dotOperators")
+  doAssert compileOption("experimental", "callOperator")
+  doAssert compileOption("experimental", "parallel")
+  doAssert compileOption("experimental", "destructor")
+  doAssert compileOption("experimental", "notnil")
+  doAssert compileOption("experimental", "dynamicBindSym")
+  doAssert compileOption("experimental", "codeReordering")
+  doAssert compileOption("experimental", "compiletimeFFI")
+  doAssert compileOption("experimental", "vmopsDanger")
+  doAssert compileOption("experimental", "strictFuncs")
+  doAssert compileOption("experimental", "views")
+  doAssert compileOption("experimental", "strictNotNil")
+  doAssert compileOption("experimental", "strictEffects")
+  doAssert compileOption("experimental", "flexibleOptionalParams")
+  doAssert compileOption("experimental", "strictDefs")
+  doAssert compileOption("experimental", "strictCaseObjects")
+  doAssert compileOption("experimental", "inferGenericTypes")
+  doAssert compileOption("experimental", "openSym")
+  doAssert compileOption("experimental", "vtables")
diff --git a/tests/compileoption/texperimental.nims b/tests/compileoption/texperimental.nims
new file mode 100644
index 000000000..c2c357caa
--- /dev/null
+++ b/tests/compileoption/texperimental.nims
@@ -0,0 +1,19 @@
+switch("experimental", "dotOperators")
+switch("experimental", "callOperator")
+switch("experimental", "parallel")
+switch("experimental", "destructor")
+switch("experimental", "notnil")
+switch("experimental", "dynamicBindSym")
+switch("experimental", "codeReordering")
+switch("experimental", "compiletimeFFI")
+switch("experimental", "vmopsDanger")
+switch("experimental", "strictFuncs")
+switch("experimental", "views")
+switch("experimental", "strictNotNil")
+switch("experimental", "strictEffects")
+switch("experimental", "flexibleOptionalParams")
+switch("experimental", "strictDefs")
+switch("experimental", "strictCaseObjects")
+switch("experimental", "inferGenericTypes")
+switch("experimental", "openSym")
+switch("experimental", "vtables")
diff --git a/tests/compiler/tbrees.nim b/tests/compiler/tbtrees.nim
index 5f6482ed9..973c26420 100644
--- a/tests/compiler/tbrees.nim
+++ b/tests/compiler/tbtrees.nim
@@ -68,7 +68,7 @@ proc main =
   when true:
     var b2 = initBTree[int, string]()
     var t2 = initTable[int, string]()
-    const iters = 100_000
+    const iters = 100
     for i in 1..iters:
       let x = rand(high(int))
       if not t2.hasKey(x):
diff --git a/tests/compiler/tgrammar.nim b/tests/compiler/tgrammar.nim
new file mode 100644
index 000000000..772d0f0cc
--- /dev/null
+++ b/tests/compiler/tgrammar.nim
@@ -0,0 +1,7 @@
+discard """
+  matrix: "-d:nimTestGrammar"
+"""
+
+import compiler/parser
+
+checkSameGrammar()
diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim
index ab995cb6d..2d6cfa0ca 100644
--- a/tests/compilerapi/tcompilerapi.nim
+++ b/tests/compilerapi/tcompilerapi.nim
@@ -23,7 +23,7 @@ proc initInterpreter(script: string): Interpreter =
 
 proc main() =
   let i = initInterpreter("myscript.nim")
-  i.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) =
+  i.implementRoutine("nim", "exposed", "addFloats", proc (a: VmArgs) =
     setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2))
   )
   i.evalScript()
@@ -69,3 +69,30 @@ block error_hook:
 
   doAssertRaises(VMQuit):
     i.evalScript()
+
+block resetmacrocache:
+  let std = findNimStdLibCompileTime()
+  let intr = createInterpreter("script.nim", [std, std / "pure", std / "core"])
+  proc evalString(intr: Interpreter; code: string) =
+    let stream = llStreamOpen(code)
+    intr.evalScript(stream)
+    llStreamClose(stream)
+  let code = """
+import std/[macrocache, macros]
+static:
+  let counter = CacheCounter"valTest"
+  inc counter
+  assert counter.value == 1
+
+  const mySeq = CacheSeq"addTest"
+  mySeq.add(newLit(5))
+  mySeq.add(newLit("hello ic"))
+  assert mySeq.len == 2
+
+  const mcTable = CacheTable"subTest"
+  mcTable["toAdd"] = newStmtList() #would crash if not empty
+  assert mcTable.len == 1
+"""
+  intr.evalString(code)
+  intr.evalString(code)
+  destroyInterpreter(intr)
\ No newline at end of file
diff --git a/tests/concepts/t20237.nim b/tests/concepts/t20237.nim
new file mode 100644
index 000000000..175c7a9d1
--- /dev/null
+++ b/tests/concepts/t20237.nim
@@ -0,0 +1,3 @@
+type Foo* = concept
+  ## doc comment
+  proc foo(x: Self)
diff --git a/tests/concepts/t976.nim b/tests/concepts/t976.nim
new file mode 100644
index 000000000..776d53827
--- /dev/null
+++ b/tests/concepts/t976.nim
@@ -0,0 +1,57 @@
+discard """
+  output: '''
+Printable
+'''
+joinable: false
+"""
+
+#[
+  The converter is a proper example of a confounding variable
+  Moved to an isolated file
+]#
+
+type
+  Obj1[T] = object
+      v: T
+converter toObj1[T](t: T): Obj1[T] =
+  return Obj1[T](v: t)
+block t976:
+  type
+    int1 = distinct int
+    int2 = distinct int
+    int1g = concept x
+      x is int1
+    int2g = concept x
+      x is int2
+
+  proc take[T: int1g](value: int1) =
+    when T is int2:
+      static: error("killed in take(int1)")
+
+  proc take[T: int2g](vale: int2) =
+    when T is int1:
+      static: error("killed in take(int2)")
+
+  var i1: int1 = 1.int1
+  var i2: int2 = 2.int2
+
+  take[int1](i1)
+  take[int2](i2)
+
+  template reject(e) =
+    static: assert(not compiles(e))
+
+  reject take[string](i2)
+  reject take[int1](i2)
+
+  # bug #6249
+  type
+      Obj2 = ref object
+      PrintAble = concept x
+          $x is string
+
+  proc `$`[T](nt: Obj1[T]): string =
+      when T is PrintAble: result = "Printable"
+      else: result = "Non Printable"
+
+  echo Obj2()
\ No newline at end of file
diff --git a/tests/concepts/tconcepts.nim b/tests/concepts/tconcepts.nim
index acdff6f24..ea3ddc401 100644
--- a/tests/concepts/tconcepts.nim
+++ b/tests/concepts/tconcepts.nim
@@ -31,6 +31,7 @@ e
 20
 10
 5
+9
 '''
 """
 
@@ -438,3 +439,13 @@ import mvarconcept
 block tvar:
   # bug #2346, bug #2404
   echo randomInt(5)
+
+block tcomment:
+  type
+    Foo = concept
+      ## Some comment
+      proc bar(x: Self)
+
+  proc bar(x: int) = echo x
+  proc foo(x: Foo) = x.bar
+  foo(9)
diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim
index 802582f57..c6d0267c5 100644
--- a/tests/concepts/tconcepts_issues.nim
+++ b/tests/concepts/tconcepts_issues.nim
@@ -1,7 +1,6 @@
 discard """
   output: '''
 20.0 USD
-Printable
 true
 true
 true
@@ -27,6 +26,8 @@ false
 true
 -1
 Meow
+10 0.0
+1 2.0
 '''
 joinable: false
 """
@@ -76,55 +77,6 @@ block t3414:
   let s2 = s1.find(10)
 
 
-
-type
-  Obj1[T] = object
-      v: T
-converter toObj1[T](t: T): Obj1[T] =
-  return Obj1[T](v: t)
-block t976:
-  type
-    int1 = distinct int
-    int2 = distinct int
-    int1g = concept x
-      x is int1
-    int2g = concept x
-      x is int2
-
-  proc take[T: int1g](value: int1) =
-    when T is int2:
-      static: error("killed in take(int1)")
-
-  proc take[T: int2g](vale: int2) =
-    when T is int1:
-      static: error("killed in take(int2)")
-
-  var i1: int1 = 1.int1
-  var i2: int2 = 2.int2
-
-  take[int1](i1)
-  take[int2](i2)
-
-  template reject(e) =
-    static: assert(not compiles(e))
-
-  reject take[string](i2)
-  reject take[int1](i2)
-
-  # bug #6249
-  type
-      Obj2 = ref object
-      PrintAble = concept x
-          $x is string
-
-  proc `$`[T](nt: Obj1[T]): string =
-      when T is PrintAble: result = "Printable"
-      else: result = "Non Printable"
-
-  echo Obj2()
-
-
-
 block t1128:
   type
     TFooContainer[T] = object
@@ -500,3 +452,105 @@ var r, b: Fp2[6, uint64]
 
 prod(r, b)
 
+
+block: # bug #21263
+  type
+    DateDayFraction = concept # no T, an atom
+      proc date(a: Self): int
+      proc fraction(b: Self): float
+    Date = distinct int
+    DateDayFractionImpl = object
+      date : int
+      fraction : float
+
+  proc date(a: Date): int = a.int
+  proc fraction(a:Date): float = 0.0
+
+  proc date(a: DateDayFractionImpl): int = a.date
+  proc fraction(b: DateDayFractionImpl): float = b.fraction
+
+
+  proc print(a: DateDayFraction) =
+    echo a.date, " ", a.fraction
+
+  print(10.Date) # ok
+  print(DateDayFractionImpl(date: 1, fraction: 2))  # error
+
+import sets
+import deques
+
+type AnyTree[V] = concept t, type T
+  for v in t.leaves(V):
+    v is V
+
+type BreadthOrder[V] = ref object
+  frontier: Deque[V]
+  visited: HashSet[V]
+
+proc expand[V, T](order: ref BreadthOrder[T], tree: AnyTree[V], node: V, transform: (V) -> (T)) =
+  for leaf in tree.leaves(node):
+    if not order.visited.containsOrIncl(transform(leaf)):
+      order.frontier.addLast(transform(leaf))
+
+proc hasNext[V](order: ref BreadthOrder[V]): bool =
+  order.frontier.len > 0
+
+proc init[V](_: typedesc[BreadthOrder]): ref BreadthOrder[V] =
+  result.new()
+  result[] = BreadthOrder[V](frontier: initDeque[V](), visited: initHashSet[V]())
+
+proc popNext[V](order: ref BreadthOrder[V]): V =
+  order.frontier.popFirst()
+
+type LevelNode[V] = tuple
+  depth: uint
+  node: V
+
+proc depthOf*[V](orderType: typedesc[BreadthOrder], tree: AnyTree[V], root, goal: V): uint =
+  if root == goal:
+    return 0
+  var order = init[LevelNode[V]](orderType)
+  order.expand(tree, root, (leaf) => (1.uint, leaf))
+  while order.hasNext():
+    let depthNode: LevelNode[V] = order.popNext()
+    if depthNode.node == goal:
+      return depthNode.depth
+    order.expand(tree, depthNode.node, (leaf) => (depthNode.depth + 1, leaf))
+
+type CappedStringTree = ref object
+  symbols: string
+  cap: Natural
+
+iterator leaves*(t: CappedStringTree, s: string): string =
+  if s.len < t.cap:
+    for c in t.symbols:
+      yield s & c
+
+block: # bug #12852
+  var tree = CappedStringTree(symbols: "^v><", cap: 5)
+
+  doAssert BreadthOrder.depthOf(tree, "", ">>>") == 3
+
+block: #bug #22723
+  type
+    Node  = concept n, type T 
+      for i in n.children:
+        i is T
+      n.parent is T
+
+    Nd = ref object
+      parent: Nd
+      children: seq[Nd]
+
+  proc addChild(parent, child: Node) =
+    parent.children.add(child)
+    child.parent = parent
+
+  proc foo =
+    var
+      a = Nd()
+      b = Nd()
+    a.addChild(b)
+    doAssert a.children.len == 1
+
+  foo()
diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim
index 49eb8eb6b..8cf04ae82 100644
--- a/tests/concepts/texplain.nim
+++ b/tests/concepts/texplain.nim
@@ -13,14 +13,8 @@ proc e(o: ExplainedConcept): int
   required type for o: ExplainedConcept
   but expression '10' is of type: int literal(10)
 texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(128, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(129, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 
 texplain.nim(168, 10) Hint: Non-matching candidates for e(10)
@@ -29,14 +23,8 @@ proc e(o: ExplainedConcept): int
   required type for o: ExplainedConcept
   but expression '10' is of type: int literal(10)
 texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(128, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(129, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous)
 texplain.nim(128, 5) ExplainedConcept: concept predicate failed
 
 texplain.nim(172, 20) Error: type mismatch: got <NonMatchingType>
@@ -88,14 +76,8 @@ proc f(o: NestedConcept)
   required type for o: NestedConcept
   but expression 'y' is of type: MatchingType
 texplain.nim(132, 6) RegularConcept: undeclared field: 'foo'
-texplain.nim(132, 6) RegularConcept: undeclared field: '.'
-texplain.nim(132, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(132, 6) RegularConcept: expression '' has no type (or is ambiguous)
 texplain.nim(132, 5) RegularConcept: concept predicate failed
 texplain.nim(133, 6) RegularConcept: undeclared field: 'bar'
-texplain.nim(133, 6) RegularConcept: undeclared field: '.'
-texplain.nim(133, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(133, 6) RegularConcept: expression '' has no type (or is ambiguous)
 texplain.nim(132, 5) RegularConcept: concept predicate failed
 texplain.nim(136, 5) NestedConcept: concept predicate failed
 
@@ -121,7 +103,25 @@ expression: f(y)'''
 
 
 
-# line 120 HERE
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# line 124 HERE
 
 type
   ExplainedConcept {.explain.} = concept o
diff --git a/tests/concepts/tusertypeclasses2.nim b/tests/concepts/tusertypeclasses2.nim
index c9978f6ef..6132bc2d8 100644
--- a/tests/concepts/tusertypeclasses2.nim
+++ b/tests/concepts/tusertypeclasses2.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
 block:
   type
     hasFieldX = concept z
@@ -42,3 +46,18 @@ block:
   foo2(x)
   foo3(x)
   foo4(x)
+
+block: # bug #9550
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo @['a', 'b' ,'c']
+
+  block:
+    type Foo = concept c
+      for v in c: (v is char)
+
+    func foo(c: Foo) = (for v in c: discard)
+    foo ['a', 'b' ,'c']
diff --git a/tests/config.nims b/tests/config.nims
index 5195ebcaf..0b2b66d81 100644
--- a/tests/config.nims
+++ b/tests/config.nims
@@ -32,13 +32,18 @@ hint("Processing", off)
 
 # experimental APIs are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575
 # sync with `kochdocs.docDefines` or refactor.
-switch("define", "nimExperimentalAsyncjsThen")
 switch("define", "nimExperimentalLinenoiseExtra")
 
 # preview APIs are expected to be the new default in upcoming versions
 switch("define", "nimPreviewFloatRoundtrip")
-switch("define", "nimPreviewDotLikeOps")
+#switch("define", "nimPreviewDotLikeOps") # deprecated?
 switch("define", "nimPreviewJsonutilsHoleyEnum")
 switch("define", "nimPreviewHashRef")
-when defined(windows):
-  switch("tlsEmulation", "off")
+switch("define", "nimPreviewRangeDefault")
+switch("define", "nimPreviewNonVarDestructor")
+
+switch("warningAserror", "UnnamedBreak")
+when not defined(testsConciseTypeMismatch):
+  switch("legacy", "verboseTypeMismatch")
+switch("experimental", "vtables")
+switch("experimental", "openSym")
diff --git a/tests/constr/tglobal.nim b/tests/constr/tglobal.nim
deleted file mode 100644
index 056ac9f81..000000000
--- a/tests/constr/tglobal.nim
+++ /dev/null
@@ -1,7 +0,0 @@
-discard """
-output: "0"
-"""
-
-# b.nim
-import a_module
-echo foo()
\ No newline at end of file
diff --git a/tests/constructors/a.nim b/tests/constructors/a.nim
new file mode 100644
index 000000000..03788fc57
--- /dev/null
+++ b/tests/constructors/a.nim
@@ -0,0 +1,2 @@
+type A* = object
+  a: uint8
\ No newline at end of file
diff --git a/tests/constructors/b.nim b/tests/constructors/b.nim
new file mode 100644
index 000000000..437dd0550
--- /dev/null
+++ b/tests/constructors/b.nim
@@ -0,0 +1,2 @@
+type B* = object
+proc A*(a, b: float): B = discard
\ No newline at end of file
diff --git a/tests/constructors/t18990.nim b/tests/constructors/t18990.nim
new file mode 100644
index 000000000..2f60f3c2c
--- /dev/null
+++ b/tests/constructors/t18990.nim
@@ -0,0 +1,3 @@
+import a, b
+discard A(1f, 1f) # works
+proc x(b = A(1f, 1f)) = discard # doesn't work
\ No newline at end of file
diff --git a/tests/constr/tconstr1.nim b/tests/constructors/tconstr1.nim
index a169bf453..a169bf453 100644
--- a/tests/constr/tconstr1.nim
+++ b/tests/constructors/tconstr1.nim
diff --git a/tests/constr/tconstr2.nim b/tests/constructors/tconstr2.nim
index 2557d7db9..2557d7db9 100644
--- a/tests/constr/tconstr2.nim
+++ b/tests/constructors/tconstr2.nim
diff --git a/tests/controlflow/tcontrolflow.nim b/tests/controlflow/tcontrolflow.nim
index 258f3f50d..dd21a2bb6 100644
--- a/tests/controlflow/tcontrolflow.nim
+++ b/tests/controlflow/tcontrolflow.nim
@@ -19,7 +19,7 @@ block tbreak:
     run = false
     block myblock:
       if true:
-        break
+        break myblock
       echo "leaving myblock"
     x = true
   doAssert(x)
@@ -95,3 +95,22 @@ block tnestif:
   else:
       writeLine(stdout, "looks like Python")
   #OUT i == 2
+
+# bug https://github.com/nim-lang/RFCs/issues/451
+for i in 1..2: # works
+  break
+
+block: # works
+  for i in 1..2:
+    break
+
+block: # works
+  block:
+    discard 12 + 3
+  for i in 1..2:
+    break
+
+block named: # works
+  if true:
+    break named
+  doAssert false, "not reached"
diff --git a/tests/controlflow/tunamedbreak.nim b/tests/controlflow/tunamedbreak.nim
new file mode 100644
index 000000000..46113cabc
--- /dev/null
+++ b/tests/controlflow/tunamedbreak.nim
@@ -0,0 +1,15 @@
+
+discard """
+  cmd: "nim check $file"
+  action: "reject"
+  nimout: '''
+tunamedbreak.nim(12, 5) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
+tunamedbreak.nim(15, 3) Error: Using an unnamed break in a block is deprecated; Use a named block with a named break instead [UnnamedBreak]
+  '''
+"""
+for i in 1..2: # errors
+  block:
+    break
+
+block: # errors
+  break
diff --git a/tests/controlflow/tunreachable.nim b/tests/controlflow/tunreachable.nim
new file mode 100644
index 000000000..06321ce8a
--- /dev/null
+++ b/tests/controlflow/tunreachable.nim
@@ -0,0 +1,79 @@
+discard """
+  cmd: "nim check --warningAsError:UnreachableCode $file"
+  action: "reject"
+  nimout: '''
+tunreachable.nim(26, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(33, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(42, 3) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(65, 5) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+tunreachable.nim(77, 5) Error: unreachable code after 'return' statement or '{.noReturn.}' proc [UnreachableCode]
+'''
+"""
+  
+# bug #9839
+template myquit1():untyped=
+  ## foo
+  quit(1)
+template myquit2():untyped=
+  echo 123
+  myquit1()
+
+proc main1()=
+
+  # BUG: uncommenting this doesn't give `Error: unreachable statement`
+  myquit2()
+
+  echo "after"
+
+main1()
+
+proc main2() =
+  myquit1()
+
+  echo "after"
+
+main2()
+
+proc main3() =
+  if true:
+    return
+  else:
+    return
+  echo "after"
+
+main3()
+
+
+block:
+  # Cases like strings are not checked for exhaustiveness unless they have an else
+  proc main4(x: string) =
+    case x
+    of "a":
+      return
+    # reachable
+    echo "after"
+
+  main4("a")
+
+  proc main5(x: string) =
+    case x
+    of "a":
+      return
+    else:
+      return
+    # unreachable
+    echo "after"
+
+  main5("a")
+
+block:
+  # In this case no else is needed because it's exhaustive
+  proc exhaustive(x: bool) =
+    case x
+    of true:
+      return
+    of false:
+      return
+    echo "after"
+
+  exhaustive(true)
diff --git a/tests/controlflow/tunreachable2.nim b/tests/controlflow/tunreachable2.nim
new file mode 100644
index 000000000..a658880f0
--- /dev/null
+++ b/tests/controlflow/tunreachable2.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--warningAsError:UnreachableCode"
+"""
+
+proc test(): bool =
+  block okay:
+    if true: break okay
+    return false
+
+  return true # Line 7 is here
+
+doAssert test()
diff --git a/tests/converter/t21531.nim b/tests/converter/t21531.nim
new file mode 100644
index 000000000..b0198684d
--- /dev/null
+++ b/tests/converter/t21531.nim
@@ -0,0 +1,10 @@
+import std/typetraits
+
+type Foo* = distinct string
+
+converter toBase*(headers: var Foo): var string =
+  headers.distinctBase
+
+proc bar*(headers: var Foo) =
+  for x in headers: discard
+    
diff --git a/tests/converter/t7097.nim b/tests/converter/t7097.nim
index fdb573588..d8e953080 100644
--- a/tests/converter/t7097.nim
+++ b/tests/converter/t7097.nim
@@ -10,8 +10,8 @@ proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange =
   let e = if iend < 0: s.len + iend + 1
           else: iend
   assert ibegin > 0 and e <= s.len
-  
-  shallow(s)
+  when defined(gcRefc):
+    shallow(s)
   result.bytes = s
   result.ibegin = ibegin
   result.iend = e
diff --git a/tests/converter/t7098.nim b/tests/converter/t7098.nim
index 8e7634882..30c9c1e25 100644
--- a/tests/converter/t7098.nim
+++ b/tests/converter/t7098.nim
@@ -14,8 +14,8 @@ proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange =
   let e = if iend < 0: s.len + iend + 1
           else: iend
   assert ibegin >= 0 and e <= s.len
-
-  shallow(s)
+  when defined(gcRefc):
+    shallow(s)
   result.bytes = s
   result.ibegin = ibegin
   result.iend = e
diff --git a/tests/converter/tconverter_unique_ptr.nim b/tests/converter/tconverter_unique_ptr.nim
index 23c1a3d96..6902f9e9e 100644
--- a/tests/converter/tconverter_unique_ptr.nim
+++ b/tests/converter/tconverter_unique_ptr.nim
@@ -22,12 +22,11 @@ proc `$`(x: MyLen): string {.borrow.}
 proc `==`(x1, x2: MyLen): bool {.borrow.}
 
 
-proc `=destroy`*(m: var MySeq) {.inline.} =
+proc `=destroy`*(m: MySeq) {.inline.} =
   if m.data != nil:
     deallocShared(m.data)
-    m.data = nil
 
-proc `=`*(m: var MySeq, m2: MySeq) =
+proc `=copy`*(m: var MySeq, m2: MySeq) =
   if m.data == m2.data: return
   if m.data != nil:
     `=destroy`(m)
@@ -77,13 +76,12 @@ converter literalToLen*(x: int{lit}): MyLen =
 # Unique pointer implementation
 #-------------------------------------------------------------
 
-proc `=destroy`*[T](p: var UniquePtr[T]) =
+proc `=destroy`*[T](p: UniquePtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
+proc `=copy`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
@@ -118,13 +116,12 @@ type
     ## as it returns only `lent T`
     val: ptr T
 
-proc `=destroy`*[T](p: var ConstPtr[T]) =
+proc `=destroy`*[T](p: ConstPtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim
index e2f8b6469..770d413f5 100644
--- a/tests/coroutines/tgc.nim
+++ b/tests/coroutines/tgc.nim
@@ -1,6 +1,6 @@
 discard """
   matrix: "--gc:refc; --gc:arc; --gc:orc"
-  target: "c"
+  targets: "c"
 """
 
 when compileOption("gc", "refc") or not defined(openbsd):
diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim
index 71782ece1..2edfcf675 100644
--- a/tests/coroutines/twait.nim
+++ b/tests/coroutines/twait.nim
@@ -1,7 +1,7 @@
 discard """
   output: "Exit 1\nExit 2"
   matrix: "--gc:refc; --gc:arc; --gc:orc"
-  target: "c"
+  targets: "c"
 """
 
 when compileOption("gc", "refc") or not defined(openbsd):
diff --git a/tests/cpp/23962.h b/tests/cpp/23962.h
new file mode 100644
index 000000000..2d8147bfb
--- /dev/null
+++ b/tests/cpp/23962.h
@@ -0,0 +1,17 @@
+#include <iostream>
+
+struct Foo {
+
+  Foo(int inX): x(inX) {
+    std::cout << "Ctor Foo(" << x << ")\n";
+  }
+  ~Foo() {
+    std::cout << "Destory Foo(" << x << ")\n";
+  }
+
+  void print() {
+    std::cout << "Foo.x = " << x << '\n';
+  }
+
+  int x;
+};
\ No newline at end of file
diff --git a/tests/cpp/fam.h b/tests/cpp/fam.h
new file mode 100644
index 000000000..ad576425b
--- /dev/null
+++ b/tests/cpp/fam.h
@@ -0,0 +1,4 @@
+struct Test{
+  ~Test() {
+  }
+};
diff --git a/tests/cpp/t12946.nim b/tests/cpp/t12946.nim
new file mode 100644
index 000000000..79cd56251
--- /dev/null
+++ b/tests/cpp/t12946.nim
@@ -0,0 +1,8 @@
+discard """
+  targets: "c cpp"
+"""
+
+import std/atomics
+type Futex = distinct Atomic[int32]
+
+var x: Futex
diff --git a/tests/cpp/t22679.nim b/tests/cpp/t22679.nim
new file mode 100644
index 000000000..81defcb58
--- /dev/null
+++ b/tests/cpp/t22679.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim cpp $file"
+  output:'''
+cppNZ.x = 123
+cppNZInit.x = 123
+hascpp.cppnz.x = 123
+hasCppInit.cppnz.x = 123
+hasCppCtor.cppnz.x = 123
+'''
+"""
+{.emit:"""/*TYPESECTION*/
+struct CppNonZero {
+  int x = 123;
+};
+""".}
+
+import sugar
+type
+  CppNonZero {.importcpp, inheritable.} = object
+    x: cint
+
+  HasCpp = object
+    cppnz: CppNonZero
+
+proc initCppNonZero: CppNonZero =
+  CppNonZero()
+
+proc initHasCpp: HasCpp =
+  HasCpp()
+
+proc ctorHasCpp: HasCpp {.constructor.} =
+  discard
+
+proc main =
+  var cppNZ: CppNonZero
+  dump cppNZ.x
+
+  var cppNZInit = initCppNonZero()
+  dump cppNZInit.x
+
+  var hascpp: HasCpp
+  dump hascpp.cppnz.x
+
+  var hasCppInit = initHasCpp()
+  dump hasCppInit.cppnz.x
+
+  var hasCppCtor = ctorHasCpp()
+  dump hasCppCtor.cppnz.x
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/t22680.nim b/tests/cpp/t22680.nim
new file mode 100644
index 000000000..80f1a8319
--- /dev/null
+++ b/tests/cpp/t22680.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim cpp $file"
+  output:'''
+cppNZ.x = 123
+cppNZInit.x = 123
+inheritCpp.x = 123
+inheritCppInit.x = 123
+inheritCppCtor.x = 123
+'''
+"""
+import std/sugar
+
+{.emit:"""/*TYPESECTION*/
+struct CppNonZero {
+  int x = 123;
+};
+""".}
+
+type
+  CppNonZero {.importcpp, inheritable.} = object
+    x: cint
+
+  InheritCpp = object of CppNonZero
+
+proc initCppNonZero: CppNonZero =
+  CppNonZero()
+
+proc initInheritCpp: InheritCpp =
+  InheritCpp()
+
+proc ctorInheritCpp: InheritCpp {.constructor.} =
+  discard
+
+proc main =
+  var cppNZ: CppNonZero
+  dump cppNZ.x
+
+  var cppNZInit = initCppNonZero()
+  dump cppNZInit.x
+
+  var inheritCpp: InheritCpp
+  dump inheritCpp.x
+
+  var inheritCppInit = initInheritCpp()
+  dump inheritCppInit.x
+
+  var inheritCppCtor = ctorInheritCpp()
+  dump inheritCppCtor.x
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/t22712.nim b/tests/cpp/t22712.nim
new file mode 100644
index 000000000..34ef67ac8
--- /dev/null
+++ b/tests/cpp/t22712.nim
@@ -0,0 +1,15 @@
+discard """
+targets: "cpp"
+errormsg: "constructor in an imported type needs importcpp pragma"
+line: 14
+"""
+{.emit: """/*TYPESECTION*/
+struct CppStruct {
+  CppStruct();
+};
+""".}
+
+type CppStruct {.importcpp.} = object
+
+proc makeCppStruct(): CppStruct {.constructor.} = 
+  discard
\ No newline at end of file
diff --git a/tests/cpp/t23306.nim b/tests/cpp/t23306.nim
new file mode 100644
index 000000000..ebb4edb8d
--- /dev/null
+++ b/tests/cpp/t23306.nim
@@ -0,0 +1,12 @@
+discard """
+targets: "cpp"
+"""
+
+type K = object
+  h: iterator(f: K): K
+
+iterator d(g: K): K {.closure.} =
+  defer:
+    discard
+
+discard K(h: d)
\ No newline at end of file
diff --git a/tests/cpp/t23434.nim b/tests/cpp/t23434.nim
new file mode 100644
index 000000000..04a83227e
--- /dev/null
+++ b/tests/cpp/t23434.nim
@@ -0,0 +1,17 @@
+discard """
+cmd:"nim cpp $file"
+errormsg: "type mismatch: got <proc (self: SomeObject){.member, gcsafe.}>"
+line: 17
+"""
+type SomeObject = object
+    value: int
+
+proc printValue(self: SomeObject) {.virtual.} =
+    echo "The value is ", self.value
+
+proc callAProc(p: proc(self: SomeObject){.noconv.}) =
+    let someObj = SomeObject(value: 4)
+    echo "calling param proc"
+    p(someObj)
+
+callAProc(printValue)
\ No newline at end of file
diff --git a/tests/cpp/t23657.nim b/tests/cpp/t23657.nim
new file mode 100644
index 000000000..63deb7fb0
--- /dev/null
+++ b/tests/cpp/t23657.nim
@@ -0,0 +1,54 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp -r $file"
+  output: '''
+1.0
+1.0
+'''
+
+"""
+{.emit:"""/*TYPESECTION*/
+struct Point {
+  float x, y, z;
+  Point(float x, float y, float z): x(x), y(y), z(z) {}
+  Point() = default;
+};
+struct Direction {
+  float x, y, z;
+  Direction(float x, float y, float z): x(x), y(y), z(z) {}
+  Direction() = default;
+};
+struct Axis {
+  Point origin;
+  Direction direction;
+  Axis(Point origin, Direction direction): origin(origin), direction(direction) {}
+  Axis() = default;
+};
+
+""".}
+
+type
+  Point {.importcpp.} = object
+    x, y, z: float
+  
+  Direction {.importcpp.} = object
+    x, y, z: float
+
+  Axis {.importcpp.} = object
+    origin: Point
+    direction: Direction
+
+proc makeAxis(origin: Point, direction: Direction): Axis {. constructor, importcpp:"Axis(@)".}
+proc makePoint(x, y, z: float): Point {. constructor, importcpp:"Point(@)".}
+proc makeDirection(x, y, z: float): Direction {. constructor, importcpp:"Direction(@)".}
+
+var axis1 = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0)) #Triggers the error (T1)
+var axis2Ctor = makeAxis(makePoint(1.0, 2.0, 3.0), makeDirection(4.0, 5.0, 6.0)) #Do not triggers
+
+proc main() = #Do not triggers as Tx are inside the body
+  let test = makeAxis(Point(x: 1.0, y: 2.0, z: 3.0), Direction(x: 4.0, y: 5.0, z: 6.0))
+  echo test.origin.x
+
+main()
+
+echo $axis1.origin.x  #Make sures it's init
\ No newline at end of file
diff --git a/tests/cpp/t23962.nim b/tests/cpp/t23962.nim
new file mode 100644
index 000000000..c79d888df
--- /dev/null
+++ b/tests/cpp/t23962.nim
@@ -0,0 +1,32 @@
+discard """
+  cmd: "nim cpp $file"
+  output: '''
+Ctor Foo(-1)
+Destory Foo(-1)
+Ctor Foo(-1)
+Destory Foo(-1)
+Ctor Foo(-1)
+Destory Foo(-1)
+Foo.x = 1
+Foo.x = 2
+Foo.x = -1
+'''
+"""
+
+type
+  Foo {.importcpp, header: "23962.h".} = object
+    x: cint
+
+proc print(f: Foo) {.importcpp.}
+
+#also tests the right constructor is used
+proc makeFoo(x: int32 = -1): Foo {.importcpp:"Foo(#)", constructor.} 
+
+proc test =
+  var xs = newSeq[Foo](3)
+  xs[0].x = 1
+  xs[1].x = 2
+  for x in xs:
+    x.print
+
+test()
\ No newline at end of file
diff --git a/tests/cpp/tasync_cpp.nim b/tests/cpp/tasync_cpp.nim
index 696274ec4..3f4ec6208 100644
--- a/tests/cpp/tasync_cpp.nim
+++ b/tests/cpp/tasync_cpp.nim
@@ -1,7 +1,7 @@
 discard """
   targets: "cpp"
   output: "hello"
-  cmd: "nim cpp --clearNimblePath --nimblePath:build/deps/pkgs $file"
+  cmd: "nim cpp --clearNimblePath --nimblePath:build/deps/pkgs2 $file"
 """
 
 # bug #3299
diff --git a/tests/cpp/tcasts.nim b/tests/cpp/tcasts.nim
index d968d87db..80527efff 100644
--- a/tests/cpp/tcasts.nim
+++ b/tests/cpp/tcasts.nim
@@ -1,6 +1,5 @@
 discard """
   cmd: "nim cpp $file"
-  output: '''{"vas": "kas", "123": "123"}'''
   targets: "cpp"
 """
 
@@ -18,4 +17,4 @@ import tables
 var t = initTable[string, string]()
 discard t.hasKeyOrPut("123", "123")
 discard t.mgetOrPut("vas", "kas")
-echo t
\ No newline at end of file
+doAssert t.len == 2
diff --git a/tests/cpp/tcodegendecl.nim b/tests/cpp/tcodegendecl.nim
new file mode 100644
index 000000000..e128c5eb7
--- /dev/null
+++ b/tests/cpp/tcodegendecl.nim
@@ -0,0 +1,17 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: "3"
+"""
+
+{.emit:"""/*TYPESECTION*/
+  int operate(int x, int y, int (*func)(const int&, const int&)){
+    return func(x, y);
+  };
+""".}
+
+proc operate(x, y: int32, fn: proc(x, y: int32 ): int32 {.cdecl.}): int32 {.importcpp:"$1(@)".}
+
+proc add(a {.codegenDecl:"const $#& $#".}, b {.codegenDecl:"const $# $#", byref.}: int32): int32  {.cdecl.} = a + b
+
+echo operate(1, 2, add)
\ No newline at end of file
diff --git a/tests/cpp/tconstructor.nim b/tests/cpp/tconstructor.nim
new file mode 100644
index 000000000..922ee54fd
--- /dev/null
+++ b/tests/cpp/tconstructor.nim
@@ -0,0 +1,131 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+1
+0
+123
+0
+123
+___
+0
+777
+10
+123
+0
+777
+10
+123
+()
+'''
+"""
+
+{.emit:"""/*TYPESECTION*/
+struct CppClass {
+  int x;
+  int y;
+  CppClass(int inX, int inY) {
+    this->x = inX;
+    this->y = inY;
+  }
+  //CppClass() = default;
+};
+""".}
+
+type  CppClass* {.importcpp, inheritable.} = object
+  x: int32
+  y: int32
+
+proc makeCppClass(x, y: int32): CppClass {.importcpp: "CppClass(@)", constructor.}
+#test globals are init with the constructor call
+var shouldCompile {.used.} = makeCppClass(1, 2)
+
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+
+#creation
+type NimClassNoNarent* = object
+  x: int32
+
+proc makeNimClassNoParent(x:int32): NimClassNoNarent {. constructor.} =
+  result.x = x
+  discard
+
+let nimClassNoParent = makeNimClassNoParent(1)
+echo nimClassNoParent.x #acess to this just fine. Notice the field will appear last because we are dealing with constructor calls here
+
+var nimClassNoParentDef {.used.}: NimClassNoNarent  #test has a default constructor. 
+
+#inheritance 
+type NimClass* = object of CppClass
+
+proc makeNimClass(x:int32): NimClass {. constructor:"NimClass('1 #1) : CppClass(0, #1) ".} =
+  result.x = x
+
+#optinially define the default constructor so we get rid of the cpp warn and we can declare the obj (note: default constructor of 'tyObject_NimClass__apRyyO8cfRsZtsldq1rjKA' is implicitly deleted because base class 'CppClass' has no default constructor)
+proc makeCppClass(): NimClass {. constructor: "NimClass() : CppClass(0, 0) ".} = 
+  result.x = 1
+
+let nimClass = makeNimClass(1)
+var nimClassDef {.used.}: NimClass  #since we explictly defined the default constructor we can declare the obj
+
+#bug: 22662
+type
+  BugClass* = object
+    x: int          # Not initialized
+
+proc makeBugClass(): BugClass {.constructor.} =
+  discard
+
+proc main =
+  for i in 0 .. 1:
+    var n = makeBugClass()
+    echo n.x
+    n.x = 123
+    echo n.x
+
+main()
+#bug:
+echo "___"
+type
+  NimClassWithDefault = object
+    x: int
+    y = 777
+    case kind: bool = true
+    of true:
+      z: int = 10
+    else: discard
+
+proc makeNimClassWithDefault(): NimClassWithDefault {.constructor.} =
+  result = NimClassWithDefault()
+
+proc init =
+  for i in 0 .. 1:
+    var n = makeNimClassWithDefault()
+    echo n.x
+    echo n.y
+    echo n.z
+    n.x = 123
+    echo n.x
+
+init()
+
+#tests that the ctor is not declared with nodecl. 
+#nodelc also prevents the creation of a default one when another is created.
+type Foo {.exportc.} = object
+
+proc makeFoo(): Foo {.used, constructor, nodecl.} = discard
+
+echo $Foo()
+
+type Boo = object
+proc `=copy`(dest: var Boo; src: Boo) = discard
+
+proc makeBoo(): Boo {.constructor.} = Boo()
+proc makeBoo2(): Boo  = Boo()
+
+block:
+  proc main =
+    var b = makeBoo()
+    var b2 = makeBoo2()
+
+  main()
\ No newline at end of file
diff --git a/tests/cpp/tfam.nim b/tests/cpp/tfam.nim
new file mode 100644
index 000000000..6bd89fe24
--- /dev/null
+++ b/tests/cpp/tfam.nim
@@ -0,0 +1,7 @@
+discard """
+  targets: "cpp"
+"""
+type 
+  Test {.importcpp, header: "fam.h".} = object
+
+let test = newSeq[Test]()
\ No newline at end of file
diff --git a/tests/cpp/tinitializers.nim b/tests/cpp/tinitializers.nim
new file mode 100644
index 000000000..0199fb96b
--- /dev/null
+++ b/tests/cpp/tinitializers.nim
@@ -0,0 +1,60 @@
+discard """
+  cmd: "nim cpp $file"
+"""
+
+{.emit:"""/*TYPESECTION*/
+struct CppStruct {
+  CppStruct(int x, char* y): x(x), y(y){}
+  void doSomething() {}
+  int x;
+  char* y;
+};
+""".}
+type
+  CppStruct {.importcpp, inheritable.} = object
+  ChildStruct = object of CppStruct
+  HasCppStruct = object
+    cppstruct: CppStruct 
+
+proc constructCppStruct(a:cint = 5, b:cstring = "hello"): CppStruct {.importcpp: "CppStruct(@)", constructor.}
+proc doSomething(this: CppStruct) {.importcpp.}
+proc returnCppStruct(): CppStruct = discard
+proc initChildStruct: ChildStruct = ChildStruct() 
+proc makeChildStruct(): ChildStruct {.constructor:"""ChildStruct(): CppStruct(5, "10")""".} = discard
+proc initHasCppStruct(x: cint): HasCppStruct =
+  HasCppStruct(cppstruct: constructCppStruct(x))
+
+proc main =
+  var hasCppStruct = initHasCppStruct(2) #generates cppstruct = { 10 } inside the struct
+  hasCppStruct.cppstruct.doSomething() 
+  discard returnCppStruct() #generates result = { 10 } 
+  discard initChildStruct() #generates ChildStruct temp ({}) bypassed with makeChildStruct
+  (proc (s:CppStruct) = discard)(CppStruct()) #CppStruct temp ({10})
+main()
+
+
+#Should handle ObjectCalls
+{.emit:"""/*TYPESECTION*/
+struct Foo {
+};
+struct Boo {
+  Boo(int x, char* y, Foo f): x(x), y(y), foo(f){}
+  int x;
+  char* y;
+  Foo foo;
+};
+""".}
+type
+  Foo {.importcpp, inheritable, bycopy.} = object
+  Boo {.importcpp, inheritable.} = object
+    x: int32
+    y: cstring
+    foo: Foo
+
+proc makeBoo(a:cint = 10, b:cstring = "hello", foo: Foo = Foo()): Boo {.importcpp, constructor.}
+
+proc main2() = 
+  let cppStruct = makeBoo()
+  (proc (s:Boo) = discard)(Boo()) 
+
+main2()
\ No newline at end of file
diff --git a/tests/cpp/tmember.nim b/tests/cpp/tmember.nim
new file mode 100644
index 000000000..1a5b6fd97
--- /dev/null
+++ b/tests/cpp/tmember.nim
@@ -0,0 +1,75 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+2
+false
+hello foo
+hello boo
+hello boo
+FunctorSupport!
+static
+static
+destructing
+destructing
+'''
+"""
+proc print(s: cstring) {.importcpp:"printf(@)", header:"<stdio.h>".}
+
+type
+  Doo  {.exportc.} = object
+    test: int
+
+proc memberProc(f: Doo) {.exportc, member.} = 
+  echo $f.test
+
+proc destructor(f: Doo) {.member: "~'1()", used.} = 
+  print "destructing\n"
+
+proc `==`(self, other: Doo): bool {.member:"operator==('2 const & #2) const -> '0"} = 
+  self.test == other.test
+
+let doo = Doo(test: 2)
+doo.memberProc()
+echo doo == Doo(test: 1)
+
+#virtual
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+type 
+  Foo {.exportc.} = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+
+proc salute(self: FooPtr) {.member: "virtual $1()".} = 
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.member: "virtual $1()".} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())  
+
+foo.salute()
+boo.salute()
+booAsFoo.salute()
+
+type
+  NimFunctor = object
+    discard
+proc invoke(f: NimFunctor, n:int) {.member:"operator ()('2 #2)" .} = 
+  echo "FunctorSupport!"
+
+{.experimental: "callOperator".}
+proc `()`(f: NimFunctor, n:int) {.importcpp:"#(@)" .} 
+NimFunctor()(1)
+
+#static
+proc staticProc(self: FooPtr) {.member: "static $1()".} = 
+  echo "static"
+
+proc importedStaticProc() {.importcpp:"Foo::staticProc()".}
+
+foo.staticProc()
+importedStaticProc()
diff --git a/tests/cpp/tmember_forward_declaration.nim b/tests/cpp/tmember_forward_declaration.nim
new file mode 100644
index 000000000..2f4a79daa
--- /dev/null
+++ b/tests/cpp/tmember_forward_declaration.nim
@@ -0,0 +1,27 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+abc called
+def called
+abc called
+'''
+"""
+
+type Foo = object
+
+proc abc(this: Foo, x: int): void {.member: "$1('2 #2)".}
+proc def(this: Foo, y: int): void {.virtual: "$1('2 #2)".}
+
+proc abc(this: Foo, x: int): void =
+  echo "abc called"
+  if x > 0:
+    this.def(x - 1)
+
+proc def(this: Foo, y: int): void =
+  echo "def called"
+  this.abc(y)
+
+var x = Foo()
+x.abc(1)
+
diff --git a/tests/cpp/tnoinitfield.nim b/tests/cpp/tnoinitfield.nim
new file mode 100644
index 000000000..4deffece8
--- /dev/null
+++ b/tests/cpp/tnoinitfield.nim
@@ -0,0 +1,30 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+'''
+"""
+{.emit: """/*TYPESECTION*/
+  struct Foo {
+    Foo(int a){};
+  };
+  struct Boo {
+    Boo(int a){};
+  };
+
+  """.}
+
+type 
+  Foo {.importcpp.} = object
+  Boo {.importcpp, noInit.} = object
+  Test {.exportc.} = object
+    foo {.noInit.}: Foo
+    boo: Boo
+
+proc makeTest(): Test {.constructor: "Test() : foo(10), boo(1)".} = 
+  discard
+
+proc main() = 
+  var t = makeTest()
+
+main()
\ No newline at end of file
diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim
index 636105f31..9f1a41a21 100644
--- a/tests/cpp/torc.nim
+++ b/tests/cpp/torc.nim
@@ -23,3 +23,53 @@ type
 
 var b = Bar()
 discard b
+
+# bug #4678
+{.emit: """/*TYPESECTION*/
+enum class SomeEnum {A,B,C};
+""".}
+type
+  EnumVector[T: enum] {.importcpp: "std::vector", header: "vector".} = object
+  SomeEnum {.importcpp, nodecl.} = enum
+    A,B,C
+
+proc asVector*[T](t: T): EnumVector[T] =
+  discard
+# Nim generates this signature here:
+# N_NIMCALL(std::vector<> , asvector_106028_3197418230)(SomeEnum t0)
+
+discard asVector(SomeEnum.A)
+
+
+block: # bug #10219
+  type
+    Vector[T]  {.importcpp: "std::vector", header: "vector".} = object
+
+  proc initVector[T](n: csize_t): Vector[T] 
+      {.importcpp: "std::vector<'*0>(@)", header: "vector".}
+
+  proc unsafeIndex[T](this: var Vector[T], i: csize_t): var T 
+      {.importcpp: "#[#]", header: "vector".}
+
+  proc `[]`[T](this: var Vector[T], i: Natural): var T {.inline, noinit.} =
+    when compileOption("boundChecks"):
+        # this.checkIndex i
+        discard
+    result = this.unsafeIndex(csize_t(i))
+
+  var v1 = initVector[int](10)
+  doAssert v1[0] == 0
+
+block: # bug #12703 bug #19588
+  type
+    cstringConstImpl {.importc:"const char*".} = cstring
+    constChar = distinct cstringConstImpl
+
+  {.emit: """
+  const char* foo() {
+    return "hello";
+  }
+  """.}
+  proc foo(): constChar {.importcpp.} # change to importcpp for C++ backend
+  doAssert $(foo().cstring) == "hello"
+
diff --git a/tests/cpp/tpassbypragmas.nim b/tests/cpp/tpassbypragmas.nim
new file mode 100644
index 000000000..f4301656a
--- /dev/null
+++ b/tests/cpp/tpassbypragmas.nim
@@ -0,0 +1,27 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+"""
+{.emit:"""/*TYPESECTION*/
+
+  template<typename T>
+  struct Box {
+      T first;
+  };
+  struct Foo {
+  void test(void (*func)(Box<Foo>& another)){
+
+    };
+  };
+""".}
+
+type 
+  Foo {.importcpp.} = object
+  Box[T] {.importcpp:"Box<'0>".} = object
+    first: T
+
+proc test(self: Foo, fn: proc(another {.byref.}: Box[Foo]) {.cdecl.}) {.importcpp.}
+
+proc fn(another {.byref.} : Box[Foo]) {.cdecl.} = discard
+
+Foo().test(fn)
\ No newline at end of file
diff --git a/tests/cpp/tretvar.nim b/tests/cpp/tretvar.nim
index 83c37721e..0c3765346 100644
--- a/tests/cpp/tretvar.nim
+++ b/tests/cpp/tretvar.nim
@@ -16,9 +16,9 @@ type
 
 proc c_str(a: stdString): cstring {.importcpp: "(char *)(#.c_str())", header: "<string>".}
 
-proc len(a: stdString): csize {.importcpp: "(#.length())", header: "<string>".}
+proc len(a: stdString): csize_t {.importcpp: "(#.length())", header: "<string>".}
 
-proc setChar(a: var stdString, i: csize, c: char) {.importcpp: "(#[#] = #)", header: "<string>".}
+proc setChar(a: var stdString, i: csize_t, c: char) {.importcpp: "(#[#] = #)", header: "<string>".}
 
 proc `*`*[T](this: stdUniquePtr[T]): var T {.noSideEffect, importcpp: "(* #)", header: "<memory>".}
 
diff --git a/tests/cpp/tvirtual.nim b/tests/cpp/tvirtual.nim
new file mode 100644
index 000000000..385d052b8
--- /dev/null
+++ b/tests/cpp/tvirtual.nim
@@ -0,0 +1,126 @@
+discard """
+  targets: "cpp"
+  cmd: "nim cpp $file"
+  output: '''
+hello foo
+hello boo
+hello boo
+Const Message: hello world
+NimPrinter: hello world
+NimPrinterConstRef: hello world
+NimPrinterConstRefByRef: hello world
+'''
+"""
+
+{.emit:"""/*TYPESECTION*/
+#include <iostream>
+  class CppPrinter {
+  public:
+    
+    virtual void printConst(char* message) const {
+        std::cout << "Const Message: " << message << std::endl;
+    }
+    virtual void printConstRef(char* message, const int& flag) const {
+        std::cout << "Const Ref Message: " << message << std::endl;
+    }  
+    virtual void printConstRef2(char* message, const int& flag) const {
+        std::cout << "Const Ref2 Message: " << message << std::endl;
+    }  
+    
+};
+""".}
+
+proc newCpp*[T](): ptr T {.importcpp:"new '*0()".}
+type 
+  Foo = object of RootObj
+  FooPtr = ptr Foo
+  Boo = object of Foo
+  BooPtr = ptr Boo
+  CppPrinter {.importcpp, inheritable.} = object
+  NimPrinter {.exportc.} = object of CppPrinter
+
+proc salute(self: FooPtr) {.virtual.} = 
+  echo "hello foo"
+
+proc salute(self: BooPtr) {.virtual.} =
+  echo "hello boo"
+
+let foo = newCpp[Foo]()
+let boo = newCpp[Boo]()
+let booAsFoo = cast[FooPtr](newCpp[Boo]())
+
+#polymorphism works
+foo.salute()
+boo.salute()
+booAsFoo.salute()
+let message = "hello world".cstring
+
+proc printConst(self: CppPrinter, message: cstring) {.importcpp.}
+CppPrinter().printConst(message)
+
+#notice override is optional. 
+#Will make the cpp compiler to fail if not virtual function with the same signature if found in the base type
+proc printConst(self: NimPrinter, message: cstring) {.virtual:"$1('2 #2) const override".} =
+  echo "NimPrinter: " & $message
+
+proc printConstRef(self: NimPrinter, message: cstring, flag: int32) {.virtual:"$1('2 #2, const '3& #3 ) const override".} =
+  echo "NimPrinterConstRef: " & $message
+
+proc printConstRef2(self: NimPrinter, message: cstring, flag {.byref.}: int32) {.virtual:"$1('2 #2, const '3 #3 ) const override".} =
+  echo "NimPrinterConstRefByRef: " & $message
+
+NimPrinter().printConst(message)
+var val : int32 = 10
+NimPrinter().printConstRef(message, val)
+NimPrinter().printConstRef2(message, val)
+
+#bug 22269
+type Doo = object
+proc naiveMember(x: Doo): int {. virtual .} = 2
+discard naiveMember(Doo())
+
+#asmnostackframe works with virtual
+{.emit:"""/*TYPESECTION*/
+  template<typename T>
+  struct Box {
+      T* first;
+     
+      Box(int x){
+        first = new T(x);
+      };
+  };
+  struct Inner {
+    int val;
+    //Coo() = default;
+    Inner(int x){
+      val = x;
+    };
+  };
+  struct Base {
+    virtual Box<Inner> test() = 0;
+  };
+""".}
+
+type 
+  Inner {.importcpp.} = object
+  Base {.importcpp, inheritable.} = object
+  Child  = object of Base
+  Box[T] {.importcpp, inheritable.} = object
+    first: T
+
+proc makeBox[T](x:int32): Box[T] {.importcpp:"Box<'0>(@)", constructor.}
+
+proc test(self: Child): Box[Inner] {.virtual, asmnostackframe.} = 
+  let res {.exportc.} = makeBox[Inner](100)
+  {.emit:"return res;".}
+  
+
+discard Child().test() 
+
+import virtualptr
+
+#We dont want to pull Loo directly by using it as we are testing that the pointer pulls it. 
+proc makeMoo(): Moo {.importcpp:"{ new Loo() }".}
+
+makeMoo().loo.salute()
+
diff --git a/tests/cpp/virtualptr.nim b/tests/cpp/virtualptr.nim
new file mode 100644
index 000000000..f96264081
--- /dev/null
+++ b/tests/cpp/virtualptr.nim
@@ -0,0 +1,9 @@
+type 
+  Loo* {.exportc.} = object
+  LooPtr* = ptr Loo
+  Moo* {.exportc.} = object 
+    loo*: LooPtr
+
+
+proc salute*(foo: LooPtr) {.virtual.} = 
+  discard 
diff --git a/tests/defer/t22309.nim b/tests/defer/t22309.nim
new file mode 100644
index 000000000..34ca4843b
--- /dev/null
+++ b/tests/defer/t22309.nim
@@ -0,0 +1,11 @@
+block:
+  defer:
+    let a = 42
+  doAssert not declared(a)
+
+proc lol() =
+  defer:
+    let a = 42
+  doAssert not declared(a)
+
+lol()
diff --git a/tests/deprecated/tannot.nim b/tests/deprecated/tannot.nim
deleted file mode 100644
index d14f6cc23..000000000
--- a/tests/deprecated/tannot.nim
+++ /dev/null
@@ -1,9 +0,0 @@
-discard """
-  nimout: '''tannot.nim(9, 1) Warning: efgh; foo1 is deprecated [Deprecated]
-tannot.nim(9, 8) Warning: abcd; foo is deprecated [Deprecated]
-'''
-"""
-
-let foo* {.deprecated: "abcd".} = 42
-var foo1* {.deprecated: "efgh".} = 42
-foo1 = foo
diff --git a/tests/deprecated/tmessages.nim b/tests/deprecated/tmessages.nim
deleted file mode 100644
index 5884e396d..000000000
--- a/tests/deprecated/tmessages.nim
+++ /dev/null
@@ -1,10 +0,0 @@
-discard """
-  nimout:'''tmessages.nim(10, 1) Warning: Deprecated since v1.2.0, use 'HelloZ'; hello is deprecated [Deprecated]
-'''
-"""
-
-proc hello[T](a: T) {.deprecated: "Deprecated since v1.2.0, use 'HelloZ'".} =
-  discard
-
-
-hello[int](12)
diff --git a/tests/deprecated/tmodule1.nim b/tests/deprecated/tmodule1.nim
deleted file mode 100644
index f26e4ce3f..000000000
--- a/tests/deprecated/tmodule1.nim
+++ /dev/null
@@ -1,33 +0,0 @@
-discard """
-  matrix: "--hint:all:off"
-  nimoutFull: true
-  nimout: '''
-tmodule1.nim(21, 8) Warning: goodbye; importme is deprecated [Deprecated]
-tmodule1.nim(24, 10) Warning: Ty is deprecated [Deprecated]
-tmodule1.nim(27, 10) Warning: hello; Ty1 is deprecated [Deprecated]
-tmodule1.nim(30, 8) Warning: aVar is deprecated [Deprecated]
-tmodule1.nim(32, 3) Warning: aProc is deprecated [Deprecated]
-tmodule1.nim(33, 3) Warning: hello; aProc1 is deprecated [Deprecated]
-'''
-"""
-
-
-
-
-
-
-
-# line 20
-import importme
-
-block:
-  var z: Ty
-  z = 0
-block:
-  var z: Ty1
-  z = 0
-block:
-  echo aVar
-block:
-  aProc()
-  aProc1()
diff --git a/tests/destructor/const_smart_ptr.nim b/tests/destructor/const_smart_ptr.nim
index 4d8c7c9a3..25dd46500 100644
--- a/tests/destructor/const_smart_ptr.nim
+++ b/tests/destructor/const_smart_ptr.nim
@@ -2,13 +2,12 @@ type
   ConstPtr*[T] = object
     val: ptr T
 
-proc `=destroy`*[T](p: var ConstPtr[T]) =
+proc `=destroy`*[T](p: ConstPtr[T]) =
   if p.val != nil:
     `=destroy`(p.val[])
     dealloc(p.val)
-    p.val = nil
 
-proc `=`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
+proc `=copy`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.error.}
 
 proc `=sink`*[T](dest: var ConstPtr[T], src: ConstPtr[T]) {.inline.} =
   if dest.val != nil and dest.val != src.val:
@@ -31,12 +30,11 @@ type
     len: int
     data: ptr UncheckedArray[float]
 
-proc `=destroy`*(m: var MySeqNonCopyable) {.inline.} =
+proc `=destroy`*(m: MySeqNonCopyable) {.inline.} =
   if m.data != nil:
     deallocShared(m.data)
-    m.data = nil
 
-proc `=`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
+proc `=copy`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.error.}
 
 proc `=sink`*(m: var MySeqNonCopyable, m2: MySeqNonCopyable) {.inline.} =
   if m.data != m2.data:
diff --git a/tests/destructor/t16607.nim b/tests/destructor/t16607.nim
index 5cc9d4a08..f98a6d517 100644
--- a/tests/destructor/t16607.nim
+++ b/tests/destructor/t16607.nim
@@ -15,8 +15,7 @@ proc initO(): O =
   O(initialized: true)
 
 proc pair(): tuple[a, b: O] =
-  result.a = initO()
-  result.b = initO()
+  result = (a: initO(), b: initO())
 
 proc main() =
   discard pair()
diff --git a/tests/destructor/t23748.nim b/tests/destructor/t23748.nim
new file mode 100644
index 000000000..a3738733e
--- /dev/null
+++ b/tests/destructor/t23748.nim
@@ -0,0 +1,31 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: '''
+hello 42
+hello 42
+len = 2
+'''
+"""
+
+# bug #23748
+
+type
+  O = ref object
+    s: string
+    cb: seq[proc()]
+
+proc push1(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  o.cb.add(proc() = echo o.s, " ", i)
+
+proc push2(o: O, i: int) =
+  let o = o
+  echo o.s, " ", i
+  proc p() = echo o.s, " ", i
+  o.cb.add(p)
+
+let o = O(s: "hello", cb: @[])
+o.push1(42)
+o.push2(42)
+echo "len = ", o.cb.len
diff --git a/tests/destructor/t23837.nim b/tests/destructor/t23837.nim
new file mode 100644
index 000000000..e219dd6b5
--- /dev/null
+++ b/tests/destructor/t23837.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+Deallocating OwnedString
+HelloWorld
+'''
+  matrix: "--cursorinference:on; --cursorinference:off"
+  target: "c"
+"""
+
+# bug #23837
+{.
+  emit: [
+    """
+#include <stdlib.h>
+#include <string.h>
+char *allocCString() {
+    char *result = (char *) malloc(10 + 1);
+    strcpy(result, "HelloWorld");
+    return result;
+}
+
+"""
+  ]
+.}
+
+proc rawWrapper(): cstring {.importc: "allocCString", cdecl.}
+proc free(p: pointer) {.importc: "free", cdecl.}
+
+# -------------------------
+
+type OwnedString = distinct cstring
+
+proc `=destroy`(s: OwnedString) =
+  free(cstring s)
+  echo "Deallocating OwnedString"
+
+func `$`(s: OwnedString): string {.borrow.}
+
+proc leakyWrapper(): string =
+  let ostring = rawWrapper().OwnedString
+  $ostring
+
+# -------------------------
+
+proc main() =
+  # destructor not called - definitely lost: 11 bytes in 1 blocks
+  # doesn't leak with --cursorInference:off
+  let s = leakyWrapper()
+  echo s
+
+main()
\ No newline at end of file
diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim
index 88f84d67c..82870ac82 100644
--- a/tests/destructor/tatomicptrs.nim
+++ b/tests/destructor/tatomicptrs.nim
@@ -39,7 +39,7 @@ proc `=destroy`*[T](dest: var SharedPtr[T]) =
     echo "deallocating"
     dest.x = nil
 
-proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
+proc `=copy`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   var s = src.x
   if s != nil: incRef(s)
   #atomicSwap(dest, s)
@@ -50,6 +50,9 @@ proc `=`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
     deallocShared(s)
     echo "deallocating"
 
+proc `=dup`*[T](src: SharedPtr[T]): SharedPtr[T] =
+  `=copy`(result, src)
+
 proc `=sink`*[T](dest: var SharedPtr[T]; src: SharedPtr[T]) =
   ## XXX make this an atomic store:
   if dest.x != src.x:
@@ -120,7 +123,7 @@ proc `=destroy`*[T](m: var MySeq[T]) {.inline.} =
     deallocShared(m.data)
     m.data = nil
 
-proc `=`*[T](m: var MySeq[T], m2: MySeq[T]) =
+proc `=copy`*[T](m: var MySeq[T], m2: MySeq[T]) =
   if m.data == m2.data: return
   if m.data != nil:
     `=destroy`(m)
@@ -131,17 +134,22 @@ proc `=`*[T](m: var MySeq[T], m2: MySeq[T]) =
     m.data = cast[ptr UncheckedArray[T]](allocShared(bytes))
     copyMem(m.data, m2.data, bytes)
 
+proc `=dup`*[T](m: MySeq[T]): MySeq[T] =
+  `=copy`[T](result, m)
+
 proc `=sink`*[T](m: var MySeq[T], m2: MySeq[T]) {.inline.} =
   if m.data != m2.data:
     if m.data != nil:
       `=destroy`(m)
     m.len = m2.len
     m.data = m2.data
+    m.refcount = m2.refcount
 
 proc len*[T](m: MySeq[T]): int {.inline.} = m.len
 
 proc newMySeq*[T](size: int, initial_value: T): MySeq[T] =
   result.len = size
+  result.refcount = 1
   if size > 0:
     result.data = cast[ptr UncheckedArray[T]](allocShared(sizeof(T) * size))
 
diff --git a/tests/destructor/tbintree2.nim b/tests/destructor/tbintree2.nim
index 0bc52457c..d56c2850b 100644
--- a/tests/destructor/tbintree2.nim
+++ b/tests/destructor/tbintree2.nim
@@ -21,21 +21,21 @@ proc merge(lower, greater: owned Node): owned Node =
   elif greater.isNil:
     result = lower
   elif lower.y < greater.y:
-    lower.right = merge(lower.right, greater)
+    lower.right = merge(move lower.right, greater)
     result = lower
   else:
-    greater.left = merge(lower, greater.left)
+    greater.left = merge(lower, move greater.left)
     result = greater
 
 proc splitBinary(orig: owned Node, value: int32): (owned Node, owned Node) =
   if orig.isNil:
     result = (nil, nil)
   elif orig.x < value:
-    let splitPair = splitBinary(orig.right, value)
+    let splitPair = splitBinary(move orig.right, value)
     orig.right = splitPair[0]
     result = (orig, splitPair[1])
   else:
-    let splitPair = splitBinary(orig.left, value)
+    let splitPair = splitBinary(move orig.left, value)
     orig.left = splitPair[1]
     result = (splitPair[0], orig)
 
diff --git a/tests/destructor/tdistinctseq.nim b/tests/destructor/tdistinctseq.nim
new file mode 100644
index 000000000..5a2ac5ead
--- /dev/null
+++ b/tests/destructor/tdistinctseq.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "-u:nimPreviewNonVarDestructor;"
+"""
+type DistinctSeq* = distinct seq[int]
+
+# `=destroy`(cast[ptr DistinctSeq](0)[])
+var x = @[].DistinctSeq
+`=destroy`(x)
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
index d27626dea..ffe87cd76 100644
--- a/tests/destructor/tdont_return_unowned_from_owned.nim
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -1,9 +1,12 @@
 discard """
   cmd: "nim check --newruntime --hints:off $file"
   nimout: '''
-tdont_return_unowned_from_owned.nim(36, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(39, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(42, 6) Error: type mismatch: got <Obj>
+tdont_return_unowned_from_owned.nim(26, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(27, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
+tdont_return_unowned_from_owned.nim(31, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(46, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
+tdont_return_unowned_from_owned.nim(49, 6) Error: type mismatch: got <Obj>
 but expected one of:
 proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
   first type mismatch at position: 2
@@ -11,17 +14,21 @@ proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
 2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
 
 expression: new(result)
-tdont_return_unowned_from_owned.nim(42, 6) Error: illformed AST:
-tdont_return_unowned_from_owned.nim(50, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(51, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(55, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+tdont_return_unowned_from_owned.nim(49, 6) Error: illformed AST: 
 '''
-  errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
+  errormsg: "illformed AST:"
 """
 
 
 
+proc testA(result: var (RootRef, RootRef)) =
+  let r: owned RootRef = RootRef()
+  result[0] = r
+  result[1] = RootRef()
 
+proc testB(): RootRef =
+  let r: owned RootRef = RootRef()
+  result = r
 
 
 
@@ -39,17 +46,11 @@ proc newObjB(): Obj =
   result = Obj()
 
 proc newObjC(): Obj =
-  new(result)
+  new(result) # illFormedAst raises GlobalError,
+              # without pipeline parsing, it needs to placed at the end
+              # in case that it disturbs other errors
 
 let a = newObjA()
 let b = newObjB()
 let c = newObjC()
 
-proc testA(result: var (RootRef, RootRef)) =
-  let r: owned RootRef = RootRef()
-  result[0] = r
-  result[1] = RootRef()
-
-proc testB(): RootRef =
-  let r: owned RootRef = RootRef()
-  result = r
diff --git a/tests/destructor/tgotoexc_leak.nim b/tests/destructor/tgotoexc_leak.nim
new file mode 100644
index 000000000..c8a234085
--- /dev/null
+++ b/tests/destructor/tgotoexc_leak.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''0
+true'''
+  cmd: "nim c --gc:arc $file"
+"""
+
+# bug #22398
+
+for i in 0 ..< 10_000:
+  try:
+    try:
+      raise newException(ValueError, "")
+    except CatchableError:
+      discard
+      raise newException(ValueError, "") # or raise getCurrentException(), just raise works ok
+  except ValueError:
+    discard
+echo getOccupiedMem()
+echo getCurrentException() == nil
diff --git a/tests/destructor/tgotoexceptions7.nim b/tests/destructor/tgotoexceptions7.nim
index 6e564a044..c04bd6ba0 100644
--- a/tests/destructor/tgotoexceptions7.nim
+++ b/tests/destructor/tgotoexceptions7.nim
@@ -25,7 +25,8 @@ proc helper = doAssert(false)
 
 proc main(i: int) =
   var obj = Obj(kind: kindA, s: "abc")
-  obj.kind = kindB
+  {.cast(uncheckedAssign).}:
+    obj.kind = kindB
   obj.i = 2
   try:
     var objA = ObjA()
diff --git a/tests/destructor/tmatrix.nim b/tests/destructor/tmatrix.nim
index 98ca95c94..2fd5af789 100644
--- a/tests/destructor/tmatrix.nim
+++ b/tests/destructor/tmatrix.nim
@@ -31,7 +31,7 @@ proc `=sink`*(a: var Matrix; b: Matrix) =
   a.m = b.m
   a.n = b.n
 
-proc `=`*(a: var Matrix; b: Matrix) =
+proc `=copy`*(a: var Matrix; b: Matrix) =
   if a.data != nil and a.data != b.data:
     dealloc(a.data)
     deallocCount.inc
@@ -43,6 +43,9 @@ proc `=`*(a: var Matrix; b: Matrix) =
     allocCount.inc
     copyMem(a.data, b.data, b.m * b.n * sizeof(float))
 
+proc `=dup`*(a: Matrix): Matrix =
+  `=copy`(result, a)
+
 proc matrix*(m, n: int, s: float): Matrix =
   ## Construct an m-by-n constant matrix.
   result.m = m
diff --git a/tests/destructor/tmove.nim b/tests/destructor/tmove.nim
new file mode 100644
index 000000000..2762aff90
--- /dev/null
+++ b/tests/destructor/tmove.nim
@@ -0,0 +1,18 @@
+discard """
+  targets: "c cpp"
+"""
+
+block:
+  var called = 0
+
+  proc bar(a: var int): var int =
+    inc called
+    result = a
+
+  proc foo =
+    var a = 2
+    var s = move bar(a)
+    doAssert called == 1
+    doAssert s == 2
+
+  foo()
diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim
index 9eee4bfdb..cdc1eb1c0 100644
--- a/tests/destructor/tmove_objconstr.nim
+++ b/tests/destructor/tmove_objconstr.nim
@@ -50,7 +50,7 @@ proc `=destroy`(o: var Pony) =
   echo "Pony is dying!"
 
 proc getPony: Pony =
-  result.name = "Sparkles"
+  result = Pony(name: "Sparkles")
 
 iterator items(p: Pony): int =
   for i in 1..4:
@@ -137,28 +137,29 @@ doAssert seq3[0] == 1.0
 var seq4, seq5: MySeqNonCopyable
 (seq4, i, seq5) = myfunc2(2, 3)
 
-seq4 = block:
-  var tmp = newMySeq(4, 1.0)
-  tmp[0] = 3.0
-  tmp
+proc foo =
+  seq4 = block:
+    var tmp = newMySeq(4, 1.0)
+    tmp[0] = 3.0
+    tmp
 
-doAssert seq4[0] == 3.0
+  doAssert seq4[0] == 3.0
 
-import macros
 
-seq4 =
-  if i > 0: newMySeq(2, 5.0)
-  elif i < -100: raise newException(ValueError, "Parse Error")
-  else: newMySeq(2, 3.0)
+  seq4 =
+    if i > 0: newMySeq(2, 5.0)
+    elif i < -100: raise newException(ValueError, "Parse Error")
+    else: newMySeq(2, 3.0)
 
-seq4 =
-  case (char) i:
-    of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
-    of 'B': quit(-1)
-    else:
-      let (x1, x2, x3) = myfunc2(2, 3)
-      x3
+  seq4 =
+    case (char) i:
+      of 'A', {'W'..'Z'}: newMySeq(2, 5.0)
+      of 'B': quit(-1)
+      else:
+        let (x1, x2, x3) = myfunc2(2, 3)
+        x3
 
+foo()
 
 #------------------------------------------------------------
 #-- Move into array constructor
diff --git a/tests/destructor/tnonvardestructor.nim b/tests/destructor/tnonvardestructor.nim
new file mode 100644
index 000000000..1b4413790
--- /dev/null
+++ b/tests/destructor/tnonvardestructor.nim
@@ -0,0 +1,247 @@
+discard """

+  targets: "c cpp"

+  matrix: "--mm:arc; --mm:orc"

+"""

+

+block:

+  type

+    PublicKey = array[32, uint8]

+    PrivateKey = array[64, uint8]

+

+  proc ed25519_create_keypair(publicKey: ptr PublicKey; privateKey: ptr PrivateKey) =

+    publicKey[][0] = uint8(88)

+

+  type

+    KeyPair = object

+      public: PublicKey

+      private: PrivateKey

+

+  proc initKeyPair(): KeyPair =

+    ed25519_create_keypair(result.public.addr, result.private.addr)

+

+  let keys = initKeyPair()

+  doAssert keys.public[0] == 88

+

+

+template minIndexByIt: untyped =

+  var other = 3

+  other

+

+proc bug20303() =

+  var hlibs = @["hello", "world", "how", "are", "you"]

+  let res = hlibs[minIndexByIt()]

+  doAssert res == "are"

+

+bug20303()

+

+proc main() = # todo bug with templates

+  block: # bug #11267

+    var a: seq[char] = block: @[]

+    doAssert a == @[]

+    # 2

+    proc b: seq[string] =

+      discard

+      @[]

+    doAssert b() == @[]

+static: main()

+main()

+

+

+type Obj = tuple

+  value: int

+  arr: seq[int]

+

+proc bug(): seq[Obj] =

+  result.add (value: 0, arr: @[])

+  result[^1].value = 1

+  result[^1].arr.add 1

+

+# bug #19990

+let s = bug()

+doAssert s[0] == (value: 1, arr: @[1])

+

+block: # bug #21974

+  type Test[T] = ref object

+      values : seq[T]

+      counter: int

+

+  proc newTest[T](): Test[T] =

+      result         = new(Test[T])

+      result.values  = newSeq[T](16)

+      result.counter = 0

+

+  proc push[T](self: Test[T], value: T) =

+      self.counter += 1

+      if self.counter >= self.values.len:

+          self.values.setLen(self.values.len * 2)

+      self.values[self.counter - 1] = value

+

+  proc pop[T](self: Test[T]): T =

+      result         = self.values[0]

+      self.values[0] = self.values[self.counter - 1] # <--- This line

+      self.counter  -= 1

+

+

+  type X = tuple

+      priority: int

+      value   : string

+

+  var a = newTest[X]()

+  a.push((1, "One"))

+  doAssert a.pop.value == "One"

+

+# bug #21987

+

+type

+  EmbeddedImage* = distinct Image

+  Image = object

+    len: int

+

+proc imageCopy*(image: Image): Image {.nodestroy.}

+

+proc `=destroy`*(x: Image) =

+  discard

+

+proc `=sink`*(dest: var Image; source: Image) =

+  `=destroy`(dest)

+  wasMoved(dest)

+

+proc `=dup`*(source: Image): Image {.nodestroy.} =

+  result = imageCopy(source)

+

+proc `=copy`*(dest: var Image; source: Image) =

+  dest = imageCopy(source) # calls =sink implicitly

+

+proc `=destroy`*(x: EmbeddedImage) = discard

+

+proc `=dup`*(source: EmbeddedImage): EmbeddedImage {.nodestroy.} = source

+

+proc `=copy`*(dest: var EmbeddedImage; source: EmbeddedImage) {.nodestroy.} =

+  dest = source

+

+proc imageCopy*(image: Image): Image =

+  result = image

+

+proc main2 =

+  block:

+    var a = Image(len: 2).EmbeddedImage

+    var b = Image(len: 1).EmbeddedImage

+    b = a

+    doAssert Image(a).len == 2

+    doAssert Image(b).len == 2

+

+  block:

+    var a = Image(len: 2)

+    var b = Image(len: 1)

+    b = a

+    doAssert a.len == 2

+    doAssert b.len == 0

+

+main2()

+

+type

+  Edge = object

+    neighbor {.cursor.}: Node

+

+  NodeObj = object

+    neighbors: seq[Edge]

+    label: string

+    visited: bool

+  Node = ref NodeObj

+

+  Graph = object

+    nodes: seq[Node]

+

+proc `=destroy`(x: NodeObj) =

+  `=destroy`(x.neighbors)

+  `=destroy`(x.label)

+

+proc addNode(self: var Graph; label: string): Node =

+  self.nodes.add(Node(label: label))

+  result = self.nodes[^1]

+

+proc addEdge(self: Graph; source, neighbor: Node) =

+  source.neighbors.add(Edge(neighbor: neighbor))

+

+block:

+  proc main =

+    var graph: Graph

+    let nodeA = graph.addNode("a")

+    let nodeB = graph.addNode("b")

+    let nodeC = graph.addNode("c")

+

+    graph.addEdge(nodeA, neighbor = nodeB)

+    graph.addEdge(nodeA, neighbor = nodeC)

+

+  main()

+

+block:

+  type RefObj = ref object

+

+  proc `[]`(val: static[int]) = # works with different name/overload or without static arg

+    discard

+

+  template noRef(T: typedesc): typedesc = # works without template indirection

+    typeof(default(T)[])

+

+  proc `=destroy`(x: noRef(RefObj)) =

+    discard

+

+  proc foo =

+    var x = new RefObj

+    doAssert $(x[]) == "()"

+

+  # bug #11705

+  foo()

+

+block: # bug #22197

+  type

+    H5IdObj = object

+    H5Id = ref H5IdObj

+

+    FileID = distinct H5Id

+

+    H5GroupObj = object

+      file_id: FileID

+    H5Group = ref H5GroupObj

+

+  ## This would make it work!

+  #proc `=destroy`*(x: FileID) = `=destroy`(cast[H5Id](x))

+  ## If this does not exist, it also works!

+  proc newFileID(): FileID = FileID(H5Id())

+

+  proc `=destroy`(grp: H5GroupObj) =

+    ## Closes the group and resets all references to nil.

+    if cast[pointer](grp.fileId) != nil:

+      `=destroy`(grp.file_id)

+

+  var grp = H5Group()

+  reset(grp.file_id)

+  reset(grp)

+

+import std/tables

+

+block: # bug #22286

+  type

+    A = object

+    B = object

+      a: A

+    C = object

+      b: B

+

+  proc `=destroy`(self: A) =

+    echo "destroyed"

+

+  proc `=destroy`(self: C) =

+    `=destroy`(self.b)

+

+  var c = C()

+

+block: # https://forum.nim-lang.org/t/10642

+  type AObj = object

+    name: string

+    tableField: Table[string, string]

+

+  proc `=destroy`(x: AObj) =

+    `=destroy`(x.name)

+    `=destroy`(x.tableField)

diff --git a/tests/destructor/topttree.nim b/tests/destructor/topttree.nim
index fa5495689..8cf757e8b 100644
--- a/tests/destructor/topttree.nim
+++ b/tests/destructor/topttree.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''10.0
 60.0
 90.0
diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim
index ef20672d5..eb5588b1a 100644
--- a/tests/destructor/tprevent_assign2.nim
+++ b/tests/destructor/tprevent_assign2.nim
@@ -1,7 +1,7 @@
 discard """
-  errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  errormsg: "'=dup' is not available for type <Foo>, which is inferred from unavailable '=copy'; requires a copy because it's not the last read of 'otherTree'; another read is done here: tprevent_assign2.nim(51, 31); routine: preventThis"
   file: "tprevent_assign2.nim"
-  line: 48
+  line: 49
 """
 
 type
@@ -9,7 +9,8 @@ type
     x: int
 
 proc `=destroy`(f: var Foo) = f.x = 0
-proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+
 proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
 
 proc createTree(x: int): Foo =
@@ -18,7 +19,7 @@ proc createTree(x: int): Foo =
 proc take2(a, b: sink Foo) =
   echo a.x, " ", b.x
 
-proc allowThis() =
+when false:
   var otherTree: Foo
   try:
     for i in 0..3:
@@ -51,5 +52,5 @@ proc preventThis() =
       else:
         discard
 
-allowThis()
+#allowThis()
 preventThis()
diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim
index 0577aa5ff..aa834a66c 100644
--- a/tests/destructor/tprevent_assign3.nim
+++ b/tests/destructor/tprevent_assign3.nim
@@ -1,7 +1,7 @@
 discard """
-  errormsg: "'=copy' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
+  errormsg: "'=dup' is not available for type <Foo>; requires a copy because it's not the last read of 'otherTree'"
   file: "tprevent_assign3.nim"
-  line: 46
+  line: 47
 """
 
 type
@@ -9,7 +9,8 @@ type
     x: int
 
 proc `=destroy`(f: var Foo) = f.x = 0
-proc `=`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=copy`(a: var Foo; b: Foo) {.error.} # = a.x = b.x
+proc `=dup`(a: Foo): Foo {.error.}
 proc `=sink`(a: var Foo; b: Foo) = a.x = b.x
 
 proc createTree(x: int): Foo =
@@ -18,7 +19,7 @@ proc createTree(x: int): Foo =
 proc take2(a, b: sink Foo) =
   echo a.x, " ", b.x
 
-proc allowThis() =
+when false:
   var otherTree: Foo
   try:
     for i in 0..3:
@@ -47,7 +48,7 @@ proc preventThis2() =
   finally:
     echo otherTree
 
-allowThis()
+#allowThis()
 preventThis2()
 
 
diff --git a/tests/destructor/tsink.nim b/tests/destructor/tsink.nim
new file mode 100644
index 000000000..e8750ad7c
--- /dev/null
+++ b/tests/destructor/tsink.nim
@@ -0,0 +1,70 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+type AnObject = object of RootObj
+  value*: int
+
+proc mutate(shit: sink AnObject) =
+  shit.value = 1
+
+proc foo = # bug #23359
+  var bar = AnObject(value: 42)
+  mutate(bar)
+  doAssert bar.value == 42
+
+foo()
+
+block: # bug #23902
+  proc foo(a: sink string): auto = (a, a)
+
+  proc bar(a: sink int): auto = return a
+
+  proc foo(a: sink string) =
+    var x = (a, a)
+
+block: # bug #24175
+  block:
+    func mutate(o: sink string): string =
+      o[1] = '1'
+      result = o
+
+    static:
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    func foo() =
+      let s = "999"
+      let m = mutate(s)
+      doAssert s == "999"
+      doAssert m == "919"
+
+    static:
+      foo()
+    foo()
+
+  block:
+    type O = object
+      a: int
+
+    func mutate(o: sink O): O =
+      o.a += 1
+      o
+
+    static:
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    proc foo() =
+      let x = O(a: 1)
+      let y = mutate(x)
+      doAssert x.a == 1
+      doAssert y.a == 2
+
+    static:
+      foo()
+    foo()
diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim
index ce96b741e..69348d530 100644
--- a/tests/destructor/tuse_ownedref_after_move.nim
+++ b/tests/destructor/tuse_ownedref_after_move.nim
@@ -1,6 +1,6 @@
 discard """
   cmd: '''nim c --newruntime $file'''
-  errormsg: "'=copy' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)"
+  errormsg: "'=copy' is not available for type <owned Button>; requires a copy because it's not the last read of ':envAlt.b1'; routine: main"
   line: 48
 """
 
diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim
index 917cf0eb3..48bdf67dd 100644
--- a/tests/destructor/tv2_cast.nim
+++ b/tests/destructor/tv2_cast.nim
@@ -3,81 +3,51 @@ discard """
 @[116, 101, 115, 116]
 @[1953719668, 875770417]
 destroying O1'''
-  cmd: '''nim c --gc:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file'''
-  nimout: '''--expandArc: main
+  cmd: '''nim c --mm:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file'''
+  nimout: '''
+--expandArc: main
 
 var
   data
   :tmpD
-  :tmpD_1
-  :tmpD_2
-data =
-  wasMoved(:tmpD)
-  `=copy`(:tmpD, cast[string](
-    :tmpD_2 = encode(cast[seq[byte]](
-      :tmpD_1 = newString(100)
-      :tmpD_1))
-    :tmpD_2))
-  :tmpD
-`=destroy`(:tmpD_2)
-`=destroy_1`(:tmpD_1)
-`=destroy_1`(data)
+data = cast[string](encode(cast[seq[byte]](
+  :tmpD = newString(100)
+  :tmpD)))
+`=destroy`(:tmpD)
+`=destroy`(data)
 -- end of expandArc ------------------------
 --expandArc: main1
 
 var
   s
   data
-  :tmpD
-  :tmpD_1
 s = newString(100)
-data =
-  wasMoved(:tmpD)
-  `=copy`(:tmpD, cast[string](
-    :tmpD_1 = encode(toOpenArrayByte(s, 0, len(s) - 1))
-    :tmpD_1))
-  :tmpD
-`=destroy`(:tmpD_1)
-`=destroy_1`(data)
-`=destroy_1`(s)
+data = cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1)))
+`=destroy`(data)
+`=destroy`(s)
 -- end of expandArc ------------------------
 --expandArc: main2
 
 var
   s
   data
-  :tmpD
-  :tmpD_1
 s = newSeq(100)
-data =
-  wasMoved(:tmpD)
-  `=copy`(:tmpD, cast[string](
-    :tmpD_1 = encode(s)
-    :tmpD_1))
-  :tmpD
-`=destroy`(:tmpD_1)
-`=destroy_1`(data)
-`=destroy`(s)
+data = cast[string](encode(s))
+`=destroy`(data)
+`=destroy_1`(s)
 -- end of expandArc ------------------------
 --expandArc: main3
 
 var
   data
   :tmpD
-  :tmpD_1
-  :tmpD_2
-data =
-  wasMoved(:tmpD)
-  `=copy`(:tmpD, cast[string](
-    :tmpD_2 = encode do:
-      :tmpD_1 = newSeq(100)
-      :tmpD_1
-    :tmpD_2))
-  :tmpD
-`=destroy`(:tmpD_2)
-`=destroy`(:tmpD_1)
+data = cast[string](encode do:
+  :tmpD = newSeq(100)
+  :tmpD)
+`=destroy`(:tmpD)
 `=destroy_1`(data)
--- end of expandArc ------------------------'''
+-- end of expandArc ------------------------
+'''
 """
 
 func encode*(src: openArray[byte]): seq[byte] =
diff --git a/tests/destructor/twasmoved.nim b/tests/destructor/twasmoved.nim
new file mode 100644
index 000000000..566322702
--- /dev/null
+++ b/tests/destructor/twasmoved.nim
@@ -0,0 +1,14 @@
+type
+  Foo = object
+    id: int
+
+proc `=wasMoved`(x: var Foo) =
+  x.id = -1
+
+proc foo =
+  var s = Foo(id: 999)
+  var m = move s
+  doAssert s.id == -1
+  doAssert m.id == 999
+
+foo()
diff --git a/tests/destructor/twasmoved_error.nim b/tests/destructor/twasmoved_error.nim
new file mode 100644
index 000000000..1cd57e3df
--- /dev/null
+++ b/tests/destructor/twasmoved_error.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: '''nim c --mm:arc $file'''
+  errormsg: "'=wasMoved' is not available for type <Game>; routine: main"
+"""
+
+# bug #19291
+
+const
+  screenWidth = 800
+  screenHeight = 450
+
+var
+  ready = false
+type
+  Game = object
+
+proc `=destroy`(x: var Game) =
+  assert ready, "Window is already opened"
+  ready = false
+
+proc `=sink`(x: var Game; y: Game) {.error.}
+proc `=copy`(x: var Game; y: Game) {.error.}
+proc `=wasMoved`(x: var Game) {.error.}
+
+proc initGame(width, height: int32, title: string): Game =
+  assert not ready, "Window is already closed"
+  ready = true
+
+proc update(x: Game) = discard
+
+proc main =
+  var g = initGame(screenWidth, screenHeight, "Tetris raylib")
+  g.update()
+  var g2 = g
+  echo "hello"
+
+main()
diff --git a/tests/discard/t23677.nim b/tests/discard/t23677.nim
new file mode 100644
index 000000000..1ed7386bd
--- /dev/null
+++ b/tests/discard/t23677.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "expression '0' is of type 'int literal(0)' and has to be used (or discarded); start of expression here: t23677.nim(1, 1)"
+  line: 10
+  column: 3
+"""
+
+# issue #23677
+
+if true:
+  0
+else: 
+  raise newException(ValueError, "err") 
diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim
index 032050139..84b669ed8 100644
--- a/tests/discard/tdiscardable.nim
+++ b/tests/discard/tdiscardable.nim
@@ -5,6 +5,7 @@ tdiscardable
 1
 something defered
 something defered
+hi
 '''
 """
 
@@ -65,3 +66,110 @@ proc main2() =
 
 main1()
 main2()
+
+block: # bug #13583
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        hello()
+
+    let t = test
+
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        block:
+          yield 12
+          hello()
+
+    let t = test
+    doAssert t() == 12
+
+  block:
+    proc hello(): int {.discardable.} = 12
+
+    iterator test(): int {.closure.} =
+      while true:
+        yield 12
+        hello()
+
+    let t = test
+    doAssert t() == 12
+
+block:
+  proc bar(): string {.discardable.} =
+    "15"
+
+  proc foo(): int =
+    while true:
+      raise newException(ValueError, "check")
+    12
+
+  doAssertRaises(ValueError):
+    doAssert foo() == 12
+
+block: # issue #10440
+  proc x(): int {.discardable.} = discard
+  try:
+    x()
+  finally:
+    echo "hi"
+
+import macros
+
+block: # issue #14665
+  macro test(): untyped =   
+    let b = @[1, 2, 3, 4]
+
+    result = nnkStmtList.newTree()
+    var i = 0
+    while i < b.len:
+      if false:
+        # this quote do is mandatory, removing it fixes the problem
+        result.add quote do:
+          let testtest = 5
+      else:
+        result.add quote do:
+          let test = 6
+        inc i
+        # removing this continue fixes the problem too
+        continue
+      inc i
+  test()
+
+block: # bug #23775
+  proc retInt(): int {.discardable.} =
+    42
+
+  proc retString(): string {.discardable.} =
+    "text"
+
+  type
+    Enum = enum
+      A, B, C, D
+
+  proc doStuff(msg: Enum) =
+    case msg:
+    of A:
+      retString()
+    of B:
+      retInt()
+    of C:
+      discard retString()
+    else:
+      let _ = retString()
+
+  doStuff(C)
+
+block:
+  proc test(): (int, int) {.discardable.} =
+    discard
+
+  if true:
+    test()
+  else:
+    quit()
diff --git a/tests/discard/tfinallyerrmsg.nim b/tests/discard/tfinallyerrmsg.nim
new file mode 100644
index 000000000..fbc8140aa
--- /dev/null
+++ b/tests/discard/tfinallyerrmsg.nim
@@ -0,0 +1,19 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+block: # issue #19672
+  try:
+    10 #[tt.Error
+    ^ expression '10' is of type 'int literal(10)' and has to be used (or discarded); start of expression here: tfinallyerrmsg.nim(5, 1)]#
+  finally:
+    echo "Finally block"
+
+block: # issue #13871
+  template t(body: int) =
+    try:
+      body
+    finally:
+      echo "expression"
+  t: 2 #[tt.Error
+     ^ expression '2' is of type 'int literal(2)' and has to be used (or discarded)]#
diff --git a/tests/discard/tillegaldiscardtypes.nim b/tests/discard/tillegaldiscardtypes.nim
new file mode 100644
index 000000000..b7877bcd2
--- /dev/null
+++ b/tests/discard/tillegaldiscardtypes.nim
@@ -0,0 +1,14 @@
+discard """
+  cmd: "nim check $file"
+  errormsg: "statement returns no value that can be discarded"
+  nimout: '''
+tillegaldiscardtypes.nim(11, 3) Error: statement returns no value that can be discarded
+tillegaldiscardtypes.nim(12, 3) Error: statement returns no value that can be discarded
+'''
+"""
+
+proc b(v: int) = # bug #21360
+  discard @[]
+  discard []
+
+b(0)
\ No newline at end of file
diff --git a/tests/borrow/tborrow.nim b/tests/distinct/tborrow.nim
index 35652e2e0..e34248de5 100644
--- a/tests/borrow/tborrow.nim
+++ b/tests/distinct/tborrow.nim
@@ -88,4 +88,45 @@ block: # Borrow from generic alias
     c = default(C)
     e = default(E)
   assert c.i == int(0)
-  assert e.i == 0d
\ No newline at end of file
+  assert e.i == 0d
+
+block: # issue #22069
+  type
+    Vehicle[C: static[int]] = object
+      color: array[C, int]
+    Car[C: static[int]] {.borrow: `.`.} = distinct Vehicle[C]
+    MuscleCar = Car[128]
+  var x: MuscleCar
+  doAssert x.color is array[128, int]
+
+block: # issue #22646
+  type
+    Vec[N : static[int], T: SomeNumber] = object
+      arr: array[N, T]
+    Vec3[T: SomeNumber] = Vec[3, T]
+
+  proc `[]=`[N,T](v: var Vec[N,T]; ix:int; c:T): void {.inline.} = v.arr[ix] = c
+  proc `[]`[N,T](v: Vec[N,T]; ix: int): T {.inline.} = v.arr[ix]
+
+  proc dot[N,T](u,v: Vec[N,T]): T {. inline .} = discard
+  proc length[N,T](v: Vec[N,T]): T = discard
+  proc cross[T](v1,v2:Vec[3,T]): Vec[3,T] = discard
+  proc normalizeWorks[T](v: Vec[3,T]): Vec[3,T] = discard ## <- Explicit size makes it work!
+  proc foo[N,T](u, v: Vec[N,T]): Vec[N,T] = discard ## <- broken
+  proc normalize[N,T](v: Vec[N,T]): Vec[N,T] = discard ## <- broken
+
+  type Color = distinct Vec3[float]
+
+  template borrowOps(typ: typedesc): untyped =
+    proc `[]=`(v: var typ; ix: int; c: float): void {.borrow.}
+    proc `[]`(v: typ; ix: int): float {.borrow.}
+    proc dot(v, u: typ): float {.borrow.}
+    proc cross(v, u: typ): typ {.borrow.}
+    proc length(v: typ): float {.borrow.}
+    proc normalizeWorks(v: typ): typ {.borrow.} ## Up to here everything works
+    proc foo(u, v: typ): typ {.borrow.} ## Broken
+    proc normalize(v: typ): typ {.borrow.} ## Broken
+  borrowOps(Color)
+  var x: Vec[3, float]
+  let y = Color(x)
+  doAssert Vec3[float](y) == x
diff --git a/tests/distinct/tcomplexaddressableconv.nim b/tests/distinct/tcomplexaddressableconv.nim
new file mode 100644
index 000000000..00e96bfeb
--- /dev/null
+++ b/tests/distinct/tcomplexaddressableconv.nim
@@ -0,0 +1,21 @@
+# issue #22523
+
+from std/typetraits import distinctBase
+
+type
+  V[p: static int] = distinct int
+  D[p: static int] = distinct int
+  T = V[1]
+
+proc f(y: var T) = discard
+
+var a: D[0]
+
+static:
+  doAssert distinctBase(T) is distinctBase(D[0])
+  doAssert distinctBase(T) is int
+  doAssert distinctBase(D[0]) is int
+  doAssert T(a) is T
+
+f(cast[ptr T](addr a)[])
+f(T(a))
diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim
index 8ec083020..b6ba7aa99 100644
--- a/tests/distinct/tdistinct.nim
+++ b/tests/distinct/tdistinct.nim
@@ -159,7 +159,7 @@ block: #17322
 
 type Foo = distinct string
 
-template main() =
+proc main() = # proc instead of template because of MCS/UFCS.
   # xxx put everything here to test under RT + VM
   block: # bug #12282
     block:
@@ -199,5 +199,29 @@ template main() =
       var c: B
 
 
+  block: # bug #9423
+    block:
+      type Foo = seq[int]
+      type Foo2 = distinct Foo
+      template fn() =
+        var a = Foo2(@[1])
+        a.Foo.add 2
+        doAssert a.Foo == @[1, 2]
+      fn()
+
+    block:
+      type Stack[T] = distinct seq[T]
+      proc newStack[T](): Stack[T] =
+        Stack[T](newSeq[T]())
+      proc push[T](stack: var Stack[T], elem: T) =
+        seq[T](stack).add(elem)
+      proc len[T](stack: Stack[T]): int =
+        seq[T](stack).len
+      proc fn() = 
+        var stack = newStack[int]()
+        stack.push(5)
+        doAssert stack.len == 1
+      fn()
+
 static: main()
 main()
diff --git a/tests/distinct/tinvalidborrow.nim b/tests/distinct/tinvalidborrow.nim
new file mode 100644
index 000000000..d4b19fa8d
--- /dev/null
+++ b/tests/distinct/tinvalidborrow.nim
@@ -0,0 +1,42 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tinvalidborrow.nim(25, 3) Error: only a 'distinct' type can borrow `.`
+tinvalidborrow.nim(26, 3) Error: only a 'distinct' type can borrow `.`
+tinvalidborrow.nim(27, 1) Error: borrow proc without distinct type parameter is meaningless
+tinvalidborrow.nim(36, 1) Error: borrow with generic parameter is not supported
+tinvalidborrow.nim(41, 1) Error: borrow from proc return type mismatch: 'T'
+tinvalidborrow.nim(42, 1) Error: borrow from '[]=' is not supported
+'''
+"""
+
+
+
+
+
+# bug #516
+
+type
+  TAtom = culong
+  Test {.borrow:`.`.} = distinct int
+  Foo[T] = object
+    a: int
+  Bar[T] {.borrow:`.`.} = Foo[T]
+  OtherFoo {.borrow:`.`.} = Foo[int]
+proc `==`*(a, b: TAtom): bool {.borrow.}
+
+var
+  d, e: TAtom
+
+discard( $(d == e) )
+
+# issue #4121
+type HeapQueue[T] = distinct seq[T]
+proc len*[T](h: HeapQueue[T]): int {.borrow.}
+
+# issue #3564
+type vec4[T] = distinct array[4, float32]
+
+proc `[]`(v: vec4, i: int): float32 {.borrow.}
+proc `[]=`(v: vec4, i: int, va: float32) {.borrow.}
diff --git a/tests/borrow/typeclassborrow.nim b/tests/distinct/typeclassborrow.nim
index 62cb77d67..5e0c63953 100644
--- a/tests/borrow/typeclassborrow.nim
+++ b/tests/distinct/typeclassborrow.nim
@@ -1,3 +1,5 @@
+import std/tables
+
 type
   Foo = distinct seq[int]
   Bar[N: static[int]] = distinct seq[int]
@@ -29,4 +31,26 @@ baz.doThing()
 
 assert $seq[int](foo) == $foo
 assert $seq[int](bar) == $bar
-assert $seq[int](baz) == $baz
\ No newline at end of file
+assert $seq[int](baz) == $baz
+
+type
+  Fine* = distinct string
+
+proc `==`*(x, y: Fine): bool {.borrow.} =
+  ## Here is the documentation
+  runnableExamples:
+    var x = Fine("1234")
+    var y = Fine("1234")
+    doAssert x == y
+  doAssert false
+
+
+var x = Fine("1234")
+var y = Fine("1234")
+doAssert x == y
+
+block: # bug #22902
+  type
+    DistinctTable = distinct Table[int, int]
+
+  proc `[]`(t: DistinctTable; key: int): lent int {.borrow.}
diff --git a/tests/dll/nimhcr_basic.nim b/tests/dll/nimhcr_basic.nim
new file mode 100644
index 000000000..2e1f39ae0
--- /dev/null
+++ b/tests/dll/nimhcr_basic.nim
@@ -0,0 +1,8 @@
+discard """
+  output: '''
+Hello world
+'''
+"""
+# for now orc only tests successful compilation
+
+echo "Hello world"
diff --git a/tests/dll/nimhcr_unit.nim b/tests/dll/nimhcr_unit.nim
index 0b924bdf7..249f3f9f1 100644
--- a/tests/dll/nimhcr_unit.nim
+++ b/tests/dll/nimhcr_unit.nim
@@ -106,14 +106,14 @@ macro carryOutTests(callingConv: untyped): untyped =
       echo `procName`, " implementation #1 ", x
       return x + 1
 
-    let fp1 = cast[F](hcrRegisterProc("dummy_module", `procName`, `p1`))
+    let fp1 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p1`)))
     echo fp1(10)
 
     proc `p2`(x: int): int {.placeholder.} =
       echo `procName`, " implementation #2 ", x
       return x + 2
 
-    let fp2 = cast[F](hcrRegisterProc("dummy_module", `procName`, `p2`))
+    let fp2 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p2`)))
     echo fp1(20)
     echo fp2(20)
 
@@ -121,7 +121,7 @@ macro carryOutTests(callingConv: untyped): untyped =
       echo `procName`, " implementation #3 ", x
       return x + 3
 
-    let fp3 = cast[F](hcrRegisterProc("dummy_module", `procName`, `p3`))
+    let fp3 = cast[F](hcrRegisterProc("dummy_module", `procName`, cast[pointer](`p3`)))
     echo fp1(30)
     echo fp2(30)
     echo fp3(30)
diff --git a/tests/effects/tdiagnostic_messages.nim b/tests/effects/tdiagnostic_messages.nim
index 2ce4895a3..b1acf8c5c 100644
--- a/tests/effects/tdiagnostic_messages.nim
+++ b/tests/effects/tdiagnostic_messages.nim
@@ -12,7 +12,7 @@ tdiagnostic_messages.nim(36, 6) Error: 'a' can have side effects
 >>> tdiagnostic_messages.nim(32, 33) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaPointer'
 >>>> tdiagnostic_messages.nim(27, 6) Hint: 'indirectCallViaPointer' called by 'callWithSideEffects'
 >>>>> tdiagnostic_messages.nim(28, 32) Hint: 'indirectCallViaPointer' calls routine via pointer indirection
->>> tdiagnostic_messages.nim(33, 10) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho'
+>>> tdiagnostic_messages.nim(33, 3) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho'
 >>>> tdiagnostic_messages.nim(24, 6) Hint: 'myEcho' called by 'callWithSideEffects'
 >>> tdiagnostic_messages.nim(34, 3) Hint: 'callWithSideEffects' accesses global state 'globalVar'
 >>>> tdiagnostic_messages.nim(23, 5) Hint: 'globalVar' accessed by 'callWithSideEffects'
diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim
index ca9021999..1d267b5fa 100644
--- a/tests/effects/teffects1.nim
+++ b/tests/effects/teffects1.nim
@@ -17,11 +17,16 @@ proc forw: int {. .}
 proc lier(): int {.raises: [IO2Error].} = #[tt.Hint
                             ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]#
   writeLine stdout, "arg" #[tt.Error
-            ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]#
+  ^ writeLine stdout, ["arg"] can raise an unlisted exception: ref IOError ]#
 
 proc forw: int =
   raise newException(IOError, "arg")
 
+block:
+  proc someProc(t: string) {.raises: [Defect].} =
+    discard
+  let vh: proc(topic: string) {.raises: [].} = someProc
+
 {.push raises: [Defect].}
 
 type
@@ -34,7 +39,7 @@ proc foo(x: int): string {.nimcall, raises: [ValueError].} =
 
 var p: MyProcType = foo #[tt.Error
                     ^
-type mismatch: got <proc (x: int): string{.nimcall, noSideEffect, gcsafe, locks: 0.}> but expected 'MyProcType = proc (x: int): string{.closure.}'
+type mismatch: got <proc (x: int): string{.nimcall, raises: [ValueError], noSideEffect, gcsafe.}> but expected 'MyProcType = proc (x: int): string{.closure.}'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
 .raise effects differ
 ]#
diff --git a/tests/effects/teffects11.nim b/tests/effects/teffects11.nim
index e4098e959..20b7026ab 100644
--- a/tests/effects/teffects11.nim
+++ b/tests/effects/teffects11.nim
@@ -1,6 +1,6 @@
 discard """
 action: compile
-errormsg: "type mismatch: got <proc (x: int){.gcsafe, locks: 0.}>"
+errormsg: "type mismatch: got <proc (x: int){.gcsafe.}>"
 line: 21
 """
 
diff --git a/tests/effects/teffects19.nim b/tests/effects/teffects19.nim
index 49fb87523..6b4ab0819 100644
--- a/tests/effects/teffects19.nim
+++ b/tests/effects/teffects19.nim
@@ -1,6 +1,6 @@
 discard """
 action: compile
-errormsg: "type mismatch: got <proc (i: int){.gcsafe, locks: 0.}>"
+errormsg: "type mismatch: got <proc (i: int){.gcsafe.}>"
 line: 23
 """
 
diff --git a/tests/effects/teffects6.nim b/tests/effects/teffects6.nim
index 4a39e0dca..d3af22434 100644
--- a/tests/effects/teffects6.nim
+++ b/tests/effects/teffects6.nim
@@ -21,7 +21,7 @@ createMenuItem(s, "Go to definition...",
 )
 
 
-proc noRaise(x: proc()) {.raises: [].} =
+proc noRaise(x: proc()) {.raises: [], effectsOf: x.} =
   # unknown call that might raise anything, but valid:
   x()
 
diff --git a/tests/effects/teffectsmisc.nim b/tests/effects/teffectsmisc.nim
new file mode 100644
index 000000000..8fb95b275
--- /dev/null
+++ b/tests/effects/teffectsmisc.nim
@@ -0,0 +1,39 @@
+discard """
+  output: '''
+printing from adder
+'''
+"""
+
+import std/sugar
+
+block:
+  proc makeAdder(a: int): (int) -> void =
+    proc discard_adder(x: int) {.closure.} =
+      discard a + x
+
+    proc echo_adder(x: int) {.closure.} =
+      echo("printing from adder")
+
+    if a > 0:
+      discard_adder
+    else:
+      echo_adder
+
+  let newAdder = makeAdder(0)
+  newAdder(5)
+
+block:
+  proc makeAdder(a: int): (int) -> void =
+    proc discard_adder(x: int) {.closure.} =
+      discard a + x
+
+    proc echo_adder(x: int) {.closure.} =
+      echo("printing from adder")
+
+    if a > 0:
+      echo_adder
+    else:
+      discard_adder
+
+  let newAdder = makeAdder(0)
+  newAdder(5)
diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim
index 8784cbbb1..9934d27a7 100644
--- a/tests/effects/tfuncs_cannot_mutate.nim
+++ b/tests/effects/tfuncs_cannot_mutate.nim
@@ -1,9 +1,6 @@
 discard """
-  errormsg: "'mutate' can have side effects"
-  nimout: '''an object reachable from 'n' is potentially mutated
-tfuncs_cannot_mutate.nim(39, 15) the mutation is here
-tfuncs_cannot_mutate.nim(37, 7) is the statement that connected the mutation to the parameter
-'''
+  errormsg: "cannot mutate location select(x, z).data within a strict func"
+  line: 35
 """
 
 {.experimental: "strictFuncs".}
@@ -25,8 +22,7 @@ func len(n: Node): int =
     it = it.ri
 
 func doNotDistract(n: Node) =
-  var m = Node()
-  m.data = "abc"
+  var m = Node(data: "abc")
 
 func select(a, b: Node): Node = b
 
diff --git a/tests/effects/tfuncs_cannot_mutate2.nim b/tests/effects/tfuncs_cannot_mutate2.nim
index d5082e57b..86f811017 100644
--- a/tests/effects/tfuncs_cannot_mutate2.nim
+++ b/tests/effects/tfuncs_cannot_mutate2.nim
@@ -1,9 +1,6 @@
 discard """
-  errormsg: "'copy' can have side effects"
-  nimout: '''an object reachable from 'y' is potentially mutated
-tfuncs_cannot_mutate2.nim(15, 7) the mutation is here
-tfuncs_cannot_mutate2.nim(13, 10) is the statement that connected the mutation to the parameter
-'''
+  errormsg: "cannot mutate location x[0].a within a strict func"
+  line: 12
 """
 
 {.experimental: "strictFuncs".}
diff --git a/tests/effects/tfuncs_cannot_mutate3.nim b/tests/effects/tfuncs_cannot_mutate3.nim
new file mode 100644
index 000000000..029152029
--- /dev/null
+++ b/tests/effects/tfuncs_cannot_mutate3.nim
@@ -0,0 +1,35 @@
+discard """
+  errormsg: "cannot mutate location kid.parent within a strict func"
+  line: 16
+"""
+
+{.experimental: "strictFuncs".}
+
+type
+  Node = ref object
+    name: string
+    kids: seq[Node]
+    parent: Node
+
+func initParents(tree: Node) =
+  for kid in tree.kids:
+    kid.parent = tree
+    initParents(kid)
+
+proc process(intro: Node): Node =
+  var tree = Node(name: "root", kids: @[
+    intro,
+    Node(name: "one", kids: @[
+      Node(name: "two"),
+      Node(name: "three"),
+    ]),
+    Node(name: "four"),
+  ])
+  initParents(tree)
+
+proc main() =
+  var intro = Node(name: "intro")
+  var tree = process(intro)
+  echo intro.parent.name
+
+main()
diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim
index a94a8d746..0ae4a0db9 100644
--- a/tests/effects/tfuncs_cannot_mutate_simple.nim
+++ b/tests/effects/tfuncs_cannot_mutate_simple.nim
@@ -1,7 +1,6 @@
 discard """
-  errormsg: "'edit' can have side effects"
-  nimout: '''an object reachable from 'x' is potentially mutated
-tfuncs_cannot_mutate_simple.nim(16, 4) the mutation is here'''
+  errormsg: '''cannot mutate location x.data within a strict func'''
+  line: 15
 """
 
 {.experimental: "strictFuncs".}
diff --git a/tests/effects/tgcsafe.nim b/tests/effects/tgcsafe.nim
index 363624f19..cfac3ddd8 100644
--- a/tests/effects/tgcsafe.nim
+++ b/tests/effects/tgcsafe.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "'mainUnsafe' is not GC-safe"
+  errormsg: "'mainUnsafe' is not GC-safe as it performs an indirect call here"
   line: 26
   cmd: "nim $target --hints:on --threads:on $options $file"
 """
@@ -13,7 +13,7 @@ proc myproc(i: int) {.gcsafe.} =
   if isNil(global_proc):
     return
 
-proc mymap(x: proc ()) =
+proc mymap(x: proc ()) {.effectsOf: x.} =
   x()
 
 var
diff --git a/tests/effects/tgcsafe2.nim b/tests/effects/tgcsafe2.nim
index 07da4e3f8..6268592a9 100644
--- a/tests/effects/tgcsafe2.nim
+++ b/tests/effects/tgcsafe2.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: '''type mismatch: got <proc (s: string){.locks: 0.}>'''
+  errormsg: '''type mismatch: got <proc (s: string)>'''
   line: 11
 """
 #5620
diff --git a/tests/effects/thooks.nim b/tests/effects/thooks.nim
new file mode 100644
index 000000000..23cc005cd
--- /dev/null
+++ b/tests/effects/thooks.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--warningAsError:Effect"
+"""
+
+import std/isolation
+
+# bug #23129
+type
+  Thing = object
+    x: string
+
+proc send(x: string) =
+  let wrapper = Thing(x: x)
+  discard isolate(wrapper)
+
+send("la")
\ No newline at end of file
diff --git a/tests/effects/tlaxeffects.nim b/tests/effects/tlaxeffects.nim
new file mode 100644
index 000000000..7eedc372a
--- /dev/null
+++ b/tests/effects/tlaxeffects.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim $target $options --legacy:laxEffects $file"
+"""
+
+
+type
+  Foo = object
+    bar: seq[Foo]
+
+proc `==`(a, b: Foo): bool =
+  a.bar == b.bar
diff --git a/tests/effects/tnosideeffect.nim b/tests/effects/tnosideeffect.nim
index 9cabb35a2..9fc2f74d4 100644
--- a/tests/effects/tnosideeffect.nim
+++ b/tests/effects/tnosideeffect.nim
@@ -1,5 +1,5 @@
 block: # `.noSideEffect`
-  func foo(bar: proc(): int): int = bar()
+  func foo(bar: proc(): int): int {.effectsOf: bar.} = bar()
   var count = 0
   proc fn1(): int = 1
   proc fn2(): int = (count.inc; count)
diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim
deleted file mode 100644
index 1126aa77e..000000000
--- a/tests/effects/toutparam.nim
+++ /dev/null
@@ -1,28 +0,0 @@
-discard """
-  cmd: '''nim c --warningAsError:Uninit:on --skipCfg --skipParentCfg $file'''
-  errormsg: "use explicit initialization of 'x' for clarity [Uninit]"
-  line: 24
-  disabled: "true"
-"""
-
-proc gah[T](x: out T) =
-  x = 3
-
-proc main =
-  var a: array[2, int]
-  var x: int
-  gah(x)
-  a[0] = 3
-  a[x] = 3
-  echo x
-
-main()
-
-proc mainB =
-  var a: array[2, int]
-  var x: int
-  a[0] = 3
-  a[x] = 3
-  echo x
-
-mainB()
diff --git a/tests/effects/tstrict_caseobjects.nim b/tests/effects/tstrict_caseobjects.nim
new file mode 100644
index 000000000..20bc810e0
--- /dev/null
+++ b/tests/effects/tstrict_caseobjects.nim
@@ -0,0 +1,47 @@
+discard """
+  errormsg: "field access outside of valid case branch: x.x"
+  line: 45
+"""
+
+{.experimental: "strictCaseObjects".}
+
+type
+  NodeKind = enum
+    nkParent,
+    nkChild
+  
+  Node {.acyclic.} = ref object
+    case kind: NodeKind
+    of nkParent:
+      children: seq[Node]
+    of nkChild:
+      name: string
+
+let list = @[Node(kind: nkParent, children: @[]), Node(kind: nkChild, name: "hello")]
+for node in list:
+  case node.kind
+  of nkChild: 
+    echo $node.name # here this time there is a warning
+  else: discard
+
+
+type
+  Foo = object
+    case b: bool
+    of false:
+      s: string
+    of true:
+      x: int
+
+var x = Foo(b: true, x: 4)
+case x.b
+of true:
+  echo x.x
+of false:
+  echo "no"
+
+case x.b
+of false:
+  echo x.x
+of true:
+  echo "no"
diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim
index 027b46474..0d98a0343 100644
--- a/tests/effects/tstrict_effects3.nim
+++ b/tests/effects/tstrict_effects3.nim
@@ -44,3 +44,14 @@ proc fail() = discard
 f1()
 f2()
 
+import std/json
+
+# bug #22254
+proc senri(a, b: seq[JsonNode]) {.raises: [].} = discard a == b
+
+# bug #22253
+proc serika() {.raises: [].} = discard default(JsonNode) == nil
+
+senri(@[newJBool(true)], @[newJBool(false)])
+serika()
+
diff --git a/tests/effects/tstrict_funcs_imports.nim b/tests/effects/tstrict_funcs_imports.nim
index 264771a1a..bf68b61b2 100644
--- a/tests/effects/tstrict_funcs_imports.nim
+++ b/tests/effects/tstrict_funcs_imports.nim
@@ -7,11 +7,19 @@ discard """
 when defined(linux):
   import linenoise
 
+when defined(nimPreviewSlimSystem):
+  import std/[
+    assertions,
+    formatfloat,
+    objectdollar,
+    syncio,
+    widestrs,
+  ]
+
 import
   algorithm,
   asyncdispatch,
   asyncfile,
-  asyncftpclient,
   asyncfutures,
   asynchttpserver,
   asyncmacro,
@@ -31,11 +39,6 @@ import
   cpuload,
   critbits,
   cstrutils,
-  db_common,
-  db_mysql,
-  db_odbc,
-  db_postgres,
-  db_sqlite,
   deques,
   distros,
   dynlib,
@@ -63,7 +66,6 @@ import
   macros,
   marshal,
   math,
-  md5,
   memfiles,
   mersenne,
   mimetypes,
@@ -87,7 +89,6 @@ import
   pegs,
   posix_utils,
   prelude,
-  punycode,
   random,
   rationals,
   rdstdin,
@@ -102,7 +103,6 @@ import
   sets,
   sharedlist,
   sharedtables,
-  smtp,
   ssl_certs,
   ssl_config,
   stats,
@@ -154,7 +154,6 @@ import std/[
   monotimes,
   packedsets,
   setutils,
-  sha1,
   socketstreams,
   stackframes,
   sums,
diff --git a/tests/effects/tstrictfuncs_misc.nim b/tests/effects/tstrictfuncs_misc.nim
new file mode 100644
index 000000000..8c573bb3a
--- /dev/null
+++ b/tests/effects/tstrictfuncs_misc.nim
@@ -0,0 +1,65 @@
+discard """
+  action: compile
+"""
+
+{.experimental: "strictFuncs".}
+
+func sortedFake1[T](a: openArray[T]): seq[T] =
+  for i in 0 .. a.high: result.add a[i]
+func sortedFake2[T](a: openArray[T]): seq[T] =
+  result = newSeq[T](a.len)
+  for i in 0 .. a.high: result[i] = a[i]
+type Foo1 = object
+type Foo2 = ref object
+block:
+  let a1 = sortedFake1([Foo1()]) # ok
+  let a2 = sortedFake1([Foo2()]) # ok
+block:
+  let a1 = sortedFake2([Foo1()]) # ok
+  let a2 = sortedFake2([Foo2()]) # error: Error: 'sortedFake2' can have side effects
+
+
+import std/sequtils
+type Foob = ref object
+  x: int
+let a1 = zip(@[1,2], @[1,2]) # ok
+let a2 = zip(@[Foob(x: 1)], @[Foob(x: 2)]) # error in 1.6.0 RC2, but not 1.4.x
+
+
+# bug #20863
+type
+  Fooc = ref object
+
+func twice(foo: Fooc) =
+  var a = newSeq[Fooc](2)
+  a[0] = foo # No error.
+  a[1] = foo # Error: 'twice' can have side effects.
+
+let foo = Fooc()
+twice(foo)
+
+# bug #17387
+import json
+
+func parseColumn(columnNode: JsonNode) =
+  let columnName = columnNode["name"].str
+
+parseColumn(%*{"a": "b"})
+
+type
+  MyTable = object
+    data: seq[int]
+
+  JsonNode3 = ref object
+    fields: MyTable
+
+proc `[]`(t: MyTable, key: string): int =
+  result = t.data[0]
+
+proc `[]`(x: JsonNode3, key: string): int =
+  result = x.fields[key]
+
+func parseColumn(columnNode: JsonNode3) =
+  var columnName = columnNode["test"]
+
+parseColumn(JsonNode3())
diff --git a/tests/enum/t21863.nim b/tests/enum/t21863.nim
new file mode 100644
index 000000000..d0d8b1fcd
--- /dev/null
+++ b/tests/enum/t21863.nim
@@ -0,0 +1,28 @@
+discard """
+cmd: "nim check --hints:off $file"
+action: reject
+nimout: '''
+t21863.nim(28, 16) Error: undeclared field: 'A' 
+  found 'A' [enumField declared in t21863.nim(24, 18)]
+  found 'A' [enumField declared in t21863.nim(25, 18)]
+t21863.nim(28, 16) Error: undeclared field: '.'
+t21863.nim(28, 16) Error: undeclared field: '.'
+t21863.nim(28, 16) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+
+
+
+
+
+
+
+
+block:
+  type
+    EnumA = enum A, B
+    EnumB = enum A
+    EnumC = enum C
+
+  discard EnumC.A
diff --git a/tests/enum/tambiguousoverloads.nim b/tests/enum/tambiguousoverloads.nim
new file mode 100644
index 000000000..12c78c848
--- /dev/null
+++ b/tests/enum/tambiguousoverloads.nim
@@ -0,0 +1,26 @@
+discard """
+cmd: "nim check --hints:off $file"
+"""
+
+block: # bug #21887
+  type
+    EnumA = enum A = 300, B
+    EnumB = enum A = 10
+    EnumC = enum C
+
+  doAssert typeof(EnumC(A)) is EnumC #[tt.Error
+                        ^ ambiguous identifier: 'A' -- use one of the following:
+  EnumA.A: EnumA
+  EnumB.A: EnumB]#
+
+block: # issue #22598
+  type
+    A = enum
+      red
+    B = enum
+      red
+
+  let a = red #[tt.Error
+          ^ ambiguous identifier: 'red' -- use one of the following:
+  A.red: A
+  B.red: B]#
diff --git a/tests/enum/tcrossmodule.nim b/tests/enum/tcrossmodule.nim
index 1e97fd1ee..c21072198 100644
--- a/tests/enum/tcrossmodule.nim
+++ b/tests/enum/tcrossmodule.nim
@@ -8,3 +8,8 @@ template t =
   doAssert some(Success)
 
 t()
+
+block: # legacy support for behavior before overloadableEnums
+  # warning: ambiguous enum field 'Success' assumed to be of type MyEnum
+  let x = {Success}
+  doAssert x is set[MyEnum]
diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim
index 37383890c..a03019c5d 100644
--- a/tests/enum/tenum.nim
+++ b/tests/enum/tenum.nim
@@ -155,11 +155,113 @@ block nonzero: # bug #6959
     C
   let slice = SomeEnum.low..SomeEnum.high
 
-block size_one_byte: #issue 15752
+block size_one_byte: # bug #15752
   type
     Flag = enum
       Disabled = 0x00
       Enabled = 0xFF
 
   static:
-    assert 1 == sizeof(Flag)
\ No newline at end of file
+    assert 1 == sizeof(Flag)
+
+block: # bug #12589
+  when not defined(i386):
+    type
+      OGRwkbGeometryType {.size: sizeof(cuint).} = enum
+        wkbPoint25D = 0x80000001.cuint, wkbLineString25D = 0x80000002,
+        wkbPolygon25D = 0x80000003
+
+    proc typ(): OGRwkbGeometryType =
+      return wkbPoint25D
+
+    when not defined(gcRefc):
+      doAssert $typ() == "wkbPoint25D"
+
+    block: # bug #21280
+      type
+        Test = enum
+          B = 19
+          A = int64.high()
+
+      doAssert ord(A) == int64.high()
+
+import std/enumutils
+from std/sequtils import toSeq
+import std/macros
+
+block: # unordered enum
+  block:
+    type
+      unordered_enum = enum
+        a = 1
+        b = 0
+
+    doAssert (ord(a), ord(b)) == (1, 0)
+    doAssert unordered_enum.toSeq == @[a, b]
+
+  block:
+    type
+      unordered_enum = enum
+        a = 1
+        b = 0
+        c
+
+    doAssert (ord(a), ord(b), ord(c)) == (1, 0, 2)
+
+  block:
+    type
+      unordered_enum = enum
+        a = 100
+        b
+        c = 50
+        d
+
+    doAssert (ord(a), ord(b), ord(c), ord(d)) == (100, 101, 50, 51)
+
+  block:
+    type
+      unordered_enum = enum
+        a = 7
+        b = 6
+        c = 5
+        d
+
+    doAssert (ord(a), ord(b), ord(c), ord(d)) == (7, 6, 5, 8)
+    doAssert unordered_enum.toSeq == @[a, b, c, d]
+
+  block:
+    type
+      unordered_enum = enum
+        a = 100
+        b
+        c = 500
+        d
+        e
+        f = 50
+        g
+        h
+
+    doAssert (ord(a), ord(b), ord(c), ord(d), ord(e), ord(f), ord(g), ord(h)) == 
+             (100, 101, 500, 501, 502, 50, 51, 52)
+
+  block:
+    type
+      unordered_enum = enum
+        A
+        B
+        C = -1
+        D
+        E
+        G = -999
+
+    doAssert (ord(A), ord(B), ord(C), ord(D), ord(E), ord(G)) ==
+             (0, 1, -1, 2, 3, -999)
+
+  block:
+    type
+      SomeEnum = enum
+        seA = 3
+        seB = 2
+        seC = "foo"
+
+    doAssert (ord(seA), ord(seB), ord(seC)) == (3, 2, 4)
diff --git a/tests/enum/tenum_duplicate.nim b/tests/enum/tenum_duplicate.nim
new file mode 100644
index 000000000..4bcad7f6f
--- /dev/null
+++ b/tests/enum/tenum_duplicate.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "duplicate value in enum 'd'"
+"""
+
+type
+  unordered_enum = enum
+    a = 1
+    b = 0
+    c
+    d = 2
diff --git a/tests/enum/tenum_invalid.nim b/tests/enum/tenum_invalid.nim
new file mode 100644
index 000000000..8ae0a1057
--- /dev/null
+++ b/tests/enum/tenum_invalid.nim
@@ -0,0 +1,8 @@
+discard """
+cmd: "nim check $file"
+"""
+
+type
+  Test = enum
+    A = 9.0 #[tt.Error
+        ^ ordinal type expected; given: float]#
diff --git a/tests/enum/toverloadedname.nim b/tests/enum/toverloadedname.nim
new file mode 100644
index 000000000..d11b0fb83
--- /dev/null
+++ b/tests/enum/toverloadedname.nim
@@ -0,0 +1,7 @@
+block: # issue #23998
+  type
+    Enum {.pure.} = enum
+      a
+    Obj = object
+      a: Enum
+  proc test(a: Enum) = discard Obj(a: a)
diff --git a/tests/enum/tpure_enums_conflict.nim b/tests/enum/tpure_enums_conflict.nim
index 3c7528a72..3cf335440 100644
--- a/tests/enum/tpure_enums_conflict.nim
+++ b/tests/enum/tpure_enums_conflict.nim
@@ -1,6 +1,5 @@
 discard """
-  errormsg: "ambiguous identifier: 'amb'"
-  line: 19
+  matrix: "-d:testsConciseTypeMismatch"
 """
 
 # bug #8066
@@ -16,4 +15,13 @@ when true:
 
   echo valueA # MyEnum.valueA
   echo MyEnum.amb # OK.
-  echo amb    # Error: Unclear whether it's MyEnum.amb or OtherEnum.amb
+  echo amb #[tt.Error
+  ^ type mismatch
+Expression: echo amb
+  [1] amb: MyEnum | OtherEnum
+
+Expected one of (first mismatch at [position]):
+[1] proc echo(x: varargs[typed, `$$`])
+  ambiguous identifier: 'amb' -- use one of the following:
+    MyEnum.amb: MyEnum
+    OtherEnum.amb: OtherEnum]#
diff --git a/tests/enum/tpure_enums_conflict_legacy.nim b/tests/enum/tpure_enums_conflict_legacy.nim
new file mode 100644
index 000000000..e592925bc
--- /dev/null
+++ b/tests/enum/tpure_enums_conflict_legacy.nim
@@ -0,0 +1,25 @@
+# bug #8066
+
+when true:
+  type
+    MyEnum {.pure.} = enum
+      valueA, valueB, valueC, valueD, amb
+
+    OtherEnum {.pure.} = enum
+      valueX, valueY, valueZ, amb
+
+
+  echo valueA # MyEnum.valueA
+  echo MyEnum.amb # OK.
+  echo amb #[tt.Error
+  ^ type mismatch: got <MyEnum | OtherEnum>
+but expected one of:
+proc echo(x: varargs[typed, `$$`])
+  first type mismatch at position: 1
+  required type for x: varargs[typed]
+  but expression 'amb' is of type: None
+  ambiguous identifier: 'amb' -- use one of the following:
+    MyEnum.amb: MyEnum
+    OtherEnum.amb: OtherEnum
+
+expression: echo amb]#
diff --git a/tests/enum/tredefinition.nim b/tests/enum/tredefinition.nim
new file mode 100644
index 000000000..615ca6b1c
--- /dev/null
+++ b/tests/enum/tredefinition.nim
@@ -0,0 +1,9 @@
+discard """
+  cmd: '''nim check --hints:off $file'''
+  action: reject
+nimout: '''
+tredefinition.nim(9, 25) Error: redefinition of 'Key_a'; previous declaration here: tredefinition.nim(9, 18)
+'''
+"""
+
+type Key* = enum Key_A, Key_a
\ No newline at end of file
diff --git a/tests/enum/ttypenameconflict.nim b/tests/enum/ttypenameconflict.nim
new file mode 100644
index 000000000..b13bf00ce
--- /dev/null
+++ b/tests/enum/ttypenameconflict.nim
@@ -0,0 +1,13 @@
+# issue #23689
+
+type
+  MyEnum {.pure.} = enum
+    A, B, C, D
+
+  B = object
+    field: int
+
+let x: MyEnum = B
+doAssert $x == "B"
+doAssert typeof(x) is MyEnum
+doAssert x in {A, B}
diff --git a/tests/errmsgs/mambparam1.nim b/tests/errmsgs/mambparam1.nim
new file mode 100644
index 000000000..1a5133c3c
--- /dev/null
+++ b/tests/errmsgs/mambparam1.nim
@@ -0,0 +1 @@
+const test* = "foo"
diff --git a/tests/errmsgs/mambparam2.nim b/tests/errmsgs/mambparam2.nim
new file mode 100644
index 000000000..073e3f8c8
--- /dev/null
+++ b/tests/errmsgs/mambparam2.nim
@@ -0,0 +1,2 @@
+import mambparam1
+export test
diff --git a/tests/errmsgs/mambparam3.nim b/tests/errmsgs/mambparam3.nim
new file mode 100644
index 000000000..5469244e2
--- /dev/null
+++ b/tests/errmsgs/mambparam3.nim
@@ -0,0 +1 @@
+const test* = "bar"
diff --git a/tests/clearmsg/mb.nim b/tests/errmsgs/mb.nim
index 2d21e2396..2d21e2396 100644
--- a/tests/clearmsg/mb.nim
+++ b/tests/errmsgs/mb.nim
diff --git a/tests/clearmsg/mc.nim b/tests/errmsgs/mc.nim
index 79d7431df..79d7431df 100644
--- a/tests/clearmsg/mc.nim
+++ b/tests/errmsgs/mc.nim
diff --git a/tests/errmsgs/t10489_a.nim b/tests/errmsgs/t10489_a.nim
index 71a6cc3c4..c762ce876 100644
--- a/tests/errmsgs/t10489_a.nim
+++ b/tests/errmsgs/t10489_a.nim
@@ -1,5 +1,5 @@
 discard """
-errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe, locks: 0.}' for let. Did you mean to call the macro with '()'?"
+errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for let. Did you mean to call the macro with '()'?"
 line: 9
 """
 
diff --git a/tests/errmsgs/t10489_b.nim b/tests/errmsgs/t10489_b.nim
index 4b0b876e5..df05f0e6e 100644
--- a/tests/errmsgs/t10489_b.nim
+++ b/tests/errmsgs/t10489_b.nim
@@ -1,5 +1,5 @@
 discard """
-errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe, locks: 0.}' for const. Did you mean to call the macro with '()'?"
+errormsg: "invalid type: 'macro (body: untyped): untyped{.noSideEffect, gcsafe.}' for const. Did you mean to call the macro with '()'?"
 line: 9
 """
 
diff --git a/tests/errmsgs/t10542.nim b/tests/errmsgs/t10542.nim
new file mode 100644
index 000000000..b57dbf18b
--- /dev/null
+++ b/tests/errmsgs/t10542.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--hintaserror:ConvFromXtoItselfNotNeeded"
+"""
+
+# bug #10542
+
+proc f(args: varargs[string, string], length: int) =
+  doAssert args.len == length
+
+# main use case that requires type conversion (no warning here)
+f("a", "b", 2)
+f("a", 1)
+
+
+proc m(args: varargs[cstring, cstring]) =
+  doAssert args.len == 2
+
+# main use case that requires type conversion (no warning here)
+m("a", "b")
+
+# if an argument already is cstring there's a warning
+let x: cstring = "x"
+m("a", x)
+m(x, "a")
\ No newline at end of file
diff --git a/tests/errmsgs/t10735.nim b/tests/errmsgs/t10735.nim
index 307acac2d..a39cd196e 100644
--- a/tests/errmsgs/t10735.nim
+++ b/tests/errmsgs/t10735.nim
@@ -1,36 +1,63 @@
 discard """
   cmd: "nim check $file"
-  errormsg: "selector must be of an ordinal type, float or string"
+  errormsg: "illformed AST: case buf[pos]"
   nimout: '''
-t10735.nim(38, 5) Error: 'let' symbol requires an initialization
-t10735.nim(39, 10) Error: undeclared identifier: 'pos'
-t10735.nim(39, 9) Error: type mismatch: got <cstring, >
+t10735.nim(65, 5) Error: 'let' symbol requires an initialization
+t10735.nim(66, 10) Error: undeclared identifier: 'pos'
+t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous)
+t10735.nim(66, 10) Error: expression 'pos' has no type (or is ambiguous)
+t10735.nim(66, 9) Error: type mismatch: got <cstring, >
 but expected one of:
 proc `[]`(s: string; i: BackwardsIndex): char
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
 proc `[]`(s: var string; i: BackwardsIndex): var char
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: var string
+  but expression 'buf' is of type: cstring
 proc `[]`[I: Ordinal; T](a: T; i: I): T
   first type mismatch at position: 0
 proc `[]`[Idx, T; U, V: Ordinal](a: array[Idx, T]; x: HSlice[U, V]): seq[T]
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for a: array[Idx, T]
+  but expression 'buf' is of type: cstring
 proc `[]`[Idx, T](a: array[Idx, T]; i: BackwardsIndex): T
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for a: array[Idx, T]
+  but expression 'buf' is of type: cstring
 proc `[]`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for a: var array[Idx, T]
+  but expression 'buf' is of type: cstring
 proc `[]`[T, U: Ordinal](s: string; x: HSlice[T, U]): string
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
 proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T]
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: openArray[T]
+  but expression 'buf' is of type: cstring
 proc `[]`[T](s: openArray[T]; i: BackwardsIndex): T
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: openArray[T]
+  but expression 'buf' is of type: cstring
 proc `[]`[T](s: var openArray[T]; i: BackwardsIndex): var T
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: var openArray[T]
+  but expression 'buf' is of type: cstring
+template `[]`(a: WideCStringObj; idx: int): Utf16Char
+  first type mismatch at position: 1
+  required type for a: WideCStringObj
+  but expression 'buf' is of type: cstring
 template `[]`(s: string; i: int): char
-  first type mismatch at position: 0
+  first type mismatch at position: 1
+  required type for s: string
+  but expression 'buf' is of type: cstring
 
-expression: `[]`(buf, pos)
-t10735.nim(39, 9) Error: selector must be of an ordinal type, float or string
+expression: buf[pos]
+t10735.nim(66, 9) Error: expression '' has no type (or is ambiguous)
+t10735.nim(68, 3) Error: illformed AST: case buf[pos]
 '''
   joinable: false
 """
diff --git a/tests/errmsgs/t14444.nim b/tests/errmsgs/t14444.nim
new file mode 100644
index 000000000..27365236e
--- /dev/null
+++ b/tests/errmsgs/t14444.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--hints:off"
+  exitcode: "1"
+  output: '''
+t14444.nim(13)           t14444
+fatal.nim(53)            sysFatal
+Error: unhandled exception: index out of bounds, the container is empty [IndexDefect]
+'''
+"""
+
+when true: # bug #14444
+  var i: string
+  i[10] = 'j'
+  echo i
\ No newline at end of file
diff --git a/tests/errmsgs/t16654.nim b/tests/errmsgs/t16654.nim
new file mode 100644
index 000000000..b2b57619b
--- /dev/null
+++ b/tests/errmsgs/t16654.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim check $options $file"
+  errormsg: "type mismatch: got <int literal(1), proc (r: GenericParam): auto>"
+"""
+
+when true: # bug #16654
+  func fn[T](a: T, op: proc(a: T): float) = discard
+  proc main() =
+    let v = 1
+    proc bar(r: auto): auto = v
+    fn(1, bar)
+  main()
diff --git a/tests/errmsgs/t17460.nim b/tests/errmsgs/t17460.nim
index e377bc48a..bb8e21198 100644
--- a/tests/errmsgs/t17460.nim
+++ b/tests/errmsgs/t17460.nim
@@ -1,6 +1,6 @@
 discard """
   cmd: "nim check $options $file"
-  errormsg: "wrong number of variables"
+  errormsg: "tuple expected for tuple unpacking, but got 'array[0..2, int]'"
 """
 
 iterator xclusters*[T](a: openArray[T]; s: static[int]): array[s, T] {.inline.} =
@@ -16,4 +16,4 @@ proc m =
   for (i, j, k) in xclusters([1, 2, 3, 4, 5], 3):
     echo i, j, k
 
-m()
\ No newline at end of file
+m()
diff --git a/tests/errmsgs/t18886.nim b/tests/errmsgs/t18886.nim
new file mode 100644
index 000000000..8ed160c64
--- /dev/null
+++ b/tests/errmsgs/t18886.nim
@@ -0,0 +1,18 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t18886.nim(18, 24) Error: ambiguous identifier: 'bar' -- you need a helper proc to disambiguate the following:
+  t18886.bar: proc (i: ptr int){.noSideEffect, gcsafe.}
+  t18886.bar: proc (i: ptr char){.noSideEffect, gcsafe.}
+'''
+"""
+
+type Foo = (proc(_: pointer): void)
+
+
+proc bar(i: ptr[int]) = discard
+proc bar(i: ptr[char]) = discard
+
+
+let fooBar = cast[Foo](bar)
\ No newline at end of file
diff --git a/tests/errmsgs/t19224.nim b/tests/errmsgs/t19224.nim
new file mode 100644
index 000000000..7a9ecb2e7
--- /dev/null
+++ b/tests/errmsgs/t19224.nim
@@ -0,0 +1,12 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: ""
+nimout: '''
+t19224.nim(10, 10) Error: cannot infer element type of items([])
+t19224.nim(12, 10) Error: cannot infer element type of items(@[])
+'''
+"""
+
+for _ in []: discard
+
+for _ in @[]: discard
diff --git a/tests/errmsgs/t21257.nim b/tests/errmsgs/t21257.nim
new file mode 100644
index 000000000..eecdb1dfe
--- /dev/null
+++ b/tests/errmsgs/t21257.nim
@@ -0,0 +1,20 @@
+discard """
+  action: compile
+  cmd: "nim check $file" 
+"""
+
+type AC_WINCTRL_Fields* = distinct uint8
+
+type AC_STATUSA_WSTATE0* {.pure.} = enum
+  ABOVE = 0x0,
+  INSIDE = 0x1,
+  BELOW = 0x2,
+
+type AC_WINCTRL_WINTSEL0* {.pure.} = enum
+  ABOVE = 0x0,
+  INSIDE = 0x1,
+  BELOW = 0x2,
+  OUTSIDE = 0x3,
+
+proc write*(WINTSEL0: AC_WINCTRL_WINTSEL0 = ABOVE) =
+  discard
diff --git a/tests/errmsgs/t22097.nim b/tests/errmsgs/t22097.nim
new file mode 100644
index 000000000..bb24ee8d3
--- /dev/null
+++ b/tests/errmsgs/t22097.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "type mismatch: got <uint8>"
+"""
+
+proc toUInt16(x: var uint16) =
+  discard
+
+var x = uint8(1)
+toUInt16 x
diff --git a/tests/errmsgs/t22284.nim b/tests/errmsgs/t22284.nim
new file mode 100644
index 000000000..827155e6b
--- /dev/null
+++ b/tests/errmsgs/t22284.nim
@@ -0,0 +1,25 @@
+discard """
+  errormsg: "j(uRef, proc (config: F; sources: auto) {.raises: [].} = discard ) can raise an unlisted exception: Exception"
+"""
+
+import std/macros
+
+macro h(): untyped =
+  result = newTree(nnkStmtList)
+  result.add quote do:
+    new int
+
+type F = object
+
+proc j[SecondarySources](
+    uRef: ref SecondarySources,
+    u: proc (config: F, sources: ref SecondarySources)): F =
+  u(result, uRef)
+
+template programMain(body: untyped) =
+  proc main {.raises: [].} = body  # doesn't SIGSEGV without this {.raises: [].}
+  main()
+
+programMain:
+  var uRef = h()
+  discard j(uRef, u = proc(config: F, sources: auto) {.raises: [].} = discard)
\ No newline at end of file
diff --git a/tests/errmsgs/t22753.nim b/tests/errmsgs/t22753.nim
new file mode 100644
index 000000000..8a504109a
--- /dev/null
+++ b/tests/errmsgs/t22753.nim
@@ -0,0 +1,52 @@
+discard """
+cmd: "nim check --hints:off $file"
+errormsg: "type mismatch"
+nimoutFull: true
+nimout: '''
+t22753.nim(51, 13) Error: array expects two type parameters
+t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous)
+t22753.nim(52, 1) Error: expression 'x' has no type (or is ambiguous)
+t22753.nim(52, 2) Error: type mismatch: got <>
+but expected one of:
+proc `[]=`(s: var string; i: BackwardsIndex; x: char)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+proc `[]=`[I: Ordinal; T, S](a: T; i: I; x: sink S)
+  first type mismatch at position: 0
+proc `[]=`[Idx, T; U, V: Ordinal](a: var array[Idx, T]; x: HSlice[U, V];
+                                  b: openArray[T])
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.U, []=.V]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[Idx, T](a: var array[Idx, T]; i: BackwardsIndex; x: T)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T, U: Ordinal](s: var string; x: HSlice[T, U]; b: string)
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.T, []=.U]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T; U, V: Ordinal](s: var seq[T]; x: HSlice[U, V]; b: openArray[T])
+  first type mismatch at position: 2
+  required type for x: HSlice[[]=.U, []=.V]
+  but expression '0' is of type: int literal(0)
+proc `[]=`[T](s: var openArray[T]; i: BackwardsIndex; x: T)
+  first type mismatch at position: 2
+  required type for i: BackwardsIndex
+  but expression '0' is of type: int literal(0)
+template `[]=`(a: WideCStringObj; idx: int; val: Utf16Char)
+  first type mismatch at position: 3
+  required type for val: Utf16Char
+  but expression '9' is of type: int literal(9)
+template `[]=`(s: string; i: int; val: char)
+  first type mismatch at position: 3
+  required type for val: char
+  but expression '9' is of type: int literal(9)
+
+expression: x[0] = 9
+'''
+"""
+
+var x: array[3] # bug #22753
+x[0] = 9
diff --git a/tests/errmsgs/t22852.nim b/tests/errmsgs/t22852.nim
new file mode 100644
index 000000000..7c352a49c
--- /dev/null
+++ b/tests/errmsgs/t22852.nim
@@ -0,0 +1,9 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+Error: unhandled exception: value out of range: -2 notin 0 .. 9223372036854775807 [RangeDefect]
+'''
+"""
+
+# bug #22852
+echo [0][2..^2]
diff --git a/tests/errmsgs/t22996.nim b/tests/errmsgs/t22996.nim
new file mode 100644
index 000000000..3a51fd8b0
--- /dev/null
+++ b/tests/errmsgs/t22996.nim
@@ -0,0 +1,7 @@
+discard """
+  errormsg: "invalid type: 'typedesc[string]' for const"
+"""
+
+# bug #22996
+type MyObject = ref object
+  _ = string
diff --git a/tests/errmsgs/t23060.nim b/tests/errmsgs/t23060.nim
new file mode 100644
index 000000000..abb79bcc3
--- /dev/null
+++ b/tests/errmsgs/t23060.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "undeclared identifier: '♔♕♖♗♘♙'"
+"""
+
+♔♕♖♗♘♙ = 1
\ No newline at end of file
diff --git a/tests/errmsgs/t23419.nim b/tests/errmsgs/t23419.nim
new file mode 100644
index 000000000..59a72f081
--- /dev/null
+++ b/tests/errmsgs/t23419.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "invalid type: 'void' in this context: '(array[0..-1, void],)' for var"
+"""
+
+var a: (array[0, void], )
diff --git a/tests/errmsgs/t23435.nim b/tests/errmsgs/t23435.nim
new file mode 100644
index 000000000..5e2e4c82a
--- /dev/null
+++ b/tests/errmsgs/t23435.nim
@@ -0,0 +1,12 @@
+discard """
+  outputsub: "Error: unhandled exception: value out of range: -15 notin 0 .. 9223372036854775807 [RangeDefect]"
+  exitcode: "1"
+"""
+
+# bug #23435
+proc foo() =
+  for _ in @[1, 3, 5]:
+    discard "abcde"[25..<10]
+    break
+
+foo()
diff --git a/tests/errmsgs/t23536.nim b/tests/errmsgs/t23536.nim
new file mode 100644
index 000000000..610a85bab
--- /dev/null
+++ b/tests/errmsgs/t23536.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--stackTrace:on --excessiveStackTrace:off"
+"""
+
+const expected = """
+wrong trace:
+t23536.nim(22)           t23536
+t23536.nim(17)           foo
+assertions.nim(41)       failedAssertImpl
+assertions.nim(36)       raiseAssert
+fatal.nim(53)            sysFatal
+"""
+
+
+try:
+  proc foo = # bug #23536
+    doAssert false
+
+  for i in 0 .. 1:
+
+
+    foo()
+except AssertionDefect:
+  let e = getCurrentException()
+  let trace = e.getStackTrace
+  doAssert "wrong trace:\n" & trace == expected
diff --git a/tests/errmsgs/t2614.nim b/tests/errmsgs/t2614.nim
index 4034249e7..031ecb9d1 100644
--- a/tests/errmsgs/t2614.nim
+++ b/tests/errmsgs/t2614.nim
@@ -2,9 +2,9 @@ discard """
   cmd: "nim check $options --hints:off $file"
   errormsg: ""
   nimout: '''
-t2614.nim(19, 27) Error: type mismatch: got <array[0..1, proc (){.locks: <unknown>.}]> but expected 'array[0..1, proc (){.closure.}]'
+t2614.nim(19, 27) Error: type mismatch: got <array[0..1, proc ()]> but expected 'array[0..1, proc (){.closure.}]'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
-t2614.nim(21, 22) Error: type mismatch: got <seq[proc (){.locks: <unknown>.}]> but expected 'seq[proc (){.closure.}]'
+t2614.nim(21, 22) Error: type mismatch: got <seq[proc ()]> but expected 'seq[proc (){.closure.}]'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
 '''
 """
diff --git a/tests/errmsgs/t5167_4.nim b/tests/errmsgs/t5167_4.nim
index 7a263622b..dafd7754d 100644
--- a/tests/errmsgs/t5167_4.nim
+++ b/tests/errmsgs/t5167_4.nim
@@ -1,5 +1,5 @@
 discard """
-errormsg: "type mismatch: got <proc [*missing parameters*](x: int) | proc (x: string){.gcsafe, locks: 0.}>"
+errormsg: "type mismatch: got <proc [*missing parameters*](x: int) | proc (x: string){.gcsafe.}>"
 line: 19
 """
 
diff --git a/tests/errmsgs/t5167_5.nim b/tests/errmsgs/t5167_5.nim
index a9e260845..dea7e40b3 100644
--- a/tests/errmsgs/t5167_5.nim
+++ b/tests/errmsgs/t5167_5.nim
@@ -1,13 +1,9 @@
 discard """
-cmd: "nim check $file"
-errormsg: "'t' has unspecified generic parameters"
-nimout: '''
-t5167_5.nim(10, 16) Error: expression 'system' has no type (or is ambiguous)
-t5167_5.nim(21, 9) Error: 't' has unspecified generic parameters
-'''
+cmd: "nim check --mm:refc $file"
 """
 # issue #11942
-discard newSeq[system]()
+discard newSeq[system]() #[tt.Error
+               ^ expression 'system' has no type (or is ambiguous)]#
 
 # issue #5167
 template t[B]() =
@@ -18,8 +14,12 @@ macro m[T]: untyped = nil
 proc bar(x: proc (x: int)) =
   echo "bar"
 
-let x = t
-bar t
+let x = t #[tt.Error
+        ^ 't' has unspecified generic parameters]#
+bar t #[tt.Error
+^ type mismatch: got <template [*missing parameters*]()>]#
 
-let y = m
-bar m
+let y = m #[tt.Error
+        ^ 'm' has unspecified generic parameters]#
+bar m #[tt.Error
+^ type mismatch: got <macro [*missing parameters*](): untyped{.noSideEffect, gcsafe.}>]#
diff --git a/tests/errmsgs/t6499.nim b/tests/errmsgs/t6499.nim
new file mode 100644
index 000000000..25e78fdbb
--- /dev/null
+++ b/tests/errmsgs/t6499.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "'chr' is a built-in and cannot be used as a first-class procedure"
+"""
+
+# bug #6499
+let x = (chr, 0)
diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim
index 10bb86299..c35a3abcc 100644
--- a/tests/errmsgs/t8064.nim
+++ b/tests/errmsgs/t8064.nim
@@ -1,6 +1,9 @@
-discard """
-  errormsg: "expression has no type: values"
-"""
 import tables
 
-values
\ No newline at end of file
+values
+
+
+discard """
+  # either this or "expression has no type":
+  errormsg: "ambiguous identifier: 'values' -- use one of the following:"
+"""
diff --git a/tests/errmsgs/t8794.nim b/tests/errmsgs/t8794.nim
index 9db54a9c7..36f05dbad 100644
--- a/tests/errmsgs/t8794.nim
+++ b/tests/errmsgs/t8794.nim
@@ -1,33 +1,16 @@
 discard """
   cmd: "nim check $options $file"
-  errormsg: ""
-  nimout: '''
-t8794.nim(39, 27) Error: undeclared field: 'a3' for type m8794.Foo3 [type declared in m8794.nim(1, 6)]
-'''
 """
 
-
-
-
-
-
-
-
-
-
-
-
-## line 20
-
 ## issue #8794
 
 import m8794
 
-when false: # pending https://github.com/nim-lang/Nim/pull/10091 add this
-  type Foo = object
-    a1: int
+type Foo = object
+  a1: int
 
-  discard Foo().a2
+discard Foo().a2 #[tt.Error
+             ^ undeclared field: 'a2' for type t8794.Foo [type declared in t8794.nim(9, 6)]]#
 
 type Foo3b = Foo3
 var x2: Foo3b
@@ -36,4 +19,5 @@ proc getFun[T](): T =
   var a: T
   a
 
-discard getFun[type(x2)]().a3
+discard getFun[type(x2)]().a3 #[tt.Error
+                          ^ undeclared field: 'a3' for type m8794.Foo3 [type declared in m8794.nim(1, 6)]]#
diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim
index b72a158c7..b5ff58367 100644
--- a/tests/errmsgs/t9768.nim
+++ b/tests/errmsgs/t9768.nim
@@ -1,13 +1,14 @@
 discard """
-  errormsg: "unhandled exception:"
-  file: "system/fatal.nim"
+  errormsg: "unhandled exception: t9768.nim(24, 3) `a < 4`  [AssertionDefect]"
+  file: "std/assertions.nim"
+  matrix: "-d:nimPreviewSlimSystem"
   nimout: '''
 stack trace: (most recent call last)
-t9768.nim(28, 33)        main
-t9768.nim(23, 11)        foo1
+t9768.nim(29, 33)        main
+t9768.nim(24, 11)        foo1
 '''
 """
-
+import std/assertions
 
 
 
diff --git a/tests/clearmsg/ta.nim b/tests/errmsgs/ta.nim
index 31baae773..31baae773 100644
--- a/tests/clearmsg/ta.nim
+++ b/tests/errmsgs/ta.nim
diff --git a/tests/errmsgs/tambparam.nim b/tests/errmsgs/tambparam.nim
new file mode 100644
index 000000000..5b56a3fce
--- /dev/null
+++ b/tests/errmsgs/tambparam.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+"""
+
+import mambparam2, mambparam3
+
+echo test #[tt.Error
+^ type mismatch
+Expression: echo test
+  [1] test: string | string
+
+Expected one of (first mismatch at [position]):
+[1] proc echo(x: varargs[typed, `$$`])
+  ambiguous identifier: 'test' -- use one of the following:
+    mambparam1.test: string
+    mambparam3.test: string]#
diff --git a/tests/errmsgs/tambparam_legacy.nim b/tests/errmsgs/tambparam_legacy.nim
new file mode 100644
index 000000000..bd99a3aac
--- /dev/null
+++ b/tests/errmsgs/tambparam_legacy.nim
@@ -0,0 +1,14 @@
+import mambparam2, mambparam3
+
+echo test #[tt.Error
+^ type mismatch: got <string | string>
+but expected one of:
+proc echo(x: varargs[typed, `$$`])
+  first type mismatch at position: 1
+  required type for x: varargs[typed]
+  but expression 'test' is of type: None
+  ambiguous identifier: 'test' -- use one of the following:
+    mambparam1.test: string
+    mambparam3.test: string
+
+expression: echo test]#
diff --git a/tests/errmsgs/tassignunpack.nim b/tests/errmsgs/tassignunpack.nim
new file mode 100644
index 000000000..09d928a54
--- /dev/null
+++ b/tests/errmsgs/tassignunpack.nim
@@ -0,0 +1,3 @@
+var a, b = 0
+(a, b) = 1 #[tt.Error
+         ^ tuple expected for tuple unpacking, but got 'int literal(1)']#
diff --git a/tests/errmsgs/tcall_with_default_arg.nim b/tests/errmsgs/tcall_with_default_arg.nim
index 1cc86638f..44752f1ee 100644
--- a/tests/errmsgs/tcall_with_default_arg.nim
+++ b/tests/errmsgs/tcall_with_default_arg.nim
@@ -1,5 +1,5 @@
 discard """
-outputsub: '''tcall_with_default_arg.nim(16) anotherFoo'''
+outputsub: '''tcall_with_default_arg.nim(8) fail'''
 exitcode: 1
 """
 # issue: #5604
diff --git a/tests/errmsgs/tcase_stmt.nim b/tests/errmsgs/tcase_stmt.nim
new file mode 100644
index 000000000..cf63b9c17
--- /dev/null
+++ b/tests/errmsgs/tcase_stmt.nim
@@ -0,0 +1,29 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+tcase_stmt.nim(22, 7) Error: selector must be of an ordinal type, float or string
+tcase_stmt.nim(28, 6) Error: selector must be of an ordinal type, float or string
+
+
+
+
+
+
+
+'''
+"""
+
+
+
+# bug #19682
+type A = object
+
+case A()
+else:
+  discard
+
+# bug #20283
+
+case @[]
+else: discard
diff --git a/tests/errmsgs/tconcisetypemismatch.nim b/tests/errmsgs/tconcisetypemismatch.nim
new file mode 100644
index 000000000..3093cc24e
--- /dev/null
+++ b/tests/errmsgs/tconcisetypemismatch.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file"
+  errormsg: "type mismatch"
+  nimout: '''
+tconcisetypemismatch.nim(23, 47) Error: type mismatch
+Expression: int(inNanoseconds(t2 - t1)) / 100.5
+  [1] int(inNanoseconds(t2 - t1)): int
+  [2] 100.5: float64
+
+Expected one of (first mismatch at [position]):
+[1] proc `/`(x, y: float): float
+[1] proc `/`(x, y: float32): float32
+[2] proc `/`(x, y: int): float
+'''
+"""
+
+import std/monotimes
+from times import inNanoseconds
+
+let t1 = getMonotime()
+let result = 1 + 2
+let t2 = getMonotime()
+echo "Elapsed: ", (t2 - t1).inNanoseconds.int / 100.5
diff --git a/tests/errmsgs/tconstinfo.nim b/tests/errmsgs/tconstinfo.nim
new file mode 100644
index 000000000..170972b9f
--- /dev/null
+++ b/tests/errmsgs/tconstinfo.nim
@@ -0,0 +1,7 @@
+# https://forum.nim-lang.org/t/9762
+
+const foo = "abc"
+case 'a'
+of foo: echo "should error" #[tt.Error
+   ^ type mismatch: got <string> but expected 'char']#
+else: discard
diff --git a/tests/clearmsg/tconsttypemismatch.nim b/tests/errmsgs/tconsttypemismatch.nim
index 727bfbffb..727bfbffb 100644
--- a/tests/clearmsg/tconsttypemismatch.nim
+++ b/tests/errmsgs/tconsttypemismatch.nim
diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim
index 701adeebf..66496d364 100644
--- a/tests/errmsgs/tgcsafety.nim
+++ b/tests/errmsgs/tgcsafety.nim
@@ -1,8 +1,8 @@
 discard """
 cmd: "nim check $file"
-errormsg: "type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]{.locks: <unknown>.}>"
+errormsg: "type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]>"
 nimout: '''
-tgcsafety.nim(31, 18) Error: type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]{.locks: <unknown>.}>
+tgcsafety.nim(31, 18) Error: type mismatch: got <AsyncHttpServer, Port, proc (req: Request): Future[system.void]>
 but expected one of:
 proc serve(server: AsyncHttpServer; port: Port;
            callback: proc (request: Request): Future[void] {.closure, gcsafe.};
@@ -10,7 +10,7 @@ proc serve(server: AsyncHttpServer; port: Port;
     Future[void])
   first type mismatch at position: 3
   required type for callback: proc (request: Request): Future[system.void]{.closure, gcsafe.}
-  but expression 'cb' is of type: proc (req: Request): Future[system.void]{.locks: <unknown>.}
+  but expression 'cb' is of type: proc (req: Request): Future[system.void]
   This expression is not GC-safe. Annotate the proc with {.gcsafe.} to get extended error information.
 
 expression: serve(server, Port(7898), cb)
diff --git a/tests/errmsgs/tgenericmismatchsegfault.nim b/tests/errmsgs/tgenericmismatchsegfault.nim
new file mode 100644
index 000000000..dbb783cb3
--- /dev/null
+++ b/tests/errmsgs/tgenericmismatchsegfault.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+"""
+
+template v[T](c: SomeOrdinal): T = T(c)
+discard v[int, char]('A') #[tt.Error
+                    ^ type mismatch
+Expression: v[int, char]('A')
+  [1] 'A': char
+
+Expected one of (first mismatch at [position]):
+[2] template v[T](c: SomeOrdinal): T
+  generic parameter mismatch, expected SomeOrdinal but got 'char' of type: char]#
diff --git a/tests/errmsgs/tgenericmismatchsegfault_legacy.nim b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim
new file mode 100644
index 000000000..1532611b9
--- /dev/null
+++ b/tests/errmsgs/tgenericmismatchsegfault_legacy.nim
@@ -0,0 +1,10 @@
+template v[T](c: SomeOrdinal): T = T(c)
+discard v[int, char]('A') #[tt.Error
+                    ^ type mismatch: got <char>
+but expected one of:
+template v[T](c: SomeOrdinal): T
+  first type mismatch at position: 2 in generic parameters
+  required type for SomeOrdinal: SomeOrdinal
+  but expression 'char' is of type: char
+
+expression: v[int, char]('A')]#
diff --git a/tests/errmsgs/tinconsistentgensym.nim b/tests/errmsgs/tinconsistentgensym.nim
new file mode 100644
index 000000000..8e4c85106
--- /dev/null
+++ b/tests/errmsgs/tinconsistentgensym.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+block:
+  template foo =
+    when false:
+      let x = 123
+    else:
+      template x: untyped {.inject.} = 456
+    echo x #[tt.Error
+         ^ undeclared identifier: 'x`gensym0'; if declared in a template, this identifier may be inconsistently marked inject or gensym]#
+  foo()
+
+block:
+  template foo(y: static bool) =
+    block:
+      when y:
+        let x {.gensym.} = 123
+      else:
+        let x {.inject.} = 456
+      echo x #[tt.Error
+           ^ undeclared identifier: 'x']#
+  foo(false)
+  foo(true)
diff --git a/tests/clearmsg/tmacroerrorproc.nim b/tests/errmsgs/tmacroerrorproc.nim
index 86726af72..86726af72 100644
--- a/tests/clearmsg/tmacroerrorproc.nim
+++ b/tests/errmsgs/tmacroerrorproc.nim
diff --git a/tests/errmsgs/tmetaobjectfields.nim b/tests/errmsgs/tmetaobjectfields.nim
new file mode 100644
index 000000000..47d3acf18
--- /dev/null
+++ b/tests/errmsgs/tmetaobjectfields.nim
@@ -0,0 +1,75 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  action: "reject"
+  nimout: '''
+tmetaobjectfields.nim(26, 5) Error: 'array' is not a concrete type
+tmetaobjectfields.nim(30, 5) Error: 'seq' is not a concrete type
+tmetaobjectfields.nim(34, 5) Error: 'set' is not a concrete type
+tmetaobjectfields.nim(37, 3) Error: 'sink' is not a concrete type
+tmetaobjectfields.nim(39, 3) Error: 'lent' is not a concrete type
+tmetaobjectfields.nim(56, 16) Error: 'seq' is not a concrete type
+tmetaobjectfields.nim(60, 5) Error: 'ptr' is not a concrete type
+tmetaobjectfields.nim(61, 5) Error: 'ref' is not a concrete type
+tmetaobjectfields.nim(62, 5) Error: 'auto' is not a concrete type
+tmetaobjectfields.nim(63, 5) Error: 'UncheckedArray' is not a concrete type
+tmetaobjectfields.nim(68, 5) Error: 'object' is not a concrete type
+tmetaobjectfields.nim(72, 5) Error: 'Type3011:ObjectType' is not a concrete type
+'''
+"""
+
+
+# bug #6982
+# bug #19546
+# bug #23531
+type
+  ExampleObj1 = object
+    arr: array
+
+type
+  ExampleObj2 = object
+    arr: seq
+
+type
+  ExampleObj3 = object
+    arr: set
+
+type A = object
+  b: sink
+  # a: openarray
+  c: lent
+
+type PropertyKind = enum
+  tInt,
+  tFloat,
+  tBool,
+  tString,
+  tArray
+
+type
+  Property = ref PropertyObj
+  PropertyObj = object
+    case kind: PropertyKind
+    of tInt: intValue: int
+    of tFloat: floatValue: float
+    of tBool: boolValue: bool
+    of tString: stringValue: string
+    of tArray: arrayValue: seq
+
+type
+  RegressionTest = object
+    a: ptr
+    b: ref
+    c: auto
+    d: UncheckedArray
+
+# bug #3011
+type
+  Type3011 = ref object 
+    context: ref object
+
+type
+  Value3011 = ref object
+    typ: Type3011
+
+proc x3011(): Value3011 =
+  nil
diff --git a/tests/misc/tnoop.nim b/tests/errmsgs/tnoop.nim
index f55f2441a..f55f2441a 100644
--- a/tests/misc/tnoop.nim
+++ b/tests/errmsgs/tnoop.nim
diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim
index f92589d96..16f319f3b 100644
--- a/tests/errmsgs/tproc_mismatch.nim
+++ b/tests/errmsgs/tproc_mismatch.nim
@@ -3,36 +3,36 @@ discard """
   cmd: '''nim check --hints:off $options $file'''
   nimoutFull: true
   nimout: '''
-tproc_mismatch.nim(38, 52) Error: type mismatch: got <proc (a: int, c: float){.cdecl, noSideEffect, gcsafe, locks: 0.}> but expected 'proc (a: int, c: float){.closure, noSideEffect.}'
+tproc_mismatch.nim(38, 52) Error: type mismatch: got <proc (a: int, c: float){.cdecl, noSideEffect, gcsafe.}> but expected 'proc (a: int, c: float){.closure, noSideEffect.}'
   Calling convention mismatch: got '{.cdecl.}', but expected '{.closure.}'.
-tproc_mismatch.nim(42, 6) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe, locks: 0.}>
+tproc_mismatch.nim(42, 6) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe.}>
 but expected one of:
 proc bar(a: proc ())
   first type mismatch at position: 1
   required type for a: proc (){.closure.}
-  but expression 'fn1' is of type: proc (){.inline, noSideEffect, gcsafe, locks: 0.}
+  but expression 'fn1' is of type: proc (){.inline, noSideEffect, gcsafe.}
   Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'.
 
 expression: bar(fn1)
-tproc_mismatch.nim(46, 8) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe, locks: 0.}> but expected 'proc (){.closure.}'
+tproc_mismatch.nim(46, 8) Error: type mismatch: got <proc (){.inline, noSideEffect, gcsafe.}> but expected 'proc (){.closure.}'
   Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'.
-tproc_mismatch.nim(51, 8) Error: type mismatch: got <proc (){.locks: 0.}> but expected 'proc (){.closure, noSideEffect.}'
+tproc_mismatch.nim(51, 8) Error: type mismatch: got <proc ()> but expected 'proc (){.closure, noSideEffect.}'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
   Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'.
-tproc_mismatch.nim(55, 8) Error: type mismatch: got <proc (a: int){.noSideEffect, gcsafe, locks: 0.}> but expected 'proc (a: float){.closure.}'
+tproc_mismatch.nim(55, 8) Error: type mismatch: got <proc (a: int){.noSideEffect, gcsafe.}> but expected 'proc (a: float){.closure.}'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
-tproc_mismatch.nim(64, 9) Error: type mismatch: got <proc (a: int){.locks: 0.}> but expected 'proc (a: int){.closure, gcsafe.}'
+tproc_mismatch.nim(64, 9) Error: type mismatch: got <proc (a: int)> but expected 'proc (a: int){.closure, gcsafe.}'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.closure.}'.
   Pragma mismatch: got '{..}', but expected '{.gcsafe.}'.
 tproc_mismatch.nim(72, 9) Error: type mismatch: got <proc (a: int): int{.nimcall.}> but expected 'proc (a: int): int{.cdecl.}'
   Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'.
 tproc_mismatch.nim(73, 9) Error: type mismatch: got <proc (a: int): int{.cdecl.}> but expected 'proc (a: int): int{.nimcall.}'
   Calling convention mismatch: got '{.cdecl.}', but expected '{.nimcall.}'.
-tproc_mismatch.nim(77, 9) Error: type mismatch: got <proc (a: int){.closure, locks: 3.}> but expected 'proc (a: int){.closure, locks: 1.}'
-  Pragma mismatch: got '{.locks: 3.}', but expected '{.locks: 1.}'.
-lock levels differ
 '''
 """
+
+
+
 block: # CallConv mismatch
   func a(a: int, c: float) {.cdecl.} = discard
   var b: proc(a: int, c: float) {.noSideEffect.} = a
@@ -71,7 +71,4 @@ block: # Indrection through pragmas
   var fn2: proc(a: int): int {.inl2, p2.}
   fn2 = fn1
   fn1 = fn2
-block: # Lock levels differ
-  var fn1: proc(a: int){.locks: 3.}
-  var fn2: proc(a: int){.locks: 1.}
-  fn2 = fn1
+
diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim
index 8617984fb..b0a008840 100644
--- a/tests/errmsgs/tproper_stacktrace.nim
+++ b/tests/errmsgs/tproper_stacktrace.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--stackTrace:on --hint:all:off --warnings:off"
   output: '''ok'''
 """
 import strscans, strutils
@@ -76,10 +77,10 @@ when true:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(86) tproper_stacktrace
-      tproper_stacktrace.nim(76) foo
-      tproper_stacktrace.nim(73) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(87) tproper_stacktrace
+      tproper_stacktrace.nim(77) foo
+      tproper_stacktrace.nim(74) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -93,9 +94,9 @@ when true:
       bar(x)
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(103) tproper_stacktrace
-      tproper_stacktrace.nim(90) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(104) tproper_stacktrace
+      tproper_stacktrace.nim(91) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -110,10 +111,10 @@ when true:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(120) tproper_stacktrace
-      tproper_stacktrace.nim(110) foo
-      tproper_stacktrace.nim(107) bar
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(121) tproper_stacktrace
+      tproper_stacktrace.nim(111) foo
+      tproper_stacktrace.nim(108) bar
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
@@ -129,10 +130,10 @@ when true:
       bar()
 
     const expectedStackTrace = """
-      tproper_stacktrace.nim(139) tproper_stacktrace
-      tproper_stacktrace.nim(129) foo
-      tproper_stacktrace.nim(125) baz
-      tproper_stacktrace.nim(7) raiseTestException
+      tproper_stacktrace.nim(140) tproper_stacktrace
+      tproper_stacktrace.nim(130) foo
+      tproper_stacktrace.nim(126) baz
+      tproper_stacktrace.nim(8) raiseTestException
     """
 
     verifyStackTrace expectedStackTrace:
diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim
index 44b208c87..5a6ca3fcb 100644
--- a/tests/errmsgs/tproper_stacktrace2.nim
+++ b/tests/errmsgs/tproper_stacktrace2.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: '''tproper_stacktrace2.nim(20) main'''
+  matrix: "--stackTrace:on"
+  outputsub: '''tproper_stacktrace2.nim(21) main'''
   exitcode: 1
 """
 
diff --git a/tests/errmsgs/tproper_stacktrace3.nim b/tests/errmsgs/tproper_stacktrace3.nim
index c292fa092..97d63e6ec 100644
--- a/tests/errmsgs/tproper_stacktrace3.nim
+++ b/tests/errmsgs/tproper_stacktrace3.nim
@@ -1,5 +1,6 @@
 discard """
-  outputsub: '''tproper_stacktrace3.nim(21) main'''
+  matrix: "--stackTrace:on"
+  outputsub: '''tproper_stacktrace3.nim(22) main'''
   exitcode: 1
 """
 
diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim
index 8f32ef9e6..85ed34169 100644
--- a/tests/errmsgs/tsigmatch.nim
+++ b/tests/errmsgs/tsigmatch.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: "nim check --showAllMismatches:on --hints:off $file"
+  cmd: "nim check --mm:refc --showAllMismatches:on --hints:off $file"
   nimout: '''
 tsigmatch.nim(111, 4) Error: type mismatch: got <A, string>
 but expected one of:
@@ -12,34 +12,34 @@ proc f(b: B)
   but expression 'A()' is of type: A
 
 expression: f(A(), "extra")
-tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe, locks: 0.})>
+tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe.})>
 but expected one of:
 proc foo(x: (string, proc ()))
   first type mismatch at position: 1
   required type for x: (string, proc (){.closure.})
-  but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe, locks: 0.})
+  but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe.})
 
 expression: foo(("foobar", proc () = echo(["Hello!"])))
-tsigmatch.nim(132, 11) Error: type mismatch: got <proc (s: string): string{.noSideEffect, gcsafe, locks: 0.}>
+tsigmatch.nim(132, 11) Error: type mismatch: got <proc (s: string): string{.noSideEffect, gcsafe.}>
 but expected one of:
 proc foo[T, S](op: proc (x: T): S {.cdecl.}): auto
   first type mismatch at position: 1
   required type for op: proc (x: T): S{.cdecl.}
-  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe, locks: 0.}
+  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.}
 proc foo[T, S](op: proc (x: T): S {.safecall.}): auto
   first type mismatch at position: 1
   required type for op: proc (x: T): S{.safecall.}
-  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe, locks: 0.}
+  but expression 'fun' is of type: proc (s: string): string{.noSideEffect, gcsafe.}
 
 expression: foo(fun)
-tsigmatch.nim(143, 13) Error: type mismatch: got <array[0..0, proc (x: int){.gcsafe, locks: 0.}]>
+tsigmatch.nim(143, 13) Error: type mismatch: got <array[0..0, proc (x: int){.gcsafe.}]>
 but expected one of:
-proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe, locks: 0.}])
+proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}])
   first type mismatch at position: 1
-  required type for fs: openArray[proc (x: int){.closure, gcsafe, locks: 0.}]
-  but expression '[proc (x: int) {.gcsafe, locks: 0.} = echo [x]]' is of type: array[0..0, proc (x: int){.gcsafe, locks: 0.}]
+  required type for fs: openArray[proc (x: int){.closure, gcsafe.}]
+  but expression '[proc (x: int) {.gcsafe.} = echo [x]]' is of type: array[0..0, proc (x: int){.gcsafe.}]
 
-expression: takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo [x]])
+expression: takesFuncs([proc (x: int) {.gcsafe.} = echo [x]])
 tsigmatch.nim(149, 4) Error: type mismatch: got <int literal(10), a0: int literal(5), string>
 but expected one of:
 proc f(a0: uint8; b: string)
@@ -135,12 +135,12 @@ block:
   # bug #10285 Function signature don't match when inside seq/array/openArray
   # Note: the error message now shows `closure` which helps debugging the issue
   # out why it doesn't match
-  proc takesFunc(f: proc (x: int) {.gcsafe, locks: 0.}) =
+  proc takesFunc(f: proc (x: int) {.gcsafe.}) =
     echo "takes single Func"
-  proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe, locks: 0.}]) =
+  proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe.}]) =
     echo "takes multiple Func"
-  takesFunc(proc (x: int) {.gcsafe, locks: 0.} = echo x)         # works
-  takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo x])      # fails
+  takesFunc(proc (x: int) {.gcsafe.} = echo x)         # works
+  takesFuncs([proc (x: int) {.gcsafe.} = echo x])      # fails
 
 block:
   # bug https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970465
diff --git a/tests/errmsgs/tsigmatch2.nim b/tests/errmsgs/tsigmatch2.nim
index 4996634c9..31c966337 100644
--- a/tests/errmsgs/tsigmatch2.nim
+++ b/tests/errmsgs/tsigmatch2.nim
@@ -14,7 +14,7 @@ proc foo(i: Foo): string
 
 expression: foo(1.2)
 tsigmatch2.nim(40, 14) Error: expression '' has no type (or is ambiguous)
-tsigmatch2.nim(46, 7) Error: type mismatch: got <int literal(1)>
+tsigmatch2.nim(46, 3) Error: type mismatch: got <int literal(1)>
 but expected one of:
 proc foo(args: varargs[string, myproc])
   first type mismatch at position: 1
@@ -44,4 +44,4 @@ block:
     let temp = 12.isNil
   proc foo(args: varargs[string, myproc]) = discard
   foo 1
-static: echo "done"
\ No newline at end of file
+static: echo "done"
diff --git a/tests/misc/tsimtych.nim b/tests/errmsgs/tsimpletypecheck.nim
index 74a6ad4c0..422437d3a 100644
--- a/tests/misc/tsimtych.nim
+++ b/tests/errmsgs/tsimpletypecheck.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: "type mismatch: got <bool> but expected \'string\'"
-  file: "tsimtych.nim"
+  file: "tsimpletypecheck.nim"
   line: 10
 """
 # Test 2
diff --git a/tests/errmsgs/tsubscriptmismatch.nim b/tests/errmsgs/tsubscriptmismatch.nim
new file mode 100644
index 000000000..a2b297b68
--- /dev/null
+++ b/tests/errmsgs/tsubscriptmismatch.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "-d:testsConciseTypeMismatch"
+  nimout: '''
+[1] proc `[]`[T; U, V: Ordinal](s: openArray[T]; x: HSlice[U, V]): seq[T]
+'''
+"""
+
+type Foo = object
+let x = Foo()
+discard x[1] #[tt.Error
+         ^ type mismatch]#
diff --git a/tests/errmsgs/tsubscriptmismatch_legacy.nim b/tests/errmsgs/tsubscriptmismatch_legacy.nim
new file mode 100644
index 000000000..3e1f1eb71
--- /dev/null
+++ b/tests/errmsgs/tsubscriptmismatch_legacy.nim
@@ -0,0 +1,10 @@
+discard """
+  nimout: '''
+  but expression 'x' is of type: Foo
+'''
+"""
+
+type Foo = object
+let x = Foo()
+discard x[1] #[tt.Error
+         ^ type mismatch: got <Foo, int literal(1)>]#
diff --git a/tests/errmsgs/ttupleindexoutofbounds.nim b/tests/errmsgs/ttupleindexoutofbounds.nim
new file mode 100644
index 000000000..ae634dddb
--- /dev/null
+++ b/tests/errmsgs/ttupleindexoutofbounds.nim
@@ -0,0 +1,2 @@
+let a = (1, 2)[4] #[tt.Error
+              ^ invalid index 4 in subscript for tuple of length 2]#
diff --git a/tests/errmsgs/ttypeAllowed.nim b/tests/errmsgs/ttypeAllowed.nim
index 516b616e0..fdb4c70b8 100644
--- a/tests/errmsgs/ttypeAllowed.nim
+++ b/tests/errmsgs/ttypeAllowed.nim
@@ -2,10 +2,10 @@ discard """
 cmd: "nim check $file"
 errormsg: ""
 nimout: '''
-ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for let
-ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for const
-ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for var
-ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for result
+ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for let
+ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for const
+ttypeAllowed.nim(21, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for var
+ttypeAllowed.nim(26, 10) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe.}' for result
 '''
 """
 
diff --git a/tests/errmsgs/tundeclared_routine.nim b/tests/errmsgs/tundeclared_routine.nim
index 2f1320fff..41b1d35f4 100644
--- a/tests/errmsgs/tundeclared_routine.nim
+++ b/tests/errmsgs/tundeclared_routine.nim
@@ -9,7 +9,7 @@ tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma
 tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)]
   found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)]
 tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)]
-tundeclared_routine.nim(44, 15) Error: attempting to call routine: 'bad5'
+tundeclared_routine.nim(44, 11) Error: undeclared identifier: 'bad5'
 '''
 """
 
diff --git a/tests/misc/tnoinst.nim b/tests/errmsgs/tunresolvedinnerproc.nim
index 85db1e8e7..7655a5a41 100644
--- a/tests/misc/tnoinst.nim
+++ b/tests/errmsgs/tunresolvedinnerproc.nim
@@ -1,16 +1,10 @@
-discard """
-  errormsg: "instantiate 'notConcrete' explicitly"
-  line: 12
-  disabled: "true"
-"""
-
 proc wrap[T]() =
   proc notConcrete[T](x, y: int): int =
     var dummy: T
     result = x - y
 
   var x: proc (x, y: T): int
-  x = notConcrete
-
+  x = notConcrete #[tt.Error
+      ^ 'notConcrete' doesn't have a concrete type, due to unspecified generic parameters.]#
 
 wrap[int]()
diff --git a/tests/errmsgs/tuntypedoverload.nim b/tests/errmsgs/tuntypedoverload.nim
new file mode 100644
index 000000000..1b1c2809c
--- /dev/null
+++ b/tests/errmsgs/tuntypedoverload.nim
@@ -0,0 +1,37 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+block:
+  template foo(x: var int, y: untyped) = discard
+  var a: float
+  foo(a, undeclared) #[tt.Error
+     ^ type mismatch: got <float, untyped>]# # `untyped` is arbitary
+  # previous error: undeclared identifier: 'undeclared'
+
+block: # issue #8697
+  type
+    Fruit = enum
+      apple
+      banana
+      orange
+  macro hello(x, y: untyped) = discard
+  hello(apple, banana, orange) #[tt.Error
+       ^ type mismatch: got <Fruit, Fruit, Fruit>]#
+
+block: # issue #23265
+  template declareFoo(fooName: untyped, value: uint16) =
+    const `fooName Value` {.inject.} = value
+
+  declareFoo(FOO, 0xFFFF)
+  declareFoo(BAR, 0xFFFFF) #[tt.Error
+            ^ type mismatch: got <untyped, int literal(1048575)>]#
+
+block: # issue #9620
+  template forLoop(index: untyped, length: int{lvalue}, body: untyped) =
+    for `index`{.inject.} in 0 ..< length:
+      body
+  var x = newSeq[int](10)
+  forLoop(i, x.len): #[tt.Error
+         ^ type mismatch: got <untyped, int, void>]#
+    x[i] = i
diff --git a/tests/errmsgs/twrong_explicit_typeargs.nim b/tests/errmsgs/twrong_explicit_typeargs.nim
new file mode 100644
index 000000000..5236e5f4f
--- /dev/null
+++ b/tests/errmsgs/twrong_explicit_typeargs.nim
@@ -0,0 +1,26 @@
+discard """
+  cmd: "nim c --hints:off -d:testsConciseTypeMismatch $file"
+  action: reject
+  nimout: '''
+twrong_explicit_typeargs.nim(26, 29) Error: type mismatch
+Expression: newImage[string](320, 200)
+  [1] 320: int literal(320)
+  [2] 200: int literal(200)
+
+Expected one of (first mismatch at [position]):
+[1] proc newImage[T: int32 | int64](w, h: int): ref Image[T]
+  generic parameter mismatch, expected int32 or int64 but got 'string' of type: string
+'''
+"""
+
+# bug #4084
+type
+  Image[T] = object
+    data: seq[T]
+
+proc newImage[T: int32|int64](w, h: int): ref Image[T] =
+  new(result)
+  result.data = newSeq[T](w * h)
+
+var correct = newImage[int32](320, 200)
+var wrong = newImage[string](320, 200)
diff --git a/tests/errmsgs/twrong_explicit_typeargs_legacy.nim b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim
new file mode 100644
index 000000000..cfa528c54
--- /dev/null
+++ b/tests/errmsgs/twrong_explicit_typeargs_legacy.nim
@@ -0,0 +1,25 @@
+discard """
+  action: reject
+  nimout: '''
+twrong_explicit_typeargs_legacy.nim(25, 29) Error: type mismatch: got <int literal(320), int literal(200)>
+but expected one of:
+proc newImage[T: int32 | int64](w, h: int): ref Image[T]
+  first type mismatch at position: 1 in generic parameters
+  required type for T: int32 or int64
+  but expression 'string' is of type: string
+
+expression: newImage[string](320, 200)
+'''
+"""
+
+# bug #4084
+type
+  Image[T] = object
+    data: seq[T]
+
+proc newImage[T: int32|int64](w, h: int): ref Image[T] =
+  new(result)
+  result.data = newSeq[T](w * h)
+
+var correct = newImage[int32](320, 200)
+var wrong = newImage[string](320, 200)
diff --git a/tests/exception/m21261.nim b/tests/exception/m21261.nim
new file mode 100644
index 000000000..11b12fb5b
--- /dev/null
+++ b/tests/exception/m21261.nim
@@ -0,0 +1 @@
+raise (ref Exception)(msg: "something")
\ No newline at end of file
diff --git a/tests/exception/m22469.nim b/tests/exception/m22469.nim
new file mode 100644
index 000000000..201698701
--- /dev/null
+++ b/tests/exception/m22469.nim
@@ -0,0 +1,4 @@
+# ModuleB
+echo "First top-level statement of ModuleB"
+echo high(int) + 1
+echo "ModuleB last statement"
\ No newline at end of file
diff --git a/tests/exception/t13115.nim b/tests/exception/t13115.nim
index ee1daed26..5db8f9107 100644
--- a/tests/exception/t13115.nim
+++ b/tests/exception/t13115.nim
@@ -13,13 +13,6 @@ else:
     const nim = getCurrentCompilerExe()
     const file = currentSourcePath
     for b in "c js cpp".split:
-      when defined(openbsd):
-        if b == "js":
-          # xxx bug: pending #13115
-          # remove special case once nodejs updated >= 12.16.2
-          # refs https://github.com/nim-lang/Nim/pull/16167#issuecomment-738270751
-          continue
-
       # save CI time by avoiding mostly redundant combinations as far as this bug is concerned
       var opts = case b
         of "c": @["", "-d:nim_t13115_static", "-d:danger", "-d:debug"]
diff --git a/tests/exception/t20613.nim b/tests/exception/t20613.nim
new file mode 100644
index 000000000..6edb69415
--- /dev/null
+++ b/tests/exception/t20613.nim
@@ -0,0 +1,8 @@
+discard """
+  matrix: "; --panics:on"
+"""
+
+func test =
+  if 0 > 10:
+    raiseAssert "hey"
+test()
diff --git a/tests/exception/t21261.nim b/tests/exception/t21261.nim
new file mode 100644
index 000000000..84817d854
--- /dev/null
+++ b/tests/exception/t21261.nim
@@ -0,0 +1,9 @@
+discard """
+  exitcode: 1
+  outputsub: '''
+m21261.nim(1)            m21261
+Error: unhandled exception: something [Exception]
+'''
+"""
+
+import m21261
\ No newline at end of file
diff --git a/tests/exception/t22008.nim b/tests/exception/t22008.nim
new file mode 100644
index 000000000..c0758e7b4
--- /dev/null
+++ b/tests/exception/t22008.nim
@@ -0,0 +1,8 @@
+template detect(v: untyped) =
+  doAssert typeof(v) is int
+
+detect:
+  try:
+    raise (ref ValueError)()
+  except ValueError:
+    42
\ No newline at end of file
diff --git a/tests/exception/t22469.nim b/tests/exception/t22469.nim
new file mode 100644
index 000000000..a76c74967
--- /dev/null
+++ b/tests/exception/t22469.nim
@@ -0,0 +1,16 @@
+discard """
+  exitcode: 1
+  output: '''
+First top-level statement of ModuleB
+m22469.nim(3)            m22469
+fatal.nim(53)            sysFatal
+Error: unhandled exception: over- or underflow [OverflowDefect]
+'''
+"""
+
+# bug #22469
+
+# ModuleA
+import m22469
+echo "ModuleA about to have exception"
+echo high(int) + 1
diff --git a/tests/exception/texceptionbreak.nim b/tests/exception/texceptionbreak.nim
index 6548192c6..b8ce7eead 100644
--- a/tests/exception/texceptionbreak.nim
+++ b/tests/exception/texceptionbreak.nim
@@ -29,16 +29,16 @@ echo "2"
 try:
   raise newException(OSError, "Problem")
 except OSError:
-  block:
-    break
+  block label:
+    break label
 
 echo "3"
 
 # Fourth Variety
-block:
+block label:
   try:
     raise newException(OSError, "Problem")
   except OSError:
-    break
+    break label
 
 echo "4"
diff --git a/tests/exprs/t22604.nim b/tests/exprs/t22604.nim
new file mode 100644
index 000000000..c41cd3dfa
--- /dev/null
+++ b/tests/exprs/t22604.nim
@@ -0,0 +1,36 @@
+# if
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      if true:
+        continue
+      else:
+        raiseAssert "Won't get here"
+
+# nested case
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      case true
+      of true:
+        continue
+      of false:
+        raiseAssert "Won't get here"
+
+# try except
+for i in 0..<1:
+  let x =
+    case false
+    of true:
+      42
+    of false:
+      try:
+        continue
+      except:
+        raiseAssert "Won't get here"
\ No newline at end of file
diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim
index 5bedca371..2bb61eb58 100644
--- a/tests/float/tfloat4.nim
+++ b/tests/float/tfloat4.nim
@@ -8,7 +8,7 @@ proc c_sprintf(buf, fmt: cstring) {.importc:"sprintf", header: "<stdio.h>", vara
 
 proc floatToStr(f: float64): string =
   var buffer: array[128, char]
-  c_sprintf(addr buffer, "%.16e", f)
+  c_sprintf(cast[cstring](addr buffer), "%.16e", f)
   result = ""
   for ch in buffer:
     if ch == '\0':
@@ -56,8 +56,14 @@ doAssert 0.9999999999999999 == ".9999999999999999".parseFloat
 
 # bug #18400
 var s = [-13.888888'f32]
-assert $s[0] == "-13.888888"
+doAssert $s[0] == "-13.888888"
 var x = 1.23456789012345'f32
-assert $x == "1.2345679"
+doAssert $x == "1.2345679"
+
+# bug #21847
+doAssert parseFloat"0e+42" == 0.0
+doAssert parseFloat"0e+42949672969" == 0.0
+doAssert parseFloat"0e+42949672970" == 0.0
+doAssert parseFloat"0e+42949623223346323563272970" == 0.0
 
 echo("passed all tests.")
diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim
index 480396e81..aaed2d615 100644
--- a/tests/float/tfloats.nim
+++ b/tests/float/tfloats.nim
@@ -41,8 +41,7 @@ template main =
     test ".1", 0.1
     test "-.1", -0.1
     test "-0", -0.0
-    when false: # pending bug #18246
-      test "-0", -0.0
+    test "-0", -0'f # see #18246, -0 won't work
     test ".1e-1", 0.1e-1
     test "0_1_2_3.0_1_2_3E+0_1_2", 123.0123e12
     test "0_1_2.e-0", 12e0
@@ -157,9 +156,8 @@ template main =
         when nimvm:
           discard # xxx, refs #12884
         else:
-          when not defined(js):
-            doAssert x == 1.2345679'f32
-            doAssert $x == "1.2345679"
+          doAssert x == 1.2345679'f32
+          doAssert $x == "1.2345679"
 
 static: main()
 main()
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim
index 0265431d0..e67beb513 100644
--- a/tests/gc/closureleak.nim
+++ b/tests/gc/closureleak.nim
@@ -11,9 +11,19 @@ var foo_counter = 0
 var alive_foos = newseq[int](0)
 
 when defined(gcDestructors):
-  proc `=destroy`(some: var TFoo) =
+  proc `=destroy`(some: TFoo) =
     alive_foos.del alive_foos.find(some.id)
-    `=destroy`(some.fn)
+    # TODO: fixme: investigate why `=destroy` requires `some.fn` to be `gcsafe`
+    # the debugging info below came from `symPrototype` in the liftdestructors
+    # proc (){.closure, gcsafe.}, {tfThread, tfHasAsgn, tfCheckedForDestructor, tfExplicitCallConv}
+    # var proc (){.closure, gcsafe.}, {tfHasGCedMem}
+    # it worked by accident with var T destructors because in the sempass2
+    #
+    # let argtype = skipTypes(a.typ, abstractInst) # !!! it does't skip `tyVar`
+    # if argtype.kind == tyProc and notGcSafe(argtype) and not tracked.inEnforcedGcSafe:
+    #   localError(tracked.config, n.info, $n & " is not GC safe")
+    {.cast(gcsafe).}:
+      `=destroy`(some.fn)
 
 else:
   proc free*(some: ref TFoo) =
diff --git a/tests/gc/tdisable_orc.nim b/tests/gc/tdisable_orc.nim
new file mode 100644
index 000000000..b5f161c79
--- /dev/null
+++ b/tests/gc/tdisable_orc.nim
@@ -0,0 +1,9 @@
+discard """
+  joinable: false
+"""
+
+import std/asyncdispatch
+
+# bug #22256
+GC_disableMarkAndSweep()
+waitFor sleepAsync(1000)
diff --git a/tests/generics/m14509.nim b/tests/generics/m14509.nim
new file mode 100644
index 000000000..cabc4f308
--- /dev/null
+++ b/tests/generics/m14509.nim
@@ -0,0 +1,16 @@
+import macros
+
+type float32x4 = array[4, float32]
+type float32x8 = array[8, float32]
+
+{.experimental: "dynamicBindSym".}
+macro dispatch(N: static int, T: type SomeNumber): untyped =
+  let BaseT = getTypeInst(T)[1]
+  result = bindSym($BaseT & "x" & $N)
+
+type
+  VecIntrin*[N: static int, T: SomeNumber] = dispatch(N, T)
+
+func `$`*[N, T](vec: VecIntrin[N, T]): string =
+  ## Display a vector
+  $cast[array[N, T]](vec)
diff --git a/tests/generics/m22373a.nim b/tests/generics/m22373a.nim
new file mode 100644
index 000000000..28e087ca6
--- /dev/null
+++ b/tests/generics/m22373a.nim
@@ -0,0 +1,7 @@
+# module a for t22373
+
+# original:
+type LightClientHeader* = object
+
+# simplified:
+type TypeOrTemplate* = object
diff --git a/tests/generics/m22373b.nim b/tests/generics/m22373b.nim
new file mode 100644
index 000000000..67ee4211b
--- /dev/null
+++ b/tests/generics/m22373b.nim
@@ -0,0 +1,18 @@
+# module b for t22373
+
+import m22373a
+
+# original:
+type
+  LightClientDataFork* {.pure.} = enum
+    None = 0,
+    Altair = 1
+template LightClientHeader*(kind: static LightClientDataFork): auto =
+  when kind == LightClientDataFork.Altair:
+    typedesc[m22373a.LightClientHeader]
+  else:
+    static: raiseAssert "Unreachable"
+
+# simplified:
+template TypeOrTemplate*(num: int): untyped =
+  typedesc[m22373a.TypeOrTemplate]
diff --git a/tests/generics/m3770.nim b/tests/generics/m3770.nim
new file mode 100644
index 000000000..7f5714a2b
--- /dev/null
+++ b/tests/generics/m3770.nim
@@ -0,0 +1,11 @@
+type
+  Noice* = object
+    hidden: int
+
+template jjj*: Noice =
+  Noice(hidden: 15)
+
+type Opt* = object
+  o: int
+
+template none*(O: type Opt): Opt = Opt(o: 0)
diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim
index 3112c133f..090b97771 100644
--- a/tests/generics/mdotlookup.nim
+++ b/tests/generics/mdotlookup.nim
@@ -14,3 +14,15 @@ var intset = initHashSet[int]()
 proc fn*[T](a: T) =
   if a in intset: echo("true")
   else: echo("false")
+
+import strutils
+
+proc doStrip*[T](a: T): string =
+  result = ($a).strip()
+
+type Foo = int32
+proc baz2*[T](y: int): auto =
+  result = y.Foo
+
+proc set*(x: var int, a, b: string) =
+  x = a.len + b.len
diff --git a/tests/friends/mfriends.nim b/tests/generics/mfriends.nim
index 19672289e..19672289e 100644
--- a/tests/friends/mfriends.nim
+++ b/tests/generics/mfriends.nim
diff --git a/tests/generics/mopensymimport1.nim b/tests/generics/mopensymimport1.nim
new file mode 100644
index 000000000..912db1302
--- /dev/null
+++ b/tests/generics/mopensymimport1.nim
@@ -0,0 +1,34 @@
+type
+  Result*[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr*[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
diff --git a/tests/generics/mopensymimport2.nim b/tests/generics/mopensymimport2.nim
new file mode 100644
index 000000000..c17aafd00
--- /dev/null
+++ b/tests/generics/mopensymimport2.nim
@@ -0,0 +1,16 @@
+{.experimental: "openSym".}
+
+import mopensymimport1
+
+type Xxx = enum
+  error
+  value
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g*(T: type): string =
+  let x = f().valueOr:
+    return $error
+
+  "ok"
diff --git a/tests/generics/mtypenodes.nim b/tests/generics/mtypenodes.nim
new file mode 100644
index 000000000..e1132241b
--- /dev/null
+++ b/tests/generics/mtypenodes.nim
@@ -0,0 +1,6 @@
+# issue #22699
+
+type Private = distinct int
+
+proc chop*[T](x: int): int =
+  cast[int](cast[tuple[field: Private]](x))
diff --git a/tests/generics/muninstantiatedgenericcalls.nim b/tests/generics/muninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..caed07c98
--- /dev/null
+++ b/tests/generics/muninstantiatedgenericcalls.nim
@@ -0,0 +1,26 @@
+import std/bitops
+
+const
+  lengths = block:
+    var v: array[64, int8]
+    for i in 0..<64:
+      v[i] = int8((i + 7) div 7)
+    v
+
+type
+  Leb128* = object
+
+{.push checks: off.}
+func len(T: type Leb128, x: SomeUnsignedInt): int8 =
+  if x == 0: 1
+  else: lengths[fastLog2(x)]
+{.pop.}
+
+# note private to test scoping issue:
+func maxLen(T: type Leb128, I: type): int8 =
+  Leb128.len(I.high)
+
+type
+  Leb128Buf*[T: SomeUnsignedInt] = object
+    data*: array[maxLen(Leb128, T), byte] 
+    len*: int8
diff --git a/tests/generics/t12938.nim b/tests/generics/t12938.nim
new file mode 100644
index 000000000..e09d65c7a
--- /dev/null
+++ b/tests/generics/t12938.nim
@@ -0,0 +1,9 @@
+type
+  ExampleArray[Size, T] = array[Size, T]
+
+var integerArray: ExampleArray[32, int]  # Compiler crash!
+doAssert integerArray.len == 32
+
+const Size = 2
+var integerArray2: ExampleArray[Size, int]
+doAssert integerArray2.len == 2
diff --git a/tests/generics/t14193.nim b/tests/generics/t14193.nim
new file mode 100644
index 000000000..213b1a8e6
--- /dev/null
+++ b/tests/generics/t14193.nim
@@ -0,0 +1,6 @@
+type
+  Task*[N: int] = object
+    env*: array[N, byte]
+
+var task14193: Task[20]
+doAssert task14193.env.len == 20
diff --git a/tests/generics/t14509.nim b/tests/generics/t14509.nim
new file mode 100644
index 000000000..ef3143ee4
--- /dev/null
+++ b/tests/generics/t14509.nim
@@ -0,0 +1,4 @@
+import m14509
+
+var v: VecIntrin[4, float32]
+doAssert $v == "[0.0, 0.0, 0.0, 0.0]"
diff --git a/tests/generics/t1500.nim b/tests/generics/t1500.nim
new file mode 100644
index 000000000..6dd457d33
--- /dev/null
+++ b/tests/generics/t1500.nim
@@ -0,0 +1,8 @@
+#issue 1500
+
+type
+  TFtpBase*[SockType] = object
+    job: TFTPJob[SockType]
+  PFtpBase*[SockType] = ref TFtpBase[SockType]
+  TFtpClient* = TFtpBase[string]
+  TFTPJob[T] = object
\ No newline at end of file
diff --git a/tests/generics/t17509.nim b/tests/generics/t17509.nim
new file mode 100644
index 000000000..89f507577
--- /dev/null
+++ b/tests/generics/t17509.nim
@@ -0,0 +1,25 @@
+type List[O] = object
+  next: ptr List[O]
+
+proc initList[O](l: ptr List[O]) =
+  l[].next = l
+
+type
+  PolytopeVertex[R] = object
+    list: List[PolytopeVertex[R]]
+
+  PolytopeEdge[R] = object
+    list: List[PolytopeEdge[R]]
+
+  Polytope[R] = object
+    vertices: List[PolytopeVertex[R]]
+    edges: List[PolytopeEdge[R]]
+
+var pt: Polytope[float]
+
+static:
+  doAssert pt.vertices.next is (ptr List[PolytopeVertex[float]])
+  doAssert pt.edges.next is (ptr List[PolytopeEdge[float]])
+
+initList(addr pt.vertices)
+initList(addr pt.edges)
\ No newline at end of file
diff --git a/tests/generics/t18823.nim b/tests/generics/t18823.nim
new file mode 100644
index 000000000..94c79aebe
--- /dev/null
+++ b/tests/generics/t18823.nim
@@ -0,0 +1,6 @@
+type BitsRange[T] = range[0..sizeof(T)*8-1]
+
+proc bar[T](a: T; b: BitsRange[T]) =
+  discard
+
+bar(1, 2.Natural)
diff --git a/tests/generics/t19848.nim b/tests/generics/t19848.nim
new file mode 100644
index 000000000..f80f0e298
--- /dev/null
+++ b/tests/generics/t19848.nim
@@ -0,0 +1,16 @@
+discard """
+  output: '''
+todo
+'''
+"""
+
+type
+  Maybe[T] = object
+  List[T] = object
+
+proc dump[M: Maybe](a: List[M]) =
+  echo "todo"
+
+var a: List[Maybe[int]]
+  
+dump(a)
diff --git a/tests/generics/t20996.nim b/tests/generics/t20996.nim
new file mode 100644
index 000000000..8aa83c4e2
--- /dev/null
+++ b/tests/generics/t20996.nim
@@ -0,0 +1,15 @@
+discard """
+  action: compile
+"""
+
+import std/macros
+
+macro matchMe(x: typed): untyped =
+  discard x.getTypeImpl
+
+type
+  ElementRT = object
+  Element[Z] = ElementRT # this version is needed, even though we don't use it
+
+let ar = ElementRT()
+matchMe(ar)
diff --git a/tests/generics/t21742.nim b/tests/generics/t21742.nim
new file mode 100644
index 000000000..c49c8ee97
--- /dev/null
+++ b/tests/generics/t21742.nim
@@ -0,0 +1,10 @@
+type
+  Foo[T] = object
+    x:T
+  Bar[T,R] = Foo[T]
+  Baz = Bar[int,float]
+
+proc qux[T,R](x: Bar[T,R]) = discard
+
+var b:Baz
+b.qux()
\ No newline at end of file
diff --git a/tests/generics/t21760.nim b/tests/generics/t21760.nim
new file mode 100644
index 000000000..5343279bb
--- /dev/null
+++ b/tests/generics/t21760.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+type Url = object
+
+proc myInit(_: type[Url], params = default(Table[string, string])): Url =
+  discard
+
+discard myInit(Url)
\ No newline at end of file
diff --git a/tests/generics/t21958.nim b/tests/generics/t21958.nim
new file mode 100644
index 000000000..f566b57cb
--- /dev/null
+++ b/tests/generics/t21958.nim
@@ -0,0 +1,11 @@
+discard """
+  action: compile
+"""
+
+type
+  Ct*[T: SomeUnsignedInt] = distinct T
+
+template `shr`*[T: Ct](x: T, y: SomeInteger): T = T(T.T(x) shr y)
+
+var x: Ct[uint64]
+let y {.used.} = x shr 2
\ No newline at end of file
diff --git a/tests/generics/t22373.nim b/tests/generics/t22373.nim
new file mode 100644
index 000000000..ecfaf0f1b
--- /dev/null
+++ b/tests/generics/t22373.nim
@@ -0,0 +1,16 @@
+# issue #22373
+
+import m22373a
+import m22373b
+
+# original:
+template lazy_header(name: untyped): untyped {.dirty.} =
+  var `name _ ptr`: ptr[data_fork.LightClientHeader]  # this data_fork.Foo part seems required to reproduce
+proc createLightClientUpdates(data_fork: static LightClientDataFork) =
+  lazy_header(attested_header)
+createLightClientUpdates(LightClientDataFork.Altair)
+
+# simplified:
+proc generic[T](abc: T) =
+  var x: abc.TypeOrTemplate
+generic(123)
diff --git a/tests/generics/t22826.nim b/tests/generics/t22826.nim
new file mode 100644
index 000000000..914d4243a
--- /dev/null
+++ b/tests/generics/t22826.nim
@@ -0,0 +1,8 @@
+import std/tables
+
+var a: Table[string, float]
+
+type Value*[T] = object
+  table: Table[string, Value[T]]
+
+discard toTable({"a": Value[float]()})
\ No newline at end of file
diff --git a/tests/generics/t23186.nim b/tests/generics/t23186.nim
new file mode 100644
index 000000000..76f38da6b
--- /dev/null
+++ b/tests/generics/t23186.nim
@@ -0,0 +1,155 @@
+# issue #23186
+
+block: # simplified
+  template typedTempl(x: int, body): untyped =
+    body
+  proc generic1[T]() =
+    discard
+  proc generic2[T]() =
+    typedTempl(1):
+      let x = generic1[T]
+  generic2[int]()
+
+import std/macros
+
+when not compiles(len((1, 2))):
+  import std/typetraits
+
+  func len(x: tuple): int =
+    arity(type(x))
+
+block: # full issue example
+  type FieldDescription = object
+    name: NimNode
+  func isTuple(t: NimNode): bool =
+    t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+  proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                                n: NimNode,
+                                parentCaseField: NimNode = nil,
+                                parentCaseBranch: NimNode = nil,
+                                isDiscriminator = false) =
+    case n.kind
+    of nnkRecList:
+      for entry in n:
+        collectFieldsFromRecList result, entry,
+                                parentCaseField, parentCaseBranch
+    of nnkIdentDefs:
+      for i in 0 ..< n.len - 2:
+        var field: FieldDescription
+        field.name = n[i]
+        if field.name.kind == nnkPragmaExpr:
+          field.name = field.name[0]
+        if field.name.kind == nnkPostfix:
+          field.name = field.name[1]
+        result.add field
+    of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+      discard
+    else:
+      doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+  proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                                objectType: NimNode) =
+    var objectType = objectType
+    if objectType.kind == nnkRefTy:
+      objectType = objectType[0]
+    let recList = objectType[2]
+    collectFieldsFromRecList result, recList
+  proc recordFields(typeImpl: NimNode): seq[FieldDescription] =
+    let objectType = case typeImpl.kind
+      of nnkObjectTy: typeImpl
+      of nnkTypeDef: typeImpl[2]
+      else:
+        macros.error("object type expected", typeImpl)
+        return
+    collectFieldsInHierarchy(result, objectType)
+  proc skipPragma(n: NimNode): NimNode =
+    if n.kind == nnkPragmaExpr: n[0]
+    else: n
+  func declval(T: type): T =
+    doAssert false,
+      "declval should be used only in `typeof` expressions and concepts"
+    default(ptr T)[]
+  macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+    var typeAst = getType(T)[1]
+    var typeImpl: NimNode
+    let isSymbol = not typeAst.isTuple
+    if not isSymbol:
+      typeImpl = typeAst
+    else:
+      typeImpl = getImpl(typeAst)
+    result = newStmtList()
+    var i = 0
+    for field in recordFields(typeImpl):
+      let
+        fieldIdent = field.name
+        realFieldName = newLit($fieldIdent.skipPragma)
+        fieldName = realFieldName
+        fieldIndex = newLit(i)
+      let fieldNameDefs =
+        if isSymbol:
+          quote:
+            const fieldName {.inject, used.} = `fieldName`
+            const realFieldName {.inject, used.} = `realFieldName`
+        else:
+          quote:
+            const fieldName {.inject, used.} = $`fieldIndex`
+            const realFieldName {.inject, used.} = $`fieldIndex`
+            # we can't access .Fieldn, so our helper knows
+            # to parseInt this
+      let field =
+        if isSymbol:
+          quote do: declval(`T`).`fieldIdent`
+        else:
+          quote do: declval(`T`)[`fieldIndex`]
+      result.add quote do:
+        block:
+          `fieldNameDefs`
+          type FieldType {.inject, used.} = type(`field`)
+          `body`
+      i += 1
+  template enumAllSerializedFields(T: type, body): untyped =
+    when T is ref|ptr:
+      type TT = type(default(T)[])
+      enumAllSerializedFieldsImpl(TT, body)
+    else:
+      enumAllSerializedFieldsImpl(T, body)
+  type
+    MemRange = object
+      startAddr: ptr byte
+      length: int
+    SszNavigator[T] = object
+      m: MemRange
+  func sszMount(data: openArray[byte], T: type): SszNavigator[T] =
+    let startAddr = unsafeAddr data[0]
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  func sszMount(data: openArray[char], T: type): SszNavigator[T] =
+    let startAddr = cast[ptr byte](unsafeAddr data[0])
+    SszNavigator[T](m: MemRange(startAddr: startAddr, length: data.len))
+  template sszMount(data: MemRange, T: type): SszNavigator[T] =
+    SszNavigator[T](m: data)
+  func navigateToField[T](
+      n: SszNavigator[T],
+      FieldType: type): SszNavigator[FieldType] =
+    default(SszNavigator[FieldType])
+  type
+    FieldInfo = ref object
+      navigator: proc (m: MemRange): MemRange {.
+        gcsafe, noSideEffect, raises: [IOError] .}
+  func fieldNavigatorImpl[RecordType; FieldType; fieldName: static string](
+      m: MemRange): MemRange =
+    var typedNavigator = sszMount(m, RecordType)
+    discard navigateToField(typedNavigator, FieldType)
+    default(MemRange)
+  func genTypeInfo(T: type) =
+    when T is object:
+      enumAllSerializedFields(T):
+        discard FieldInfo(navigator: fieldNavigatorImpl[T, FieldType, fieldName])
+  type
+    Foo = object
+      bar: Bar
+    BarList = seq[uint64]
+    Bar = object
+      b: BarList
+      baz: Baz
+    Baz = object
+      i: uint64
+  genTypeInfo(Foo)
diff --git a/tests/generics/t23790.nim b/tests/generics/t23790.nim
new file mode 100644
index 000000000..9ac0df6a1
--- /dev/null
+++ b/tests/generics/t23790.nim
@@ -0,0 +1,14 @@
+# bug #23790
+
+discard compiles($default(seq[seq[ref int]]))
+discard compiles($default(seq[seq[ref uint]]))
+discard compiles($default(seq[seq[ref int8]]))
+discard compiles($default(seq[seq[ref uint8]]))
+discard compiles($default(seq[seq[ref int16]]))
+discard compiles($default(seq[seq[ref uint16]]))
+discard compiles($default(seq[seq[ref int32]]))
+discard compiles($default(seq[seq[ref uint32]]))
+discard compiles($default(seq[seq[ref int64]]))
+discard compiles($default(seq[seq[ref uint64]]))
+proc s(_: int | string) = discard
+s(0)
diff --git a/tests/generics/t23853.nim b/tests/generics/t23853.nim
new file mode 100644
index 000000000..bc9514a53
--- /dev/null
+++ b/tests/generics/t23853.nim
@@ -0,0 +1,91 @@
+# issue #23853
+
+block simplified:
+  type QuadraticExt[F] = object
+    coords: array[2, F]
+  template Name(E: type QuadraticExt): int = 123
+  template getBigInt(Name: static int): untyped = int
+  type Foo[GT] = object
+    a: getBigInt(GT.Name)
+  var x: Foo[QuadraticExt[int]]
+  
+import std/macros
+
+type
+  Algebra* = enum
+    BN254_Snarks
+    BLS12_381
+
+  Fp*[Name: static Algebra] = object
+    limbs*: array[4, uint64]
+
+  QuadraticExt*[F] = object
+    ## Quadratic Extension field
+    coords*: array[2, F]
+
+  CubicExt*[F] = object
+    ## Cubic Extension field
+    coords*: array[3, F]
+
+  ExtensionField*[F] = QuadraticExt[F] or CubicExt[F]
+
+  Fp2*[Name: static Algebra] =
+    QuadraticExt[Fp[Name]]
+
+  Fp4*[Name: static Algebra] =
+    QuadraticExt[Fp2[Name]]
+
+  Fp6*[Name: static Algebra] =
+    CubicExt[Fp2[Name]]
+
+  Fp12*[Name: static Algebra] =
+    CubicExt[Fp4[Name]]
+    # QuadraticExt[Fp6[Name]]
+
+template Name*(E: type ExtensionField): Algebra =
+  E.F.Name
+
+const BLS12_381_Order = [uint64 0x1, 0x2, 0x3, 0x4]
+const BLS12_381_Modulus = [uint64 0x5, 0x6, 0x7, 0x8]
+
+
+{.experimental: "dynamicBindSym".}
+
+macro baseFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Modulus")
+
+macro scalarFieldModulus*(Name: static Algebra): untyped =
+  result = bindSym($Name & "_Order")
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+template getBigInt*(Name: static Algebra, kind: static FieldKind): untyped =
+  # Workaround:
+  # in `ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]
+  # EC.getScalarField is not accepted by the compiler
+  #
+  # and `ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits]]` gets undeclared field: 'Name'
+  #
+  # but `ptr UncheckedArray[getBigInt(EC.getName(), kScalarField)]` works fine
+  when kind == kBaseField:
+    Name.baseFieldModulus().typeof()
+  else:
+    Name.scalarFieldModulus().typeof()
+
+# ------------------------------------------------------------------------------
+
+type BenchMultiexpContext*[GT] = object
+  elems: seq[GT]
+  exponents: seq[getBigInt(GT.Name, kScalarField)]
+
+proc createBenchMultiExpContext*(GT: typedesc, inputSizes: openArray[int]): BenchMultiexpContext[GT] =
+  discard
+
+# ------------------------------------------------------------------------------
+
+proc main() =
+  let ctx = createBenchMultiExpContext(Fp12[BLS12_381], [2, 4, 8, 16])
+
+main()
diff --git a/tests/generics/t23854.nim b/tests/generics/t23854.nim
new file mode 100644
index 000000000..f1175c8b2
--- /dev/null
+++ b/tests/generics/t23854.nim
@@ -0,0 +1,71 @@
+# issue #23854, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+type FieldKind* = enum
+  kBaseField
+  kScalarField
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+template getScalarField*(EC: type EC_ShortW_Aff): untyped =
+  Fr[EC.F.Name]
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity1*: ptr UncheckedArray[BigInt[EC.getScalarField().bits()]]  # Error: in expression 'EC.getScalarField()': identifier expected, but found 'EC.getScalarField'
+    rootsOfUnity2*: ptr UncheckedArray[BigInt[getScalarField(EC).bits()]] # Compiler SIGSEGV: Illegal Storage Access
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity1) == 255
+  doAssert getBits(ctx.rootsOfUnity2) == 255
+  doAssert ctx.rootsOfUnity1[0].limbs.len == wordsRequired(255)
+  doAssert ctx.rootsOfUnity2[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t23855.nim b/tests/generics/t23855.nim
new file mode 100644
index 000000000..da8135a98
--- /dev/null
+++ b/tests/generics/t23855.nim
@@ -0,0 +1,61 @@
+# issue #23855, not entirely fixed
+
+import std/bitops
+
+const WordBitWidth = sizeof(pointer) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(uint32(WordBitWidth))
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  Algebra* = enum
+    BLS12_381
+
+  BigInt*[bits: static int] = object
+    limbs*: array[wordsRequired(bits), uint]
+
+  Fr*[Name: static Algebra] = object
+    residue_form*: BigInt[255]
+
+  Fp*[Name: static Algebra] = object
+    residue_form*: BigInt[381]
+
+  FF*[Name: static Algebra] = Fp[Name] or Fr[Name]
+
+template getBigInt*[Name: static Algebra](T: type FF[Name]): untyped =
+  ## Get the underlying BigInt type.
+  typeof(default(T).residue_form)
+
+type
+  EC_ShortW_Aff*[F] = object
+    ## Elliptic curve point for a curve in Short Weierstrass form
+    ##   y² = x³ + a x + b
+    ##
+    ## over a field F
+    x*, y*: F
+
+func bits*[Name: static Algebra](T: type FF[Name]): static int =
+  T.getBigInt().bits
+
+# ------------------------------------------------------------------------------
+
+type
+  ECFFT_Descriptor*[EC] = object
+    ## Metadata for FFT on Elliptic Curve
+    order*: int
+    rootsOfUnity*: ptr UncheckedArray[BigInt[Fr[EC.F.Name].bits()]] # Undeclared identifier `Name`
+
+func new*(T: type ECFFT_Descriptor): T =
+  discard
+
+# ------------------------------------------------------------------------------
+
+template getBits[bits: static int](x: ptr UncheckedArray[BigInt[bits]]): int = bits
+
+proc main() =
+  let ctx = ECFFT_Descriptor[EC_ShortW_Aff[Fp[BLS12_381]]].new()
+  doAssert getBits(ctx.rootsOfUnity) == 255
+  doAssert ctx.rootsOfUnity[0].limbs.len == wordsRequired(255)
+
+main()
diff --git a/tests/generics/t3770.nim b/tests/generics/t3770.nim
new file mode 100644
index 000000000..ffccbeeb5
--- /dev/null
+++ b/tests/generics/t3770.nim
@@ -0,0 +1,13 @@
+# bug #3770
+import m3770
+
+doAssert $jjj() == "(hidden: 15)"  # works
+
+proc someGeneric(_: type) =
+  doAssert $jjj() == "(hidden: 15)" # fails: "Error: the field 'hidden' is not accessible."
+
+someGeneric(int)
+
+# bug #20900
+proc c(y: int | int, w: Opt = Opt.none) = discard
+c(0)
diff --git a/tests/generics/t500.nim b/tests/generics/t500.nim
new file mode 100644
index 000000000..2486359aa
--- /dev/null
+++ b/tests/generics/t500.nim
@@ -0,0 +1,8 @@
+discard """
+action: compile
+"""
+
+type
+  TTest = tuple[x: range[0..80], y: range[0..25]]
+
+let x: TTest = (2, 23)
diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim
index 991f39dd1..fb7db22f8 100644
--- a/tests/generics/t6137.nim
+++ b/tests/generics/t6137.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "\'vectFunc\' doesn't have a concrete type, due to unspecified generic parameters."
-  line: 28
+  errormsg: "cannot instantiate: 'T'"
+  line: 19
 """
 
 type
diff --git a/tests/generics/t6637.nim b/tests/generics/t6637.nim
new file mode 100644
index 000000000..dd4f339a2
--- /dev/null
+++ b/tests/generics/t6637.nim
@@ -0,0 +1,9 @@
+
+type
+  Grid2D*[I: SomeInteger, w, h: static[I], T] = object
+    grid: array[w, array[h, T]]
+  Grid2DIns = Grid2D[int, 2, 3, uint8]
+
+let a = Grid2DIns()
+doAssert a.grid.len == 2
+doAssert a.grid[0].len == 3
diff --git a/tests/generics/t7446.nim b/tests/generics/t7446.nim
new file mode 100644
index 000000000..71aa8f0e8
--- /dev/null
+++ b/tests/generics/t7446.nim
@@ -0,0 +1,10 @@
+proc foo(x: Natural or SomeUnsignedInt):int = 
+  when x is int:
+    result = 1
+  else:
+    result = 2
+let a = 10
+doAssert foo(a) == 1
+
+let b = 10'u8
+doAssert foo(b) == 2
\ No newline at end of file
diff --git a/tests/generics/t7839.nim b/tests/generics/t7839.nim
new file mode 100644
index 000000000..4d17b438b
--- /dev/null
+++ b/tests/generics/t7839.nim
@@ -0,0 +1,9 @@
+type A[I: SomeOrdinal, E] = tuple # same for object
+  length: int
+
+doAssert A.sizeof == sizeof(int) # works without the following proc
+
+proc newA*[I: SomeOrdinal, E](): A[I, E] = # works without `SomeOrdinal`
+  discard
+
+discard newA[uint8, int]()
diff --git a/tests/generics/taliashijack.nim b/tests/generics/taliashijack.nim
new file mode 100644
index 000000000..fdebadded
--- /dev/null
+++ b/tests/generics/taliashijack.nim
@@ -0,0 +1,8 @@
+# issue #23977
+
+type Foo[T] = int
+
+proc foo(T: typedesc) =
+  var a: T
+
+foo(int)
diff --git a/tests/generics/tbadcache.nim b/tests/generics/tbadcache.nim
new file mode 100644
index 000000000..33e65be3a
--- /dev/null
+++ b/tests/generics/tbadcache.nim
@@ -0,0 +1,26 @@
+# issue #16128
+
+import std/[tables, hashes]
+
+type
+  NodeId*[L] = object
+    isSource: bool
+    index: Table[NodeId[L], seq[NodeId[L]]]
+
+func hash*[L](id: NodeId[L]): Hash = discard
+func `==`[L](a, b: NodeId[L]): bool = discard
+
+proc makeIndex*[T, L](tree: T) =
+  var parent = NodeId[L]()
+  var tmp: Table[NodeId[L], seq[NodeId[L]]]
+  tmp[parent] = @[parent]
+
+proc simpleTreeDiff*[T, L](source, target: T) =
+  # Swapping these two lines makes error disappear
+  var m: Table[NodeId[L], NodeId[L]]
+  makeIndex[T, L](target)
+
+var tmp: Table[string, seq[string]] # removing this forward declaration also removes error
+
+proc diff(x1, x2: string): auto =
+  simpleTreeDiff[int, string](12, 12)
diff --git a/tests/generics/tbaddeprecated.nim b/tests/generics/tbaddeprecated.nim
new file mode 100644
index 000000000..335234a25
--- /dev/null
+++ b/tests/generics/tbaddeprecated.nim
@@ -0,0 +1,55 @@
+discard """
+  output: '''
+not deprecated
+not deprecated
+not error
+not error
+'''
+"""
+
+# issue #21724
+
+block: # deprecated
+  {.push warningAsError[Deprecated]: on.}
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not deprecated"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not deprecated"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.deprecated: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not deprecated"
+  gen3(SomeObj(hey: true))
+  {.pop.}
+block: # error
+  type
+    SomeObj = object
+      hey: bool
+  proc hey() {.error: "Shouldn't use this".} = echo "hey"
+  proc gen(o: auto) =
+    doAssert not compiles(o.hey())
+    if o.hey:
+      echo "not error"
+  gen(SomeObj(hey: true))
+  doAssert not (compiles do:
+    proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+    proc gen2(o: auto) =
+      if o.hey():
+        echo "not error"
+    gen2(SomeObj(hey: true)))
+  proc hey(o: SomeObj) {.error: "Shouldn't use this".} = echo "hey"
+  proc gen3(o: auto) =
+    if o.hey:
+      echo "not error"
+  gen3(SomeObj(hey: true))
diff --git a/tests/generics/tbracketinstantiation.nim b/tests/generics/tbracketinstantiation.nim
new file mode 100644
index 000000000..22a86af4c
--- /dev/null
+++ b/tests/generics/tbracketinstantiation.nim
@@ -0,0 +1,86 @@
+discard """
+  nimout: '''
+type
+  Bob = object
+type
+  Another = object
+'''
+"""
+
+block: # issue #22645
+  type
+    Opt[T] = object
+    FutureBase = ref object of RootObj
+    Future[T] = ref object of FutureBase ## Typed future.
+      internalValue: T ## Stored value
+  template err[T](E: type Opt[T]): E = E()
+  proc works(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      Future[Opt[int]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+  proc breaks(): Future[Opt[int]] {.stackTrace: off, gcsafe, raises: [].} =
+    var chronosInternalRetFuture: FutureBase
+    template result(): untyped {.used.} =
+      cast[Future[Opt[int]]](chronosInternalRetFuture).internalValue
+    result = err(type(result))
+
+import macros
+
+block: # issue #16118
+  macro thing(name: static[string]) =
+    result = newStmtList(
+      nnkTypeSection.newTree(
+        nnkTypeDef.newTree(
+          ident(name),
+          newEmptyNode(),
+          nnkObjectTy.newTree(
+            newEmptyNode(),
+            newEmptyNode(),
+            nnkRecList.newTree()))))
+  template foo(name: string): untyped =
+    thing(name)
+  expandMacros:
+    foo("Bob")
+  block:
+    expandMacros:
+      foo("Another")
+
+block: # issue #19670
+  type
+    Past[Z] = object
+    OpenObject = object
+
+  macro rewriter(prc: untyped): untyped =
+    prc.body.add(nnkCall.newTree(
+      prc.params[0]
+    ))
+    prc
+    
+  macro macroAsync(name, restype: untyped): untyped =
+    quote do:
+      proc `name`(): Past[seq[`restype`]] {.rewriter.} = discard
+      
+  macroAsync(testMacro, OpenObject)
+
+import asyncdispatch
+
+block: # issue #11838 long
+  type
+    R[P] = object
+      updates: seq[P]
+    D[T, P] = ref object
+      ps: seq[P]
+      t: T
+  proc newD[T, P](ps: seq[P], t: T): D[T, P] =
+    D[T, P](ps: ps, t: t)
+  proc loop[T, P](d: D[T, P]) =
+    var results = newSeq[Future[R[P]]](10)
+  let d = newD[string, int](@[1], "")
+  d.loop()
+
+block: # issue #11838 minimal
+  type R[T] = object
+  proc loop[T]() =
+    discard newSeq[R[R[T]]]()
+  loop[int]()
diff --git a/tests/generics/tcalltype.nim b/tests/generics/tcalltype.nim
new file mode 100644
index 000000000..cba691f77
--- /dev/null
+++ b/tests/generics/tcalltype.nim
@@ -0,0 +1,26 @@
+discard """
+  joinable: false # breaks everything because of #23977
+"""
+
+# issue #23406
+
+template helper(_: untyped): untyped =
+  int
+
+type # Each of them should always be `int`.
+  GenA[T] = helper int
+  GenB[T] = helper(int)
+  GenC[T] = helper helper(int)
+
+block:
+  template helper(_: untyped): untyped =
+    float
+
+  type
+    A = GenA[int]
+    B = GenB[int]
+    C = GenC[int]
+
+  assert A is int # OK.
+  assert B is int # Fails; it is `float`!
+  assert C is int # OK.
diff --git a/tests/constraints/tconstraints.nim b/tests/generics/tconstraints.nim
index 3ca01cfd5..3ca01cfd5 100644
--- a/tests/constraints/tconstraints.nim
+++ b/tests/generics/tconstraints.nim
diff --git a/tests/friends/tfriends.nim b/tests/generics/tfriends.nim
index 1e70d50a5..1e70d50a5 100644
--- a/tests/friends/tfriends.nim
+++ b/tests/generics/tfriends.nim
diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim
index 44c34917d..16a148f7b 100644
--- a/tests/generics/tgeneric0.nim
+++ b/tests/generics/tgeneric0.nim
@@ -9,7 +9,7 @@ float32
 """
 
 
-import tables
+import std/tables
 
 
 block tgeneric0:
@@ -153,3 +153,44 @@ proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard
 
 proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard
 
+type
+  AtomicType = pointer|ptr|int
+
+  Atomic[T: AtomicType] = distinct T
+
+  Block[T: AtomicType] = object
+
+  AtomicContainer[T: AtomicType] = object
+    b: Atomic[ptr Block[T]]
+
+# bug #8295
+var x = AtomicContainer[int]()
+doAssert (ptr Block[int])(x.b) == nil
+
+
+# bug #23233
+type
+  JsonObjectType*[T: string or uint64] = Table[string, JsonValueRef[T]]
+
+  JsonValueRef*[T: string or uint64] = object
+    objVal*: JsonObjectType[T]
+
+proc scanValue[K](val: var K) =
+  var map: JsonObjectType[K.T]
+  var newVal: K
+  map["one"] = newVal
+
+block:
+  var a: JsonValueRef[uint64]
+  scanValue(a)
+
+  var b: JsonValueRef[string]
+  scanValue(b)
+
+block: # bug #21347
+  type K[T] = object
+  template s[T]() = discard
+  proc b1(n: bool | bool) = s[K[K[int]]]()
+  proc b2(n: bool) =        s[K[K[int]]]()
+  b1(false)   # Error: 's' has unspecified generic parameters
+  b2(false)   # Builds, on its own
diff --git a/tests/generics/tgeneric3.nim b/tests/generics/tgeneric3.nim
index 07ab822ae..29a73afc6 100644
--- a/tests/generics/tgeneric3.nim
+++ b/tests/generics/tgeneric3.nim
@@ -1,9 +1,9 @@
 discard """
 output: '''
 312
-1000000
-1000000
-500000
+1000
+1000
+500
 0
 '''
 """
@@ -445,7 +445,7 @@ proc test() =
   var it1 = internalFind(root, 312)
   echo it1.value
 
-  for i in 1..1_000_000:
+  for i in 1..1_000:
     root = internalPut(root, i, i, oldvalue)
 
   var cnt = 0
diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim
index db7a16569..3068a22f2 100644
--- a/tests/generics/tgenerics_issues.nim
+++ b/tests/generics/tgenerics_issues.nim
@@ -872,3 +872,23 @@ block: # Ensure no segfault from constraint
     a = Regex[int]()
     b = Regex[bool]()
     c = MyOtherType[seq[int]]()
+
+block: # https://github.com/nim-lang/Nim/issues/20416
+  type
+    Item[T] = object
+      link:ptr Item[T]
+      data:T
+
+    KVSeq[A,B] = seq[(A,B)]
+
+    MyTable[A,B] = object
+      data: KVSeq[A,B]
+
+    Container[T] = object
+      a: MyTable[int,ref Item[T]]
+
+  proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles
+
+  proc p2[T](sg:Container[T]) = discard
+  var v : Container[int]
+  p2(v)
diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim
index 6c76502e1..53661236e 100644
--- a/tests/generics/tgenerics_various.nim
+++ b/tests/generics/tgenerics_various.nim
@@ -127,42 +127,6 @@ block trefs:
 
 
 
-block tsharedcases:
-  proc typeNameLen(x: typedesc): int {.compileTime.} =
-    result = x.name.len
-  macro selectType(a, b: typedesc): typedesc =
-    result = a
-
-  type
-    Foo[T] = object
-      data1: array[T.high, int]
-      data2: array[typeNameLen(T), float]
-      data3: array[0..T.typeNameLen, selectType(float, int)]
-    MyEnum = enum A, B, C, D
-
-  var f1: Foo[MyEnum]
-  var f2: Foo[int8]
-
-  doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
-  doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
-
-  doAssert high(f2.data1) == 126 # 127 - 1 == 126
-  doAssert high(f2.data2) == 3 # int8.len - 1 == 3
-
-  static:
-    doAssert high(f1.data1) == ord(C)
-    doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
-
-    doAssert high(f2.data1) == 126
-    doAssert high(f2.data2) == 3
-
-    doAssert high(f1.data3) == 6 # length of MyEnum
-    doAssert high(f2.data3) == 4 # length of int8
-
-    doAssert f2.data3[0] is float
-
-
-
 block tmap_auto:
   let x = map(@[1, 2, 3], x => x+10)
   doAssert x == @[11, 12, 13]
@@ -253,3 +217,38 @@ block:
 
   var x: Que[int]
   doAssert(x.x == 0)
+
+
+# bug #4466
+proc identity[T](t: T): T = t
+
+proc doSomething[A, B](t: tuple[a: A, b: B]) = discard
+
+discard identity((c: 1, d: 2))
+doSomething(identity((1, 2)))
+
+# bug #6231
+proc myProc[T, U](x: T or U) = discard
+
+myProc[int, string](x = 2)
+
+block: # issue #8390
+  proc x[T:SomeFloat](q: openarray[T], y: T = 1): string =
+    doAssert $q.type == $openarray[y.type]
+    $y.type
+
+  doAssert x(@[1.0]) == $1.0.type
+
+
+block: # issue #9381
+  var evalCount {.compileTime.} = 0
+
+  macro test(t: typed): untyped =
+    inc evalCount
+    t
+
+  type GenericObj[T] = object
+    f: test(T)
+
+  var x: GenericObj[int]
+  static: doAssert evalCount == 1
diff --git a/tests/generics/tgenericwhen.nim b/tests/generics/tgenericwhen.nim
new file mode 100644
index 000000000..87672a699
--- /dev/null
+++ b/tests/generics/tgenericwhen.nim
@@ -0,0 +1,58 @@
+discard """
+  targets: "c js"
+"""
+
+block: # issue #24041
+  type ArrayBuf[N: static int, T = byte] = object
+    when sizeof(int) > sizeof(uint8):
+      when N <= int(uint8.high):
+        n: uint8
+      else:
+        when sizeof(int) > sizeof(uint16):
+          when N <= int(uint16.high):
+            n: uint16
+          else:
+            when sizeof(int) > sizeof(uint32):
+              when N <= int(uint32.high):
+                n: uint32
+              else:
+                n: int
+            else:
+              n: int
+        else:
+          n: int
+    else:
+      n: int
+
+  var x: ArrayBuf[8]
+  doAssert x.n is uint8
+  when sizeof(int) > sizeof(uint32):
+    var y: ArrayBuf[int(uint32.high) * 8]
+    doAssert y.n is int
+
+block: # constant condition after dynamic one
+  type Foo[T] = object
+    when T is int:
+      a: int
+    elif true:
+      a: string
+    else:
+      a: bool
+  var x: Foo[string]
+  doAssert x.a is string
+  var y: Foo[int]
+  doAssert y.a is int
+  var z: Foo[float]
+  doAssert z.a is string
+
+block: # issue #4774, but not with threads
+  const hasThreadSupport = not defined(js)
+  when hasThreadSupport:
+    type Channel[T] = object
+      value: T
+  type
+    SomeObj[T] = object
+      when hasThreadSupport:
+        channel: ptr Channel[T]
+  var x: SomeObj[int]
+  doAssert compiles(x.channel) == hasThreadSupport
diff --git a/tests/generics/tgensyminst.nim b/tests/generics/tgensyminst.nim
new file mode 100644
index 000000000..3f30188d8
--- /dev/null
+++ b/tests/generics/tgensyminst.nim
@@ -0,0 +1,29 @@
+# issue #24048
+
+import macros
+
+proc map(fn: proc(val: int): void) = fn(1)
+
+# This works fine, and is the exact same function call as what's
+# generated by the macro `aBug`.
+map proc(val: auto): void =
+  let variable = 123
+
+macro aBug() =
+  # 1. let sym = ident("variable")
+  let sym = genSym(nskLet, "variable")
+  let letStmt = newLetStmt(sym, newLit(123))
+
+  let lambda = newProc(
+    params = @[
+      ident("void"),
+      newIdentDefs(ident("val"), ident("auto")),
+      # 2. newIdentDefs(ident("val"), ident("int")),
+    ],
+    body = newStmtList(letStmt),
+    procType = nnkLambda
+  )
+
+  result = newCall(bindSym("map"), lambda)
+
+aBug()
diff --git a/tests/generics/timpl_ast.nim b/tests/generics/timpl_ast.nim
new file mode 100644
index 000000000..97fe128cd
--- /dev/null
+++ b/tests/generics/timpl_ast.nim
@@ -0,0 +1,80 @@
+import std/macros
+import std/assertions
+
+block: # issue #16639
+  type Foo[T] = object
+    when true:
+      x: float
+
+  type Bar = object
+    when true:
+      x: float
+
+  macro test() =
+    let a = getImpl(bindSym"Foo")[^1]
+    let b = getImpl(bindSym"Bar")[^1]
+    doAssert treeRepr(a) == treeRepr(b)
+
+  test()
+
+import strutils
+
+block: # issues #9899, ##14708
+  macro implRepr(a: typed): string =
+    result = newLit(repr(a.getImpl))
+
+  type
+    Option[T] = object
+      when false: discard # issue #14708
+      when false: x: int
+      when T is (ref | ptr):
+        val: T
+      else:
+        val: T
+        has: bool
+
+  static: # check information is retained
+    let r = implRepr(Option)
+    doAssert "when T is" in r
+    doAssert r.count("val: T") == 2
+    doAssert "has: bool" in r
+
+  block: # try to compile the output
+    macro parse(s: static string) =
+      result = parseStmt(s)
+    parse("type " & implRepr(Option))
+
+block: # issue #22639
+  type
+    Spectrum[N: static int] = object
+      data: array[N, float]
+    AngleInterpolator = object
+      data: seq[Spectrum[60]]
+  proc initInterpolator(num: int): AngleInterpolator =
+    result = AngleInterpolator()
+    for i in 0 ..< num:
+      result.data.add Spectrum[60]()
+  macro genCompatibleTuple(t: typed): untyped =
+    let typ = t.getType[1].getTypeImpl[2]
+    result = nnkTupleTy.newTree()
+    for i, ch in typ: # is `nnkObjectTy`
+      result.add nnkIdentDefs.newTree(ident(ch[0].strVal), # ch is `nnkIdentDefs`
+                                      ch[1],
+                                      newEmptyNode())
+  proc fullSize[T: object | tuple](x: T): int =
+    var tmp: genCompatibleTuple(T)
+    result = 0
+    for field, val in fieldPairs(x):
+      result += sizeof(val)
+    doAssert result == sizeof(tmp)
+
+  let reflectivity = initInterpolator(1)
+  for el in reflectivity.data:
+    doAssert fullSize(el) == sizeof(el)
+  doAssert fullSize(reflectivity.data[0]) == sizeof(reflectivity.data[0])
+  doAssert genCompatibleTuple(Spectrum[60]) is tuple[data: array[60, float]]
+  doAssert genCompatibleTuple(Spectrum[120]) is tuple[data: array[120, float]]
+  type Foo[T] = object
+    data: T
+  doAssert genCompatibleTuple(Foo[int]) is tuple[data: int]
+  doAssert genCompatibleTuple(Foo[float]) is tuple[data: float]
diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim
index ba24b79e9..7220b7429 100644
--- a/tests/generics/timplicit_and_explicit.nim
+++ b/tests/generics/timplicit_and_explicit.nim
@@ -3,8 +3,8 @@ block: # basic test
   proc doStuff[T](a: SomeInteger): T = discard
   proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard
   assert typeof(doStuff[int](100)) is int
-  assert typeof(doStuff[int](100, 1.0)) is float
-  assert typeof(doStuff[int](100, "Hello")) is string
+  assert typeof(doStuff[int, float](100, 1.0)) is float
+  assert typeof(doStuff[int, string](100, "Hello")) is string
 
   proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z)
 
@@ -34,7 +34,8 @@ block: #15622
   proc test1[T](a: T, b: static[string] = "") = discard
   test1[int64](123)
   proc test2[T](a: T, b: static[string] = "") = discard
-  test2[int64, static[string]](123)
+  doAssert not (compiles do:
+    test2[int64, static[string]](123))
 
 block: #4688
   proc convertTo[T](v: int or float): T = (T)(v)
@@ -42,4 +43,23 @@ block: #4688
 
 block: #4164
   proc printStr[T](s: static[string]): T = discard
-  discard printStr[int]("hello static")
\ No newline at end of file
+  discard printStr[int]("hello static")
+
+import macros
+
+block: # issue #9040, statics with template, macro, symchoice explicit generics
+  block: # macro
+    macro fun[N: static int](): untyped =
+      newLit 1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # template
+    template fun[N: static int](): untyped =
+      1
+    const a = fun[2]()
+    doAssert a == 1
+  block: # symchoice
+    proc newSeq[x: static int](): int = 1
+    template foo: int =
+      newSeq[2]()
+    doAssert foo() == 1
diff --git a/tests/generics/timports.nim b/tests/generics/timports.nim
index 800ae7f88..e252ad194 100644
--- a/tests/generics/timports.nim
+++ b/tests/generics/timports.nim
@@ -7,7 +7,7 @@ false
 '''
 """
 
-import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc
+import mbind_bracket, mclosed_sym, mdotlookup, mmodule_same_as_proc, mtypenodes
 
 
 block tbind_bracket:
@@ -31,15 +31,33 @@ block tclosed_sym:
   proc same(r:R, d:int) = echo "TEST1"
   doIt(Data[int](d:123), R())
 
+import strutils, unicode # ambiguous `strip`
 
 block tdotlookup:
   foo(7)
   # bug #1444
   fn(4)
-
+  doAssert doStrip(123) == "123"
+  # bug #14254
+  doAssert baz2[float](1'i8) == 1
+  # bug #21883
+  proc abc[T: not not int](x: T): T =
+    var x = x
+    x.set("hello", "world")
+    result = x
+  doAssert abc(5) == 10
+  block: # ensure normal call is consistent with dot call 
+    proc T(x: int): float = x.float
+    proc foo[T](x: int) =
+      doAssert typeof(T(x)) is typeof(x.T)
+    foo[uint](123)
 
 block tmodule_same_as_proc:
   # bug #1965
   proc test[T](t: T) =
     mmodule_same_as_proc"a"
   test(0)
+
+block ttypenodes:
+  # issue #22699
+  doAssert chop[bool](42) == 42
diff --git a/tests/generics/tmacroinjectedsym.nim b/tests/generics/tmacroinjectedsym.nim
new file mode 100644
index 000000000..985e415f2
--- /dev/null
+++ b/tests/generics/tmacroinjectedsym.nim
@@ -0,0 +1,186 @@
+{.experimental: "openSym".}
+
+block: # issue #22605, normal call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = valueOr 123:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, method call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  proc g(T: type): string =
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "good"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = 123.valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605, original complex example
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate*: bool
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate*: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate*: T
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            vResultPrivate*: T
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g(int) == "f"
+
+  proc g2(T: type): string =
+    bind error # use the bad version on purpose
+    let x = f().valueOr:
+      return $error
+
+    "ok"
+
+  doAssert g2(int) == "error"
+
+block: # issue #23865
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate: bool
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate: T
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            vResultPrivate: T
+
+  func error[T, E](self: Result[T, E]): E =
+    ## Fetch error of result if set, or raise Defect
+    case self.oResultPrivate
+    of true:
+      when T isnot void:
+        raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
+      else:
+        raiseResultDefect("Trying to access error when value is set")
+    of false:
+      when E isnot void:
+        self.eResultPrivate
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+  proc g(T: type): string =
+    let x = f().valueOr:
+      return $error
+    "ok"
+  doAssert g(int) == "f"
+
+import sequtils
+
+block: # issue #12283
+  var b = 5
+  type Foo[T] = object
+    h, w: int
+  proc bar[T](foos: seq[Foo[T]]): T =
+    let w = foldl(foos, a + b.w, 0)
+    w
+  let foos = @[Foo[int](h: 3, w: 5), Foo[int](h: 4, w: 6)]
+  doAssert bar(foos) == 11
diff --git a/tests/generics/tmacroinjectedsymwarning.nim b/tests/generics/tmacroinjectedsymwarning.nim
new file mode 100644
index 000000000..77119004b
--- /dev/null
+++ b/tests/generics/tmacroinjectedsymwarning.nim
@@ -0,0 +1,59 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+type Xxx = enum
+  error
+  value
+
+type
+  Result[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+proc g(T: type): string =
+  let x = f().valueOr:
+    {.push warningAsError[IgnoredSymbolInjection]: on.}
+    # test spurious error
+    discard true
+    let _ = f
+    {.pop.}
+    return $error #[tt.Warning
+            ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in tmacroinjectedsymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]#
+
+  "ok"
+
+discard g(int)
diff --git a/tests/generics/tnestedissues.nim b/tests/generics/tnestedissues.nim
new file mode 100644
index 000000000..e96a1927e
--- /dev/null
+++ b/tests/generics/tnestedissues.nim
@@ -0,0 +1,24 @@
+block: # issue #23568
+  type G[T] = object
+    j: T
+  proc s[T](u: int) = discard
+  proc s[T]() = discard
+  proc c(e: int | int): G[G[G[int]]] = s[G[G[int]]]()
+  discard c(0)
+
+import std/options
+
+block: # issue #23310
+  type
+    BID = string or uint64
+    Future[T] = ref object of RootObj
+      internalValue: T
+    InternalRaisesFuture[T] = ref object of Future[T]
+  proc newInternalRaisesFutureImpl[T](): InternalRaisesFuture[T] =
+    let fut = InternalRaisesFuture[T]()
+  template newFuture[T](): auto =
+    newInternalRaisesFutureImpl[T]()
+  proc problematic(blockId: BID): Future[Option[seq[int]]] =
+    let resultFuture = newFuture[Option[seq[int]]]()
+    return resultFuture
+  let x = problematic("latest")
diff --git a/tests/generics/tnestedtemplate.nim b/tests/generics/tnestedtemplate.nim
new file mode 100644
index 000000000..22d0a2d3c
--- /dev/null
+++ b/tests/generics/tnestedtemplate.nim
@@ -0,0 +1,9 @@
+block: # issue #13979
+  var s: seq[int]
+  proc filterScanline[T](input: openArray[T]) =
+    template currPix: untyped = input[i]
+    for i in 0..<input.len:
+      s.add currPix
+  let pix = [1, 2, 3]
+  filterScanline(pix)
+  doAssert s == @[1, 2, 3]
diff --git a/tests/generics/tobjecttyperel.nim b/tests/generics/tobjecttyperel.nim
index 80fe23459..6f223c154 100644
--- a/tests/generics/tobjecttyperel.nim
+++ b/tests/generics/tobjecttyperel.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "-d:nimInternalNonVtablesTesting"
   output: '''(peel: 0, color: 15)
 (color: 15)
 17
diff --git a/tests/generics/topensymimport.nim b/tests/generics/topensymimport.nim
new file mode 100644
index 000000000..a47496827
--- /dev/null
+++ b/tests/generics/topensymimport.nim
@@ -0,0 +1,5 @@
+# issue #23386
+
+import mopensymimport2
+
+doAssert g(int) == "f"
diff --git a/tests/generics/toverloading_typedesc.nim b/tests/generics/toverloading_typedesc.nim
index 5ab700828..4d748bfee 100644
--- a/tests/generics/toverloading_typedesc.nim
+++ b/tests/generics/toverloading_typedesc.nim
@@ -1,7 +1,3 @@
-discard """
-  exitcode: 0
-  disabled: '''true'''
-"""
 import moverloading_typedesc
 import tables
 
@@ -9,7 +5,6 @@ type
   LFoo = object
   LBar = object
 
-
 when true:
   doAssert FBar.new() == 3
 
diff --git a/tests/generics/tprevent_double_bind.nim b/tests/generics/tprevent_double_bind.nim
index 86e080ab6..d8fc6e5d3 100644
--- a/tests/generics/tprevent_double_bind.nim
+++ b/tests/generics/tprevent_double_bind.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe, locks: 0.}>"
+  errormsg: "type mismatch: got <TT[seq[string]], proc (v: int){.gcsafe.}>"
   line: 20
 """
 
diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim
index 40ff1647b..801f0e444 100644
--- a/tests/generics/treentranttypes.nim
+++ b/tests/generics/treentranttypes.nim
@@ -101,3 +101,14 @@ echo @(b.arr[0].arr), @(b.arr[1].arr)
 let y = b
 echo @(y.arr[0].arr), @(y.arr[1].arr)
 
+import macros
+
+block: # issue #5121
+  type
+    A = object
+    AConst[X] = A
+
+  macro dumpType(t: typedesc): untyped =
+    result = newTree(nnkTupleConstr, newLit $t.getType[1].typeKind, newLit t.getType[1].treeRepr)
+
+  doAssert dumpType(A) == ("ntyObject", "Sym \"A\"")
diff --git a/tests/generics/treturn_inference.nim b/tests/generics/treturn_inference.nim
new file mode 100644
index 000000000..331a9d4db
--- /dev/null
+++ b/tests/generics/treturn_inference.nim
@@ -0,0 +1,184 @@
+
+{.experimental: "inferGenericTypes".}
+
+import std/tables
+
+block:
+  type
+    MyOption[T, Z] = object
+      x: T
+      y: Z
+
+  proc none[T, Z](): MyOption[T, Z] =
+    when T is int:
+      result.x = 22
+    when Z is float:
+      result.y = 12.0
+
+  proc myGenericProc[T, Z](): MyOption[T, Z] =
+    none() # implied by return type
+
+  let a = myGenericProc[int, float]()
+  doAssert a.x == 22
+  doAssert a.y == 12.0
+
+  let b: MyOption[int, float] = none() # implied by type of b
+  doAssert b.x == 22
+  doAssert b.y == 12.0
+
+# Simple template based result with inferred type for errors
+block:
+  type
+    ResultKind {.pure.} = enum
+      Ok
+      Err
+
+    Result[T] = object
+      case kind: ResultKind
+      of Ok:
+        data: T
+      of Err:
+        errmsg: cstring
+
+  template err[T](msg: static cstring): Result[T] =
+    Result[T](kind : ResultKind.Err, errmsg : msg)
+
+  proc testproc(): Result[int] =
+    err("Inferred error!") # implied by proc return
+  let r = testproc()
+  doAssert r.kind == ResultKind.Err
+  doAssert r.errmsg == "Inferred error!"
+
+# Builtin seq
+block:
+  let x: seq[int] = newSeq(1)
+  doAssert x is seq[int]
+  doAssert x.len() == 1
+
+  type
+    MyType[T, Z] = object
+      x: T
+      y: Z
+
+  let y: seq[MyType[int, float]] = newSeq(2)
+  doAssert y is seq[MyType[int, float]]
+  doAssert y.len() == 2
+
+  let z = MyType[seq[float], string](
+    x : newSeq(3),
+    y : "test"
+  )
+  doAssert z.x is seq[float]
+  doAssert z.x.len() == 3
+  doAssert z.y is string
+  doAssert z.y == "test"
+
+# array
+block:
+  proc giveArray[N, T](): array[N, T] =
+    for i in 0 .. N.high:
+      result[i] = i
+  var x: array[2, int] = giveArray()
+  doAssert x == [0, 1]
+
+# tuples
+block:
+  proc giveTuple[T, Z]: (T, Z, T) = discard
+  let x: (int, float, int) = giveTuple()
+  doAssert x is (int, float, int)
+  doAssert x == (0, 0.0, 0)
+
+  proc giveNamedTuple[T, Z]: tuple[a: T, b: Z] = discard
+  let y: tuple[a: int, b: float] = giveNamedTuple()
+  doAssert y is (int, float)
+  doAssert y is tuple[a: int, b: float]
+  doAssert y == (0, 0.0)
+
+  proc giveNestedTuple[T, Z]: ((T, Z), Z) = discard
+  let z: ((int, float), float) = giveNestedTuple()
+  doAssert z is ((int, float), float)
+  doAssert z == ((0, 0.0), 0.0)
+
+  # nesting inside a generic type
+  type MyType[T] = object
+    x: T
+  let a = MyType[(int, MyType[float])](x : giveNamedTuple())
+  doAssert a.x is (int, MyType[float])
+
+
+# basic constructors
+block:
+  type MyType[T] = object
+    x: T
+
+  proc giveValue[T](): T =
+    when T is int:
+      12
+    else:
+      default(T)
+
+  let x = MyType[int](x : giveValue())
+  doAssert x.x is int
+  doAssert x.x == 12
+
+  let y = MyType[MyType[float]](x : MyType[float](x : giveValue()))
+  doAssert y.x is MyType[float]
+  doAssert y.x.x is float
+  doAssert y.x.x == 0.0
+
+  # 'MyType[float]' is bound to 'T' directly
+  #  instead of mapping 'T' to 'float'
+  let z = MyType[MyType[float]](x : giveValue())
+  doAssert z.x is MyType[float]
+  doAssert z.x.x == 0.0
+
+  type Foo = object
+    x: Table[int, float]
+
+  let a = Foo(x: initTable())
+  doAssert a.x is Table[int, float]
+
+# partial binding
+block:
+  type
+    ResultKind = enum
+      Ok, Error
+
+    Result[T, E] = object
+      case kind: ResultKind
+      of Ok:
+        okVal: T
+      of Error:
+        errVal: E
+
+  proc err[T, E](myParam: E): Result[T, E] =
+    Result[T, E](kind : Error, errVal : myParam)
+
+  proc doStuff(): Result[int, string] = 
+    err("Error")
+
+  let res = doStuff()
+  doAssert res.kind == Error
+  doAssert res.errVal == "Error"
+
+# ufcs
+block:
+  proc getValue[T](_: string): T =
+    doAssert T is int
+    44
+  
+  proc `'test`[T](_: string): T =
+    55
+
+  let a: int = getValue("")
+  let b: int = "".getValue()
+  let c: int = "".getValue
+  let d: int = getValue ""
+  let e: int = getValue""
+  let f: int = 12345'test
+  doAssert a == 44
+  doAssert b == 44
+  doAssert c == 44
+  doAssert d == 44
+  doAssert e == 44
+  doAssert f == 55
diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim
index 3c9201548..d356b9d1c 100644
--- a/tests/generics/tstatic_constrained.nim
+++ b/tests/generics/tstatic_constrained.nim
@@ -8,7 +8,7 @@ but expected: <T: float or string, Y>
 tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
 got: <typedesc[int], int literal(10)>
 but expected: <T: float or string, Y>
-tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [proxy]
+tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [error]
 tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous)
 tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
 got: <typedesc[byte], uint8>
@@ -16,7 +16,7 @@ but expected: <T: float or string, Y>
 tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)]
 got: <typedesc[byte], uint8>
 but expected: <T: float or string, Y>
-tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [proxy]
+tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [error]
 tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous)
 tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)]
 got: <typedesc[float], float64>
@@ -76,4 +76,4 @@ block:
     b: MyType[string, "hello"]
     c: MyType[float, 10d]
     d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()]
-    e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()]
\ No newline at end of file
+    e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()]
diff --git a/tests/generics/tuninstantiated_failure.nim b/tests/generics/tuninstantiated_failure.nim
new file mode 100644
index 000000000..f3d5b34b8
--- /dev/null
+++ b/tests/generics/tuninstantiated_failure.nim
@@ -0,0 +1,16 @@
+discard """
+cmd: "nim check $file"
+"""
+
+type
+  Test[T, K] = object
+    name: string
+  Something = Test[int]
+
+func `[]`[T, K](x: var Test[T, K], idx: int): var Test[T, K] =
+  x
+
+var b: Something
+# Should give an error since Something isn't a valid Test
+b[0].name = "Test" #[tt.Error
+ ^  expression '' has no type (or is ambiguous)]#
diff --git a/tests/generics/tuninstantiatedgenericcalls.nim b/tests/generics/tuninstantiatedgenericcalls.nim
new file mode 100644
index 000000000..f33fc8967
--- /dev/null
+++ b/tests/generics/tuninstantiatedgenericcalls.nim
@@ -0,0 +1,517 @@
+# Cases that used to only work due to weird workarounds in the compiler
+# involving not instantiating calls in generic bodies which are removed
+# due to breaking statics.
+# The issue was that these calls are compiled as regular expressions at
+# the generic declaration with unresolved generic parameter types,
+# which are special cased in some places in the compiler, but sometimes
+# treated like real types.
+
+block:
+  type Base10 = object
+
+  func maxLen(T: typedesc[Base10], I: type): int8 =
+    when I is uint8:
+      3
+    elif I is uint16:
+      5
+    elif I is uint32:
+      10
+    elif I is uint64:
+      20
+    else:
+      when sizeof(uint) == 4:
+        10
+      else:
+        20
+  
+  type
+    Base10Buf[T: SomeUnsignedInt] = object
+      data: array[maxLen(Base10, T), byte]
+      len: int8
+
+  var x: Base10Buf[uint32]
+  doAssert x.data.len == 10
+  var y: Base10Buf[uint16]
+  doAssert y.data.len == 5
+
+import typetraits
+
+block thardcases:
+  proc typeNameLen(x: typedesc): int {.compileTime.} =
+    result = x.name.len
+  macro selectType(a, b: typedesc): typedesc =
+    result = a
+
+  type
+    Foo[T] = object
+      data1: array[T.high, int]
+      data2: array[typeNameLen(T), float]
+      data3: array[0..T.typeNameLen, selectType(float, int)]
+  
+  type MyEnum = enum A, B, C, D
+
+  var f1: Foo[MyEnum]
+  var f2: Foo[int8]
+
+  doAssert high(f1.data1) == 2 # (D = 3) - 1 == 2
+  doAssert high(f1.data2) == 5 # (MyEnum.len = 6) - 1 == 5
+
+  doAssert high(f2.data1) == 126 # 127 - 1 == 126
+  doAssert high(f2.data2) == 3 # int8.len - 1 == 3
+
+  static:
+    doAssert high(f1.data1) == ord(C)
+    doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high
+
+    doAssert high(f2.data1) == 126
+    doAssert high(f2.data2) == 3
+
+    doAssert high(f1.data3) == 6 # length of MyEnum
+    doAssert high(f2.data3) == 4 # length of int8
+
+    doAssert f2.data3[0] is float
+
+import muninstantiatedgenericcalls
+
+block:
+  var x: Leb128Buf[uint32]
+  doAssert x.data.len == 5
+  var y: Leb128Buf[uint16]
+  doAssert y.data.len == 3
+
+import macros
+
+block: # issue #12415
+  macro isSomePointerImpl(t: typedesc): bool =
+    var impl = t.getTypeInst[1].getTypeImpl
+    if impl.kind == nnkDistinctTy:
+      impl = impl[0].getTypeImpl
+    if impl.kind in {nnkPtrTy,nnkRefTy}:
+      result = newLit(true)
+    elif impl.kind == nnkSym and impl.eqIdent("pointer"):
+      result = newLit(true)
+    else:
+      result = newLit(false)
+
+  proc isSomePointer[T](t: typedesc[T]): bool {.compileTime.} =
+    isSomePointerImpl(t)
+
+  type
+    Option[T] = object
+      ## An optional type that stores its value and state separately in a boolean.
+      when isSomePointer(typedesc(T)):
+        val: T
+      else:
+        val: T
+        has: bool
+  var x: Option[ref int]
+  doAssert not compiles(x.has)
+  var y: Option[int]
+  doAssert compiles(y.has)
+
+block: # issue #2002
+  proc isNillable(T: typedesc): bool =
+    when compiles((let v: T = nil)):
+      return true
+    else:
+      return false
+
+  type
+    Foo[T] = object
+      when isNillable(T):
+        nillable: float
+      else:
+        notnillable: int
+
+  var val1: Foo[ref int]
+  doAssert compiles(val1.nillable)
+  doAssert not compiles(val1.notnillable)
+  var val2: Foo[int]
+  doAssert not compiles(val2.nillable)
+  doAssert compiles(val2.notnillable)
+
+block: # issue #1771
+  type
+    Foo[X, T] = object
+      bar: array[X.low..X.high, T]
+
+  proc test[X, T](f: Foo[X, T]): T =
+    f.bar[X.low]
+
+  var a: Foo[range[0..2], float]
+  doAssert test(a) == 0.0
+
+block: # issue #23730
+  proc test(M: static[int]): array[1 shl M, int] = discard
+  doAssert len(test(3)) == 8
+  doAssert len(test(5)) == 32
+
+block: # issue #19819
+  type
+    Example[N: static int] = distinct int
+    What[E: Example] = Example[E.N + E.N]
+
+block: # issue #23339
+  type
+    A = object
+    B = object
+  template aToB(t: typedesc[A]): typedesc = B
+  type
+    Inner[I] = object
+      innerField: I
+    Outer[O] = object
+      outerField: Inner[O.aToB]
+  var x: Outer[A]
+  doAssert typeof(x.outerField.innerField) is B
+
+block: # deref syntax
+  type
+    Enqueueable = concept x
+      x is ptr
+    Foo[T: Enqueueable] = object
+      x: typeof(default(T)[])
+
+  proc p[T](f: Foo[T]) =
+    var bar: Foo[T]
+    discard
+  var foo: Foo[ptr int]
+  p(foo)
+  doAssert foo.x is int
+  foo.x = 123
+  doAssert foo.x == 123
+  inc foo.x
+  doAssert foo.x == 124
+
+block:
+  type Generic[T] = object
+    field: T
+  macro foo(x: typed): untyped = x
+  macro bar[T](x: typedesc[Generic[T]]): untyped = x
+  type
+    Foo[T] = object
+      field: Generic[int].foo()
+    Foo2[T] = object
+      field: Generic[T].foo()
+    Bar[T] = object
+      field: Generic[int].bar()
+    Bar2[T] = object
+      field: Generic[T].bar()
+  var x: Foo[int]
+  var x2: Foo2[int]
+  var y: Bar[int]
+  var y2: Bar2[int]
+
+block:
+  macro pick(x: static int): untyped =
+    if x < 100:
+      result = bindSym"int"
+    else:
+      result = bindSym"float"
+  
+  type Foo[T: static int] = object
+    fixed1: pick(25)
+    fixed2: pick(125)
+    unknown: pick(T)
+  
+  var a: Foo[123]
+  doAssert a.fixed1 is int
+  doAssert a.fixed2 is float
+  doAssert a.unknown is float
+  var b: Foo[23]
+  doAssert b.fixed1 is int
+  doAssert b.fixed2 is float
+  doAssert b.unknown is int
+
+import std/sequtils
+
+block: # version of #23432 with `typed`, don't delay instantiation
+  type
+    Future[T] = object
+    InternalRaisesFuture[T, E] = object
+  macro Raising[T](F: typedesc[Future[T]], E: varargs[typed]): untyped =
+    let raises = nnkTupleConstr.newTree(E.mapIt(it))
+    nnkBracketExpr.newTree(
+      ident "InternalRaisesFuture",
+      nnkDotExpr.newTree(F, ident"T"),
+      raises
+    )
+  type X[E] = Future[void].Raising(E)
+  proc f(x: X) = discard
+  var v: Future[void].Raising([ValueError])
+  f(v)
+
+block: # issue #22647
+  proc c0(n: static int): int = 8
+  proc c1(n: static int): int = n div 2
+  proc c2(n: static int): int = n * 2
+  proc c3(n: static int, n2: int): int = n * n2
+  proc `**`(n: static int, n2: int): int = n * n2
+  proc c4(n: int, n2: int): int = n * n2
+
+  type
+    a[N: static int] = object
+      f0 : array[N, int]
+
+    b[N: static int] = object
+      f0 : a[c0(N)]  # does not work
+      f1 : a[c1(N)]  # does not work
+      f2 : a[c2(N)]  # does not work
+      f3 : a[N * 2]  # does not work
+      f4 : a[N]      # works
+      f5: a[c3(N, 2)]
+      f6: a[N ** 2]
+      f7: a[2 * N]
+      f8: a[c4(N, 2)]
+
+  proc p[N: static int](x : a[N]) = discard x.f0[0]
+  template check(x, s: untyped) =
+    p(x)
+    doAssert x is a[s]
+    doAssert x.N == s
+    doAssert typeof(x).N == s
+    doAssert x.f0 == default(array[s, int])
+    doAssert x.f0.len == s
+    proc p2[N: static int](y : a[N]) {.gensym.} =
+      doAssert y is a[s]
+      doAssert y.N == s
+      doAssert typeof(y).N == s
+      doAssert y.f0 == default(array[s, int])
+      doAssert y.f0.len == s
+    p2(x)
+    proc p3(z: typeof(x)) {.gensym.} = discard
+    p3(default(a[s]))
+  proc p[N: static int](x : b[N]) =
+    x.f0.check(8)
+    x.f1.check(2)
+    x.f2.check(8)
+    x.f3.check(8)
+    x.f4.check(4)
+    x.f5.check(8)
+    x.f6.check(8)
+    x.f7.check(8)
+    x.f8.check(8)
+
+  var x: b[4]
+  x.p()
+
+block: # issue #1969
+  type ZeroGenerator = object
+  proc next(g: ZeroGenerator): int = 0
+  # This compiles.
+  type TripleOfInts = tuple
+    a, b, c: typeof(new(ZeroGenerator)[].next)
+  # This raises a compiler error before it's even instantiated.
+  # The `new` proc can't be resolved because `Generator` is not defined.
+  type TripleLike[Generator] = tuple
+    a, b, c: typeof(new(Generator)[].next)
+
+import std/atomics
+
+block: # issue #12720
+  const CacheLineSize = 128
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      pad: array[CacheLineSize - sizeof(default(T)[]), byte]
+      dummy: typeof(default(T)[])
+
+block: # issue #12714
+  type
+    Enqueueable = concept x, type T
+      x is ptr
+      x.next is Atomic[pointer]
+    MyChannel[T: Enqueueable] = object
+      dummy: type(default(T)[])
+
+block: # issue #24044
+  type ArrayBuf[N: static int, T = byte] = object
+    buf: array[N, T]
+  template maxLen(T: type): int =
+    sizeof(T) * 2
+  type MyBuf[I] = ArrayBuf[maxLen(I)]
+  var v: MyBuf[int]
+
+block: # issue #15959
+  proc my[T](a: T): typeof(a[0]) = discard
+  proc my2[T](a: T): array[sizeof(a[0]), T] = discard
+  proc byLent2[T](a: T): lent type(a[0]) = a[0] # Error: type mismatch: got <T, int literal(0)>
+  proc byLent3[T](a: T): lent typeof(a[0]) = a[0] # ditto
+  proc byLent4[T](a: T): lent[type(a[0])] = a[0] # Error: no generic parameters allowed for lent
+  var x = @[1, 2, 3]
+  doAssert my(x) is int
+  doAssert my2(x) is array[sizeof(int), seq[int]]
+  doAssert byLent2(x) == 1
+  doAssert byLent2(x) is lent int
+  doAssert byLent3(x) == 1
+  doAssert byLent3(x) is lent int
+  doAssert byLent4(x) == 1
+  doAssert byLent4(x) is lent int
+  proc fn[U](a: U): auto = a
+  proc my3[T](a: T, b: typeof(fn(a))) = discard
+  my3(x, x)
+  doAssert not compiles(my3(x, x[0]))
+
+block: # issue #22342, type section version of #22607
+  type GenAlias[isInt: static bool] = (
+    when isInt:
+      int
+    else:
+      float
+  )
+  doAssert GenAlias[true] is int
+  doAssert GenAlias[false] is float
+  proc foo(T: static bool): GenAlias[T] = discard
+  doAssert foo(true) is int
+  doAssert foo(false) is float
+  proc foo[T: static bool](v: var GenAlias[T]) =
+    v += 1
+  var x: int
+  foo[true](x)
+  doAssert not compiles(foo[false](x))
+  foo[true](x)
+  doAssert x == 2
+  var y: float
+  foo[false](y)
+  doAssert not compiles(foo[true](y))
+  foo[false](y)
+  doAssert y == 2
+
+block: # `when`, test no constant semchecks
+  type Foo[T] = (
+    when false:
+      {.error: "bad".}
+    elif defined(neverDefined):
+      {.error: "bad 2".}
+    else:
+      T
+  )
+  var x: Foo[int]
+  type Bar[T] = (
+    when true:
+      T
+    elif defined(js):
+      {.error: "bad".}
+    else:
+      {.error: "bad 2".}
+  )
+  var y: Bar[int]
+
+block: # weird regression
+  type
+    Foo[T] = distinct int
+    Bar[T, U] = distinct int
+  proc foo[T, U](x: static Foo[T], y: static Bar[T, U]): Foo[T] =
+    # signature gives:
+    # Error: cannot instantiate Bar
+    # got: <typedesc[T], U>
+    # but expected: <T, U>
+    x
+  doAssert foo(Foo[int](1), Bar[int, int](2)).int == 1
+
+block: # issue #24090
+  type M[V] = object
+  template y[V](N: type M, v: V): M[V] = default(M[V])
+  proc d(x: int | int, f: M[int] = M.y(0)) = discard
+  d(0, M.y(0))
+  type Foo[T] = object
+    x: typeof(M.y(default(T)))
+  var a: Foo[int]
+  doAssert a.x is M[int]
+  var b: Foo[float]
+  doAssert b.x is M[float]
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(M()) # actually fails here immediately
+    var bar: Bar[int])
+  doAssert not (compiles do:
+    type Bar[T] = object
+      x: typeof(default(M))
+    var bar: Bar[int]
+    # gives "undeclared identifier x" because of #24091,
+    # normally it should fail in the line above
+    echo bar.x)
+  proc foo[T: M](x: T = default(T)) = discard x
+  foo[M[int]]()
+  doAssert not compiles(foo())
+
+block: # above but encountered by sigmatch using replaceTypeVarsN
+  type Opt[T] = object
+    x: T
+  proc none[T](x: type Opt, y: typedesc[T]): Opt[T] = discard
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # real version of above
+  type Opt[T] = object
+    x: T
+  template none(x: type Opt, T: type): Opt[T] = Opt[T]()
+  proc foo[T](x: T, a = Opt.none(int)) = discard
+  foo(1, a = Opt.none(int))
+  foo(1)
+
+block: # issue #20880
+  type
+    Child[n: static int] = object
+      data: array[n, int]
+    Parent[n: static int] = object
+      child: Child[3*n]
+  const n = 3
+  doAssert $(typeof Parent[n*3]()) == "Parent[9]"
+  doAssert $(typeof Parent[1]().child) == "Child[3]"
+  doAssert Parent[1]().child.data.len == 3
+
+{.experimental: "dynamicBindSym".}
+block: # issue #16774
+  type SecretWord = distinct uint64
+  const WordBitWidth = 8 * sizeof(uint64)
+  func wordsRequired(bits: int): int {.compileTime.} =
+    ## Compute the number of limbs required
+    # from the **announced** bit length
+    (bits + WordBitWidth - 1) div WordBitWidth
+  type
+    Curve = enum BLS12_381
+    BigInt[bits: static int] = object
+      limbs: array[bits.wordsRequired, SecretWord]
+  const BLS12_381_Modulus = default(BigInt[381])
+  macro Mod(C: static Curve): untyped =
+    ## Get the Modulus associated to a curve
+    result = bindSym($C & "_Modulus")
+  macro getCurveBitwidth(C: static Curve): untyped =
+    result = nnkDotExpr.newTree(
+      getAST(Mod(C)),
+      ident"bits"
+    )
+  type Fp[C: static Curve] = object
+    ## Finite Fields / Modular arithmetic
+    ## modulo the curve modulus
+    mres: BigInt[getCurveBitwidth(C)]
+  var x: Fp[BLS12_381]
+  doAssert x.mres.limbs.len == wordsRequired(getCurveBitWidth(BLS12_381))
+  # minimized, as if we haven't tested it already:
+  macro makeIntLit(c: static int): untyped =
+    result = newLit(c)
+  type Test[T: static int] = object
+    myArray: array[makeIntLit(T), int]
+  var y: Test[2]
+  doAssert y.myArray.len == 2
+  var z: Test[4]
+  doAssert z.myArray.len == 4
+
+block: # issue #16175
+  type
+    Thing[D: static uint] = object
+      when D == 0:
+        kid: char
+      else:
+        kid: Thing[D-1]
+  var t2 = Thing[3]()
+  doAssert t2.kid is Thing[2.uint]
+  doAssert t2.kid.kid is Thing[1.uint]
+  doAssert t2.kid.kid.kid is Thing[0.uint]
+  doAssert t2.kid.kid.kid.kid is char
+  var s = Thing[1]()
+  doAssert s.kid is Thing[0.uint]
+  doAssert s.kid.kid is char
diff --git a/tests/generics/twrong_explicit_typeargs.nim b/tests/generics/twrong_explicit_typeargs.nim
deleted file mode 100644
index e47b38e99..000000000
--- a/tests/generics/twrong_explicit_typeargs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  errormsg: "cannot instantiate: 'newImage[string]'"
-  line: 16
-"""
-
-# bug #4084
-type
-  Image[T] = object
-    data: seq[T]
-
-proc newImage[T: int32|int64](w, h: int): ref Image[T] =
-  new(result)
-  result.data = newSeq[T](w * h)
-
-var correct = newImage[int32](320, 200)
-var wrong = newImage[string](320, 200)
diff --git a/tests/generics/twrong_generic_object.nim b/tests/generics/twrong_generic_object.nim
index 442b89ea1..4951f735f 100644
--- a/tests/generics/twrong_generic_object.nim
+++ b/tests/generics/twrong_generic_object.nim
@@ -1,6 +1,6 @@
 discard """
-  errormsg: "cannot instantiate: 'GenericNodeObj[T]'; Maybe generic arguments are missing?"
-  line: 21
+  errormsg: "'Node' is not a concrete type"
+  line: 11
 """
 # bug #2509
 type
diff --git a/tests/constr/a_module.nim b/tests/global/a_module.nim
index 1d3f4848c..1d3f4848c 100644
--- a/tests/constr/a_module.nim
+++ b/tests/global/a_module.nim
diff --git a/tests/global/t15005.nim b/tests/global/t15005.nim
new file mode 100644
index 000000000..6395b1dde
--- /dev/null
+++ b/tests/global/t15005.nim
@@ -0,0 +1,18 @@
+type
+  T = ref object
+    data: string
+
+template foo(): T =
+  var a15005 {.global.}: T
+  once:
+    a15005 = T(data: "hi")
+
+  a15005
+
+proc test() =
+  var b15005 = foo()
+
+  doAssert b15005.data == "hi"
+
+test()
+test()
diff --git a/tests/global/t21896.nim b/tests/global/t21896.nim
new file mode 100644
index 000000000..c7765c4dd
--- /dev/null
+++ b/tests/global/t21896.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot assign local to global variable"
+  line: 7
+"""
+
+proc example(a:int) =
+  let b {.global.} = a
+
+example(1)
diff --git a/tests/global/t3505.nim b/tests/global/t3505.nim
new file mode 100644
index 000000000..437a02ae6
--- /dev/null
+++ b/tests/global/t3505.nim
@@ -0,0 +1,41 @@
+discard """
+cmd: "nim check $options --hints:off $file"
+action: "reject"
+nimout: '''
+t3505.nim(22, 22) Error: cannot assign local to global variable
+t3505.nim(31, 28) Error: cannot assign local to global variable
+t3505.nim(39, 29) Error: cannot assign local to global variable
+
+
+
+
+'''
+"""
+
+
+
+
+
+
+proc foo =
+  let a = 0
+  var b {.global.} = a
+foo()
+
+# issue #5132
+proc initX(it: float): int = 8
+proc initX2(it: int): int = it
+
+proc main() =
+  var f: float
+  var x {.global.} = initX2(initX(f))
+  
+main()
+
+# issue #20866
+proc foo2() =
+  iterator bar() {.closure.} =
+    discard
+  var g {.global.} = rawProc(bar)
+
+foo2()
\ No newline at end of file
diff --git a/tests/global/tglobal2.nim b/tests/global/tglobal2.nim
new file mode 100644
index 000000000..73a575bbd
--- /dev/null
+++ b/tests/global/tglobal2.nim
@@ -0,0 +1,9 @@
+# b.nim
+import a_module
+doAssert foo() == 0
+
+proc hello(x: type) =
+  var s {.global.} = default(x)
+  doAssert s == 0
+
+hello(int)
diff --git a/tests/ic/config.nims b/tests/ic/config.nims
index a522efb0d..a622ec309 100644
--- a/tests/ic/config.nims
+++ b/tests/ic/config.nims
@@ -1,3 +1 @@
-when defined(windows):

-  --tlsEmulation:off

 --mm:refc

diff --git a/tests/ic/tgenericinst.nim b/tests/ic/tgenericinst.nim
new file mode 100644
index 000000000..3346764f5
--- /dev/null
+++ b/tests/ic/tgenericinst.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim cpp --incremental:on $file"
+"""
+
+{.emit:"""/*TYPESECTION*/
+#include <iostream>
+  struct Foo { };
+""".}
+
+type Foo {.importcpp.} = object
+echo $Foo() #Notice the generic is instantiate in the this module if not, it wouldnt find Foo
\ No newline at end of file
diff --git a/tests/implicit/timplicit.nim b/tests/implicit/timplicit.nim
deleted file mode 100644
index bb701249c..000000000
--- a/tests/implicit/timplicit.nim
+++ /dev/null
@@ -1,49 +0,0 @@
-discard """
-  output: '''
-1
-2
-3
-4
-2
-88
-timplicit done
-'''
-"""
-
-
-for x in [1, 2, 3, 4]:
-  echo x
-
-
-type
-  TValue* {.pure, final.} = object of RootObj
-    a: int
-  PValue = ref TValue
-  PPValue = ptr PValue
-
-
-var x: PValue
-new x
-var sp: PPValue = addr x
-
-sp.a = 2
-if sp.a == 2: echo 2  # with sp[].a the error is gone
-
-# Test the new auto-deref a little
-
-{.experimental.}
-
-proc p(x: var int; y: int) = x += y
-
-block:
-  var x: ref int
-  new(x)
-
-  x.p(44)
-
-  var indirect = p
-  x.indirect(44)
-
-  echo x[]
-
-  echo "timplicit done"
diff --git a/tests/import/buzz/m21496.nim b/tests/import/buzz/m21496.nim
new file mode 100644
index 000000000..677324000
--- /dev/null
+++ b/tests/import/buzz/m21496.nim
@@ -0,0 +1 @@
+proc fb*: string = "buzz!"
\ No newline at end of file
diff --git a/tests/import/fizz/m21496.nim b/tests/import/fizz/m21496.nim
new file mode 100644
index 000000000..525b653d6
--- /dev/null
+++ b/tests/import/fizz/m21496.nim
@@ -0,0 +1 @@
+proc fb*: string = "fizz!"
\ No newline at end of file
diff --git a/tests/import/t21496.nim b/tests/import/t21496.nim
new file mode 100644
index 000000000..1a4907b12
--- /dev/null
+++ b/tests/import/t21496.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "ambiguous identifier: 'm21496'"
+"""
+
+import fizz/m21496, buzz/m21496
+
+# bug #21496
+
+discard m21496.fb()
diff --git a/tests/import/t22065.nim b/tests/import/t22065.nim
new file mode 100644
index 000000000..dfd87a107
--- /dev/null
+++ b/tests/import/t22065.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "cannot open file: ./sugar"
+"""
+
+import ./sugar
\ No newline at end of file
diff --git a/tests/import/t22208.nim b/tests/import/t22208.nim
new file mode 100644
index 000000000..b935d751c
--- /dev/null
+++ b/tests/import/t22208.nim
@@ -0,0 +1,6 @@
+import fizz/m21496 as alas
+import buzz/m21496
+
+# bug #21496
+
+doAssert m21496.fb() == "buzz!"
diff --git a/tests/import/t23167.nim b/tests/import/t23167.nim
new file mode 100644
index 000000000..0891ee2af
--- /dev/null
+++ b/tests/import/t23167.nim
@@ -0,0 +1,5 @@
+# bug #23167
+template sharedImport() =
+  import std / os
+
+sharedImport()
diff --git a/tests/importalls/m4.nim b/tests/importalls/m4.nim
index b682b766a..77ec65c61 100644
--- a/tests/importalls/m4.nim
+++ b/tests/importalls/m4.nim
@@ -1,4 +1,3 @@
-{.warning[UnusedImport]: off.} # xxx bug: this shouldn't be needed since we have `export m3`
 import ./m3 {.all.}
 import ./m3 as m3b
 export m3b
diff --git a/tests/init/tcompiles.nim b/tests/init/tcompiles.nim
new file mode 100644
index 000000000..67e17b241
--- /dev/null
+++ b/tests/init/tcompiles.nim
@@ -0,0 +1,101 @@
+discard """
+  matrix: "--warningAsError:ProveInit --warningAsError:Uninit"
+"""
+
+{.experimental: "strictdefs".}
+
+type Test = object
+  id: int
+
+proc foo {.noreturn.} = discard
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      foo()
+    else:
+      foo()
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      foo()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      return Test()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      return Test()
+    else:
+      return Test()
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+    else:
+      result = Test()
+      return
+
+  discard test(true)
+
+block:
+  proc test(x: bool): Test =
+    if x:
+      result = Test()
+      return
+    else:
+      raise newException(ValueError, "unreachable")
+
+  discard test(true)
+
+# bug #21615
+# bug #16735
+
+block:
+  type Test {.requiresInit.} = object
+    id: int
+
+  proc bar(): int =
+    raise newException(CatchableError, "error")
+
+  proc test(): Test =
+    raise newException(CatchableError, "")
+
+  template catchError(body) =
+    var done = false
+    try:
+      body
+    except CatchableError:
+      done = true
+    doAssert done
+
+  catchError:
+    echo test()
+
+  catchError:
+    echo bar()
+
+block:
+  proc foo(x: ptr int) =
+    discard
+
+  proc main =
+    var s: int
+    foo(addr s)
+
+  main()
diff --git a/tests/init/tinitchecks_v2.nim b/tests/init/tinitchecks_v2.nim
new file mode 100644
index 000000000..f7716bcca
--- /dev/null
+++ b/tests/init/tinitchecks_v2.nim
@@ -0,0 +1,83 @@
+discard """
+cmd: "nim check $file"
+action: "compile"
+"""
+
+{.experimental: "strictDefs".}
+
+proc myopen(f: out File; s: string): bool =
+  f = default(File)
+  result = false
+
+proc main =
+  var f: File
+  if myopen(f, "aarg"):
+    f.close
+
+proc invalid =
+  var s: seq[string]
+  s.add "abc" #[tt.Warning
+  ^ use explicit initialization of 's' for clarity [Uninit] ]#
+
+proc valid =
+  var s: seq[string] = @[]
+  s.add "abc" # valid!
+
+main()
+invalid()
+valid()
+
+proc branchy(cond: bool) =
+  var s: seq[string]
+  if cond:
+    s = @["y"]
+  else:
+    s = @[]
+  s.add "abc" # valid!
+
+branchy true
+
+proc p(x: out int; y: out string; cond: bool) = #[tt.Warning
+                   ^ Cannot prove that 'y' is initialized. This will become a compile time error in the future. [ProveInit] ]#
+  x = 4
+  if cond:
+    y = "abc"
+  # error: not every path initializes 'y'
+
+var gl: int
+var gs: string
+p gl, gs, false
+
+proc canRaise(x: int): int =
+  result = x
+  raise newException(ValueError, "wrong")
+
+proc currentlyValid(x: out int; y: out string; cond: bool) =
+  x = canRaise(45)
+  y = "abc" # <-- error: not every path initializes 'y'
+
+currentlyValid gl, gs, false
+
+block: # previously effects/toutparam
+  proc gah[T](x: out T) =
+    x = 3
+
+  proc arr1 =
+    var a: array[2, int]
+    var x: int
+    gah(x)
+    a[0] = 3
+    a[x] = 3
+    echo x
+
+  arr1()
+
+  proc arr2 =
+    var a: array[2, int]
+    var x: int
+    a[0] = 3
+    a[x] = 3 #[tt.Warning
+      ^ use explicit initialization of 'x' for clarity [Uninit] ]#
+    echo x
+
+  arr2()
diff --git a/tests/init/tlet.nim b/tests/init/tlet.nim
new file mode 100644
index 000000000..e32bedb18
--- /dev/null
+++ b/tests/init/tlet.nim
@@ -0,0 +1,55 @@
+{.experimental: "strictDefs".}
+
+proc bar(x: out string) =
+  x = "abc"
+
+template moe = # bug #21043
+  try:
+    discard
+  except ValueError as e:
+    echo(e.msg)
+
+template moe0 {.dirty.} = # bug #21043
+  try:
+    discard
+  except ValueError as e:
+    echo(e.msg)
+
+proc foo() =
+  block:
+    let x: string
+    if true:
+      x = "abc"
+    else:
+      x = "def"
+    doAssert x == "abc"
+  block:
+    let y: string
+    bar(y)
+    doAssert y == "abc"
+  block:
+    let x: string
+    if true:
+      x = "abc"
+      discard "abc"
+    else:
+      x = "def"
+      discard "def"
+    doAssert x == "abc"
+  block: #
+    let x {.used.} : int
+  block: #
+    let x: float
+    x = 1.234
+    doAssert x == 1.234
+
+  block:
+    try:
+      discard
+    except ValueError as e:
+      echo(e.msg)
+  moe()
+  moe0()
+
+static: foo()
+foo()
diff --git a/tests/init/tlet_uninit2.nim b/tests/init/tlet_uninit2.nim
new file mode 100644
index 000000000..174aa28f7
--- /dev/null
+++ b/tests/init/tlet_uninit2.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "'let' symbol requires an initialization"
+"""
+
+let x: int
\ No newline at end of file
diff --git a/tests/init/tlet_uninit3.nim b/tests/init/tlet_uninit3.nim
new file mode 100644
index 000000000..cb786f8c8
--- /dev/null
+++ b/tests/init/tlet_uninit3.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: "nim check $file"
+  action: "reject"
+  nimout: '''
+tlet_uninit3.nim(13, 5) Error: 'let' symbol requires an initialization
+tlet_uninit3.nim(19, 5) Error: 'x' cannot be assigned to
+tlet_uninit3.nim(23, 11) Error: 'let' symbol requires an initialization
+'''
+"""
+
+{.experimental: "strictDefs".}
+
+let global {.used.}: int
+
+proc foo() =
+  block:
+    let x: int
+    x = 13
+    x = 14
+
+  block:
+    let x: int
+    doAssert x == 0
+foo()
diff --git a/tests/init/tlet_uninit4.nim b/tests/init/tlet_uninit4.nim
new file mode 100644
index 000000000..c57d15ff8
--- /dev/null
+++ b/tests/init/tlet_uninit4.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "type mismatch: got <string>"
+"""
+
+{.experimental: "strictDefs".}
+
+proc foo(x: var string) =
+  echo x
+
+proc bar() =
+  let x: string
+  foo(x)
diff --git a/tests/init/toutparam_subtype.nim b/tests/init/toutparam_subtype.nim
new file mode 100644
index 000000000..3597f1459
--- /dev/null
+++ b/tests/init/toutparam_subtype.nim
@@ -0,0 +1,24 @@
+discard """
+cmd: "nim check $file"
+action: "compile"
+errormsg: "type mismatch: got <Subclass[system.int]>"
+line: 21
+"""
+
+{.experimental: "strictDefs".}
+
+type
+  Superclass[T] = object of RootObj
+    a: T
+  Subclass[T] = object of Superclass[T]
+    s: string
+
+proc init[T](x: out Superclass[T]) =
+  x = Superclass(a: 8)
+
+proc subtypeCheck =
+  var v: Subclass[int]
+  init(v)
+  echo v.s # the 's' field was never initialized!
+
+subtypeCheck()
diff --git a/tests/init/toutparams.nim b/tests/init/toutparams.nim
new file mode 100644
index 000000000..590768599
--- /dev/null
+++ b/tests/init/toutparams.nim
@@ -0,0 +1,14 @@
+discard """
+  matrix: "--warningAsError:ProveInit"
+"""
+
+{.experimental: "strictdefs".}
+
+proc foo(x: out int) =
+  x = 1
+
+proc bar(x: out int) =
+  foo(x)
+
+var s: int
+bar(s)
diff --git a/tests/misc/tproveinit.nim b/tests/init/tproveinit.nim
index c9f688309..c9f688309 100644
--- a/tests/misc/tproveinit.nim
+++ b/tests/init/tproveinit.nim
diff --git a/tests/init/treturns.nim b/tests/init/treturns.nim
new file mode 100644
index 000000000..18cebe0b1
--- /dev/null
+++ b/tests/init/treturns.nim
@@ -0,0 +1,106 @@
+{.experimental: "strictdefs".}
+
+type Test = object
+  id: int
+
+proc foo {.noreturn.} = discard
+
+proc test1(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return Test()
+  else:
+    return
+
+proc test0(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    foo()
+
+proc test2(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    return
+
+proc test3(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    return Test()
+
+proc test4(): Test =
+  if true: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return
+  else:
+    result = Test()
+    return
+
+proc test5(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    return Test()
+
+proc test6(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    return
+
+proc test7(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    return
+  else:
+    discard
+
+proc test8(x: bool): Test =
+  case x: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+  of true:
+    discard
+  else:
+    raise
+
+proc hasImportStmt(): bool =
+  if false: #[tt.Warning
+  ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+    return true
+  else:
+    discard
+
+discard hasImportStmt()
+
+block:
+  proc hasImportStmt(): bool =
+    if false: #[tt.Warning
+    ^ Cannot prove that 'result' is initialized. This will become a compile time error in the future. [ProveInit]]#
+      return true
+    else:
+      return
+
+  discard hasImportStmt()
+
+block:
+  block:
+    proc foo(x: var int) =
+      discard
+
+    proc main =
+      var s: int
+      foo(s)#[tt.Warning
+          ^ use explicit initialization of 's' for clarity [Uninit]]#
+
+    main()
+
diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim
index 9a4161a30..ac3007e8d 100644
--- a/tests/init/tuninit1.nim
+++ b/tests/init/tuninit1.nim
@@ -4,7 +4,7 @@ discard """
 """
 
 import strutils
-
+{.experimental: "strictDefs".}
 {.warning[Uninit]:on.}
 
 proc p =
diff --git a/tests/int/t1.nim b/tests/int/t1.nim
new file mode 100644
index 000000000..6e5cdc8d4
--- /dev/null
+++ b/tests/int/t1.nim
@@ -0,0 +1,61 @@
+discard """
+  targets: "c cpp"
+"""
+
+doAssert typeOf(1.int64 + 1.int) is int64
+doAssert typeOf(1.uint64 + 1.uint) is uint64
+doAssert int64 is SomeNumber
+doAssert int64 is (SomeNumber and not(uint32|uint64|uint|int))
+doAssert int64 is (not(uint32|uint64|uint|int))
+doAssert int isnot int64
+doAssert int64 isnot int
+var myInt16 = 5i16
+var myInt: int
+doAssert typeOf(myInt16 + 34) is int16    # of type `int16`
+doAssert typeOf(myInt16 + myInt) is int   # of type `int`
+doAssert typeOf(myInt16 + 2i32) is int32  # of type `int32`
+doAssert int32 isnot int64
+doAssert int32 isnot int
+
+block: # bug #23947
+  template foo =
+    let test_u64 : uint64 =   0xFF07.uint64
+    let test_u8  : uint8  = test_u64.uint8
+        # Error: illegal conversion from '65287' to '[0..255]'
+    doAssert test_u8 == 7
+
+  static: foo()
+  foo()
+
+block:
+  # bug #22085
+  const
+    x = uint32(uint64.high) # vm error
+    u = uint64.high
+    v = uint32(u) # vm error
+
+  let
+    z = uint64.high
+    y = uint32(z)  # runtime ok
+
+  let
+    w = uint32(uint64.high)  # semfold error
+
+  doAssert x == w
+  doAssert v == y
+
+  # bug #14522
+  doAssert 0xFF000000_00000000.uint64 == 18374686479671623680'u64
+
+block: # bug #23954
+  let testRT_u8 : uint8 = 0x107.uint8
+  doAssert testRT_u8 == 7
+  const testCT_u8 : uint8 = 0x107.uint8
+  doAssert testCT_u8 == 7
+
+block: # issue #24104
+  type P = distinct uint  # uint, uint8, uint16, uint32, uint64 
+  let v = 0.P
+  case v
+  of 0.P: discard
+  else:   discard
diff --git a/tests/arithm/tarithm.nim b/tests/int/tarithm.nim
index d0943d225..d0943d225 100644
--- a/tests/arithm/tarithm.nim
+++ b/tests/int/tarithm.nim
diff --git a/tests/arithm/tashr.nim b/tests/int/tashr.nim
index aeb3b6843..aeb3b6843 100644
--- a/tests/arithm/tashr.nim
+++ b/tests/int/tashr.nim
diff --git a/tests/arithm/tdiv.nim b/tests/int/tdiv.nim
index 5d8eed84d..5d8eed84d 100644
--- a/tests/arithm/tdiv.nim
+++ b/tests/int/tdiv.nim
diff --git a/tests/misc/tints.nim b/tests/int/tints.nim
index d24cbd4ac..773e8ccad 100644
--- a/tests/misc/tints.nim
+++ b/tests/int/tints.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
   output: '''
 0 0
 0 0
@@ -6,6 +7,9 @@ Success'''
 """
 # Test the different integer operations
 
+
+import std/private/jsutils
+
 var testNumber = 0
 
 template test(opr, a, b, c: untyped): untyped =
@@ -23,28 +27,37 @@ template test(opr, a, b, c: untyped): untyped =
 
 test(`+`, 12'i8, -13'i16, -1'i16)
 test(`shl`, 0b11, 0b100, 0b110000)
+whenJsNoBigInt64: discard
+do:
+  test(`shl`, 0b11'i64, 0b100'i64, 0b110000'i64)
 when not defined(js):
+  # mixed type shr needlessly complicates codegen with bigint
+  # and thus is not yet supported in JS for 64 bit ints
   test(`shl`, 0b11'i32, 0b100'i64, 0b110000'i64)
 test(`shl`, 0b11'i32, 0b100'i32, 0b110000'i32)
 
 test(`or`, 0xf0f0'i16, 0x0d0d'i16, 0xfdfd'i16)
 test(`and`, 0xf0f0'i16, 0xfdfd'i16, 0xf0f0'i16)
 
-when not defined(js):
+whenJsNoBigInt64: discard
+do:
   test(`shr`, 0xffffffffffffffff'i64, 0x4'i64, 0xffffffffffffffff'i64)
 test(`shr`, 0xffff'i16, 0x4'i16, 0xffff'i16)
 test(`shr`, 0xff'i8, 0x4'i8, 0xff'i8)
 
-when not defined(js):
+whenJsNoBigInt64: discard
+do:
   test(`shr`, 0xffffffff'i64, 0x4'i64, 0x0fffffff'i64)
 test(`shr`, 0xffffffff'i32, 0x4'i32, 0xffffffff'i32)
 
-when not defined(js):
+whenJsNoBigInt64: discard
+do:
   test(`shl`, 0xffffffffffffffff'i64, 0x4'i64, 0xfffffffffffffff0'i64)
 test(`shl`, 0xffff'i16, 0x4'i16, 0xfff0'i16)
 test(`shl`, 0xff'i8, 0x4'i8, 0xf0'i8)
 
-when not defined(js):
+whenJsNoBigInt64: discard
+do:
   test(`shl`, 0xffffffff'i64, 0x4'i64, 0xffffffff0'i64)
 test(`shl`, 0xffffffff'i32, 0x4'i32, 0xfffffff0'i32)
 
@@ -80,6 +93,58 @@ block: # Casts to uint
 # issue #7174
 let c = 1'u
 let val = c > 0
-doAssert val 
+doAssert val
+
+block: # bug #6752
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    let x = 711127'i64
+    doAssert x * 86400'i64 == 61441372800'i64
+
+block: # bug #17604
+  let a = 2147483648'u
+  doAssert (a and a) == a
+  doAssert (a or 0) == a
+
+block: # bitwise not
+  let
+    z8 = 0'u8
+    z16 = 0'u16
+    z32 = 0'u32
+    z64 = 0'u64
+  doAssert (not z8) == uint8.high
+  doAssert (not z16) == uint16.high
+  doAssert (not z32) == uint32.high
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert (not z64) == uint64.high
+
+block: # shl
+  let i8 = int8.high
+  let i16 = int16.high
+  let i32 = int32.high
+  let i64 = int64.high
+  doAssert i8 shl 1 == -2
+  doAssert i8 shl 2 == -4
+  doAssert i16 shl 1 == -2
+  doAssert i16 shl 2 == -4
+  doAssert i32 shl 1 == -2
+  doAssert i32 shl 2 == -4
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert i64 shl 1 == -2
+    doAssert i64 shl 2 == -4
+
+  let u8 = uint8.high
+  let u16 = uint16.high
+  let u32 = uint32.high
+  let u64 = uint64.high
+  doAssert u8 shl 1 == u8 - 1
+  doAssert u16 shl 1 == u16 - 1
+  doAssert u32 shl 1 == u32 - 1
+  when not defined(js) or (defined(js) and compileOption("jsbigint64")):
+    doAssert u64 shl 1 == u64 - 1
+
+block: # bug #23378
+  var neg = -1  # prevent compile-time evaluation
+  let n = abs BiggestInt neg
+  doAssert n == 1
 
 echo("Success") #OUT Success
diff --git a/tests/misc/tunsigned64mod.nim b/tests/int/tunsigned64mod.nim
index ca3286df3..ca3286df3 100644
--- a/tests/misc/tunsigned64mod.nim
+++ b/tests/int/tunsigned64mod.nim
diff --git a/tests/misc/tunsignedcmp.nim b/tests/int/tunsignedcmp.nim
index 11b67ac5f..11b67ac5f 100644
--- a/tests/misc/tunsignedcmp.nim
+++ b/tests/int/tunsignedcmp.nim
diff --git a/tests/misc/tunsignedcomp.nim b/tests/int/tunsignedcomp.nim
index 970c4ae9d..970c4ae9d 100644
--- a/tests/misc/tunsignedcomp.nim
+++ b/tests/int/tunsignedcomp.nim
diff --git a/tests/misc/tunsignedconv.nim b/tests/int/tunsignedconv.nim
index b04ddd2bb..6c73521d3 100644
--- a/tests/misc/tunsignedconv.nim
+++ b/tests/int/tunsignedconv.nim
@@ -1,15 +1,19 @@
+discard """
+  targets: "c cpp js"
+"""
+
 # Tests unsigned literals and implicit conversion between uints and ints
 
-var h8:uint8 = 128
-var h16:uint16 = 32768
-var h32:uint32 = 2147483648'u32
-var h64:uint64 = 9223372036854775808'u64
-var foobar:uint64 = 9223372036854775813'u64 # Issue 728
+var h8: uint8 = 128
+var h16: uint16 = 32768
+var h32: uint32 = 2147483648'u32
+var h64: uint64 = 9223372036854775808'u64
+var foobar: uint64 = 9223372036854775813'u64 # Issue 728
 
-var v8:uint8 = 10
-var v16:uint16 = 10
-var v32:uint32 = 10
-var v64:uint64 = 10
+var v8: uint8 = 10
+var v16: uint16 = 10
+var v32: uint32 = 10
+var v64: uint64 = 10
 
 # u8 + literal produces u8:
 var a8: uint8 = v8 + 10
@@ -57,8 +61,8 @@ const x1 = cast[uint](-1)
 discard $(x1,)
 
 # bug #13698
-let n: csize = 1 # xxx should that be csize_t or is that essential here?
-doAssert $n.int32 == "1"
+let n2: csize_t = 1
+doAssert $n2.int32 == "1"
 
 # bug #14616
 
@@ -66,7 +70,8 @@ let limit = 1'u64
 
 let rangeVar = 0'u64 ..< limit
 
-doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar)
+when not defined(gcRefc):
+  doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar)
 
 # bug #15210
 
@@ -94,3 +99,17 @@ template main() =
 
 static: main()
 main()
+
+block:
+  let a = uint64.high
+  let b = uint32.high
+
+  doAssert a.uint64 == a
+  doAssert a.uint32 == uint32.high
+  doAssert a.uint16 == uint16.high
+  doAssert a.uint8 == uint8.high
+
+  doAssert b.uint64 == b
+  doAssert b.uint32 == b
+  doAssert b.uint16 == uint16.high
+  doAssert b.uint8 == uint8.high
diff --git a/tests/misc/tunsignedinc.nim b/tests/int/tunsignedinc.nim
index 9d1a4bbb4..9392f1b74 100644
--- a/tests/misc/tunsignedinc.nim
+++ b/tests/int/tunsignedinc.nim
@@ -32,3 +32,9 @@ block t4175:
   const j = 0u - 1u
   doAssert i == j
   doAssert j + 1u == 0u
+
+block: # https://forum.nim-lang.org/t/12465#76998
+  var a: int = 1
+  var x: uint8 = 1
+  a.inc(x)   # Error: type mismatch
+  doAssert a == 2
diff --git a/tests/misc/tunsignedmisc.nim b/tests/int/tunsignedmisc.nim
index b2a3849cf..b2a3849cf 100644
--- a/tests/misc/tunsignedmisc.nim
+++ b/tests/int/tunsignedmisc.nim
diff --git a/tests/int/twrongexplicitvarconv.nim b/tests/int/twrongexplicitvarconv.nim
new file mode 100644
index 000000000..79f770e8e
--- /dev/null
+++ b/tests/int/twrongexplicitvarconv.nim
@@ -0,0 +1,16 @@
+discard """
+  action: reject
+  nimout: '''
+  but expression 'int(a)' is immutable, not 'var'
+'''
+"""
+
+proc `++`(n: var int) =
+  n += 1
+
+var a: int32 = 15
+
+++int(a) #[tt.Error
+^ type mismatch: got <int>]#
+
+echo a
diff --git a/tests/int/twrongvarconv.nim b/tests/int/twrongvarconv.nim
new file mode 100644
index 000000000..db6ac2c53
--- /dev/null
+++ b/tests/int/twrongvarconv.nim
@@ -0,0 +1,9 @@
+proc `++`(n: var int) =
+  n += 1
+
+var a: int32 = 15
+
+++a #[tt.Error
+^ type mismatch: got <int32>]#
+
+echo a
diff --git a/tests/isolate/tisolated_lock.nim b/tests/isolate/tisolated_lock.nim
new file mode 100644
index 000000000..312abf0f6
--- /dev/null
+++ b/tests/isolate/tisolated_lock.nim
@@ -0,0 +1,67 @@
+discard """
+  cmd: "nim $target --threads:on $options $file"
+  action: "compile"
+"""
+
+import std / [os, locks, atomics, isolation]
+
+type
+  MyList {.acyclic.} = ref object
+    data: string
+    next: Isolated[MyList]
+
+template withMyLock*(a: Lock, body: untyped) =
+  acquire(a)
+  {.gcsafe.}:
+    try:
+      body
+    finally:
+      release(a)
+
+var head: Isolated[MyList]
+var headL: Lock
+
+var shouldStop: Atomic[bool]
+
+initLock headL
+
+proc send(x: sink string) =
+  withMyLock headL:
+    head = isolate MyList(data: x, next: move head)
+
+proc worker() {.thread.} =
+  var workItem = MyList(nil)
+  var echoed = 0
+  while true:
+    withMyLock headL:
+      var h = extract head
+      if h != nil:
+        workItem = h
+        # workitem is now isolated:
+        head = move h.next
+      else:
+        workItem = nil
+    # workItem is isolated, so we can access it outside
+    # the lock:
+    if workItem.isNil:
+      if shouldStop.load:
+        break
+      else:
+        # give producer time to breath:
+        os.sleep 30
+    else:
+      if echoed < 100:
+        echo workItem.data
+      inc echoed
+
+var thr: Thread[void]
+createThread(thr, worker)
+
+send "abc"
+send "def"
+for i in 0 ..< 10_000:
+  send "xzy"
+  send "zzz"
+shouldStop.store true
+
+joinThread(thr)
diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim
index 8ad96f0da..c971943ee 100644
--- a/tests/iter/t1550.nim
+++ b/tests/iter/t1550.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
 type
   A[T] = iterator(x: T): T {.gcsafe, closure.}
 
diff --git a/tests/iter/t20891.nim b/tests/iter/t20891.nim
new file mode 100644
index 000000000..34deec41b
--- /dev/null
+++ b/tests/iter/t20891.nim
@@ -0,0 +1,28 @@
+import macros, tables
+
+var mapping {.compileTime.}: Table[string, NimNode]
+
+macro register(a: static[string], b: typed): untyped =
+  mapping[a] = b
+
+macro getPtr(a: static[string]): untyped =
+  result = mapping[a]
+
+proc foo() =
+  iterator it() {.closure.} =
+    discard
+  proc getIterPtr(): pointer {.nimcall.} =
+    rawProc(it)
+  register("foo", getIterPtr())
+  discard getIterPtr() # Comment either this to make it work
+foo() # or this
+
+proc bar() =
+  iterator it() {.closure.} =
+    discard getPtr("foo") # Or this
+    discard
+  proc getIterPtr(): pointer {.nimcall.} =
+    rawProc(it)
+  register("bar", getIterPtr())
+  discard getIterPtr()
+bar()
diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim
new file mode 100644
index 000000000..4d0396294
--- /dev/null
+++ b/tests/iter/t21306.nim
@@ -0,0 +1,118 @@
+discard """
+  targets: "c js"
+"""
+
+# bug #21306
+type
+  FutureState {.pure.} = enum
+    Pending, Finished, Cancelled, Failed
+
+  FutureBase = ref object of RootObj
+    state: FutureState
+    error: ref CatchableError
+    id: uint
+
+  Future[T] = ref object of FutureBase
+    closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.}
+    value: T
+
+template setupFutureBase() =
+  new(result)
+  result.state = FutureState.Pending
+
+proc newFutureImpl[T](): Future[T] =
+  setupFutureBase()
+
+template newFuture[T](fromProc: static[string] = ""): Future[T] =
+  newFutureImpl[T]()
+
+proc internalRead[T](fut: Future[T]): T =
+  when T isnot void:
+    return fut.value
+
+template await[T](f: Future[T]): untyped =
+  when declared(chronosInternalRetFuture):
+    when not declaredInScope(chronosInternalTmpFuture):
+      var chronosInternalTmpFuture {.inject.}: FutureBase = f
+    else:
+      chronosInternalTmpFuture = f
+
+    yield chronosInternalTmpFuture
+
+    when T isnot void:
+      cast[type(f)](chronosInternalTmpFuture).internalRead()
+
+type
+  VerifierError {.pure.} = enum
+    Invalid
+    MissingParent
+    UnviableFork
+    Duplicate
+  ProcessingCallback = proc() {.gcsafe, raises: [Defect].}
+  BlockVerifier =
+    proc(signedBlock: int):
+      Future[VerifierError] {.gcsafe, raises: [Defect].}
+
+  SyncQueueKind {.pure.} = enum
+    Forward, Backward
+
+  SyncRequest[T] = object
+    kind: SyncQueueKind
+    index: uint64
+    slot: uint64
+    count: uint64
+    item: T
+
+  SyncResult[T] = object
+    request: SyncRequest[T]
+    data: seq[ref int]
+
+  SyncQueue[T] = ref object
+    kind: SyncQueueKind
+    readyQueue: seq[SyncResult[T]]
+    blockVerifier: BlockVerifier
+
+iterator blocks[T](sq: SyncQueue[T],
+                    sr: SyncResult[T]): ref int =
+  case sq.kind
+  of SyncQueueKind.Forward:
+    for i in countup(0, len(sr.data) - 1):
+      yield sr.data[i]
+  of SyncQueueKind.Backward:
+    for i in countdown(len(sr.data) - 1, 0):
+      yield sr.data[i]
+
+proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int];
+             processingCb: ProcessingCallback = nil): Future[void] {.
+    stackTrace: off, gcsafe.} =
+  iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {.
+      closure, gcsafe, raises: [Defect, CatchableError, Exception].} =
+    block:
+      template result(): auto {.used.} =
+        {.fatal: "You should not reference the `result` variable inside" &
+            " a void async proc".}
+
+      let item = default(SyncResult[T])
+      for blk in sq.blocks(item):
+        let res = await sq.blockVerifier(blk[])
+
+  var resultFuture = newFuture[void]("push")
+  resultFuture.closure = push_436208182
+  return resultFuture
+
+type
+  SomeTPeer = ref object
+    score: int
+
+proc getSlice(): seq[ref int] =
+  discard
+
+template smokeTest(kkind: SyncQueueKind, start, finish: uint64,
+                   chunkSize: uint64) =
+  var queue: SyncQueue[SomeTPeer]
+  var request: SyncRequest[SomeTPeer]
+  discard queue.push(request, getSlice())
+
+for k in {SyncQueueKind.Forward}:
+  for item in [(uint64(1181), uint64(1399), 41'u64)]:
+    smokeTest(k, item[0], item[1], item[2])
\ No newline at end of file
diff --git a/tests/iter/t21737.nim b/tests/iter/t21737.nim
new file mode 100644
index 000000000..da06faea7
--- /dev/null
+++ b/tests/iter/t21737.nim
@@ -0,0 +1,22 @@
+discard """
+  action: compile
+"""
+
+template mytoSeq*(iter: untyped): untyped =
+  var result: seq[typeof(iter)]# = @[]
+  for x in iter:
+    result.add(x)
+  result
+
+iterator test(dir:int): int =
+  yield 1234
+
+iterator walkGlobKinds (): int =
+  let dir2 = 123
+  let it = mytoSeq(test(dir2))  
+
+proc main()=
+    let it = iterator(): int=
+      for path in walkGlobKinds():
+          yield path
+main()
diff --git a/tests/iter/t22148.nim b/tests/iter/t22148.nim
new file mode 100644
index 000000000..9954eed87
--- /dev/null
+++ b/tests/iter/t22148.nim
@@ -0,0 +1,15 @@
+discard """

+  action: compile

+"""

+

+import std/memfiles

+

+# bug #22148

+proc make*(input: string) =

+  var inp = memfiles.open(input)

+  for line in memSlices(inp):

+    let lineF = MemFile(mem: line.data, size: line.size)

+    for word in memSlices(lineF, ','):

+      discard

+

+make("") # Must call to trigger

diff --git a/tests/iter/t22548.nim b/tests/iter/t22548.nim
new file mode 100644
index 000000000..b9abb75d0
--- /dev/null
+++ b/tests/iter/t22548.nim
@@ -0,0 +1,21 @@
+discard """
+  action: compile
+"""
+
+type Xxx[T] = object
+
+iterator x(v: string): char =
+  var v2: Xxx[int]
+
+  var y: v2.T
+
+  echo y
+
+proc bbb(vv: string): proc () =
+  proc xxx() =
+    for c in x(vv):
+      echo c
+
+  return xxx
+
+bbb("test")()
diff --git a/tests/iter/t22619.nim b/tests/iter/t22619.nim
new file mode 100644
index 000000000..6a98391f3
--- /dev/null
+++ b/tests/iter/t22619.nim
@@ -0,0 +1,83 @@
+# bug #22619
+
+when false: # todo fixme
+  block:
+    type
+      Resource = object
+        value: int
+  
+      Object = object
+        r {.cursor.}: Resource
+        s {.cursor.}: seq[Resource]
+  
+    var numDestroy = 0
+  
+    proc `=copy`(x: var Resource, y: Resource) {.error.} # disallow full copies
+    proc `=destroy`(x: Resource) =
+      inc numDestroy
+  
+    proc test() =
+      # perform the test in procedure so that globals aren't used (their different
+      # semantics with regards to destruction would interfere)
+      var
+        r = Resource(value: 1) # initialize a resource
+        s = @[Resource(value: 2)]
+  
+      # make sure no copy is required in the initializer expression:
+      var o = Object(r: r, s: s)
+  
+      # copying the object doesn't perform a full copy of the cursor fields:
+      var o2 = o
+      discard addr(o2) # prevent `o2` from being turned into a cursor
+  
+      # check that the fields were shallow-copied:
+      doAssert o2.r.value == 1
+      doAssert o2.s[0].value == 2
+  
+      # make sure no copy is required with normal field assignments:
+      o.r = r
+      o.s = s
+  
+  
+      # when `o` and `o2` are destroyed, their destructor must not be called on
+      # their fields
+  
+    test()
+  
+    # one call for the `r` local and one for the object in `s`
+    doAssert numDestroy == 2
+
+block:
+  type Value = distinct int
+
+  var numDestroy = 0
+
+  when defined(gcRefc):
+    proc `=destroy`(x: var Value) =
+      inc numDestroy
+  else:
+    proc `=destroy`(x: Value) =
+      inc numDestroy
+
+  iterator iter(s: seq[Value]): int {.closure.} =
+    # because it is used across yields, `s2` is lifted into the iterator's
+    # environment. Since non-ref cursors in object didn't have their hooks
+    # disabled inside the environments lifted hooks, this led to double
+    # frees
+    var s2 {.cursor.} = s
+    var i = 0
+    let L = s2.len
+    while i < L:
+      yield s2[i].int
+      inc i
+
+  proc test() =
+    var s = @[Value(1), Value(2)]
+    let cl = iter
+    # make sure resuming the iterator works:
+    doAssert cl(s) == 1
+    doAssert cl(s) == 2
+    doAssert cl(s) == 0
+
+  test()
+  doAssert numDestroy == 2
diff --git a/tests/iter/t2771.nim b/tests/iter/t2771.nim
index 49befb0a9..71a8a9dcd 100644
--- a/tests/iter/t2771.nim
+++ b/tests/iter/t2771.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
 template t1(i: int): int=
   i+1
 template t2(i: int): int=
diff --git a/tests/iter/tanoniter1.nim b/tests/iter/tanoniter1.nim
index 9f0d0a74b..fee16497f 100644
--- a/tests/iter/tanoniter1.nim
+++ b/tests/iter/tanoniter1.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c js"
   output: '''1
 2
 3
diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim
index 85611373c..4a2639852 100644
--- a/tests/iter/tclosureiters.nim
+++ b/tests/iter/tclosureiters.nim
@@ -1,4 +1,5 @@
 discard """
+  targets: "c js"
   output: '''0
 1
 2
@@ -152,15 +153,18 @@ iterator filesIt(path: string): auto {.closure.} =
       yield prefix / f
 
 # bug #13815
-var love = iterator: int {.closure.} =
-  yield cast[type(
-    block:
-      var a = 0
-      yield a
-      a)](0)
-
-for i in love():
-  echo i
+when not defined(js):
+  var love = iterator: int {.closure.} =
+    yield cast[type(
+      block:
+        var a = 0
+        yield a
+        a)](0)
+
+  for i in love():
+    echo i
+else:
+  echo 0
 
 # bug #18474
 iterator pairs(): (int, int) {.closure.} =
diff --git a/tests/iter/tgeniteratorinblock.nim b/tests/iter/tgeniteratorinblock.nim
new file mode 100644
index 000000000..2ab903996
--- /dev/null
+++ b/tests/iter/tgeniteratorinblock.nim
@@ -0,0 +1,54 @@
+discard """
+  output: '''30
+60
+90
+150
+180
+210
+240
+60
+180
+240
+[60, 180, 240]
+[60, 180]'''
+"""
+import std/enumerate
+
+template map[T; Y](i: iterable[T], fn: proc(x: T): Y): untyped =
+  iterator internal(): Y  {.gensym.} =
+    for it in i:
+      yield fn(it)
+  internal()
+
+template filter[T](i: iterable[T], fn: proc(x: T): bool): untyped =
+  iterator internal(): T {.gensym.} =
+    for it in i:
+      if fn(it):
+        yield it
+  internal()
+
+template group[T](i: iterable[T], amount: static int): untyped =
+  iterator internal(): array[amount, T] {.gensym.} =
+    var val: array[amount, T]
+    for ind, it in enumerate i:
+      val[ind mod amount] = it
+      if ind mod amount == amount - 1:
+        yield val
+  internal()
+
+var a = [10, 20, 30, 50, 60, 70, 80]
+
+proc mapFn(x: int): int = x * 3
+proc filterFn(x: int): bool = x mod 20 == 0
+
+for x in a.items.map(mapFn):
+  echo x
+
+for y in a.items.map(mapFn).filter(filterFn):
+  echo y
+
+for y in a.items.map(mapFn).filter(filterFn).group(3):
+  echo y
+
+for y in a.items.map(mapFn).filter(filterFn).group(2):
+  echo y
diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim
index 3a2450ae3..b03d43f36 100644
--- a/tests/iter/titer.nim
+++ b/tests/iter/titer.nim
@@ -112,3 +112,36 @@ let res = collect:
       fn2(v2)
 
 doAssert res == @[42, 43, 43, 44]
+
+block: # bug #21110
+  iterator p(): int =
+    when nimvm:
+      yield 0
+    else:
+      yield 0
+
+  template foo =
+    for k in p():
+      let m = ""
+      proc e() = discard m & ""
+      e()
+  static: foo()
+  foo()
+
+
+# bug #15924
+iterator walk(): (int, int) {.closure.} =
+  yield (10,11)
+
+for (i,j) in walk():
+  doAssert i == 10
+
+proc main123() =
+  let x = false
+  iterator it(): (bool, bool) {.closure.} = # normally {.closure.} here makes #21476 work
+    discard x
+
+  for (_, _) in it():
+    discard
+
+main123()
diff --git a/tests/iter/titer11.nim b/tests/iter/titer11.nim
index 2b39c74f7..153b3c29a 100644
--- a/tests/iter/titer11.nim
+++ b/tests/iter/titer11.nim
@@ -1,4 +1,5 @@
 discard """
+targets: "c js"
 output: '''
 [
 1
diff --git a/tests/iter/titer12.nim b/tests/iter/titer12.nim
index f7fc64da4..f264a0e82 100644
--- a/tests/iter/titer12.nim
+++ b/tests/iter/titer12.nim
@@ -1,4 +1,5 @@
 discard """
+targets: "c js"
 output: '''
 Selecting 2
 1.0
diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim
index 9c7a5eab0..c82b3902d 100644
--- a/tests/iter/titer_issues.nim
+++ b/tests/iter/titer_issues.nim
@@ -1,4 +1,5 @@
 discard """
+  target: "c js"
   output: '''
 0
 1
@@ -38,6 +39,10 @@ nested finally
 outer finally
 nested finally
 outer finally
+In defer
+trying
+exception caught
+finally block
 '''
 """
 
@@ -362,3 +367,45 @@ block:
 
   for _ in p4():
     discard
+
+# bug #18824
+iterator poc_iterator: int {.closure.}  =
+  block bug18824:
+    try:
+      break bug18824
+    finally:
+      echo "In defer"
+
+for _ in poc_iterator():
+  discard
+
+# bug #20624
+iterator tryFinally() {.closure.} =
+  block route:
+    try:
+      echo "trying"
+      raise
+    except:
+      echo "exception caught"
+      break route
+    finally:
+      echo "finally block"
+
+var x = tryFinally
+x()
+
+block: # bug #24033
+  type Query = ref object
+
+  iterator pairs(query: Query): (int, (string, float32)) =
+    var output: (int, (string, float32)) = (0, ("foo", 3.14))
+    for id in @[0, 1, 2]:
+      output[0] = id
+      yield output
+
+  var collections: seq[(int, string, string)]
+
+  for id, (str, num) in Query():
+    collections.add (id, str, $num)
+
+  doAssert collections[1] == (1, "foo", "3.14")
diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim
index ad1192bd8..b2fe71ceb 100644
--- a/tests/iter/titervaropenarray.nim
+++ b/tests/iter/titervaropenarray.nim
@@ -1,6 +1,6 @@
 discard """
   output: "123"
-  targets: "c"
+  targets: "c cpp"
 """
 # Try to break the transformation pass:
 iterator iterAndZero(a: var openArray[int]): int =
diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim
index 9df201dd4..e51ab7f0d 100644
--- a/tests/iter/tyieldintry.nim
+++ b/tests/iter/tyieldintry.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "; --experimental:strictdefs; -d:nimOptIters"
   targets: "c cpp"
 """
 
@@ -503,3 +504,26 @@ block: # void iterator
     except:
       discard
   var a = it
+
+if defined(nimOptIters): # Locals present in only 1 state should be on the stack
+  proc checkOnStack(a: pointer, shouldBeOnStack: bool) =
+    # Quick and dirty way to check if a points to stack
+    var dummy = 0
+    let dummyAddr = addr dummy
+    let distance = abs(cast[int](dummyAddr) - cast[int](a))
+    const requiredDistance = 300
+    if shouldBeOnStack:
+      doAssert(distance <= requiredDistance, "a is not on stack, but should")
+    else:
+      doAssert(distance > requiredDistance, "a is on stack, but should not")
+
+  iterator it(): int {.closure.} =
+    var a = 1
+    var b = 2
+    var c {.liftLocals.} = 3
+    checkOnStack(addr a, true)
+    checkOnStack(addr b, false)
+    checkOnStack(addr c, false)
+    yield a
+    yield b
+  test(it, 1, 2)
diff --git a/tests/js/t20235.nim b/tests/js/t20235.nim
new file mode 100644
index 000000000..3a69c2bd6
--- /dev/null
+++ b/tests/js/t20235.nim
@@ -0,0 +1,11 @@
+discard """
+  action: "run"
+  output: "0 4"
+"""
+
+proc main =
+  var s = ""
+  s.setLen(4)
+  echo s[0].ord, " ", s.len
+
+main()
diff --git a/tests/js/t21209.nim b/tests/js/t21209.nim
new file mode 100644
index 000000000..4de34f035
--- /dev/null
+++ b/tests/js/t21209.nim
@@ -0,0 +1,6 @@
+discard """
+  action: "compile"
+  cmd: "nim check --warning[UnusedImport]:off $file"
+"""
+
+import std/times
diff --git a/tests/js/t21209.nims b/tests/js/t21209.nims
new file mode 100644
index 000000000..318e28f97
--- /dev/null
+++ b/tests/js/t21209.nims
@@ -0,0 +1 @@
+--b:js
\ No newline at end of file
diff --git a/tests/js/t21247.nim b/tests/js/t21247.nim
new file mode 100644
index 000000000..5b38787b3
--- /dev/null
+++ b/tests/js/t21247.nim
@@ -0,0 +1,15 @@
+import std/typetraits
+
+type
+  QueryParams* = distinct seq[(string, string)]
+
+converter toBase*(params: var QueryParams): var seq[(string, string)] =
+  params.distinctBase
+
+proc foo(): QueryParams =
+  # Issue was that the implicit converter call didn't say that it took the
+  # address of the parameter it was converting. This led to the parameter not being
+  # passed as a fat pointer which toBase expected
+  result.add(("hello", "world"))
+
+assert foo().distinctBase() == @[("hello", "world")]
diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim
new file mode 100644
index 000000000..3caeb090a
--- /dev/null
+++ b/tests/js/t21439.nim
@@ -0,0 +1,11 @@
+proc test(a: openArray[string]): proc =
+  let a = @a
+  result = proc =
+    for i in a:
+      discard i
+
+
+const a = ["t1", "t2"]
+
+discard test(a)
+
diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim
index 0d071dbbf..a1a3b718e 100644
--- a/tests/js/t7109.nim
+++ b/tests/js/t7109.nim
@@ -1,8 +1,8 @@
-discard """
-  errormsg: "Closure iterators are not supported by JS backend!"
-"""
-
 iterator iter*(): int {.closure.} =
   yield 3
 
 var x = iter
+doAssert x() == 3
+
+let fIt = iterator(): int = yield 70
+doAssert fIt() == 70
diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim
index 43cf3f6f2..38c88efa8 100644
--- a/tests/js/t8821.nim
+++ b/tests/js/t8821.nim
@@ -1,6 +1,3 @@
-discard """
-  errormsg: "Your case statement contains too many branches, consider using if/else instead!"
-"""
 
 proc isInt32(i: int): bool =
   case i 
@@ -9,4 +6,4 @@ proc isInt32(i: int): bool =
   else:
     return false
 
-discard isInt32(1)
\ No newline at end of file
+doAssert isInt32(1) == true
\ No newline at end of file
diff --git a/tests/js/tasyncjs.nim b/tests/js/tasyncjs.nim
index 00753a16c..f3b273c44 100644
--- a/tests/js/tasyncjs.nim
+++ b/tests/js/tasyncjs.nim
@@ -94,4 +94,14 @@ proc main() {.async.} =
     doAssert "foobar: 7" in $reason.message
   echo "done" # justified here to make sure we're running this, since it's inside `async`
 
+block asyncPragmaInType:
+  type Handler = proc () {.async.}
+  proc foo() {.async.} = discard
+  var x: Handler = foo
+
+block: # 13341
+  proc f {.async.} =
+    proc g: int =
+      result = 123
+
 discard main()
diff --git a/tests/js/tcodegendeclproc.nim b/tests/js/tcodegendeclproc.nim
index 3acf0bc13..33064bdf1 100644
--- a/tests/js/tcodegendeclproc.nim
+++ b/tests/js/tcodegendeclproc.nim
@@ -3,7 +3,7 @@ discard """
 -1
 8
 '''
-  ccodecheck: "'console.log(-1); function fac_' \\d+ '(n_' \\d+ ')'"
+  ccodecheck: "'console.log(-1); function fac__tcodegendeclproc_u1(n_p0)'"
 """
 proc fac(n: int): int {.codegenDecl: "console.log(-1); function $2($3)".} =
   return n
diff --git a/tests/js/tdanger.nim b/tests/js/tdanger.nim
new file mode 100644
index 000000000..9088859a8
--- /dev/null
+++ b/tests/js/tdanger.nim
@@ -0,0 +1,17 @@
+discard """
+  matrix: ";--d:danger"
+"""
+
+block:
+  proc foo() =
+    var name = int64(12)
+    var x = uint32(name)
+    var m = x + 12
+
+    var y = int32(name)
+    var n = y + 1
+
+    doAssert m == uint32(n + 11)
+
+
+  foo()
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 06a30c44d..f27ea5546 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--legacy:jsnolambdalifting;"
 output: '''
 3
 2
@@ -180,7 +181,7 @@ block: # Test lit
 block: # Test bindMethod
   type TestObject = object
     a: int
-    onWhatever: proc(e: int): int
+    onWhatever: proc(e: int): int {.nimcall.}
   proc handleWhatever(this: TestObject, e: int): int =
     e + this.a
   block:
@@ -217,7 +218,7 @@ block:
   on("click") do (e: Event):
     console.log e
 
-  jslib.on("reloaded") do:
+  jslib.on("reloaded") do ():
     console.log jsarguments[0]
 
   # this test case is different from the above, because
@@ -265,3 +266,9 @@ block: # test **
   doAssert to(`**`(a + a, b), int) == 2
 
   doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4
+
+block: # issue #21208
+  type MyEnum = enum baz
+  var obj: JsObject
+  {.emit: "`obj` = {bar: {baz: 123}};".}
+  discard obj.bar.baz
diff --git a/tests/js/tjsffi_old.nim b/tests/js/tjsffi_old.nim
index 078d208c5..378003f4e 100644
--- a/tests/js/tjsffi_old.nim
+++ b/tests/js/tjsffi_old.nim
@@ -279,8 +279,8 @@ block:
 block:
   type TestObject = object
     a: int
-    onWhatever: proc(e: int): int
-  proc handleWhatever(this: TestObject, e: int): int =
+    onWhatever: proc(e: int): int {.nimcall.}
+  proc handleWhatever(this: TestObject, e: int): int {.nimcall.} =
     e + this.a
   proc test(): bool =
     let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
@@ -321,7 +321,7 @@ block:
   on("click") do (e: Event):
     console.log e
 
-  jslib.on("reloaded") do:
+  jslib.on("reloaded") do ():
     console.log jsarguments[0]
 
   # this test case is different from the above, because
diff --git a/tests/js/tjsnimscombined.nim b/tests/js/tjsnimscombined.nim
new file mode 100644
index 000000000..4d3e6c453
--- /dev/null
+++ b/tests/js/tjsnimscombined.nim
@@ -0,0 +1 @@
+import std/jsffi
diff --git a/tests/js/tjsnimscombined.nims b/tests/js/tjsnimscombined.nims
new file mode 100644
index 000000000..01b93d3fa
--- /dev/null
+++ b/tests/js/tjsnimscombined.nims
@@ -0,0 +1 @@
+# test the condition where both `js` and `nimscript` are defined (nimscript receives priority)
diff --git a/tests/js/tnativeexc.nim b/tests/js/tnativeexc.nim
index ea371c1cd..8b2b43e8f 100644
--- a/tests/js/tnativeexc.nim
+++ b/tests/js/tnativeexc.nim
@@ -18,7 +18,7 @@ try:
 except JsEvalError:
   doAssert false
 except JsSyntaxError as se:
-  doAssert se.message == "Unexpected token ; in JSON at position 0"
+  doAssert se.message == "Unexpected token ';', \";;\" is not valid JSON"
 except JsError as e:
   doAssert false
 
diff --git a/tests/js/tneginthash.nim b/tests/js/tneginthash.nim
new file mode 100644
index 000000000..c082405c9
--- /dev/null
+++ b/tests/js/tneginthash.nim
@@ -0,0 +1,21 @@
+# issue #19929
+
+import std/[tables, hashes]
+
+type Foo = object
+  a: int
+
+proc hash(f: Foo): Hash =
+  var h: Hash = 0
+  h = h !& hash(f.a)
+  result = !$h
+
+proc transpose[T, S](data: array[T, S]): Table[S, T] =
+  for i, x in data:
+    result[x] = i
+
+const xs = [Foo(a: 5), Foo(a: -5)]
+const x = transpose(xs)
+
+doAssert x[Foo(a: -5)] == 1
+doAssert x[Foo(a: 5)] == 0
diff --git a/tests/js/tos.nim b/tests/js/tos.nim
index bfe3cd9b4..40fb52bcf 100644
--- a/tests/js/tos.nim
+++ b/tests/js/tos.nim
@@ -13,11 +13,9 @@ block:
   doAssert not "foo".isAbsolute
   doAssert relativePath("", "bar") == ""
   doAssert normalizedPath(".///foo//./") == "foo"
-  let cwd = getCurrentDir()
 
-  let isWindows = '\\' in cwd
-  # defined(windows) doesn't work with -d:nodejs but should
-  # these actually break because of that (see https://github.com/nim-lang/Nim/issues/13469)
-  if not isWindows:
+  when nimvm: discard
+  else:
+    let cwd = getCurrentDir()
     doAssert cwd.isAbsolute
-    doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo"
+    doAssert relativePath(getCurrentDir() / "foo", "bar") == ".." / "foo"
diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim
index c63b64d3a..a562ad63b 100644
--- a/tests/js/trepr.nim
+++ b/tests/js/trepr.nim
@@ -283,7 +283,7 @@ block bunch:
     result[] = b
 
   var
-    aa: A
+    aa = default(A)
     bb: B = B(a: "inner", b: @['o', 'b', 'j'])
     cc: A = A(a: 12, b: 1, c: 1.2, d: '\0', e: eC,
                 f: "hello", g: {'A'}, h: {2'i16},
@@ -303,7 +303,7 @@ g = {},
 h = {},
 i = ["", "", ""],
 j = @[],
-k = 0,
+k = -12,
 l = [a = "",
 b = @[]],
 m = nil,
diff --git a/tests/js/tsourcemap.nim b/tests/js/tsourcemap.nim
new file mode 100644
index 000000000..d358e4a57
--- /dev/null
+++ b/tests/js/tsourcemap.nim
@@ -0,0 +1,96 @@
+discard """
+  action: "run"
+  targets: "js"
+  cmd: "nim js -r -d:nodejs $options --sourceMap:on $file"
+"""
+import std/[os, json, strutils, sequtils, algorithm, assertions, paths, compilesettings]
+
+# Implements a very basic sourcemap parser and then runs it on itself.
+# Allows to check for basic problems such as bad counts and lines missing (e.g. issue #21052)
+
+type
+  SourceMap = object
+    version:   int
+    sources:   seq[string]
+    names:     seq[string]
+    mappings:  string
+    file:      string
+
+  Line = object
+    line, column: int
+    file: string
+
+const
+  flag = 1 shl 5
+  signBit = 0b1
+  fourBits = 0b1111
+  fiveBits = 0b11111
+  mask = (1 shl 5) - 1
+  alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+
+var b64Table: seq[int] = 0.repeat(max(alphabet.mapIt(it.ord)) + 1)
+for i, b in alphabet.pairs:
+  b64Table[b.ord] = i
+
+# From https://github.com/juancarlospaco/nodejs/blob/main/src/nodejs/jsfs.nim
+proc importFs*() {.importjs: "var fs = require(\"fs\");".}
+proc readFileSync*(path: cstring): cstring {.importjs: "(fs.$1(#).toString())".}
+importFS()
+# Read in needed files
+let
+  jsFileName = string(querySetting(outDir).Path / "tsourcemap.js".Path)
+  mapFileName = jsFileName & ".map"
+
+  data = parseJson($mapFileName.cstring.readFileSync()).to(SourceMap)
+  jsFile = $readFileSync(jsFileName.cstring)
+
+proc decodeVLQ(inp: string): seq[int] =
+  var
+    shift, value: int
+  for v in inp.mapIt(b64Table[it.ord]):
+    value += (v and mask) shl shift
+    if (v and flag) > 0:
+      shift += 5
+      continue
+    result &= (value shr 1) * (if (value and 1) > 0: -1 else: 1)
+    shift = 0
+    value = 0
+
+
+# Keep track of state
+var
+  line = 0
+  source = 0
+  name = 0
+  column = 0
+  jsLine = 1
+  lines: seq[Line]
+
+for gline in data.mappings.split(';'):
+  jsLine += 1
+  var jsColumn = 0
+  for item in gline.strip().split(','):
+    let value = item.decodeVLQ()
+    doAssert value.len in [0, 1, 4, 5]
+    if value.len == 0:
+      continue
+    jsColumn += value[0]
+    if value.len >= 4:
+      source += value[1]
+      line += value[2]
+      column += value[3]
+      lines &= Line(line: line, column: column, file: data.sources[source])
+
+let jsLines = jsFile.splitLines().len
+# There needs to be a mapping for every line in the JS
+# If there isn't then the JS lines wont match up with Nim lines.
+# Except we don't care about the final line since that doesn't need to line up
+doAssert data.mappings.count(';') == jsLines - 1
+
+# Check we can find this file somewhere in the source map
+var foundSelf = false
+for line in lines:
+  if "tsourcemap.nim" in line.file:
+    foundSelf = true
+    doAssert line.line in 0..<jsLines, "Lines is out of bounds for file"
+doAssert foundSelf, "Couldn't find tsourcemap.nim in source map"
diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim
index 30aca7561..db851ba28 100644
--- a/tests/js/tstdlib_imports.nim
+++ b/tests/js/tstdlib_imports.nim
@@ -4,6 +4,11 @@ discard """
 
 {.warning[UnusedImport]: off.}
 
+when defined(nimPreviewSlimSystem):
+  import std/[
+    syncio, assertions, formatfloat, objectdollar, widestrs
+  ]
+
 import std/[
   # Core:
   bitops, typetraits, lenientops, macros, volatile, typeinfo,
@@ -20,7 +25,7 @@ import std/[
 
   # Strings:
   cstrutils, editdistance, wordwrap, parseutils, ropes,
-  pegs, punycode, strformat, strmisc, strscans, strtabs,
+  pegs, strformat, strmisc, strscans, strtabs,
   strutils, unicode, unidecode,
   # fails due to FFI: encodings
 
@@ -40,7 +45,7 @@ import std/[
   # Internet protocols:
   cookies, httpcore, mimetypes, uri,
   # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver,
-  # asyncnet, cgi, httpclient, nativesockets, net, selectors, smtp
+  # asyncnet, cgi, httpclient, nativesockets, net, selectors
   # works but no need to test: asyncstreams, asyncfutures
   
   # Threading:
@@ -57,7 +62,7 @@ import std/[
   htmlgen,
 
   # Hashing:
-  base64, hashes, md5,
+  base64, hashes,
   # fails due to cstring cast/endians import: oids
   # fails due to copyMem/endians import: sha1
 
diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim
index 4b5ce1de8..1e584f735 100644
--- a/tests/js/tstdlib_various.nim
+++ b/tests/js/tstdlib_various.nim
@@ -150,12 +150,7 @@ block tsplit2:
     s.add("#")
     s.add(w)
 
-  var errored = false
-  try:
-    discard "hello".split("")
-  except AssertionDefect:
-    errored = true
-  doAssert errored
+  doAssert "true".split("") == @["true"]
 
 block txmlgen:
   var nim = "Nim"
diff --git a/tests/js/ttypedarray.nim b/tests/js/ttypedarray.nim
index 222f66569..4807cb103 100644
--- a/tests/js/ttypedarray.nim
+++ b/tests/js/ttypedarray.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--jsbigint64:off -d:nimStringHash2; --jsbigint64:on"
+"""
+
 import std/private/jsutils
 
 proc main()=
@@ -5,9 +9,10 @@ proc main()=
   doAssert fn(array[2, int8].default) == "Int8Array"
   doAssert fn(array[2, uint8].default) == "Uint8Array"
   doAssert fn(array[2, byte].default) == "Uint8Array"
-  # doAssert fn(array[2, char].default) == "Uint8Array" # xxx fails; bug?
-  doAssert fn(array[2, uint64].default) == "Array"
-    # pending https://github.com/nim-lang/RFCs/issues/187 maybe use `BigUint64Array`
+  doAssert fn(array[2, char].default) == "Uint8Array"
+  whenJsNoBigInt64: discard
+  do:
+    doAssert fn(array[2, uint64].default) == "BigUint64Array"
   doAssert fn([1'u8]) == "Uint8Array"
   doAssert fn([1'u16]) == "Uint16Array"
   doAssert fn([byte(1)]) == "Uint8Array"
diff --git a/tests/lent/t16898.nim b/tests/lent/t16898.nim
index ea7c934ef..a69c6d244 100644
--- a/tests/lent/t16898.nim
+++ b/tests/lent/t16898.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "invalid type: 'lent QuadraticExt' in this context: 'proc (r: var QuadraticExt, a: lent QuadraticExt, b: lent QuadraticExt){.noSideEffect, gcsafe, locks: 0.}' for proc"
+  errormsg: "invalid type: 'lent QuadraticExt' in this context: 'proc (r: var QuadraticExt, a: lent QuadraticExt, b: lent QuadraticExt){.noSideEffect, gcsafe.}' for proc"
 """
 
 # bug #16898
diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim
index 92d731451..ce9b89adf 100644
--- a/tests/lent/tbasic_lent_check.nim
+++ b/tests/lent/tbasic_lent_check.nim
@@ -28,7 +28,7 @@ template main2 = # bug #15958
   doAssert byLent(a) == [11,12]
   doAssert sameAddress(byLent(a), a)
   doAssert byLent(b) == @[21,23]
-  # pending bug #16073
+  # bug #16073
   doAssert sameAddress(byLent(b), b)
   doAssert byLent(ss) == {1, 2, 3, 5}
   doAssert sameAddress(byLent(ss), ss)
diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim
index 912390dc1..1fb3d0c17 100644
--- a/tests/lent/tlent_from_var.nim
+++ b/tests/lent/tlent_from_var.nim
@@ -30,3 +30,78 @@ let x2 = x.byLentVar
 
 let xs2 = xs.byLentVar
 echo xs2
+
+# bug #22138
+
+type Xxx = object
+
+type
+  Opt[T] = object
+    case oResultPrivate*: bool
+    of false:
+      discard
+    of true:
+      vResultPrivate*: T
+
+func value*[T: not void](self: Opt[T]): lent T {.inline.} =
+  self.vResultPrivate
+template get*[T: not void](self: Opt[T]): T = self.value()
+
+method connect*(
+  self: Opt[(int, int)]) =
+  discard self.get()[0]
+
+block: # bug #23454
+  type
+    Letter = enum
+      A
+
+    LetterPairs = object
+      values: seq[(Letter, string)]
+
+  iterator items(list: var LetterPairs): lent (Letter, string) =
+    for item in list.values:
+      yield item
+
+  var instance = LetterPairs(values: @[(A, "foo")])
+
+  for (a, _) in instance:
+    case a
+    of A: discard
+
+block: # bug #23454
+  type
+    Letter = enum
+      A
+
+    LetterPairs = object
+      values: seq[(Letter, string)]
+
+  iterator items(list: var LetterPairs): var (Letter, string) =
+    for item in list.values.mItems:
+      yield item
+
+  var instance = LetterPairs(values: @[(A, "foo")])
+
+  for (a, _) in instance:
+    case a
+    of A: discard
+
+block: # bug #24034
+  type T = object
+    v: array[100, byte]
+
+
+  iterator pairs(t: T): (int, lent array[100, byte]) =
+    yield (0, t.v)
+
+
+  block:
+    for a, b in default(T):
+      doAssert a == 0
+      doAssert b.len == 100
+
+  block:
+    for (a, b) in pairs(default(T)):
+      doAssert a == 0
+      doAssert b.len == 100
diff --git a/tests/lent/tvm.nim b/tests/lent/tvm.nim
new file mode 100644
index 000000000..5df1d1270
--- /dev/null
+++ b/tests/lent/tvm.nim
@@ -0,0 +1,21 @@
+block: # issue #17527
+  iterator items2[IX, T](a: array[IX, T]): lent T {.inline.} =
+    var i = low(IX)
+    if i <= high(IX):
+      while true:
+        yield a[i]
+        if i >= high(IX): break
+        inc(i)
+
+  proc main() =
+    var s: seq[string] = @[]
+    for i in 0..<3:
+      for (key, val) in items2([("any", "bar")]):
+        s.add $(i, key, val)
+    doAssert s == @[
+      "(0, \"any\", \"bar\")",
+      "(1, \"any\", \"bar\")",
+      "(2, \"any\", \"bar\")"
+    ]
+
+  static: main()
diff --git a/tests/lexer/nim.cfg b/tests/lexer/nim.cfg
deleted file mode 100644
index f7a301a10..000000000
--- a/tests/lexer/nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
---experimental:unicodeOperators
diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim
index 9c49d0c08..35b4803d3 100644
--- a/tests/lexer/tcustom_numeric_literals.nim
+++ b/tests/lexer/tcustom_numeric_literals.nim
@@ -134,17 +134,14 @@ template main =
 
   block: # bug 1 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
     macro deb1(a): untyped = newLit a.repr
-    macro deb2(a): untyped = newLit a.lispRepr
+    macro deb2(a): untyped =
+      a[1] = ident($a[1])
+      newLit a.lispRepr
     doAssert deb1(-12'wrap) == "-12'wrap"
     doAssert deb1(-12'nonexistent) == "-12'nonexistent"
     doAssert deb2(-12'nonexistent) == """(DotExpr (RStrLit "-12") (Ident "\'nonexistent"))"""
-    when false: # xxx bug:
-      # this holds:
-      doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Sym "wrap2"))"""
-      doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Sym "\'wrap"))"""
-      # but instead this should hold:
-      doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Ident "wrap2"))"""
-      doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Ident "\'wrap"))"""
+    doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Ident "wrap2"))"""
+    doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Ident "\'wrap"))"""
 
   block: # bug 2 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
     template toSuf(`'suf`): untyped =
@@ -165,21 +162,16 @@ template main =
     doAssert fn2() == "[[-12]]"
     doAssert fn3() == "[[-12]]"
 
-    when false: # xxx this fails; bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
-      #[
-      possible workaround: use `genAst` (https://github.com/nim-lang/Nim/pull/17426) and this:
-      let a3 = `'wrap3`("-128")
-      ]#
-      block:
-        macro metawrap(): untyped =
-          func wrap1(a: string): string = "{" & a & "}"
-          func `'wrap3`(a: string): string = "{" & a & "}"
-          result = quote do:
-            let a1 = wrap1"-128"
-            let a2 = -128'wrap3
-        metawrap()
-        doAssert a1 == "{-128}"
-        doAssert a2 == "{-128}"
+    block: # bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947
+      macro metawrap(): untyped =
+        func wrap1(a: string): string = "{" & a & "}"
+        func `'wrap3`(a: string): string = "{" & a & "}"
+        result = quote do:
+          let a1 {.inject.} = wrap1"-128"
+          let a2 {.inject.} = -128'wrap3
+      metawrap()
+      doAssert a1 == "{-128}"
+      doAssert a2 == "{-128}"
 
 static: main()
 main()
diff --git a/tests/lexer/tlexerspaces.nim b/tests/lexer/tlexerspaces.nim
new file mode 100644
index 000000000..14b16111d
--- /dev/null
+++ b/tests/lexer/tlexerspaces.nim
@@ -0,0 +1,2 @@
+discard 12 +                                                                                                                                                                                                                                                                           5
+discard 12 + 5                                                                                                                        
diff --git a/tests/misc/trawstr.nim b/tests/lexer/trawstr.nim
index aa41071d5..aa41071d5 100644
--- a/tests/misc/trawstr.nim
+++ b/tests/lexer/trawstr.nim
diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim
index 1641e918c..5ec2b5c70 100644
--- a/tests/lexer/tunary_minus.nim
+++ b/tests/lexer/tunary_minus.nim
@@ -5,6 +5,7 @@ discard """
 # Test numeric literals and handling of minus symbol
 
 import std/[macros, strutils]
+import std/private/jsutils
 
 import mlexerutils
 
@@ -60,7 +61,8 @@ template main =
     doAssert -2147483648'i32 == int32.low
     when int.sizeof > 4:
       doAssert -9223372036854775808 == int.low
-    when not defined(js):
+    whenJsNoBigInt64: discard
+    do:
       doAssert -9223372036854775808 == int64.low
 
   block: # check when a minus (-) is an unary op
diff --git a/tests/lookups/issue_23032/deep_scope.nim b/tests/lookups/issue_23032/deep_scope.nim
new file mode 100644
index 000000000..3e25809a7
--- /dev/null
+++ b/tests/lookups/issue_23032/deep_scope.nim
@@ -0,0 +1,2 @@
+type A*[T] = object
+proc foo*(a: A[int]): bool = false
diff --git a/tests/lookups/issue_23172/m23172.nim b/tests/lookups/issue_23172/m23172.nim
new file mode 100644
index 000000000..36af48761
--- /dev/null
+++ b/tests/lookups/issue_23172/m23172.nim
@@ -0,0 +1,6 @@
+type
+  Foo* = object
+  Bar* = object
+
+func `$`*(x: Foo | Bar): string =
+  "X"
diff --git a/tests/ambsym/mambsym1.nim b/tests/lookups/mambsym1.nim
index c4902b1b4..c4902b1b4 100644
--- a/tests/ambsym/mambsym1.nim
+++ b/tests/lookups/mambsym1.nim
diff --git a/tests/ambsym/mambsym2.nim b/tests/lookups/mambsym2.nim
index 21d980073..21d980073 100644
--- a/tests/ambsym/mambsym2.nim
+++ b/tests/lookups/mambsym2.nim
diff --git a/tests/lookups/mambsym3.nim b/tests/lookups/mambsym3.nim
new file mode 100644
index 000000000..946a5ff29
--- /dev/null
+++ b/tests/lookups/mambsym3.nim
@@ -0,0 +1,4 @@
+# Module A
+var x*: string
+proc foo*(a: string) =
+  echo "A: ", a
diff --git a/tests/lookups/mambsym4.nim b/tests/lookups/mambsym4.nim
new file mode 100644
index 000000000..ed66cc16d
--- /dev/null
+++ b/tests/lookups/mambsym4.nim
@@ -0,0 +1,4 @@
+# Module B
+var x*: int
+proc foo*(b: int) =
+  echo "B: ", b
diff --git a/tests/ambsym/mambsys1.nim b/tests/lookups/mambsys1.nim
index 22e54cb94..22e54cb94 100644
--- a/tests/ambsym/mambsys1.nim
+++ b/tests/lookups/mambsys1.nim
diff --git a/tests/ambsym/mambsys2.nim b/tests/lookups/mambsys2.nim
index ef63e4f7e..ef63e4f7e 100644
--- a/tests/ambsym/mambsys2.nim
+++ b/tests/lookups/mambsys2.nim
diff --git a/tests/lookups/mambtype1.nim b/tests/lookups/mambtype1.nim
new file mode 100644
index 000000000..47046142e
--- /dev/null
+++ b/tests/lookups/mambtype1.nim
@@ -0,0 +1 @@
+type K* = object
diff --git a/tests/lookups/mambtype2.nim b/tests/lookups/mambtype2.nim
new file mode 100644
index 000000000..cf622466b
--- /dev/null
+++ b/tests/lookups/mambtype2.nim
@@ -0,0 +1,4 @@
+import ./mambtype1
+export mambtype1
+template K*(kind: static int): auto = typedesc[mambtype1.K]
+template B*(kind: static int): auto = typedesc[mambtype1.K]
diff --git a/tests/bind/mbind3.nim b/tests/lookups/mbind3.nim
index 1a7d3b63b..1a7d3b63b 100644
--- a/tests/bind/mbind3.nim
+++ b/tests/lookups/mbind3.nim
diff --git a/tests/lookups/mdisambsym1.nim b/tests/lookups/mdisambsym1.nim
new file mode 100644
index 000000000..b8beca035
--- /dev/null
+++ b/tests/lookups/mdisambsym1.nim
@@ -0,0 +1,2 @@
+proc count*(s: string): int = 
+  s.len
diff --git a/tests/lookups/mdisambsym2.nim b/tests/lookups/mdisambsym2.nim
new file mode 100644
index 000000000..1e056311d
--- /dev/null
+++ b/tests/lookups/mdisambsym2.nim
@@ -0,0 +1 @@
+var count*: int = 10
diff --git a/tests/lookups/mdisambsym3.nim b/tests/lookups/mdisambsym3.nim
new file mode 100644
index 000000000..95bd19702
--- /dev/null
+++ b/tests/lookups/mdisambsym3.nim
@@ -0,0 +1 @@
+const count* = 3.142
diff --git a/tests/lookups/mmacroamb.nim b/tests/lookups/mmacroamb.nim
new file mode 100644
index 000000000..107e51055
--- /dev/null
+++ b/tests/lookups/mmacroamb.nim
@@ -0,0 +1,10 @@
+# issue #12732
+
+import std/macros
+const getPrivate3_tmp* = 0
+const foobar1* = 0 # comment this or make private and it'll compile fine
+macro foobar4*(): untyped =
+  newLit "abc"
+template currentPkgDir2*: string = foobar4()
+macro currentPkgDir2*(dir: string): untyped =
+  newLit "abc2"
diff --git a/tests/lookups/t23032.nim b/tests/lookups/t23032.nim
new file mode 100644
index 000000000..144abcb05
--- /dev/null
+++ b/tests/lookups/t23032.nim
@@ -0,0 +1,13 @@
+discard """
+action: "run"
+outputsub: "proc (a: A[system.float]): bool{.noSideEffect, gcsafe.}"
+"""
+
+import issue_23032/deep_scope
+
+proc foo(a: A[float]):bool = true
+
+let p: proc = foo
+echo p.typeof
+doAssert p(A[float]()) == true
+doAssert compiles(doAssert p(A[int]()) == true) == false
diff --git a/tests/lookups/t23172.nim b/tests/lookups/t23172.nim
new file mode 100644
index 000000000..9edf9905a
--- /dev/null
+++ b/tests/lookups/t23172.nim
@@ -0,0 +1,9 @@
+import issue_23172/m23172
+
+type FooX = distinct Foo
+
+func `$`*(x: FooX): string =
+  $m23172.Foo(x)
+
+var a: FooX
+doAssert $a == "X"
diff --git a/tests/lookups/t23749.nim b/tests/lookups/t23749.nim
new file mode 100644
index 000000000..650f04ea4
--- /dev/null
+++ b/tests/lookups/t23749.nim
@@ -0,0 +1,37 @@
+discard """
+  action: compile
+"""
+
+{.pragma: callback, gcsafe, raises: [].}
+
+type
+  DataProc* = proc(val: openArray[byte]) {.callback.}
+  GetProc = proc (db: RootRef, key: openArray[byte], onData: DataProc): bool {.nimcall, callback.}
+  KvStoreRef* = ref object
+    obj: RootRef
+    getProc: GetProc
+
+template get(dbParam: KvStoreRef, key: openArray[byte], onData: untyped): bool =
+  let db = dbParam
+  db.getProc(db.obj, key, onData)
+
+func decode(input: openArray[byte], maxSize = 128): seq[byte] =
+  @[]
+
+proc getSnappySSZ(db: KvStoreRef, key: openArray[byte]): string =
+  var status = "not found"
+  proc decode(data: openArray[byte]) =
+    status =
+      if true: "found"
+      else: "corrupted"
+  discard db.get(key, decode)
+  status
+
+
+var ksr: KvStoreRef
+var k = [byte(1), 2, 3, 4, 5]
+
+proc foo(): string =
+  getSnappySSZ(ksr, toOpenArray(k, 1, 3))
+
+echo foo()
diff --git a/tests/lookups/tambiguousemit.nim b/tests/lookups/tambiguousemit.nim
new file mode 100644
index 000000000..4f4bacce4
--- /dev/null
+++ b/tests/lookups/tambiguousemit.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim check $options $file" # use check to assure error is pre-codegen
+  matrix: "; --backend:js" # backend shouldn't matter but at least check js
+"""
+
+proc foo(x: int) = discard
+proc foo(x: float) = discard
+
+{.emit: ["// ", foo].} #[tt.Error
+                ^ ambiguous identifier: 'foo' -- use one of the following:
+  tambiguousemit.foo: proc (x: int){.noSideEffect, gcsafe.}
+  tambiguousemit.foo: proc (x: float){.noSideEffect, gcsafe.}]#
diff --git a/tests/lookups/tambprocvar.nim b/tests/lookups/tambprocvar.nim
new file mode 100644
index 000000000..f5c3fde4f
--- /dev/null
+++ b/tests/lookups/tambprocvar.nim
@@ -0,0 +1,19 @@
+discard """
+  action: reject
+  cmd: "nim check $file"
+  nimout: '''
+tambprocvar.nim(15, 11) Error: ambiguous identifier: 'foo' -- use one of the following:
+  tambprocvar.foo: proc (x: int){.noSideEffect, gcsafe.}
+  tambprocvar.foo: proc (x: float){.noSideEffect, gcsafe.}
+'''
+"""
+
+block:
+  proc foo(x: int) = discard
+  proc foo(x: float) = discard
+
+  let x = foo
+
+block:
+  let x = `+` #[tt.Error
+          ^ ambiguous identifier: '+' -- use one of the following:]#
diff --git a/tests/ambsym/tambsym.nim b/tests/lookups/tambsym.nim
index bd0f41717..bd0f41717 100644
--- a/tests/ambsym/tambsym.nim
+++ b/tests/lookups/tambsym.nim
diff --git a/tests/ambsym/tambsym2.nim b/tests/lookups/tambsym2.nim
index 747f1a086..747f1a086 100644
--- a/tests/ambsym/tambsym2.nim
+++ b/tests/lookups/tambsym2.nim
diff --git a/tests/ambsym/tambsym3.nim b/tests/lookups/tambsym3.nim
index 0558517bd..6bbebca10 100644
--- a/tests/ambsym/tambsym3.nim
+++ b/tests/lookups/tambsym3.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "ambiguous identifier"
+  errormsg: "ambiguous identifier: 'mDec' -- use one of the following:"
   file: "tambsym3.nim"
   line: 11
 """
diff --git a/tests/lookups/tambsymmanual.nim b/tests/lookups/tambsymmanual.nim
new file mode 100644
index 000000000..3ad91b8cc
--- /dev/null
+++ b/tests/lookups/tambsymmanual.nim
@@ -0,0 +1,25 @@
+discard """
+  output: '''
+A: abc
+B: 123
+A: def
+4
+'''
+"""
+# Module C
+import mambsym3, mambsym4
+
+foo("abc") # A: abc
+foo(123) # B: 123
+let inferred: proc (x: string) = foo
+foo("def") # A: def
+
+doAssert not compiles(write(stdout, x)) # error: x is ambiguous
+write(stdout, mambsym3.x) # no error: qualifier used
+
+proc bar(a: int): int = a + 1
+doAssert bar(x) == x + 1 # no error: only A.x of type int matches
+
+var x = 4
+write(stdout, x) # not ambiguous: uses the module C's x
+echo() # for test output
diff --git a/tests/ambsym/tambsys.nim b/tests/lookups/tambsys.nim
index aa740c38f..aa740c38f 100644
--- a/tests/ambsym/tambsys.nim
+++ b/tests/lookups/tambsys.nim
diff --git a/tests/lookups/tambtype.nim b/tests/lookups/tambtype.nim
new file mode 100644
index 000000000..a292db83a
--- /dev/null
+++ b/tests/lookups/tambtype.nim
@@ -0,0 +1,20 @@
+import ./mambtype2
+
+block: # issue #23893
+  discard default(K(0))       # works
+  discard default(mambtype2.B(0))     # works
+  discard default(mambtype2.K(0))     # doesn't work
+
+block: # issue #23898, in template
+  template r() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  r()
+
+block: # in generics
+  proc foo[T]() =
+    discard default(B(0))     # compiles
+    discard default(mambtype2.B(0))   # compiles
+    discard default(K(0))     # does not compile
+  foo[int]()
diff --git a/tests/bind/tbind.nim b/tests/lookups/tbind.nim
index f3fb952e3..f3fb952e3 100644
--- a/tests/bind/tbind.nim
+++ b/tests/lookups/tbind.nim
diff --git a/tests/lookups/tdisambsym.nim b/tests/lookups/tdisambsym.nim
new file mode 100644
index 000000000..678528ad6
--- /dev/null
+++ b/tests/lookups/tdisambsym.nim
@@ -0,0 +1,8 @@
+# issue #15247
+
+import mdisambsym1, mdisambsym2, mdisambsym3
+
+proc twice(n: int): int =
+  n*2
+
+doAssert twice(count) == 20
diff --git a/tests/lookups/tenumlocalsym.nim b/tests/lookups/tenumlocalsym.nim
new file mode 100644
index 000000000..575227c07
--- /dev/null
+++ b/tests/lookups/tenumlocalsym.nim
@@ -0,0 +1,22 @@
+block:
+  type Enum = enum a, b
+
+  block:
+    let a = b
+    let x: Enum = a
+    doAssert x == b
+  
+block:
+  type
+    Enum = enum
+      a = 2
+      b = 10
+
+  iterator items2(): Enum =
+    for a in [a, b]:
+      yield a
+
+  var s = newSeq[Enum]()
+  for i in items2():
+    s.add i
+  doAssert s == @[a, b]
diff --git a/tests/gensym/tgensym.nim b/tests/lookups/tgensym.nim
index e33a2783f..e33a2783f 100644
--- a/tests/gensym/tgensym.nim
+++ b/tests/lookups/tgensym.nim
diff --git a/tests/gensym/tgensymgeneric.nim b/tests/lookups/tgensymgeneric.nim
index 6e1df4842..6e1df4842 100644
--- a/tests/gensym/tgensymgeneric.nim
+++ b/tests/lookups/tgensymgeneric.nim
diff --git a/tests/bind/tinvalidbindtypedesc.nim b/tests/lookups/tinvalidbindtypedesc.nim
index 1c71c8daf..1c71c8daf 100644
--- a/tests/bind/tinvalidbindtypedesc.nim
+++ b/tests/lookups/tinvalidbindtypedesc.nim
diff --git a/tests/lookups/tmacroamb.nim b/tests/lookups/tmacroamb.nim
new file mode 100644
index 000000000..854017e72
--- /dev/null
+++ b/tests/lookups/tmacroamb.nim
@@ -0,0 +1,5 @@
+# issue #12732
+
+import mmacroamb
+const s0 = currentPkgDir2 #[tt.Error
+           ^ ambiguous identifier: 'currentPkgDir2' -- use one of the following:]#
diff --git a/tests/lookups/tmoduleclash1.nim b/tests/lookups/tmoduleclash1.nim
new file mode 100644
index 000000000..7058f691e
--- /dev/null
+++ b/tests/lookups/tmoduleclash1.nim
@@ -0,0 +1,13 @@
+# issue #23596
+
+import std/heapqueue
+type Algo = enum heapqueue, quick
+when false:
+  let x = heapqueue
+let y: Algo = heapqueue
+proc bar*(algo=quick) =
+  var x: HeapQueue[int]
+  case algo
+  of heapqueue: echo 1 # `Algo.heapqueue` works on devel
+  of quick: echo 2
+  echo x.len
diff --git a/tests/lookups/tmoduleclash2.nim b/tests/lookups/tmoduleclash2.nim
new file mode 100644
index 000000000..958da2299
--- /dev/null
+++ b/tests/lookups/tmoduleclash2.nim
@@ -0,0 +1,6 @@
+import std/heapqueue
+proc heapqueue(x: int) = discard
+let x: proc (x: int) = heapqueue
+let y: proc = heapqueue
+when false:
+  let z = heapqueue
diff --git a/tests/bind/tnicerrorforsymchoice.nim b/tests/lookups/tnicerrorforsymchoice.nim
index 684b83239..6001b6b09 100644
--- a/tests/bind/tnicerrorforsymchoice.nim
+++ b/tests/lookups/tnicerrorforsymchoice.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe, locks: 0.}>"
+  errormsg: "type mismatch: got <proc (s: TScgi: ScgiState or AsyncScgiState) | proc (client: AsyncSocket, headers: StringTableRef, input: string){.noSideEffect, gcsafe.}>"
   line: 23
 """
 
diff --git a/tests/bind/told_bind_expr.nim b/tests/lookups/told_bind_expr.nim
index 56389eaf6..56389eaf6 100644
--- a/tests/bind/told_bind_expr.nim
+++ b/tests/lookups/told_bind_expr.nim
diff --git a/tests/lookups/tundeclaredmixin.nim b/tests/lookups/tundeclaredmixin.nim
new file mode 100644
index 000000000..74d16cdde
--- /dev/null
+++ b/tests/lookups/tundeclaredmixin.nim
@@ -0,0 +1,17 @@
+discard """
+  nimout: '''
+  mixin nothing, add
+'''
+"""
+
+# issue #22012
+
+import macros
+
+expandMacros:
+  proc foo[T](): int =
+    # `nothing` is undeclared, `add` is declared
+    mixin nothing, add
+    123
+
+  doAssert foo[int]() == 123
diff --git a/tests/macros/t14329.nim b/tests/macros/t14329.nim
new file mode 100644
index 000000000..b5606424a
--- /dev/null
+++ b/tests/macros/t14329.nim
@@ -0,0 +1,4 @@
+import macros
+
+macro myMacro(n) =
+  let x = if true: newLit"test" else: error "error", n
diff --git a/tests/macros/t15691.nim b/tests/macros/t15691.nim
new file mode 100644
index 000000000..c1e8a8648
--- /dev/null
+++ b/tests/macros/t15691.nim
@@ -0,0 +1,22 @@
+discard """
+  action: compile
+"""
+
+import std/macros
+
+macro simplifiedExpandMacros(body: typed): untyped =
+  result = body
+
+simplifiedExpandMacros:
+  proc testProc() = discard
+
+simplifiedExpandMacros:
+  template testTemplate(): untyped = discard
+
+# Error: illformed AST: macro testMacro(): untyped =
+simplifiedExpandMacros:
+  macro testMacro(): untyped = discard
+
+# Error: illformed AST: converter testConverter(x: int): float =
+simplifiedExpandMacros:
+  converter testConverter(x: int): float = discard
diff --git a/tests/macros/t15751.nim b/tests/macros/t15751.nim
new file mode 100644
index 000000000..fcabb2f9e
--- /dev/null
+++ b/tests/macros/t15751.nim
@@ -0,0 +1,11 @@
+discard """
+  cmd: "nim c --hints:off $file"
+  nimout: "out"
+"""
+
+# bug #15751
+macro print(n: untyped): untyped =
+  echo n.repr
+
+print:
+  out
diff --git a/tests/macros/t16758.nim b/tests/macros/t16758.nim
new file mode 100644
index 000000000..66b6d42c5
--- /dev/null
+++ b/tests/macros/t16758.nim
@@ -0,0 +1,38 @@
+discard """

+errormsg: "'blk.p(a)' has nil child at index 1"

+action: reject

+"""

+import macros

+

+type BlockLiteral[T] = object

+  p: T

+

+proc p[T](a:int) = echo 1

+proc p[T](a:string) = echo "a"

+

+iterator arguments(formalParams: NimNode): NimNode =

+  var iParam = 0

+  for i in 1 ..< formalParams.len:

+    let pp = formalParams[i]

+    for j in 0 .. pp.len - 3:

+      yield pp[j]

+      inc iParam

+

+macro implementInvoke(T: typedesc): untyped =

+  let t = getTypeImpl(T)[1]

+

+  let call = newCall(newDotExpr(ident"blk", ident"p"))

+  let params = copyNimTree(t[0])

+  result = newProc(ident"invoke", body = call)

+  # result[2] = newTree(nnkGenericParams,T)

+  for n in arguments(params):

+    call.add(n)

+  

+  params.insert(1, newIdentDefs(ident"blk", newTree(nnkBracketExpr, bindSym"BlockLiteral", T)))

+  result.params = params

+

+proc getInvoke(T: typedesc) =

+  implementInvoke(T)

+

+

+getInvoke(proc(a: int))

diff --git a/tests/macros/t19766_20114.nim b/tests/macros/t19766_20114.nim
new file mode 100644
index 000000000..ac336f150
--- /dev/null
+++ b/tests/macros/t19766_20114.nim
@@ -0,0 +1,16 @@
+discard """
+  action: compile
+  nimout: '''
+const
+  foo {.strdefine.} = "abc"
+let hey {.tddd.} = 5
+'''
+"""
+
+import macros
+
+template tddd {.pragma.}
+
+expandMacros:
+  const foo {.strdefine.} = "abc"
+  let hey {.tddd.} = 5
diff --git a/tests/macros/t20435.nim b/tests/macros/t20435.nim
new file mode 100644
index 000000000..824282198
--- /dev/null
+++ b/tests/macros/t20435.nim
@@ -0,0 +1,30 @@
+
+#[
+  A better test requires matching, so the use of @ working can be showcased
+  For example:
+
+  proc regularCase[T]() = 
+    case [(1, 3), (3, 4)]:
+    of [(1, @a), (_, @b)]:
+      echo a, b
+    else: discard
+]#
+
+{.experimental: "caseStmtMacros".}
+
+import macros
+
+type Foo = object
+
+macro `case`(obj: Foo) = quote do: discard
+
+proc notGeneric() =
+  case Foo()
+  of a b c d: discard
+
+proc generic[T]() =
+  case Foo()
+  of a b c d: discard
+
+notGeneric()
+generic[int]()
diff --git a/tests/macros/t21593.nim b/tests/macros/t21593.nim
new file mode 100644
index 000000000..b0b7ebe75
--- /dev/null
+++ b/tests/macros/t21593.nim
@@ -0,0 +1,13 @@
+discard """
+nimout: '''
+StmtList
+  UIntLit 18446744073709551615
+  IntLit -1'''
+"""
+
+import macros
+
+dumpTree:
+  0xFFFFFFFF_FFFFFFFF'u
+  0xFFFFFFFF_FFFFFFFF
+
diff --git a/tests/macros/t23032_1.nim b/tests/macros/t23032_1.nim
new file mode 100644
index 000000000..4e1707414
--- /dev/null
+++ b/tests/macros/t23032_1.nim
@@ -0,0 +1,19 @@
+import std/macros
+
+type A[T, H] = object
+
+proc `%*`(a: A): bool = true
+proc `%*`[T](a: A[int, T]): bool = false
+
+macro collapse(s: untyped) =
+  result = newStmtList()
+  result.add quote do:
+    doAssert(`s`(A[float, int]()) == true)
+
+macro startHere(n: untyped): untyped =
+  result = newStmtList()
+  let s = n[0]
+  result.add quote do:
+    `s`.collapse()
+
+startHere(`a` %* `b`)
diff --git a/tests/macros/t23032_2.nim b/tests/macros/t23032_2.nim
new file mode 100644
index 000000000..8dde29e10
--- /dev/null
+++ b/tests/macros/t23032_2.nim
@@ -0,0 +1,20 @@
+discard """
+  action: "reject"
+  errormsg: "ambiguous identifier: '%*'"
+"""
+import std/macros
+
+type A[T, H] = object
+
+proc `%*`[T](a: A) = discard
+proc `%*`[T](a: A[int, T]) = discard
+
+macro collapse(s: typed) = discard
+
+macro startHere(n: untyped): untyped =
+  result = newStmtList()
+  let s = n[0]
+  result.add quote do:
+    collapse(`s`.typeof())
+
+startHere(`a` %* `b`)
diff --git a/tests/macros/t23547.nim b/tests/macros/t23547.nim
new file mode 100644
index 000000000..9a2bff9ff
--- /dev/null
+++ b/tests/macros/t23547.nim
@@ -0,0 +1,23 @@
+# https://github.com/nim-lang/Nim/issues/23547
+
+type
+  A[T] = object
+    x: T
+
+proc mulCheckSparse[F](dummy: var A[F], xmulchecksparse: static A[F]) =
+  static:
+    echo "mulCheckSparse: ", typeof(dummy), ", ", typeof(xmulchecksparse) # when generic params not specified: A[system.int], A
+
+template sumImpl(xsumimpl: typed) =
+  static:
+    echo "sumImpl: ", typeof(xsumimpl) # A
+  var a = A[int](x: 55)
+  mulCheckSparse(a, xsumimpl) # fails here
+
+proc sum[T](xsum: static T) =
+  static:
+    echo "sum: ", typeof(xsum) # A[system.int]
+  sumImpl(xsum)
+
+const constA = A[int](x : 100)
+sum[A[int]](constA)
diff --git a/tests/macros/t23784.nim b/tests/macros/t23784.nim
new file mode 100644
index 000000000..31b4544c6
--- /dev/null
+++ b/tests/macros/t23784.nim
@@ -0,0 +1,157 @@
+discard """
+  joinable: false
+"""
+
+
+# debug ICE: genCheckedRecordField
+# apparently after https://github.com/nim-lang/Nim/pull/23477
+
+# bug #23784
+
+import std/bitops, std/macros
+
+# --------------------------------------------------------------
+
+type Algebra = enum
+  BN254_Snarks
+
+type SecretWord* = distinct uint64
+const WordBitWidth* = sizeof(SecretWord) * 8
+
+func wordsRequired*(bits: int): int {.inline.} =
+  const divShiftor = fastLog2(WordBitWidth)
+  result = (bits + WordBitWidth - 1) shr divShiftor
+
+type
+  BigInt*[bits: static int] = object
+    limbs*: array[bits.wordsRequired, SecretWord]  # <--- crash points to here
+
+# --------------------------------------------------------------
+
+const CurveBitWidth = [
+  BN254_Snarks: 254
+]
+
+const BN254_Snarks_Modulus = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x2, SecretWord 0x3, SecretWord 0x4])
+const BN254_Snarks_Order = BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x2, SecretWord 0x2])
+
+func montyOne*(M: BigInt[254]): BigInt[254] =
+  ## Returns "1 (mod M)" in the Montgomery domain.
+  ## This is equivalent to R (mod M) in the natural domain
+  BigInt[254](limbs: [SecretWord 0x1, SecretWord 0x1, SecretWord 0x1, SecretWord 0x1])
+
+
+{.experimental: "dynamicBindSym".}
+
+type
+  DerivedConstantMode* = enum
+    kModulus
+    kOrder
+
+macro genDerivedConstants*(mode: static DerivedConstantMode): untyped =
+  ## Generate constants derived from the main constants
+  ##
+  ## For example
+  ## - the Montgomery magic constant "R^2 mod N" in ROM
+  ##   For each curve under the private symbol "MyCurve_R2modP"
+  ## - the Montgomery magic constant -1/P mod 2^Wordbitwidth
+  ##   For each curve under the private symbol "MyCurve_NegInvModWord
+  ## - ...
+
+  # Now typedesc are NimNode and there is no way to translate
+  # NimNode -> typedesc easily so we can't
+  # "for curve in low(Curve) .. high(Curve):"
+  # As an ugly workaround, we count
+  # The item at position 0 is a pragma
+  result = newStmtList()
+
+  template used(name: string): NimNode =
+    nnkPragmaExpr.newTree(
+      ident(name),
+      nnkPragma.newTree(ident"used")
+    )
+
+  let ff = if mode == kModulus: "_Fp" else: "_Fr"
+
+  for curveSym in low(Algebra) .. high(Algebra):
+    let curve = $curveSym
+    let M = if mode == kModulus: bindSym(curve & "_Modulus")
+            else: bindSym(curve & "_Order")
+
+    # const MyCurve_montyOne = montyOne(MyCurve_Modulus)
+    result.add newConstStmt(
+      used(curve & ff & "_MontyOne"), newCall(
+        bindSym"montyOne",
+        M
+      )
+    )
+
+# --------------------------------------------------------------
+
+{.experimental: "dynamicBindSym".}
+
+genDerivedConstants(kModulus)
+genDerivedConstants(kOrder)
+
+proc bindConstant(ff: NimNode, property: string): NimNode =
+  # Need to workaround https://github.com/nim-lang/Nim/issues/14021
+  # which prevents checking if a type FF[Name] = Fp[Name] or Fr[Name]
+  # was instantiated with Fp or Fr.
+  # getTypeInst only returns FF and sameType doesn't work.
+  # so quote do + when checks.
+  let T = getTypeInst(ff)
+  T.expectKind(nnkBracketExpr)
+  doAssert T[0].eqIdent("typedesc")
+
+  let curve =
+    if T[1].kind == nnkBracketExpr: # typedesc[Fp[BLS12_381]] as used internally
+      # doAssert T[1][0].eqIdent"Fp" or T[1][0].eqIdent"Fr", "Found ident: '" & $T[1][0] & "' instead of 'Fp' or 'Fr'"
+      T[1][1].expectKind(nnkIntLit) # static enum are ints in the VM
+      $Algebra(T[1][1].intVal)
+    else: # typedesc[bls12381_fp] alias as used for C exports
+      let T1 = getTypeInst(T[1].getImpl()[2])
+      if T1.kind != nnkBracketExpr or
+         T1[1].kind != nnkIntLit:
+        echo T.repr()
+        echo T1.repr()
+        echo getTypeInst(T1).treerepr()
+        error "getTypeInst didn't return the full instantiation." &
+          " Dealing with types in macros is hard, complain at https://github.com/nim-lang/RFCs/issues/44"
+      $Algebra(T1[1].intVal)
+
+  let curve_fp = bindSym(curve & "_Fp_" & property)
+  let curve_fr = bindSym(curve & "_Fr_" & property)
+  result = quote do:
+    when `ff` is Fp:
+      `curve_fp`
+    elif `ff` is Fr:
+      `curve_fr`
+    else:
+      {.error: "Unreachable, received type: " & $`ff`.}
+
+# --------------------------------------------------------------
+
+template matchingBigInt*(Name: static Algebra): untyped =
+  ## BigInt type necessary to store the prime field Fp
+  # Workaround: https://github.com/nim-lang/Nim/issues/16774
+  # as we cannot do array accesses in type section.
+  # Due to generic sandwiches, it must be exported.
+  BigInt[CurveBitWidth[Name]]
+
+type
+  Fp*[Name: static Algebra] = object
+    mres*: matchingBigInt(Name)
+
+macro getMontyOne*(ff: type Fp): untyped =
+  ## Get one in Montgomery representation (i.e. R mod P)
+  result = bindConstant(ff, "MontyOne")
+
+func getOne*(T: type Fp): T {.noInit, inline.} =
+  result = cast[ptr T](unsafeAddr getMontyOne(T))[]
+
+# --------------------------------------------------------------
+proc foo(T: Fp) =
+  discard T
+
+let a = Fp[BN254_Snarks].getOne()
+foo(a) # oops this was a leftover that broke the bisect.
diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim
index 759ca55b5..96a37c7a2 100644
--- a/tests/macros/tastrepr.nim
+++ b/tests/macros/tastrepr.nim
@@ -8,7 +8,11 @@ for i, d in pairs(data):
   discard
 for i, (x, y) in pairs(data):
   discard
-var (a, b) = (1, 2)
+var
+  a = 1
+  b = 2
+type
+  A* = object
 
 var data = @[(1, "one"), (2, "two")]
 for (i, d) in pairs(data):
@@ -18,6 +22,11 @@ for i, d in pairs(data):
 for i, (x, y) in pairs(data):
   discard
 var (a, b) = (1, 2)
+type
+  A* = object
+
+var t04 = 1.0'f128
+t04 = 2.0'f128
 '''
 """
 
@@ -42,3 +51,8 @@ echoTypedAndUntypedRepr:
   for i, (x,y) in pairs(data):
     discard
   var (a,b) = (1,2)
+  type A* = object # issue #22933
+
+echoUntypedRepr:
+  var t04 = 1'f128
+  t04 = 2'f128
diff --git a/tests/macros/tfail_parse.nim b/tests/macros/tfail_parse.nim
new file mode 100644
index 000000000..1925f2b69
--- /dev/null
+++ b/tests/macros/tfail_parse.nim
@@ -0,0 +1,15 @@
+discard """
+action: "reject"
+cmd: "nim check $file"
+errormsg: "expected expression, but got multiple statements [ValueError]"
+file: "macros.nim"
+"""
+
+import macros
+static:
+  discard parseStmt("'")
+  discard parseExpr("'")
+  discard parseExpr("""
+proc foo()
+proc foo() = discard
+""")
diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim
index de132f253..ab33131b0 100644
--- a/tests/macros/tgetimpl.nim
+++ b/tests/macros/tgetimpl.nim
@@ -1,5 +1,5 @@
 discard """
-  nimout: '''"muhaha"
+  nimout: '''foo = "muhaha"
 proc poo(x, y: int) =
   let y = x
   echo ["poo"]'''
@@ -44,10 +44,11 @@ static:
   doAssert checkOwner(poo, 2) == "nskProc"
   doAssert checkOwner(poo, 3) == "nskModule"
   doAssert isSameOwner(foo, poo)
-  doAssert isSameOwner(foo, echo) == false
-  doAssert isSameOwner(poo, len) == false
-
-#---------------------------------------------------------------
+  proc wrappedScope() =
+    proc dummyproc() = discard
+    doAssert isSameOwner(foo, dummyproc) == false
+    doAssert isSameOwner(poo, dummyproc) == false
+  wrappedScope()
 
 macro check_gen_proc(ex: typed): (bool, bool) =
   let lenChoice = bindsym"len"
@@ -72,7 +73,9 @@ assert: check_gen_proc(len(a)) == (false, true)
 macro check(x: type): untyped =
   let z = getType(x)
   let y = getImpl(z[1])  
-  let sym = if y[0].kind == nnkSym: y[0] else: y[0][0]
+  var sym = y[0]
+  if sym.kind == nnkPragmaExpr: sym = sym[0]
+  if sym.kind == nnkPostfix: sym = sym[1]
   expectKind(z[1], nnkSym)
   expectKind(sym, nnkSym)
   expectKind(y[2], nnkObjectTy)
diff --git a/tests/macros/tgettypeinst7737.nim b/tests/macros/tgettypeinst7737.nim
new file mode 100644
index 000000000..e49f82562
--- /dev/null
+++ b/tests/macros/tgettypeinst7737.nim
@@ -0,0 +1,61 @@
+discard """
+  nimout: '''
+seq[int]
+CustomSeq[int]
+'''
+"""
+
+import macros, typetraits, sequtils
+
+block: # issue #7737 original
+  type
+    CustomSeq[T] = object
+      data: seq[T]
+
+  proc getSubType(T: NimNode): NimNode =
+    echo getTypeInst(T).repr
+    result = getTypeInst(T)[1]
+
+  macro typed_helper(x: varargs[typed]): untyped =
+    let foo = getSubType(x[0])
+    result = quote do: discard
+
+  macro untyped_heavylifting(x: varargs[untyped]): untyped =
+    var containers = nnkArgList.newTree()
+    for arg in x:
+      case arg.kind:
+      of nnkInfix:
+        if eqIdent(arg[0], "in"):
+          containers.add arg[2]
+      else:
+        discard
+    result = quote do:
+      typed_helper(`containers`)
+  var a, b, c: seq[int]
+  untyped_heavylifting z in c, x in a, y in b:
+    discard
+  ## The following gives me CustomSeq instead
+  ## of CustomSeq[int] in getTypeInst
+  var u, v, w: CustomSeq[int]
+  untyped_heavylifting z in u, x in v, y in w:
+    discard
+
+block: # issue #7737 comment
+  type
+    CustomSeq[T] = object
+      data: seq[T]
+  # when using just one argument, `foo` and `bar` should be exactly
+  # identical.
+  macro foo(arg: typed): string =
+    result = newLit(arg.getTypeInst.repr)
+  macro bar(args: varargs[typed]): untyped =
+    result = newTree(nnkBracket)
+    for arg in args:
+      result.add newLit(arg.getTypeInst.repr)
+  var
+    a: seq[int]
+    b: CustomSeq[int]
+  doAssert foo(a) == "seq[int]"
+  doAssert bar(a) == ["seq[int]"]
+  doAssert foo(b) == "CustomSeq[int]"
+  doAssert bar(b) == ["CustomSeq[int]"]
diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim
index d702db56a..e351b4527 100644
--- a/tests/macros/tmacros_various.nim
+++ b/tests/macros/tmacros_various.nim
@@ -91,7 +91,7 @@ block tlexerex:
 
 
 
-block tlineinfo:
+block tcopylineinfo:
   # issue #5617, feature request
   type Test = object
 
@@ -103,6 +103,27 @@ block tlineinfo:
   var z = mixer(Test)
   doAssert z
 
+block tsetgetlineinfo:
+  # issue #21098, feature request
+  type Test = object
+
+  macro mixer1(n: typed): untyped =
+    let x = newIdentNode("echo")
+    var lineInfo = n.lineInfoObj
+    x.setLineInfo lineInfo
+    result = newLit(x.lineInfo == n.lineInfo)
+
+  macro mixer2(n: typed): untyped =
+    let x = newIdentNode("echo")
+    var lineInfo = n.lineInfoObj
+    lineInfo.line += 1
+    x.setLineInfo lineInfo
+    result = newLit(x.lineInfo != n.lineInfo)
+
+  doAssert mixer1(Test)
+
+  doAssert mixer2(Test)
+
 
 
 block tdebugstmt:
@@ -247,6 +268,37 @@ xbenchmark:
     discard inputtest
   fastSHA("hey")
 
+block: # issue #4547
+  macro lazy(stmtList : typed) : untyped =
+    let decl = stmtList[0]
+    decl.expectKind nnkLetSection
+    let name = decl[0][0].strVal
+    let call = decl[0][2].copy
+    call.expectKind nnkCall
+    let ident = newIdentNode("get" & name)
+    result = quote do:
+      var value : type(`call`)
+      proc `ident`() : type(`call`) =
+        if value.isNil:
+          value = `call`
+        value
+  type MyObject = object
+    a,b: int    
+  # this part, the macro call and it's result (written in the comment below) is important
+  lazy:
+    let y = new(MyObject)
+  #[
+    var value: type(new(MyObject))
+    proc gety(): type(new(MyObject)) =
+      if value.isNil:
+        value = new(MyObject)
+      value    
+  ]#
+  doAssert gety().a == 0  # works and should work
+  doAssert gety().b == 0  # works and should work
+  doAssert not declared(y)
+  doAssert not compiles(y.a)       # identifier y should not exist anymore
+  doAssert not compiles(y.b)       # identifier y should not exist anymore
 
 block: # bug #13511
   type
@@ -330,3 +382,10 @@ block: # bug #15118
 
   block:
     flop("b")
+
+static:
+  block:
+    const containsTable = CacheTable"containsTable"
+    doAssert "foo" notin containsTable
+    containsTable["foo"] = newLit 42
+    doAssert "foo" in containsTable
diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim
index 79be5f764..817bc8352 100644
--- a/tests/macros/tmacrostmt.nim
+++ b/tests/macros/tmacrostmt.nim
@@ -124,7 +124,7 @@ static:
   let fn4s = "proc fn4(x: int): int =\n  if x mod 2 == 0:\n    return x + 2\n  else:\n    return 0\n"
   let fn5s = "proc fn5(a, b: float): float =\n  result = -a * a / (b * b)\n"
   let fn6s = "proc fn6() =\n  var a = @[1.0, 2.0]\n  let z = a{0, 1}\n  a{2} = 5.0\n"
-  let fnAddr = "proc fn_unsafeaddr(x: int): int =\n  result = cast[int](unsafeAddr(x))\n"
+  let fnAddr = "proc fn_unsafeaddr(x: int): int =\n  result = cast[int](addr(x))\n"
 
   doAssert fn1.repr_to_string == fn1s
   doAssert fn2.repr_to_string == fn2s
diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim
index 43819c81d..13b421303 100644
--- a/tests/macros/tmacrotypes.nim
+++ b/tests/macros/tmacrotypes.nim
@@ -1,9 +1,9 @@
 discard """
-  nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int
+  nimout: '''intProc; ntyProc; proc[int, int, float]; proc (a: int; b: float): int {.nimcall.}
 void; ntyVoid; void; void
 int; ntyInt; int; int
-proc (); ntyProc; proc[void]; proc ()
-voidProc; ntyProc; proc[void]; proc ()
+proc () {.nimcall.}; ntyProc; proc[void]; proc () {.nimcall.}
+voidProc; ntyProc; proc[void]; proc () {.nimcall.}
 listing fields for ObjType
 a: string
 b: int
diff --git a/tests/macros/tprocgettype.nim b/tests/macros/tprocgettype.nim
new file mode 100644
index 000000000..0c1cc4270
--- /dev/null
+++ b/tests/macros/tprocgettype.nim
@@ -0,0 +1,28 @@
+discard """
+  nimout: '''
+var x: proc () {.cdecl.} = foo
+var x: iterator (): int {.closure.} = bar
+'''
+"""
+
+# issue #19010
+
+import macros
+
+macro createVar(x: typed): untyped =
+  result = nnkVarSection.newTree:
+    newIdentDefs(ident"x", getTypeInst(x), copy(x))
+  
+  echo repr result
+
+block:
+  proc foo() {.cdecl.} = discard
+
+  createVar(foo)
+  x()
+
+block:
+  iterator bar(): int {.closure.} = discard
+
+  createVar(bar)
+  for a in x(): discard
diff --git a/tests/macros/tstructuredlogging.nim b/tests/macros/tstructuredlogging.nim
index 5e4fdea3f..649c6c0bd 100644
--- a/tests/macros/tstructuredlogging.nim
+++ b/tests/macros/tstructuredlogging.nim
@@ -1,7 +1,7 @@
 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
+exiting: a=12, b=overridden-b, c=100, msg=bye bye, x=16
 '''
 """
 
@@ -134,7 +134,7 @@ scope:
 
 scope:
   x = 16
-  b = "overriden-b"
+  b = "overridden-b"
 
 scope:
   c = 100
diff --git a/tests/macros/ttemplatesymbols.nim b/tests/macros/ttemplatesymbols.nim
index 280b34ff1..3182de79d 100644
--- a/tests/macros/ttemplatesymbols.nim
+++ b/tests/macros/ttemplatesymbols.nim
@@ -148,8 +148,6 @@ 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) =
diff --git a/tests/macros/ttryparseexpr.nim b/tests/macros/ttryparseexpr.nim
index fc0ee61d0..e6e9e9880 100644
--- a/tests/macros/ttryparseexpr.nim
+++ b/tests/macros/ttryparseexpr.nim
@@ -18,3 +18,7 @@ const
   c = test("\"") # bug #2504
 
 echo a, " ", b
+
+static:
+  # Issue #9918
+  discard parseStmt("echo(1+1);")
diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim
index e2c1b0a04..0ad57167b 100644
--- a/tests/manyloc/argument_parser/argument_parser.nim
+++ b/tests/manyloc/argument_parser/argument_parser.nim
@@ -168,14 +168,14 @@ template new_parsed_parameter*(tkind: Tparam_kind, expr): Tparsed_parameter =
   ## initialised with. The template figures out at compile time what field to
   ## assign the variable to, and thus you reduce code clutter and may use this
   ## to initialise single assignments variables in `let` blocks. Example:
-  ##
-  ## .. code-block:: nim
+  ##   ```nim
   ##   let
   ##     parsed_param1 = new_parsed_parameter(PK_FLOAT, 3.41)
   ##     parsed_param2 = new_parsed_parameter(PK_BIGGEST_INT, 2358123 * 23123)
   ##     # The following line doesn't compile due to
   ##     # type mismatch: got <string> but expected 'int'
   ##     #parsed_param3 = new_parsed_parameter(PK_INT, "231")
+  ##   ```
   var result {.gensym.}: Tparsed_parameter
   result.kind = tkind
   when tkind == PK_EMPTY: discard
diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim
index e466ee5e1..36538097e 100644
--- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim
+++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim
@@ -69,7 +69,7 @@ else:
     var
       task: string
       printTaskList: bool
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdLongOption, cmdShortOption:
         case key.tolower
diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim
index 794e1c843..86d0ab360 100644
--- a/tests/manyloc/keineschweine/enet_server/enet_server.nim
+++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim
@@ -113,7 +113,7 @@ when true:
 
   block:
     var zoneCfgFile = "./server_settings.json"
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdShortOption, cmdLongOption:
         case key
diff --git a/tests/manyloc/keineschweine/enet_server/server_utils.nim b/tests/manyloc/keineschweine/enet_server/server_utils.nim
index 1fb8326ed..3940dcf01 100644
--- a/tests/manyloc/keineschweine/enet_server/server_utils.nim
+++ b/tests/manyloc/keineschweine/enet_server/server_utils.nim
@@ -1,4 +1,6 @@
-import enet, sg_packets, estreams, md5, zlib_helpers, client_helpers, strutils,
+import ../../../../dist/checksums/src/checksums/md5
+
+import enet, sg_packets, estreams, zlib_helpers, client_helpers, strutils,
   idgen, sg_assets, tables, os
 type
   PClient* = ref object
diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim
index cde31bd18..123a4aebb 100644
--- a/tests/manyloc/keineschweine/keineschweine.nim
+++ b/tests/manyloc/keineschweine/keineschweine.nim
@@ -691,7 +691,7 @@ when true:
 
   block:
     var bPlayOffline = false
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdArgument:
         if key == "offline": bPlayOffline = true
diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim
index b535225dc..b21e67cf7 100644
--- a/tests/manyloc/keineschweine/lib/client_helpers.nim
+++ b/tests/manyloc/keineschweine/lib/client_helpers.nim
@@ -1,6 +1,8 @@
+import ../../../../dist/checksums/src/checksums/md5
+
 import
   tables, sg_packets, enet, estreams, sg_gui, sfml,
-  zlib_helpers, md5, sg_assets, os
+  zlib_helpers, sg_assets, os
 type
   PServer* = ptr TServer
   TServer* = object
diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim
index 5929add43..96c962dc3 100644
--- a/tests/manyloc/keineschweine/lib/sg_assets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_assets.nim
@@ -1,6 +1,8 @@
+import ../../../../dist/checksums/src/checksums/md5
+
 import
   re, json, strutils, tables, math, os, math_helpers,
-  sg_packets, md5, zlib_helpers
+  sg_packets, zlib_helpers
 
 when defined(NoSFML):
   import server_utils
diff --git a/tests/manyloc/keineschweine/lib/sg_packets.nim b/tests/manyloc/keineschweine/lib/sg_packets.nim
index 0727c699a..797a60706 100644
--- a/tests/manyloc/keineschweine/lib/sg_packets.nim
+++ b/tests/manyloc/keineschweine/lib/sg_packets.nim
@@ -1,4 +1,6 @@
-import genpacket_enet, nativesockets, net, md5, enet
+import ../../../../dist/checksums/src/checksums/md5
+
+import genpacket_enet, nativesockets, net, enet
 defPacketImports()
 
 type
diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim
index cd2b60b26..a63829691 100644
--- a/tests/manyloc/keineschweine/server/old_dirserver.nim
+++ b/tests/manyloc/keineschweine/server/old_dirserver.nim
@@ -1,9 +1,9 @@
 ## directory server
 ## handles client authorization and assets
-
+import ../../../dist/checksums/src/checksums/md5
 import
   sockets, times, streams, streams_enh, tables, json, os,
-  sg_packets, sg_assets, md5, server_utils, map_filter
+  sg_packets, sg_assets, server_utils, map_filter
 type
   THandler = proc(client: PCLient; stream: PStream)
 var
@@ -159,7 +159,7 @@ proc poll*(timeout: int = 250) =
 when true:
   import parseopt, strutils
   var cfgFile = "dirserver_settings.json"
-  for kind, key, val in getOpt():
+  for kind, key, val in getopt():
     case kind
     of cmdShortOption, cmdLongOption:
       case key
diff --git a/tests/manyloc/keineschweine/server/old_server_utils.nim b/tests/manyloc/keineschweine/server/old_server_utils.nim
index 3da6e078c..f389c0836 100644
--- a/tests/manyloc/keineschweine/server/old_server_utils.nim
+++ b/tests/manyloc/keineschweine/server/old_server_utils.nim
@@ -1,5 +1,7 @@
+import ../../../dist/checksums/src/checksums/md5
+
 import
-  streams, md5, sockets,
+  streams, sockets,
   sg_packets, zlib_helpers, idgen
 type
   TClientType* = enum
diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim
index 8bd44017e..d6fbbe99e 100644
--- a/tests/manyloc/keineschweine/server/old_sg_server.nim
+++ b/tests/manyloc/keineschweine/server/old_sg_server.nim
@@ -144,7 +144,7 @@ proc poll*(timeout: int = 250) =
 when true:
   import parseopt, strutils
   var zoneCfgFile = "./server_settings.json"
-  for kind, key, val in getOpt():
+  for kind, key, val in getopt():
     case kind
     of cmdShortOption, cmdLongOption:
       case key
diff --git a/tests/manyloc/keineschweine/server/sg_lobby.nim b/tests/manyloc/keineschweine/server/sg_lobby.nim
index d7e01e6e6..04ce10f08 100644
--- a/tests/manyloc/keineschweine/server/sg_lobby.nim
+++ b/tests/manyloc/keineschweine/server/sg_lobby.nim
@@ -1,6 +1,7 @@
+import ../../../dist/checksums/src/checksums/md5
 
 import
-  sockets, streams, tables, times, math, strutils, json, os, md5,
+  sockets, streams, tables, times, math, strutils, json, os,
   sfml, sfml_vector, sfml_colors,
   streams_enh, input_helpers, zlib_helpers, client_helpers, sg_packets, sg_assets, sg_gui
 type
diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim
index ce7faab5c..5d3173a20 100644
--- a/tests/manyloc/nake/nake.nim
+++ b/tests/manyloc/nake/nake.nim
@@ -65,7 +65,7 @@ else:
     var
       task: string
       printTaskList: bool
-    for kind, key, val in getOpt():
+    for kind, key, val in getopt():
       case kind
       of cmdLongOption, cmdShortOption:
         case key.tolowerAscii
diff --git a/tests/metatype/tcompositetypeclasses.nim b/tests/metatype/tcompositetypeclasses.nim
index d125b119b..43b6a57e4 100644
--- a/tests/metatype/tcompositetypeclasses.nim
+++ b/tests/metatype/tcompositetypeclasses.nim
@@ -30,7 +30,7 @@ accept bar(vbar)
 accept baz(vbar)
 accept baz(vbaz)
 
-#reject baz(vnotbaz) # XXX this really shouldn't compile
+reject baz(vnotbaz)
 reject bar(vfoo)
 
 # https://github.com/Araq/Nim/issues/517
diff --git a/tests/metatype/tmetatype_issues.nim b/tests/metatype/tmetatype_issues.nim
index 21c5c02f1..d33d8dd31 100644
--- a/tests/metatype/tmetatype_issues.nim
+++ b/tests/metatype/tmetatype_issues.nim
@@ -157,3 +157,12 @@ block t3338:
   var t2 = Bar[int32]()
   t2.add()
   doAssert t2.x == 5
+
+block: # issue #24203
+  proc b(G: typedesc) =
+    type U = G
+  template s(h: untyped) = h
+  s(b(typeof (0, 0)))
+  b(seq[int])
+  b((int, int))
+  b(typeof (0, 0))
diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim
index c17410a06..45c74432d 100644
--- a/tests/metatype/tmetatype_various.nim
+++ b/tests/metatype/tmetatype_various.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--mm:refc"
+  matrix: "--mm:refc; --mm:orc"
   output: '''[1, 0, 0, 0, 0, 0, 0, 0] CTBool[Ct[system.uint32]]'''
 """
 
@@ -66,3 +66,8 @@ var x: array[8, CTBool[Ct[uint32]]]
 x[0] = (CTBool[Ct[uint32]])(1)
 echo x.repr, " ", typeof(x[0])
 
+block: # bug #23139
+  type Foo = enum a, b
+
+  var x: range[a..b]
+  doAssert (repr x) == "a"
diff --git a/tests/metatype/tstatic_generic_typeclass.nim b/tests/metatype/tstatic_generic_typeclass.nim
new file mode 100644
index 000000000..0e9256a62
--- /dev/null
+++ b/tests/metatype/tstatic_generic_typeclass.nim
@@ -0,0 +1,30 @@
+type MyThing[T: static int] = object
+  when T == 300:
+    a: int
+
+var a = MyThing[300]()
+proc doThing(myThing: MyThing): string = $myThing
+proc doOtherThing[T](myThing: MyThing[T]): string = $myThing
+assert doThing(a) == $a
+assert doThing(MyThing[0]()) == $MyThing[0]()
+assert doOtherThing(a) == "(a: 0)"
+
+type
+  Backend* = enum
+    Cpu,
+    Cuda
+
+  Tensor*[B: static[Backend]; T] = object
+    shape: seq[int]
+    strides: seq[int]
+    offset: int
+    when B == Backend.Cpu:
+      data: seq[T]
+    else:
+      data_ptr: ptr T
+
+template shape*(t: Tensor): seq[int] =
+  t.shape
+
+
+assert Tensor[Cpu, int]().shape == @[]
diff --git a/tests/metatype/ttypedescnotnimnode.nim b/tests/metatype/ttypedescnotnimnode.nim
new file mode 100644
index 000000000..52a04815b
--- /dev/null
+++ b/tests/metatype/ttypedescnotnimnode.nim
@@ -0,0 +1,21 @@
+discard """
+  errormsg: "type mismatch: got <NimNode> but expected 'typedesc'"
+  line: 14
+"""
+
+import macros
+
+# This is the same example as ttypeselectors but using a proc instead of a macro
+# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone)
+# https://github.com/nim-lang/Nim/issues/7231
+
+proc getBase2*(bits: static[int]): typedesc =
+  if bits == 128:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
+  else:
+    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32"))
+
+type
+  MpUint2*[bits: static[int]] = getbase2(bits)
+# technically shouldn't error until instantiation, so instantiate it
+var x: MpUint2[123]
diff --git a/tests/metatype/ttypeselectors.nim b/tests/metatype/ttypeselectors.nim
index 5385a1be9..d0843511d 100644
--- a/tests/metatype/ttypeselectors.nim
+++ b/tests/metatype/ttypeselectors.nim
@@ -98,16 +98,3 @@ var c: Bar[32]
 echo sizeof(a)
 echo sizeof(b)
 echo sizeof(c)
-
-# This is the same example but using a proc instead of a macro
-# Instead of type mismatch for macro, proc just failed with internal error: getTypeDescAux(tyNone)
-# https://github.com/nim-lang/Nim/issues/7231
-
-proc getBase2*(bits: static[int]): typedesc =
-  if bits == 128:
-    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint64"))
-  else:
-    result = newTree(nnkBracketExpr, ident("MpUintBase"), ident("uint32"))
-
-type
-  MpUint2*[bits: static[int]] = getbase2(bits)
diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim
index bfaa23057..0523390a7 100644
--- a/tests/metatype/ttypetraits.nim
+++ b/tests/metatype/ttypetraits.nim
@@ -144,6 +144,32 @@ block distinctBase:
         doAssert($distinctBase(typeof(b2)) == "string")
         doAssert($distinctBase(typeof(c2)) == "int")
 
+block: # rangeBase
+  {.push warningAsError[EnumConv]: on.}
+  proc foo[T: not range](x: T): string =
+    $T & "(" & $x & ")"
+  proc foo[T: range](x: T): string =
+    "ranged(" & $low(T) & ".." & $high(T) & " of " & $rangeBase(T) & ") " & foo(rangeBase(x))
+  doAssert foo(123) == "int(123)"
+  type IntRange = range[0..3]
+  let x: IntRange = 2
+  doAssert foo(x) == "ranged(0..3 of int) int(2)"
+  type E = enum a, b, c, d, e, f
+  type EnumRange = range[c..e]
+  let y: EnumRange = d
+  doAssert foo(y) == "ranged(c..e of E) E(d)"
+  let z: range['a'..'z'] = 'g'
+  doAssert foo(z) == "ranged(a..z of char) char(g)"
+  {.pop.}
+
+  # works only with #24037:
+  var toChange: range[0..3] = 1
+  proc bar[T: int and not range](y: var T) =
+    inc y
+  doAssert not compiles(bar(toChange))
+  bar(rangeBase(toChange))
+  doAssert toChange == 2
+
 block: # tupleLen
   doAssert not compiles(tupleLen(int))
 
@@ -365,3 +391,14 @@ block: # enum.len
     doAssert MyEnum.enumLen == 4
     doAssert OtherEnum.enumLen == 3
     doAssert MyFlag.enumLen == 4
+
+when true: # Odd bug where alias can seep inside of `distinctBase`
+  import std/unittest
+
+  type
+    AdtChild* = concept t
+      distinctBase(t)
+
+  proc `$`*[T: AdtChild](adtChild: T): string = ""
+
+  check 10 is int
diff --git a/tests/metatype/twrong_same_type.nim b/tests/metatype/twrong_same_type.nim
new file mode 100644
index 000000000..ea903b7a3
--- /dev/null
+++ b/tests/metatype/twrong_same_type.nim
@@ -0,0 +1,28 @@
+# bug #23418
+
+template mapIt*(x: untyped): untyped =
+  type OutType {.gensym.} = typeof(x) #typeof(x, typeOfProc)
+  newSeq[OutType](5)
+
+type F[E] = object
+
+proc start(v: int): F[(ValueError,)] = discard
+proc stop(v: int): F[tuple[]] = discard
+
+assert $typeof(mapIt(start(9))) == "seq[F[(ValueError,)]]"
+assert $typeof(mapIt(stop(9))) == "seq[F[tuple[]]]"
+
+# bug #23445
+
+type F2[T; I: static int] = distinct int
+
+proc start2(v: int): F2[void, 22] = discard
+proc stop2(v: int): F2[void, 33] = discard
+
+var a = mapIt(start2(5))
+
+assert $type(a) == "seq[F2[system.void, 22]]", $type(a)
+
+var b = mapIt(stop2(5))
+
+assert $type(b) == "seq[F2[system.void, 33]]", $type(b)
diff --git a/tests/method/nim.cfg b/tests/method/nim.cfg
deleted file mode 100644
index 57698b028..000000000
--- a/tests/method/nim.cfg
+++ /dev/null
@@ -1 +0,0 @@
-multimethods:on
diff --git a/tests/method/t20515.nim b/tests/method/t20515.nim
new file mode 100644
index 000000000..1921f2e46
--- /dev/null
+++ b/tests/method/t20515.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "Base method 'zzz' requires explicit '{.gcsafe.}' to be GC-safe"
+  line: 10
+"""
+
+type
+  A = ref object of RootObj
+  B = ref object of A
+
+method zzz(a: A) {.base.} =
+  discard
+
+var s: seq[int]
+method zzz(a: B) =
+  echo s
+
+proc xxx(someObj: A) {.gcsafe.} =
+  someObj.zzz()
+
+xxx(B())
diff --git a/tests/method/t22673.nim b/tests/method/t22673.nim
new file mode 100644
index 000000000..1689e9d42
--- /dev/null
+++ b/tests/method/t22673.nim
@@ -0,0 +1,21 @@
+discard """
+  matrix: "--warningAsError:UseBase"
+"""
+
+# bug #22673
+type RefEntry = ref object of RootObj
+
+type RefFile = ref object of RefEntry
+    filename*: string
+    data*: string
+
+type RefDir = ref object of RefEntry
+    dirname*: string
+    files*: seq[RefFile]
+
+method name*(e: RefEntry): lent string {.base.} =
+  raiseAssert "Don't call the base method"
+
+method name*(e: RefFile): lent string = e.filename
+
+method name*(e: RefDir): lent string = e.dirname
\ No newline at end of file
diff --git a/tests/method/tcompilegenerics.nim b/tests/method/tcompilegenerics.nim
new file mode 100644
index 000000000..d0732895b
--- /dev/null
+++ b/tests/method/tcompilegenerics.nim
@@ -0,0 +1,24 @@
+discard """
+  matrix: "--mm:arc; --mm:refc"
+  output: '''
+newDNode base
+'''
+"""
+
+type
+  SNodeAny = ref object of RootObj
+  SNode[T] = ref object of SNodeAny
+    m: T
+  DNode[T] = ref object
+
+method getStr(s: SNode[float]): string {.base.} = "blahblah"
+
+method newDNode(s: SNodeAny) {.base.} =
+  echo "newDNode base"
+
+method newDNode[T](s: SNode[T]) =
+  echo "newDNode generic"
+
+let m = SNode[float]()
+let s = SNodeAny(m)
+newDnode(s)
\ No newline at end of file
diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim
index ab92c66d8..f8d068cc5 100644
--- a/tests/method/tgeneric_methods.nim
+++ b/tests/method/tgeneric_methods.nim
@@ -1,42 +1,42 @@
-discard """
-  matrix: "--mm:arc; --mm:refc"
-  output: '''wow2
-X 1
-X 3'''
-"""
-type
-  First[T] = ref object of RootObj
-    value: T
-
-  Second[T] = ref object of First[T]
-    value2: T
-
-method wow[T](y: int; x: First[T]) {.base.} =
-  echo "wow1"
-
-method wow[T](y: int; x: Second[T]) =
-  echo "wow2"
-
-var
-  x: Second[int]
-new(x)
-
-proc takeFirst(x: First[int]) =
-  wow(2, x)
-
-takeFirst(x)
-
-
-# bug #5479
-type
-  Base[T: static[int]] = ref object of RootObj
-
-method test[T](t: Base[T]) {.base.} =
-  echo "X ", t.T
-
-let ab = Base[1]()
-
-ab.test()
-
-let ac = Base[3]()
-ac.test()
+discard """

+  matrix: "--mm:arc --multimethods:on -d:nimInternalNonVtablesTesting; --mm:refc --multimethods:on -d:nimInternalNonVtablesTesting"

+  output: '''wow2

+X 1

+X 3'''

+"""

+type

+  First[T] = ref object of RootObj

+    value: T

+

+  Second[T] = ref object of First[T]

+    value2: T

+

+method wow[T](y: int; x: First[T]) {.base.} =

+  echo "wow1"

+

+method wow[T](y: int; x: Second[T]) =

+  echo "wow2"

+

+var

+  x: Second[int]

+new(x)

+

+proc takeFirst(x: First[int]) =

+  wow(2, x)

+

+takeFirst(x)

+

+

+# bug #5479

+type

+  Base[T: static[int]] = ref object of RootObj

+

+method test[T](t: Base[T]) {.base.} =

+  echo "X ", t.T

+

+let ab = Base[1]()

+

+ab.test()

+

+let ac = Base[3]()

+ac.test()

diff --git a/tests/method/tmethod_various.nim b/tests/method/tmethod_various.nim
index c41d04983..3b64aea8d 100644
--- a/tests/method/tmethod_various.nim
+++ b/tests/method/tmethod_various.nim
@@ -1,15 +1,12 @@
 discard """
   matrix: "--mm:arc; --mm:refc"
   output: '''
-do nothing
 HELLO WORLD!
 '''
 """
 
 
-# tmethods1
-method somethin(obj: RootObj) {.base.} =
-  echo "do nothing"
+
 
 type
   TNode* {.inheritable.} = object
@@ -23,8 +20,6 @@ type
 method foo(a: PNode, b: PSomethingElse) {.base.} = discard
 method foo(a: PNodeFoo, b: PSomethingElse) = discard
 
-var o: RootObj
-o.somethin()
 
 
 
diff --git a/tests/method/tmethods_old.nim b/tests/method/tmethods_old.nim
new file mode 100644
index 000000000..d24eb0cc7
--- /dev/null
+++ b/tests/method/tmethods_old.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--mm:arc -d:nimInternalNonVtablesTesting"
+  output: '''
+do nothing
+'''
+"""
+
+# tmethods1
+method somethin(obj: RootObj) {.base.} =
+  echo "do nothing"
+var o: RootObj
+o.somethin()
diff --git a/tests/method/tmultim.nim b/tests/method/tmultim.nim
index 126eb7526..bba4d8c5c 100644
--- a/tests/method/tmultim.nim
+++ b/tests/method/tmultim.nim
@@ -1,96 +1,97 @@
-discard """
-  output: '''
-collide: unit, thing
-collide: unit, thing
-collide: thing, unit
-collide: thing, thing
-collide: unit, thing |
-collide: unit, thing |
-collide: thing, unit |
-do nothing
-'''
-  joinable: false
-  disabled: true
-"""
-
-
-# tmultim2
-type
-  TThing {.inheritable.} = object
-  TUnit = object of TThing
-    x: int
-  TParticle = object of TThing
-    a, b: int
-
-method collide(a, b: TThing) {.base, inline.} =
-  echo "collide: thing, thing"
-
-method collide(a: TThing, b: TUnit) {.inline.} =
-  echo "collide: thing, unit"
-
-method collide(a: TUnit, b: TThing) {.inline.} =
-  echo "collide: unit, thing"
-
-proc test(a, b: TThing) {.inline.} =
-  collide(a, b)
-
-proc staticCollide(a, b: TThing) {.inline.} =
-  procCall collide(a, b)
-
-var
-  a: TThing
-  b, c: TUnit
-collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!
-test(b, c)
-collide(a, b)
-staticCollide(a, b)
-
-
-
-# tmultim6
-type
-  Thing {.inheritable.} = object
-  Unit[T] = object of Thing
-    x: T
-  Particle = object of Thing
-    a, b: int
-
-method collide(a, b: Thing) {.base, inline.} =
-  quit "to override!"
-
-method collide[T](a: Thing, b: Unit[T]) {.inline.} =
-  echo "collide: thing, unit |"
-
-method collide[T](a: Unit[T], b: Thing) {.inline.} =
-  echo "collide: unit, thing |"
-
-proc test(a, b: Thing) {.inline.} =
-  collide(a, b)
-
-var
-  aaa: Thing
-  bbb, ccc: Unit[string]
-collide(bbb, Thing(ccc))
-test(bbb, ccc)
-collide(aaa, bbb)
-
-
-
-# tmethods1
-method somethin(obj: RootObj) {.base.} =
-  echo "do nothing"
-
-type
-  TNode* {.inheritable.} = object
-  PNode* = ref TNode
-
-  PNodeFoo* = ref object of TNode
-
-  TSomethingElse = object
-  PSomethingElse = ref TSomethingElse
-
-method foo(a: PNode, b: PSomethingElse) {.base.} = discard
-method foo(a: PNodeFoo, b: PSomethingElse) = discard
-
-var o: RootObj
-o.somethin()
+discard """

+  matrix: "--multimethods:on"

+  output: '''

+collide: unit, thing

+collide: unit, thing

+collide: thing, unit

+collide: thing, thing

+collide: unit, thing |

+collide: unit, thing |

+collide: thing, unit |

+do nothing

+'''

+  joinable: false

+  disabled: true

+"""

+

+

+# tmultim2

+type

+  TThing {.inheritable.} = object

+  TUnit = object of TThing

+    x: int

+  TParticle = object of TThing

+    a, b: int

+

+method collide(a, b: TThing) {.base, inline.} =

+  echo "collide: thing, thing"

+

+method collide(a: TThing, b: TUnit) {.inline.} =

+  echo "collide: thing, unit"

+

+method collide(a: TUnit, b: TThing) {.inline.} =

+  echo "collide: unit, thing"

+

+proc test(a, b: TThing) {.inline.} =

+  collide(a, b)

+

+proc staticCollide(a, b: TThing) {.inline.} =

+  procCall collide(a, b)

+

+var

+  a: TThing

+  b, c: TUnit

+collide(b, c) # ambiguous (unit, thing) or (thing, unit)? -> prefer unit, thing!

+test(b, c)

+collide(a, b)

+staticCollide(a, b)

+

+

+

+# tmultim6

+type

+  Thing {.inheritable.} = object

+  Unit[T] = object of Thing

+    x: T

+  Particle = object of Thing

+    a, b: int

+

+method collide(a, b: Thing) {.base, inline.} =

+  quit "to override!"

+

+method collide[T](a: Thing, b: Unit[T]) {.inline.} =

+  echo "collide: thing, unit |"

+

+method collide[T](a: Unit[T], b: Thing) {.inline.} =

+  echo "collide: unit, thing |"

+

+proc test(a, b: Thing) {.inline.} =

+  collide(a, b)

+

+var

+  aaa: Thing

+  bbb, ccc: Unit[string]

+collide(bbb, Thing(ccc))

+test(bbb, ccc)

+collide(aaa, bbb)

+

+

+

+# tmethods1

+method somethin(obj: RootObj) {.base.} =

+  echo "do nothing"

+

+type

+  TNode* {.inheritable.} = object

+  PNode* = ref TNode

+

+  PNodeFoo* = ref object of TNode

+

+  TSomethingElse = object

+  PSomethingElse = ref TSomethingElse

+

+method foo(a: PNode, b: PSomethingElse) {.base.} = discard

+method foo(a: PNodeFoo, b: PSomethingElse) = discard

+

+var o: RootObj

+o.somethin()

diff --git a/tests/method/tvtable.nim b/tests/method/tvtable.nim
new file mode 100644
index 000000000..a1b33d6b7
--- /dev/null
+++ b/tests/method/tvtable.nim
@@ -0,0 +1,24 @@
+discard """

+  targets: "c cpp"

+"""

+

+type FooBase = ref object of RootObj

+  dummy: int

+type Foo = ref object of FooBase

+  value : float32

+type Foo2 = ref object of Foo

+  change : float32

+method bar(x: FooBase, a: float32) {.base.} =

+  discard

+method bar(x: Foo, a: float32)  =

+  x.value += a

+method bar(x: Foo2, a: float32)  =

+  x.value += a

+

+

+proc test() =

+  var x = new Foo2

+  x.bar(2.3)

+  doAssert x.value <= 2.3

+

+test()
\ No newline at end of file
diff --git a/tests/misc/m20456.nims b/tests/misc/m20456.nims
new file mode 100644
index 000000000..8a9313129
--- /dev/null
+++ b/tests/misc/m20456.nims
@@ -0,0 +1 @@
+echo 123
\ No newline at end of file
diff --git a/tests/misc/mjsondoc.nim b/tests/misc/mjsondoc.nim
index e4642f0b4..016c8522d 100644
--- a/tests/misc/mjsondoc.nim
+++ b/tests/misc/mjsondoc.nim
@@ -9,3 +9,6 @@ const
 type
   MyEnum* = enum
     foo, bar
+
+proc foo2*[T: int, M: string, U](x: T, y: U, z: M) =
+  echo 1
diff --git a/tests/misc/t11634.nim b/tests/misc/t11634.nim
new file mode 100644
index 000000000..390af40f4
--- /dev/null
+++ b/tests/misc/t11634.nim
@@ -0,0 +1,17 @@
+discard """
+  action: reject
+"""
+
+type Foo = ref object
+  val: int
+
+proc divmod(a, b: Foo): (Foo, Foo) =
+  (
+    Foo(val: a.val div b.val),
+    Foo(val: a.val mod b.val)
+  )
+
+block:
+  let a {.compileTime.} = Foo(val: 2)
+  let b {.compileTime.} = Foo(val: 3)
+  let (c11634 {.compileTime.}, d11634 {.compileTime.}) = divmod(a, b)
diff --git a/tests/misc/t12869.nim b/tests/misc/t12869.nim
new file mode 100644
index 000000000..054e28a03
--- /dev/null
+++ b/tests/misc/t12869.nim
@@ -0,0 +1,14 @@
+discard """
+  errormsg: "type mismatch: got <openArray[int], proc (x: GenericParam, y: GenericParam): auto, SortOrder>"
+  line: 12
+"""
+
+import sugar
+from algorithm import sorted, SortOrder
+
+let a = 5
+
+proc sorted*[T](a: openArray[T], key: proc(v: T): int, order = SortOrder.Ascending): seq[T] =
+  sorted(a, (x, y) => key(x) < key(y), order)
+
+echo sorted(@[9, 1, 8, 2, 6, 4, 5, 0], (x) => (a - x).abs)
diff --git a/tests/misc/t14667.nim b/tests/misc/t14667.nim
new file mode 100644
index 000000000..3034e2841
--- /dev/null
+++ b/tests/misc/t14667.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+
+type A = tuple
+discard ()
+discard default(A)
diff --git a/tests/misc/t15351.nim b/tests/misc/t15351.nim
new file mode 100644
index 000000000..c31e604a2
--- /dev/null
+++ b/tests/misc/t15351.nim
@@ -0,0 +1,5 @@
+discard """
+  action: "compile"
+"""
+var
+  ## TODO: broken
diff --git a/tests/misc/t16244.nim b/tests/misc/t16244.nim
new file mode 100644
index 000000000..5e8128736
--- /dev/null
+++ b/tests/misc/t16244.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "type mismatch: got <int, float64>"
+  line: 9
+"""
+
+proc g(): auto = 1
+proc h(): auto = 1.0
+
+var a = g() + h()
diff --git a/tests/misc/t16264.nim b/tests/misc/t16264.nim
new file mode 100644
index 000000000..afe319e6c
--- /dev/null
+++ b/tests/misc/t16264.nim
@@ -0,0 +1,2 @@
+import times
+doAssert low(Time) == fromUnix(0)
\ No newline at end of file
diff --git a/tests/misc/t16541.nim b/tests/misc/t16541.nim
new file mode 100644
index 000000000..452327e8f
--- /dev/null
+++ b/tests/misc/t16541.nim
@@ -0,0 +1,12 @@
+discard """
+  action: "reject"
+
+"""
+
+import strutils, sugar, nre
+
+proc my_replace*(s: string, r: Regex, by: string | (proc (match: string): string)): string =
+  nre.replace(s, r, by)
+
+discard my_replace("abcde", re"[bcd]", match => match.to_upper) == "aBCDe"
+discard my_replace("abcde", re"[bcd]", (match: string) => match.to_upper) == "aBCDe"
diff --git a/tests/misc/t18079.nim b/tests/misc/t18079.nim
new file mode 100644
index 000000000..ae64bbff9
--- /dev/null
+++ b/tests/misc/t18079.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--mm:orc"
+"""
+
+type
+  Foo = object
+    y: int
+
+  Bar = object
+    x: Foo
+
+proc baz(state: var Bar):int = 
+  state.x.y = 2
+  state.x.y
+doAssert baz((ref Bar)(x: (new Foo)[])[]) == 2
diff --git a/tests/misc/t20253.nim b/tests/misc/t20253.nim
new file mode 100644
index 000000000..d47c36c55
--- /dev/null
+++ b/tests/misc/t20253.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "'result' requires explicit initialization"
+  line: 10
+"""
+
+type Meow {.requiresInit.} = object 
+  init: bool
+
+proc initMeow(): Meow =
+  discard
diff --git a/tests/misc/t20289.nim b/tests/misc/t20289.nim
new file mode 100644
index 000000000..5a0a269f0
--- /dev/null
+++ b/tests/misc/t20289.nim
@@ -0,0 +1,15 @@
+discard """
+  action: reject
+"""
+
+type E[T] = object
+  v: T
+
+template j[T](R: type E[T], x: untyped): R = R(v: x)
+template d[T](O: type E, v: T): E[T] = E[T].j(v)
+
+proc w[T](): E[T] =
+  template r(k: int): auto = default(T)
+  E.d r
+
+discard w[int]()
diff --git a/tests/misc/t20456_2.nim b/tests/misc/t20456_2.nim
new file mode 100644
index 000000000..37e52c452
--- /dev/null
+++ b/tests/misc/t20456_2.nim
@@ -0,0 +1,14 @@
+discard """
+  joinable: false
+"""
+
+import std/[osproc, os, strformat]
+from stdtest/specialpaths import testsDir
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
+const
+  nim = getCurrentCompilerExe()
+  file = testsDir / "misc" / "m20456.nims"
+doAssert execCmd(fmt"{nim} check {file}") == 0
diff --git a/tests/misc/t20883.nim b/tests/misc/t20883.nim
new file mode 100644
index 000000000..92e7929f4
--- /dev/null
+++ b/tests/misc/t20883.nim
@@ -0,0 +1,13 @@
+discard """
+  action: reject
+nimout: '''
+t20883.nim(13, 4) template/generic instantiation of `foo` from here
+t20883.nim(9, 11) Error: cannot instantiate: 'U'
+'''
+"""
+
+proc foo*[U](x: U = U(1e-6)) =
+  echo x
+
+foo[float]()
+foo()
diff --git a/tests/misc/t21109.nim b/tests/misc/t21109.nim
new file mode 100644
index 000000000..0f7980896
--- /dev/null
+++ b/tests/misc/t21109.nim
@@ -0,0 +1,13 @@
+discard """
+  action: reject
+  errormsg: "type expected"
+  file: "iterators.nim"
+"""
+
+
+template b(j: untyped) = j
+template m() = discard
+
+b:
+  for t, f in @[]:
+    m()
diff --git a/tests/misc/t21443.nim b/tests/misc/t21443.nim
new file mode 100644
index 000000000..70413c5b3
--- /dev/null
+++ b/tests/misc/t21443.nim
@@ -0,0 +1,6 @@
+import std/envvars
+
+# bug #19292
+putEnv("NimPutEnvTest", "test")
+# bug #21122
+doAssert getEnv("NimPutEnvTest") == "test"
diff --git a/tests/misc/t23240.nim b/tests/misc/t23240.nim
new file mode 100644
index 000000000..d5edcefe8
--- /dev/null
+++ b/tests/misc/t23240.nim
@@ -0,0 +1,6 @@
+discard """
+  cmd: "nim c foo/bar.nim"
+  action: "reject"
+  errormsg: "cannot open 'foo/'"
+  file: ""
+"""
diff --git a/tests/misc/t3907.nim b/tests/misc/t3907.nim
new file mode 100644
index 000000000..45fc75e81
--- /dev/null
+++ b/tests/misc/t3907.nim
@@ -0,0 +1,10 @@
+import std/assertions
+
+let a = 0
+let b = if false: -1 else: a
+doAssert b == 0
+
+let c: range[0..high(int)] = 0
+let d = if false: -1 else: c
+
+doAssert d == 0
diff --git a/tests/misc/t6549.nim b/tests/misc/t6549.nim
new file mode 100644
index 000000000..1d7393318
--- /dev/null
+++ b/tests/misc/t6549.nim
@@ -0,0 +1,4 @@
+
+const l = $(range[low(uint64) .. high(uint64)])
+const r = "range 0..18446744073709551615(uint64)"
+doAssert l == r
diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim
index 89957e1d3..48b886cb8 100644
--- a/tests/misc/t8545.nim
+++ b/tests/misc/t8545.nim
@@ -1,5 +1,6 @@
 discard """
-  targets: "c cpp js"
+  # just tests that this doesn't crash the compiler
+  errormsg: "cannot instantiate: 'a:type'"
 """
 
 # bug #8545
diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim
index 48d4928ac..64f95c7e3 100644
--- a/tests/misc/taddr.nim
+++ b/tests/misc/taddr.nim
@@ -273,6 +273,16 @@ proc test15939() = # bug #15939 (v2)
   else: # can't take address of cstring element in js
     when not defined(js): cstringTest()
 
+block: # bug #23499
+  template volatileStore[T](dest: ptr T, val: T) =
+    dest[] = val
+
+  proc foo =
+    var ctr = 0
+    volatileStore(addr ctr, 0)
+
+  foo()
+
 template main =
   # xxx wrap all other tests here like that so they're also tested in VM
   test14420()
diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim
index 6d67b1c52..73196e76c 100644
--- a/tests/misc/tcast.nim
+++ b/tests/misc/tcast.nim
@@ -1,6 +1,7 @@
 discard """
   output: '''
 Hello World
+Hello World
 Hello World'''
   joinable: false
 """
@@ -8,7 +9,7 @@ type MyProc = proc() {.cdecl.}
 type MyProc2 = proc() {.nimcall.}
 type MyProc3 = proc() #{.closure.} is implicit
 
-proc testProc()  = echo "Hello World"
+proc testProc() {.exportc:"foo".} = echo "Hello World"
 
 template reject(x) = doAssert(not compiles(x))
 
@@ -23,6 +24,10 @@ proc callPointer(p: pointer) =
   ffunc0()
   ffunc1()
 
+    # bug #5901
+  proc foo() {.importc.}
+  (cast[proc(a: int) {.cdecl.}](foo))(5)
+
 callPointer(cast[pointer](testProc))
 
 reject: discard cast[enum](0)
diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim
index 5e8eac729..90fae868b 100644
--- a/tests/misc/tconv.nim
+++ b/tests/misc/tconv.nim
@@ -2,6 +2,9 @@ discard """
   matrix: "--warningAsError:EnumConv --warningAsError:CStringConv"
 """
 
+from std/enumutils import items  # missing from the example code
+from std/sequtils import toSeq
+
 template reject(x) =
   static: doAssert(not compiles(x))
 template accept(x) =
@@ -85,6 +88,13 @@ block: # https://github.com/nim-lang/RFCs/issues/294
   reject: Goo(k2)
   reject: k2.Goo
 
+  type KooRange = range[k2..k2]
+  accept: KooRange(k2)
+  accept: k2.KooRange
+  let k2ranged: KooRange = k2
+  accept: Koo(k2ranged)
+  accept: k2ranged.Koo
+
 reject:
   # bug #18550
   proc f(c: char): cstring =
@@ -117,4 +127,17 @@ reject:
   var va = 2
   var vb = va.Hole
 
+block: # bug #22844
+  type
+    A = enum
+      a0 = 2
+      a1 = 4
+      a2
+    B[T] = enum
+      b0 = 2
+      b1 = 4
+
+  doAssert A.toSeq == [a0, a1, a2]
+  doAssert B[float].toSeq == [B[float].b0, B[float].b1]
+
 {.pop.}
diff --git a/tests/misc/tcsharpusingstatement.nim b/tests/misc/tcsharpusingstatement.nim
index dd4cf589d..1ce553895 100644
--- a/tests/misc/tcsharpusingstatement.nim
+++ b/tests/misc/tcsharpusingstatement.nim
@@ -49,25 +49,13 @@ macro autoClose(args: varargs[untyped]): untyped =
   var finallyBlock = newNimNode(nnkStmtList)
   finallyBlock.add(closingCalls)
 
-  # XXX: Use a template here once getAst is working properly
-  var targetAst = parseStmt"""block:
-    var
-      x = foo()
-      y = bar()
-
-    try:
-      body()
-
-    finally:
-      close x
-      close y
-  """
-
-  targetAst[0][1][0] = varSection
-  targetAst[0][1][1][0] = body
-  targetAst[0][1][1][1][0] = finallyBlock
-
-  result = targetAst
+  result = quote do:
+    block:
+      `varSection`
+      try:
+        `body`
+      finally:
+        `finallyBlock`
 
 type
   TResource* = object
diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim
index c4d11c941..f3fa4711f 100644
--- a/tests/misc/tdefine.nim
+++ b/tests/misc/tdefine.nim
@@ -1,12 +1,23 @@
 discard """
 joinable: false
-cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file"
+cmd: "nim c $options -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file"
+matrix: "; -d:useGenericDefine"
 """
 
-const booldef {.booldefine.} = false
-const booldef2 {.booldefine.} = true
-const intdef {.intdefine.} = 0
-const strdef {.strdefine.} = ""
+when defined(useGenericDefine):
+  {.pragma: booldefine2, define.}
+  {.pragma: intdefine2, define.}
+  {.pragma: strdefine2, define.}
+else:
+  
+  {.pragma: booldefine2, booldefine.}
+  {.pragma: intdefine2, intdefine.}
+  {.pragma: strdefine2, strdefine.}
+
+const booldef {.booldefine2.} = false
+const booldef2 {.booldefine2.} = true
+const intdef {.intdefine2.} = 0
+const strdef {.strdefine2.} = ""
 
 doAssert defined(booldef)
 doAssert defined(booldef2)
@@ -17,17 +28,28 @@ doAssert not booldef2
 doAssert intdef == 2
 doAssert strdef == "foobar"
 
+when defined(useGenericDefine):
+  block:
+    const uintdef {.define: "intdef".}: uint = 17
+    doAssert intdef == int(uintdef)
+    const cstrdef {.define: "strdef".}: cstring = "not strdef"
+    doAssert $cstrdef == strdef
+    type FooBar = enum foo, bar, foobar
+    const enumdef {.define: "strdef".} = foo
+    doAssert $enumdef == strdef
+    doAssert enumdef == foobar
+
 # Intentionally not defined from command line
-const booldef3 {.booldefine.} = true
-const intdef2 {.intdefine.} = 1
-const strdef2 {.strdefine.} = "abc"
+const booldef3 {.booldefine2.} = true
+const intdef2 {.intdefine2.} = 1
+const strdef2 {.strdefine2.} = "abc"
 type T = object
-    when booldef3:
-        field1: int
-    when intdef2 == 1:
-        field2: int
-    when strdef2 == "abc":
-        field3: int
+  when booldef3:
+    field1: int
+  when intdef2 == 1:
+    field2: int
+  when strdef2 == "abc":
+    field3: int
 
 doAssert not defined(booldef3)
 doAssert not defined(intdef2)
@@ -35,11 +57,21 @@ doAssert not defined(strdef2)
 discard T(field1: 1, field2: 2, field3: 3)
 
 doAssert defined(namespaced.define)
-const `namespaced.define` {.booldefine.} = true
+const `namespaced.define` {.booldefine2.} = true
 doAssert not `namespaced.define`
+when defined(useGenericDefine):
+  const aliasToNamespacedDefine {.define: "namespaced.define".} = not `namespaced.define`
+else:
+  const aliasToNamespacedDefine {.booldefine: "namespaced.define".} = not `namespaced.define`
+doAssert aliasToNamespacedDefine == `namespaced.define`
 
 doAssert defined(double.namespaced.define)
-const `double.namespaced.define` {.booldefine.} = false
+const `double.namespaced.define` {.booldefine2.} = false
 doAssert `double.namespaced.define`
+when defined(useGenericDefine):
+  const aliasToDoubleNamespacedDefine {.define: "double.namespaced.define".} = not `double.namespaced.define`
+else:
+  const aliasToDoubleNamespacedDefine {.booldefine: "double.namespaced.define".} = not `double.namespaced.define`
+doAssert aliasToDoubleNamespacedDefine == `double.namespaced.define`
 
 doAssert not defined(namespaced.butnotdefined)
diff --git a/tests/misc/tgenconstraints.nim b/tests/misc/tgenconstraints.nim
deleted file mode 100644
index 829da5173..000000000
--- a/tests/misc/tgenconstraints.nim
+++ /dev/null
@@ -1,31 +0,0 @@
-discard """
-  errormsg: "cannot instantiate T2"
-  file: "tgenconstraints.nim"
-  line: 25
-  disabled: true
-"""
-
-type
-  T1[T: int|string] = object
-    x: T
-
-  T2[T: Ordinal] = object
-    x: T
-
-var x1: T1[int]
-var x2: T1[string]
-var x3: T2[int]
-
-proc foo[T](x: T): T2[T] {.discardable.} =
-  var o: T1[T]
-
-foo(10)
-
-# XXX: allow type intersections in situation like this
-proc bar(x: int|TNumber): T1[type(x)] {.discardable.} =
-  when type(x) is TNumber:
-    var o: T2[type(x)]
-
-bar "test"
-bar 100
-bar 1.1
diff --git a/tests/misc/tradix.nim b/tests/misc/tradix.nim
index 1773a9609..f4fb56849 100644
--- a/tests/misc/tradix.nim
+++ b/tests/misc/tradix.nim
@@ -40,8 +40,8 @@ type
   TRadixNode {.pure, inheritable.} = object
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
-    len: int8
-    keys: array[0..31, int8]
+    len: uint8
+    keys: array[0..31, uint8]
     vals: array[0..31, PRadixNode]
 
   TRadixNodeFull = object of TRadixNode
@@ -49,8 +49,8 @@ type
   TRadixNodeLeafBits = object of TRadixNode
     b: array[0..7, int]
   TRadixNodeLeafLinear = object of TRadixNode
-    len: int8
-    keys: array[0..31, int8]
+    len: uint8
+    keys: array[0..31, uint8]
 
 var
   root: PRadixNode
@@ -59,8 +59,8 @@ proc searchInner(r: PRadixNode, a: int): PRadixNode =
   case r.kind
   of rnLinear:
     var x = cast[ptr TRadixNodeLinear](r)
-    for i in 0..ze(x.len)-1:
-      if ze(x.keys[i]) == a: return x.vals[i]
+    for i in 0..int(x.len)-1:
+      if int(x.keys[i]) == a: return x.vals[i]
   of rnFull:
     var x = cast[ptr TRadixNodeFull](r)
     return x.b[a]
@@ -87,8 +87,8 @@ proc searchLeaf(r: PRadixNode, a: int): bool =
     return testBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    for i in 0..ze(x.len)-1:
-      if ze(x.keys[i]) == a: return true
+    for i in 0..int(x.len)-1:
+      if int(x.keys[i]) == a: return true
   else: assert(false)
 
 proc exclLeaf(r: PRadixNode, a: int) =
@@ -98,9 +98,9 @@ proc exclLeaf(r: PRadixNode, a: int) =
     resetBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == a:
+      if int(x.keys[i]) == a:
         x.keys[i] = x.keys[L-1]
         dec(x.len)
         return
@@ -131,8 +131,8 @@ proc addLeaf(r: var PRadixNode, a: int): bool =
     # a linear node:
     var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear)))
     x.kind = rnLeafLinear
-    x.len = 1'i8
-    x.keys[0] = toU8(a)
+    x.len = 1'u8
+    x.keys[0] = uint8(a)
     r = x
     return false # not already in set
   case r.kind
@@ -141,18 +141,18 @@ proc addLeaf(r: var PRadixNode, a: int): bool =
     return testOrSetBit(x.b[a /% BitsPerUnit], a)
   of rnLeafLinear:
     var x = cast[ptr TRadixNodeLeafLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == a: return true
+      if int(x.keys[i]) == a: return true
     if L <= high(x.keys):
-      x.keys[L] = toU8(a)
+      x.keys[L] = uint8(a)
       inc(x.len)
     else:
       # transform into a full node:
       var y = cast[ptr TRadixNodeLeafBits](alloc0(sizeof(TRadixNodeLeafBits)))
       y.kind = rnLeafBits
-      for i in 0..ze(x.len)-1:
-        var u = ze(x.keys[i])
+      for i in 0..int(x.len)-1:
+        var u = int(x.keys[i])
         setBit(y.b[u /% BitsPerUnit], u)
       setBit(y.b[a /% BitsPerUnit], a)
       dealloc(r)
@@ -167,26 +167,26 @@ proc addInner(r: var PRadixNode, a: int, d: int): bool =
     # a linear node:
     var x = cast[ptr TRadixNodeLinear](alloc0(sizeof(TRadixNodeLinear)))
     x.kind = rnLinear
-    x.len = 1'i8
-    x.keys[0] = toU8(k)
+    x.len = 1'u8
+    x.keys[0] = uint8(k)
     r = x
     return addInner(x.vals[0], a, d-8)
   case r.kind
   of rnLinear:
     var x = cast[ptr TRadixNodeLinear](r)
-    var L = ze(x.len)
+    var L = int(x.len)
     for i in 0..L-1:
-      if ze(x.keys[i]) == k: # already exists
+      if int(x.keys[i]) == k: # already exists
         return addInner(x.vals[i], a, d-8)
     if L <= high(x.keys):
-      x.keys[L] = toU8(k)
+      x.keys[L] = uint8(k)
       inc(x.len)
       return addInner(x.vals[L], a, d-8)
     else:
       # transform into a full node:
       var y = cast[ptr TRadixNodeFull](alloc0(sizeof(TRadixNodeFull)))
       y.kind = rnFull
-      for i in 0..L-1: y.b[ze(x.keys[i])] = x.vals[i]
+      for i in 0..L-1: y.b[int(x.keys[i])] = x.vals[i]
       dealloc(r)
       r = y
       return addInner(y.b[k], a, d-8)
@@ -211,8 +211,8 @@ iterator innerElements(r: PRadixNode): tuple[prefix: int, n: PRadixNode] =
           yield (i, r.b[i])
     of rnLinear:
       var r = cast[ptr TRadixNodeLinear](r)
-      for i in 0..ze(r.len)-1:
-        yield (ze(r.keys[i]), r.vals[i])
+      for i in 0..int(r.len)-1:
+        yield (int(r.keys[i]), r.vals[i])
     else: assert(false)
 
 iterator leafElements(r: PRadixNode): int =
@@ -228,8 +228,8 @@ iterator leafElements(r: PRadixNode): int =
               yield i*BitsPerUnit+j
     of rnLeafLinear:
       var r = cast[ptr TRadixNodeLeafLinear](r)
-      for i in 0..ze(r.len)-1:
-        yield ze(r.keys[i])
+      for i in 0..int(r.len)-1:
+        yield int(r.keys[i])
     else: assert(false)
 
 iterator elements*(r: PRadixNode): ByteAddress {.inline.} =
diff --git a/tests/misc/trfc405.nim b/tests/misc/trfc405.nim
index 8c967eb77..0828879ee 100644
--- a/tests/misc/trfc405.nim
+++ b/tests/misc/trfc405.nim
@@ -79,6 +79,34 @@ template main =
       foobar3
       foobar4
     doAssert a2 == (1, 20, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4")
+  
+  block: # issue #19015
+    template hi(a: untyped, b: varargs[untyped]): untyped =
+      a
+
+    var worked = false
+    hi:
+      worked = true
+    doAssert worked
+    worked = false
+    hi(doAssert(not worked)):
+      doesntCompile
+    hi(doAssert(not worked), doesntCompile, againDoesntCompile):
+      definitelyDoesntCompile
+
+    template hi2(a: bool, b: untyped, c: varargs[untyped]): untyped =
+      b
+      doAssert a
+
+    hi2 worked:
+      worked = true
+    doAssert worked
+    hi2 worked, doAssert(worked):
+      doesntCompile
+    hi2 worked, doAssert(worked), doesntCompile, againDoesntCompile:
+      definitelyDoesntCompile
+    hi2 worked, doAssert(worked), againDoesntCompile:
+      definitelyDoesntCompile
 
 static: main()
 main()
diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim
index add92cbfd..6e5487d1b 100644
--- a/tests/misc/trunner.nim
+++ b/tests/misc/trunner.nim
@@ -64,7 +64,7 @@ when defined(nimTrunnerFfi):
 hello world stderr
 hi stderr
 """
-    let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --experimental:compiletimeFFI")
+    let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --warnings:off --experimental:compiletimeFFI")
     doAssert output == fmt"""
 {prefix}foo
 foo:100
@@ -224,13 +224,15 @@ sub/mmain.idx""", context
     doAssert exitCode == 0, msg
 
     let data = parseJson(readFile(output))["entries"]
-    doAssert data.len == 4
+    doAssert data.len == 5
     let doSomething = data[0]
     doAssert doSomething["name"].getStr == "doSomething"
     doAssert doSomething["type"].getStr == "skProc"
     doAssert doSomething["line"].getInt == 1
     doAssert doSomething["col"].getInt == 0
     doAssert doSomething["code"].getStr == "proc doSomething(x, y: int): int {.raises: [], tags: [], forbids: [].}"
+    let foo2 = data[4]
+    doAssert $foo2["signature"] == """{"arguments":[{"name":"x","type":"T"},{"name":"y","type":"U"},{"name":"z","type":"M"}],"genericParams":[{"name":"T","types":"int"},{"name":"M","types":"string"},{"name":"U"}]}"""
 
   block: # nim jsondoc # bug #11953
     let file = testsDir / "misc/mjsondoc.nim"
@@ -241,7 +243,7 @@ sub/mmain.idx""", context
     doAssert exitCode == 0, msg
 
     let data = parseJson(readFile(destDir / "mjsondoc.json"))["entries"]
-    doAssert data.len == 4
+    doAssert data.len == 5
     let doSomething = data[0]
     doAssert doSomething["name"].getStr == "doSomething"
     doAssert doSomething["type"].getStr == "skProc"
@@ -271,10 +273,13 @@ sub/mmain.idx""", context
     check execCmdEx(cmd) == ("12\n", 0)
 
   block: # bug #15316
-    let file = testsDir / "misc/m15316.nim"
-    let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}"
-    check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " &
-          "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1)
+    when not defined(windows):
+      # This never worked reliably on Windows. Needs further investigation but it is hard to reproduce.
+      # Looks like a mild stack corruption when bailing out of nested exception handling.
+      let file = testsDir / "misc/m15316.nim"
+      let cmd = fmt"{nim} check --hints:off --nimcache:{nimcache} {file}"
+      check execCmdEx(cmd) == ("m15316.nim(1, 15) Error: expression expected, but found \')\'\nm15316.nim(2, 1) Error: expected: \':\', but got: \'[EOF]\'\nm15316.nim(2, 1) Error: expression expected, but found \'[EOF]\'\nm15316.nim(2, 1) " &
+            "Error: expected: \')\', but got: \'[EOF]\'\nError: illformed AST: \n", 1)
 
 
   block: # config.nims, nim.cfg, hintConf, bug #16557
@@ -342,8 +347,8 @@ tests/newconfig/bar/mfoo.nims""".splitLines
         when not defined(windows):
           check3 lines.len == 5
           check3 lines[0].isDots
-          check3 lines[1].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise`
-          check3 lines[2].isDots
+          # check3 lines[1].isDots # todo nim secret might use parsing pipeline
+          check3 lines[2].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise`
           check3 lines[3] == "ab"
           check3 lines[4] == ""
         else:
@@ -404,7 +409,7 @@ running: v2
 
   block: # UnusedImport
     proc fn(opt: string, expected: string) =
-      let output = runNimCmdChk("pragmas/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}")
+      let output = runNimCmdChk("msgs/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}")
       doAssert output == expected, opt & "\noutput:\n" & output & "expected:\n" & expected
     fn("-d:case1"): """
 mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport]
@@ -434,7 +439,6 @@ mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mu
     fn("-d:case2 --gc:refc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
     fn("-d:case1 -b:js"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
     fn("-d:case2 -b:js"): """field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
-    # 3 instead of k3, because of lack of RTTI
-    fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = 3'"""
+    fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'"""
 else:
   discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled
diff --git a/tests/misc/trunner_special.nim b/tests/misc/trunner_special.nim
index 50a2e4d5a..e08b419b0 100644
--- a/tests/misc/trunner_special.nim
+++ b/tests/misc/trunner_special.nim
@@ -1,6 +1,7 @@
 discard """
   targets: "c cpp"
   joinable: false
+  disabled: osx
 """
 
 #[
@@ -13,6 +14,10 @@ xxx test all tests/untestable/* here, possibly with adjustments to make running
 import std/[strformat,os,unittest,compilesettings]
 import stdtest/specialpaths
 
+
+from stdtest/testutils import disableSSLTesting
+
+
 const
   nim = getCurrentCompilerExe()
   mode = querySetting(backend)
@@ -28,4 +33,5 @@ proc main =
   block: # SSL certificate check integration tests
     runCmd fmt"{nim} r {options} -d:ssl --threads:on --mm:refc {testsDir}/untestable/thttpclient_ssl_remotenetwork.nim"
 
-main()
+when not disableSSLTesting():
+  main()
diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim
index 0d96a5e04..ce5334664 100644
--- a/tests/misc/tsizeof.nim
+++ b/tests/misc/tsizeof.nim
@@ -732,3 +732,11 @@ type
 
 doAssert sizeof(PackedUnion) == 11
 doAssert alignof(PackedUnion) == 1
+
+# bug #22553
+type
+  ChunkObj = object
+    data: UncheckedArray[byte]
+
+doAssert sizeof(ChunkObj) == 1
+doAssert offsetOf(ChunkObj, data) == 1
diff --git a/tests/misc/tsizeof3.nim b/tests/misc/tsizeof3.nim
index 1215b4bcd..f0ba8c4d0 100644
--- a/tests/misc/tsizeof3.nim
+++ b/tests/misc/tsizeof3.nim
@@ -27,3 +27,32 @@ type
 
 static:
   doAssert(compiles(offsetOf(Payload, vals)))
+
+
+type
+  GoodboySave* {.bycopy.} = object
+    saveCount: uint8
+    savePoint: uint16
+    shards: uint32
+    friendCount: uint8
+    friendCards: set[0..255]
+    locationsKnown: set[0..127]
+    locationsUnlocked: set[0..127]
+    pickupsObtained: set[0..127]
+    pickupsUsed: set[0..127]
+    pickupCount: uint8
+
+block: # bug #20914
+  block:
+    proc csizeof[T](a: T): int {.importc:"sizeof", nodecl.}
+
+    var s: GoodboySave
+    doAssert sizeof(s) == 108
+    doAssert csizeof(s) == static(sizeof(s))
+
+  block:
+    proc calignof[T](a: T): int {.importc:"alignof", header: "<stdalign.h>".}
+
+    var s: set[0..256]
+    doAssert alignof(s) == 1
+    doAssert calignof(s) == static(alignof(s))
diff --git a/tests/misc/tspellsuggest.nim b/tests/misc/tspellsuggest.nim
deleted file mode 100644
index 033ed0afc..000000000
--- a/tests/misc/tspellsuggest.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-discard """
-  # pending bug #16521 (bug 12) use `matrix`
-  cmd: "nim c --spellsuggest:15 --hints:off $file"
-  action: "reject"
-  nimout: '''
-tspellsuggest.nim(45, 13) Error: undeclared identifier: 'fooBar'
-candidates (edit distance, scope distance); see '--spellSuggest':
- (1, 0): 'fooBar8' [var declared in tspellsuggest.nim(43, 9)]
- (1, 1): 'fooBar7' [var declared in tspellsuggest.nim(41, 7)]
- (1, 3): 'fooBar1' [var declared in tspellsuggest.nim(33, 5)]
- (1, 3): 'fooBar2' [let declared in tspellsuggest.nim(34, 5)]
- (1, 3): 'fooBar3' [const declared in tspellsuggest.nim(35, 7)]
- (1, 3): 'fooBar4' [proc declared in tspellsuggest.nim(36, 6)]
- (1, 3): 'fooBar5' [template declared in tspellsuggest.nim(37, 10)]
- (1, 3): 'fooBar6' [macro declared in tspellsuggest.nim(38, 7)]
- (1, 5): 'FooBar' [type declared in mspellsuggest.nim(5, 6)]
- (1, 5): 'fooBar4' [proc declared in mspellsuggest.nim(1, 6)]
- (1, 5): 'fooBar9' [var declared in mspellsuggest.nim(2, 5)]
- (1, 5): 'fooCar' [var declared in mspellsuggest.nim(4, 5)]
- (2, 5): 'FooCar' [type declared in mspellsuggest.nim(6, 6)]
- (2, 5): 'GooBa' [type declared in mspellsuggest.nim(7, 6)]
- (3, 0): 'fooBarBaz' [const declared in tspellsuggest.nim(44, 11)]
-'''
-"""
-
-# tests `--spellsuggest:num`
-
-
-
-# line 30
-import ./mspellsuggest
-
-var fooBar1 = 0
-let fooBar2 = 0
-const fooBar3 = 0
-proc fooBar4() = discard
-template fooBar5() = discard
-macro fooBar6() = discard
-
-proc main =
-  var fooBar7 = 0
-  block:
-    var fooBar8 = 0
-    const fooBarBaz = 0
-    let x = fooBar
diff --git a/tests/misc/tspellsuggest2.nim b/tests/misc/tspellsuggest2.nim
deleted file mode 100644
index 78504c513..000000000
--- a/tests/misc/tspellsuggest2.nim
+++ /dev/null
@@ -1,45 +0,0 @@
-discard """
-  # pending bug #16521 (bug 12) use `matrix`
-  cmd: "nim c --spellsuggest --hints:off $file"
-  action: "reject"
-  nimout: '''
-tspellsuggest2.nim(45, 13) Error: undeclared identifier: 'fooBar'
-candidates (edit distance, scope distance); see '--spellSuggest':
- (1, 0): 'fooBar8' [var declared in tspellsuggest2.nim(43, 9)]
- (1, 1): 'fooBar7' [var declared in tspellsuggest2.nim(41, 7)]
- (1, 3): 'fooBar1' [var declared in tspellsuggest2.nim(33, 5)]
- (1, 3): 'fooBar2' [let declared in tspellsuggest2.nim(34, 5)]
- (1, 3): 'fooBar3' [const declared in tspellsuggest2.nim(35, 7)]
- (1, 3): 'fooBar4' [proc declared in tspellsuggest2.nim(36, 6)]
- (1, 3): 'fooBar5' [template declared in tspellsuggest2.nim(37, 10)]
- (1, 3): 'fooBar6' [macro declared in tspellsuggest2.nim(38, 7)]
- (1, 5): 'FooBar' [type declared in mspellsuggest.nim(5, 6)]
- (1, 5): 'fooBar4' [proc declared in mspellsuggest.nim(1, 6)]
- (1, 5): 'fooBar9' [var declared in mspellsuggest.nim(2, 5)]
- (1, 5): 'fooCar' [var declared in mspellsuggest.nim(4, 5)]
-'''
-"""
-
-# tests `--spellsuggest`
-
-
-
-
-
-
-# line 30
-import ./mspellsuggest
-
-var fooBar1 = 0
-let fooBar2 = 0
-const fooBar3 = 0
-proc fooBar4() = discard
-template fooBar5() = discard
-macro fooBar6() = discard
-
-proc main =
-  var fooBar7 = 0
-  block:
-    var fooBar8 = 0
-    const fooBarBaz = 0
-    let x = fooBar
diff --git a/tests/misc/tstrtabs.nim b/tests/misc/tstrtabs.nim
deleted file mode 100644
index 2f7eda9f7..000000000
--- a/tests/misc/tstrtabs.nim
+++ /dev/null
@@ -1,20 +0,0 @@
-discard """
-  targets: "c cpp js"
-"""
-
-import std/strtabs
-
-proc fun()=
-  let ret = newStringTable(modeCaseSensitive)
-  ret["foo"] = "bar"
-
-  doAssert $ret == "{foo: bar}"
-
-  let b = ret["foo"]
-  doAssert b == "bar"
-
-proc main()=
-  static: fun()
-  fun()
-
-main()
diff --git a/tests/misc/ttlsemulation.nim b/tests/misc/ttlsemulation.nim
index 47c5934e6..767a9bd4e 100644
--- a/tests/misc/ttlsemulation.nim
+++ b/tests/misc/ttlsemulation.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   matrix: "-d:nimTtlsemulationCase1 --threads --tlsEmulation:on; -d:nimTtlsemulationCase2 --threads --tlsEmulation:off; -d:nimTtlsemulationCase3 --threads"
   targets: "c cpp"
 """
diff --git a/tests/misc/tvarnums.nim b/tests/misc/tvarnums.nim
index 2aef242e1..498099c49 100644
--- a/tests/misc/tvarnums.nim
+++ b/tests/misc/tvarnums.nim
@@ -7,7 +7,7 @@ import
   strutils
 
 type
-  TBuffer = array[0..10, int8]
+  TBuffer = array[0..10, uint8]
 
 proc toVarNum(x: int32, b: var TBuffer) =
   # encoding: first bit indicates end of number (0 if at end)
@@ -21,11 +21,11 @@ proc toVarNum(x: int32, b: var TBuffer) =
     # anyway
     a = abs(x)
   # first 6 bits:
-  b[0] = toU8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63))
+  b[0] = uint8(ord(a >% 63'i32) shl 7 or (ord(x < 0'i32) shl 6) or (int(a) and 63))
   a = (a shr 6'i32) and 0x03ffffff # skip first 6 bits
   var i = 1
   while a != 0'i32:
-    b[i] = toU8(ord(a >% 127'i32) shl 7 or (int(a) and 127))
+    b[i] = uint8(ord(a >% 127'i32) shl 7 or (int(a) and 127))
     inc(i)
     a = a shr 7'i32
 
@@ -41,40 +41,40 @@ proc toVarNum64(x: int64, b: var TBuffer) =
     # anyway
     a = abs(x)
   # first 6 bits:
-  b[0] = toU8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63))
+  b[0] = uint8(ord(a >% 63'i64) shl 7 or (ord(x < 0'i64) shl 6) or int(a and 63))
   a = (a shr 6) and 0x03ffffffffffffff # skip first 6 bits
   var i = 1
   while a != 0'i64:
-    b[i] = toU8(ord(a >% 127'i64) shl 7 or int(a and 127))
+    b[i] = uint8(ord(a >% 127'i64) shl 7 or int(a and 127))
     inc(i)
     a = a shr 7
 
 proc toNum64(b: TBuffer): int64 =
   # treat first byte different:
-  result = ze64(b[0]) and 63
+  result = int64(b[0]) and 63
   var
     i = 0
     Shift = 6'i64
-  while (ze(b[i]) and 128) != 0:
+  while (int(b[i]) and 128) != 0:
     inc(i)
-    result = result or ((ze64(b[i]) and 127) shl Shift)
+    result = result or ((int64(b[i]) and 127) shl Shift)
     inc(Shift, 7)
-  if (ze(b[0]) and 64) != 0: # sign bit set?
+  if (int(b[0]) and 64) != 0: # sign bit set?
     result = not result +% 1
     # this is the same as ``- result``
     # but gives no overflow error for low(int)
 
 proc toNum(b: TBuffer): int32 =
   # treat first byte different:
-  result = int32 ze(b[0]) and 63
+  result = int32(b[0]) and 63
   var
     i = 0
     Shift = 6'i32
-  while (ze(b[i]) and 128) != 0:
+  while (int(b[i]) and 128) != 0:
     inc(i)
-    result = result or ((int32(ze(b[i])) and 127'i32) shl Shift)
+    result = result or ((int32(b[i]) and 127'i32) shl Shift)
     Shift = Shift + 7'i32
-  if (ze(b[0]) and (1 shl 6)) != 0: # sign bit set?
+  if (int(b[0]) and (1 shl 6)) != 0: # sign bit set?
     result = (not result) +% 1'i32
     # this is the same as ``- result``
     # but gives no overflow error for low(int)
diff --git a/tests/misc/tvcc.nim b/tests/misc/tvcc.nim
new file mode 100644
index 000000000..10533729c
--- /dev/null
+++ b/tests/misc/tvcc.nim
@@ -0,0 +1,9 @@
+discard """
+  matrix: "--cc:vcc"
+  disabled: "linux"
+  disabled: "bsd"
+  disabled: "osx"
+  disabled: "unix"
+  disabled: "posix"
+"""
+doAssert true
diff --git a/tests/modules/mincludeprefix.nim b/tests/modules/mincludeprefix.nim
new file mode 100644
index 000000000..6d557a430
--- /dev/null
+++ b/tests/modules/mincludeprefix.nim
@@ -0,0 +1 @@
+const bar = 456
diff --git a/tests/modules/mincludetemplate.nim b/tests/modules/mincludetemplate.nim
new file mode 100644
index 000000000..febe9bfcf
--- /dev/null
+++ b/tests/modules/mincludetemplate.nim
@@ -0,0 +1 @@
+const foo = 123
diff --git a/tests/modules/timportas.nim b/tests/modules/timportas.nim
index 2f7bf7f6a..179613c6b 100644
--- a/tests/modules/timportas.nim
+++ b/tests/modules/timportas.nim
@@ -2,15 +2,20 @@ discard """
     action: run
 """
 
-import .. / modules / [definitions as foo]
-import .. / modules / definitions as foo
+import .. / modules / [mexporta as a1, definitions as foo1]
+import .. / modules / definitions as foo2
+import ./[mexporta as a2, definitions as foo3]
 import std / times as bar
 from times as bar2 import nil
 import times as bar3 except convert
 import definitions as baz
 
-discard foo.v
+discard foo1.v
+discard foo2.v
+discard foo3.v
 discard bar.now()
 discard bar2.now()
 discard bar3.now()
 discard baz.v
+discard a1.xyz
+discard a2.xyz
diff --git a/tests/modules/tincludeprefix.nim b/tests/modules/tincludeprefix.nim
new file mode 100644
index 000000000..d45a6eff3
--- /dev/null
+++ b/tests/modules/tincludeprefix.nim
@@ -0,0 +1,3 @@
+include ./[mincludeprefix, mincludetemplate]
+doAssert foo == 123
+doAssert bar == 456
diff --git a/tests/modules/tincludetemplate.nim b/tests/modules/tincludetemplate.nim
new file mode 100644
index 000000000..77e409ee5
--- /dev/null
+++ b/tests/modules/tincludetemplate.nim
@@ -0,0 +1,5 @@
+# issue #12539
+
+template includePath(n: untyped) = include ../modules/n # But `include n` works
+includePath(mincludetemplate)
+doAssert foo == 123
diff --git a/tests/modules/tmodulesymtype.nim b/tests/modules/tmodulesymtype.nim
new file mode 100644
index 000000000..d17c4cca4
--- /dev/null
+++ b/tests/modules/tmodulesymtype.nim
@@ -0,0 +1,22 @@
+discard """
+cmd: "nim check $file"
+"""
+
+# bug #19225
+import std/sequtils
+sequtils #[tt.Error
+^ expression has no type: sequtils]#
+proc foo() =
+  block: #[tt.Error
+  ^ expression has no type: block:
+  sequtils]#
+    sequtils
+
+foo()
+
+# issue #23399
+when isMainModule:
+  sequtils #[tt.Error
+  ^ expression has no type: sequtils]#
+
+discard
diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim
index c81715cd8..ff0b2e071 100644
--- a/tests/modules/treorder.nim
+++ b/tests/modules/treorder.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: "nim -d:testdef $target $file"
+  matrix: "-d:testdef"
   output: '''works 34
 34
 defined
@@ -8,6 +8,8 @@ defined
 
 {.experimental: "codeReordering".}
 
+{.push callconv: stdcall.}
+
 proc bar(x: T)
 
 proc foo() =
@@ -41,3 +43,5 @@ using
   my, omy: int
 
 goo(3, 4)
+
+{.pop.}
diff --git a/tests/deprecated/importme.nim b/tests/msgs/mdeprecated3.nim
index 0a9c6e37d..0a9c6e37d 100644
--- a/tests/deprecated/importme.nim
+++ b/tests/msgs/mdeprecated3.nim
diff --git a/tests/misc/mspellsuggest.nim b/tests/msgs/mspellsuggest.nim
index ad449554f..ad449554f 100644
--- a/tests/misc/mspellsuggest.nim
+++ b/tests/msgs/mspellsuggest.nim
diff --git a/tests/pragmas/mused2a.nim b/tests/msgs/mused2a.nim
index d9b2bb9bf..d9b2bb9bf 100644
--- a/tests/pragmas/mused2a.nim
+++ b/tests/msgs/mused2a.nim
diff --git a/tests/pragmas/mused2b.nim b/tests/msgs/mused2b.nim
index 39c92b964..39c92b964 100644
--- a/tests/pragmas/mused2b.nim
+++ b/tests/msgs/mused2b.nim
diff --git a/tests/pragmas/mused2c.nim b/tests/msgs/mused2c.nim
index a374e634e..a374e634e 100644
--- a/tests/pragmas/mused2c.nim
+++ b/tests/msgs/mused2c.nim
diff --git a/tests/pragmas/mused3.nim b/tests/msgs/mused3.nim
index 0beec1d44..0beec1d44 100644
--- a/tests/pragmas/mused3.nim
+++ b/tests/msgs/mused3.nim
diff --git a/tests/pragmas/mused3a.nim b/tests/msgs/mused3a.nim
index c33d1ecb3..c33d1ecb3 100644
--- a/tests/pragmas/mused3a.nim
+++ b/tests/msgs/mused3a.nim
diff --git a/tests/pragmas/mused3b.nim b/tests/msgs/mused3b.nim
index de288bb08..de288bb08 100644
--- a/tests/pragmas/mused3b.nim
+++ b/tests/msgs/mused3b.nim
diff --git a/tests/msgs/tdeprecated1.nim b/tests/msgs/tdeprecated1.nim
new file mode 100644
index 000000000..f4e85da0b
--- /dev/null
+++ b/tests/msgs/tdeprecated1.nim
@@ -0,0 +1,15 @@
+let foo* {.deprecated: "abcd".} = 42
+var foo1* {.deprecated: "efgh".} = 42
+foo1 = foo #[tt.Warning
+^ efgh; foo1 is deprecated [Deprecated]; tt.Warning
+       ^ abcd; foo is deprecated [Deprecated]]#
+
+proc hello[T](a: T) {.deprecated: "Deprecated since v1.2.0, use 'HelloZ'".} =
+  discard
+
+hello[int](12) #[tt.Warning
+^ Deprecated since v1.2.0, use 'HelloZ'; hello is deprecated [Deprecated]]#
+
+const foo2* {.deprecated: "abcd".} = 42
+discard foo2 #[tt.Warning
+        ^ abcd; foo2 is deprecated [Deprecated]]#
diff --git a/tests/deprecated/tdeprecated.nim b/tests/msgs/tdeprecated2.nim
index ba8d579ad..71d20081e 100644
--- a/tests/deprecated/tdeprecated.nim
+++ b/tests/msgs/tdeprecated2.nim
@@ -1,8 +1,8 @@
 discard """
   nimout: '''
-tdeprecated.nim(23, 3) Warning: a is deprecated [Deprecated]
-tdeprecated.nim(30, 11) Warning: asdf; enum 'Foo' which contains field 'a' is deprecated [Deprecated]
-tdeprecated.nim(40, 16) Warning: use fooX instead; fooA is deprecated [Deprecated]
+tdeprecated2.nim(23, 3) Warning: a is deprecated [Deprecated]
+tdeprecated2.nim(30, 11) Warning: asdf; enum 'Foo' which contains field 'a' is deprecated [Deprecated]
+tdeprecated2.nim(40, 16) Warning: use fooX instead; fooA is deprecated [Deprecated]
 end
 '''
 """
@@ -35,7 +35,7 @@ block: # issue #8063
     Foo = enum
       fooX
 
-  {.deprecated: [fooA: fooX].}
+  const fooA {.deprecated: "use fooX instead".} = fooX
   let
     foo: Foo = fooA
   echo foo
diff --git a/tests/msgs/tdeprecated3.nim b/tests/msgs/tdeprecated3.nim
new file mode 100644
index 000000000..0c1b7deff
--- /dev/null
+++ b/tests/msgs/tdeprecated3.nim
@@ -0,0 +1,33 @@
+discard """
+  matrix: "--hint:all:off"
+  nimoutFull: true
+  nimout: '''
+tdeprecated3.nim(21, 8) Warning: goodbye; mdeprecated3 is deprecated [Deprecated]
+tdeprecated3.nim(24, 10) Warning: Ty is deprecated [Deprecated]
+tdeprecated3.nim(27, 10) Warning: hello; Ty1 is deprecated [Deprecated]
+tdeprecated3.nim(30, 8) Warning: aVar is deprecated [Deprecated]
+tdeprecated3.nim(32, 3) Warning: aProc is deprecated [Deprecated]
+tdeprecated3.nim(33, 3) Warning: hello; aProc1 is deprecated [Deprecated]
+'''
+"""
+
+
+
+
+
+
+
+# line 20
+import mdeprecated3
+
+block:
+  var z: Ty
+  z = 0
+block:
+  var z: Ty1
+  z = 0
+block:
+  echo aVar
+block:
+  aProc()
+  aProc1()
diff --git a/tests/msgs/tdeprecatedequalhook.nim b/tests/msgs/tdeprecatedequalhook.nim
new file mode 100644
index 000000000..79ee835f8
--- /dev/null
+++ b/tests/msgs/tdeprecatedequalhook.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "Overriding `=` hook is deprecated; Override `=copy` hook instead"
+  matrix: "--warningAsError[Deprecated]:on"
+"""
+
+type
+  SharedString = object
+    data: string
+
+proc `=`(x: var SharedString, y: SharedString) =
+  discard
\ No newline at end of file
diff --git a/tests/compilerfeatures/texpandmacro.nim b/tests/msgs/texpandmacro.nim
index fea8b571f..fea8b571f 100644
--- a/tests/compilerfeatures/texpandmacro.nim
+++ b/tests/msgs/texpandmacro.nim
diff --git a/tests/misc/thints_off.nim b/tests/msgs/thints_off.nim
index 5a4cadad6..5a4cadad6 100644
--- a/tests/misc/thints_off.nim
+++ b/tests/msgs/thints_off.nim
diff --git a/tests/msgs/tspellsuggest.nim b/tests/msgs/tspellsuggest.nim
new file mode 100644
index 000000000..ea0a98cd3
--- /dev/null
+++ b/tests/msgs/tspellsuggest.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--spellsuggest:15 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest.nim(45, 13) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 0): 'fooBar8'
+ (1, 1): 'fooBar7'
+ (1, 3): 'fooBar1'
+ (1, 3): 'fooBar2'
+ (1, 3): 'fooBar3'
+ (1, 3): 'fooBar4'
+ (1, 3): 'fooBar5'
+ (1, 3): 'fooBar6'
+ (1, 5): 'FooBar'
+ (1, 5): 'fooBar4'
+ (1, 5): 'fooBar9'
+ (1, 5): 'fooCar'
+ (2, 5): 'FooCar'
+ (2, 5): 'GooBa'
+ (3, 0): 'fooBarBaz'
+'''
+"""
+
+# tests `--spellsuggest:num`
+
+
+
+
+# line 30
+import ./mspellsuggest
+
+var fooBar1 = 0
+let fooBar2 = 0
+const fooBar3 = 0
+proc fooBar4() = discard
+template fooBar5() = discard
+macro fooBar6() = discard
+
+proc main =
+  var fooBar7 = 0
+  block:
+    var fooBar8 = 0
+    const fooBarBaz = 0
+    let x = fooBar
diff --git a/tests/msgs/tspellsuggest2.nim b/tests/msgs/tspellsuggest2.nim
new file mode 100644
index 000000000..4bf05799e
--- /dev/null
+++ b/tests/msgs/tspellsuggest2.nim
@@ -0,0 +1,45 @@
+discard """
+  matrix: "--spellsuggest:12 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest2.nim(45, 13) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 0): 'fooBar8'
+ (1, 1): 'fooBar7'
+ (1, 3): 'fooBar1'
+ (1, 3): 'fooBar2'
+ (1, 3): 'fooBar3'
+ (1, 3): 'fooBar4'
+ (1, 3): 'fooBar5'
+ (1, 3): 'fooBar6'
+ (1, 5): 'FooBar'
+ (1, 5): 'fooBar4'
+ (1, 5): 'fooBar9'
+ (1, 5): 'fooCar'
+'''
+"""
+
+# tests `--spellsuggest`
+
+
+
+
+
+
+
+# line 30
+import ./mspellsuggest
+
+var fooBar1 = 0
+let fooBar2 = 0
+const fooBar3 = 0
+proc fooBar4() = discard
+template fooBar5() = discard
+macro fooBar6() = discard
+
+proc main =
+  var fooBar7 = 0
+  block:
+    var fooBar8 = 0
+    const fooBarBaz = 0
+    let x = fooBar
diff --git a/tests/msgs/tspellsuggest3.nim b/tests/msgs/tspellsuggest3.nim
new file mode 100644
index 000000000..bd4d5256f
--- /dev/null
+++ b/tests/msgs/tspellsuggest3.nim
@@ -0,0 +1,21 @@
+discard """
+  matrix: "--spellsuggest:4 --hints:off"
+  action: "reject"
+  nimout: '''
+tspellsuggest3.nim(21, 1) Error: undeclared identifier: 'fooBar'
+candidates (edit distance, scope distance); see '--spellSuggest':
+ (1, 2): 'FooBar'
+ (1, 2): 'fooBar4'
+ (1, 2): 'fooBar9'
+ (1, 2): 'fooCar'
+'''
+"""
+
+
+import ./mspellsuggest
+import ./mspellsuggest
+import ./mspellsuggest
+import ./mspellsuggest
+
+
+fooBar
diff --git a/tests/pragmas/tused2.nim b/tests/msgs/tused2.nim
index f80c198d8..5ccda7737 100644
--- a/tests/pragmas/tused2.nim
+++ b/tests/msgs/tused2.nim
@@ -7,12 +7,12 @@ mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed]
 mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed]
 mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed]
 mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed]
-mused2a.nim(1, 11) Warning: imported and not used: 'strutils' [UnusedImport]
-mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport]
+mused2a.nim(1, 12) Warning: imported and not used: 'strutils' [UnusedImport]
+mused2a.nim(3, 10) Warning: imported and not used: 'os' [UnusedImport]
 mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport]
-mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport]
+mused2a.nim(6, 10) Warning: imported and not used: 'setutils' [UnusedImport]
 tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport]
-tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport]
+tused2.nim(45, 12) Warning: imported and not used: 'strutils' [UnusedImport]
 '''
 """
 
diff --git a/tests/misc/twarningaserror.nim b/tests/msgs/twarningaserror.nim
index 6f7b76095..6f7b76095 100644
--- a/tests/misc/twarningaserror.nim
+++ b/tests/msgs/twarningaserror.nim
diff --git a/tests/navigator/tincludefile.nim b/tests/navigator/tincludefile.nim
index f35ab2ec9..a913d0736 100644
--- a/tests/navigator/tincludefile.nim
+++ b/tests/navigator/tincludefile.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: true
   cmd: "nim check $options --defusages:$file,12,7 $file"
   nimout: '''def tincludefile_temp.nim(11, 10)
 usage tincludefile_temp.nim(12, 8)
@@ -7,7 +8,6 @@ usage tincludefile_temp.nim(12, 8)
 
 
 
-
 proc foo(x: int) =
   echo x
 
diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim
index b6bbdbf19..e76c921f3 100644
--- a/tests/navigator/tnav1.nim
+++ b/tests/navigator/tnav1.nim
@@ -1,11 +1,11 @@
 discard """
+  disabled: true
   cmd: "nim check $options --defusages:$file,12,7 $file"
   nimout: '''def tnav1_temp.nim(11, 10)
 usage tnav1_temp.nim(12, 8)
   '''
 """
 
-
 import std / [times]
 
 proc foo(x: int) =
diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims
index 0cb950227..f22caaacd 100644
--- a/tests/newconfig/tfoo.nims
+++ b/tests/newconfig/tfoo.nims
@@ -6,7 +6,7 @@ exec "gcc -v"
 --define:release
 
 --forceBuild
---path: "../friends"
+--path: "../generics"
 
 warning("uninit", off)
 
diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim
index 145cae39c..34e118381 100644
--- a/tests/nimdoc/m13129.nim
+++ b/tests/nimdoc/m13129.nim
@@ -4,6 +4,7 @@ when defined(cpp):
   {.push header: "<vector>".}
   type
     Vector[T] {.importcpp: "std::vector".} = object
+  {.pop.}
 elif defined(js):
   proc endsWith*(s, suffix: cstring): bool {.noSideEffect,importjs: "#.endsWith(#)".}
 elif defined(c):
diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim
index ac7a0e26f..57e725b2e 100644
--- a/tests/nimdoc/trunnableexamples.nim
+++ b/tests/nimdoc/trunnableexamples.nim
@@ -1,5 +1,5 @@
 discard """
-cmd: "nim doc --doccmd:--hints:off --hints:off $file"
+cmd: '''nim doc --doccmd:"-d:testFooExternal --hints:off" --hints:off $file'''
 action: "compile"
 nimoutFull: true
 nimout: '''
@@ -7,7 +7,6 @@ foo1
 foo2
 foo3
 foo5
-foo6
 foo7
 in examplesInTemplate1
 doc in outer
@@ -15,16 +14,11 @@ doc in inner1
 doc in inner2
 foo8
 foo9
+foo6
 '''
 joinable: false
 """
 
-#[
-pending bug #18077, use instead:
-cmd: "nim doc --doccmd:'-d:testFooExternal --hints:off' --hints:off $file"
-and merge trunnableexamples2 back here
-]#
-{.define(testFooExternal).}
 
 proc fun*() =
   runnableExamples:
@@ -43,7 +37,7 @@ proc fun*() =
     proc fun*()=echo "foo5"
     fun()
 
-  runnableExamples:
+  runnableExamples("--experimental:codeReordering --warnings:off"):
     # `codeReordering` only allowed at top level
     {.experimental: "codeReordering".}
     proc fun1() = fun2()
@@ -65,7 +59,7 @@ when true: # issue #12746
     runnableExamples:
       try:
         discard
-      except:
+      except CatchableError:
         # just the general except will work
         discard
 
@@ -196,6 +190,10 @@ runnableExamples:
   proc fun*()=echo "foo9"
   fun()
 
+# import std/assertions by default
+runnableExamples("-d:nimPreviewSlimSystem"):
+  doAssert true
+
 # note: there are yet other examples where putting runnableExamples at module
 # scope is needed, for example when using an `include` before an `import`, etc.
 
@@ -208,3 +206,8 @@ snippet:
   doAssert defined(testFooExternal)
 
 ]##
+
+when true: # runnableExamples with rdoccmd
+  runnableExamples "-d:testFoo -d:testBar":
+    doAssert defined(testFoo) and defined(testBar)
+    doAssert defined(testFooExternal)
diff --git a/tests/nimdoc/trunnableexamples2.nim b/tests/nimdoc/trunnableexamples2.nim
deleted file mode 100644
index 5a437744e..000000000
--- a/tests/nimdoc/trunnableexamples2.nim
+++ /dev/null
@@ -1,11 +0,0 @@
-discard """
-cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file"
-action: "compile"
-joinable: false
-"""
-
-# pending bug #18077, merge back inside trunnableexamples.nim
-when true: # runnableExamples with rdoccmd
-  runnableExamples "-d:testFoo -d:testBar":
-    doAssert defined(testFoo) and defined(testBar)
-    doAssert defined(testFooExternal)
diff --git a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
index a7d4ebe00..67d9323f2 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/createDatabase.nim
@@ -1,4 +1,5 @@
 discard """
+disabled: true
 output: "Database created successfully!"
 """
 
diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
index 2fac949b9..1b521521c 100644
--- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
+++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim
@@ -1,4 +1,5 @@
 discard """
+disabled: true
 action: compile
 matrix: "--threads:off"
 """
diff --git a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
index a3cab4cba..c8beb4a30 100644
--- a/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
+++ b/tests/niminaction/Chapter7/Tweeter/tests/database_test.nim
@@ -1,4 +1,5 @@
 discard """
+disabled: true
 outputsub: "All tests finished successfully!"
 """
 
diff --git a/tests/niminaction/Chapter8/sdl/sdl_test.nim b/tests/niminaction/Chapter8/sdl/sdl_test.nim
index 1c4d258fb..db1700e0d 100644
--- a/tests/niminaction/Chapter8/sdl/sdl_test.nim
+++ b/tests/niminaction/Chapter8/sdl/sdl_test.nim
@@ -18,7 +18,7 @@ renderer.setDrawColor 29, 64, 153, 255
 renderer.clear
 renderer.setDrawColor 255, 255, 255, 255
 
-when defined(c):
+when false: # no long work with gcc 14!
   # just to ensure code from NimInAction still works, but
   # the `else` branch would work as well in C mode
   var points = [
diff --git a/tests/notnil/tparse.nim b/tests/notnil/tparse.nim
new file mode 100644
index 000000000..5c938ff04
--- /dev/null
+++ b/tests/notnil/tparse.nim
@@ -0,0 +1,18 @@
+# issue #16324
+
+{.push experimental: "notnil".}
+
+block:
+  type Foo = ref object
+    value: int
+    
+  proc newFoo1(): Foo not nil =               # This compiles
+    return Foo(value: 1)
+    
+  proc newFoo2(): Foo not nil {.inline.} =    # This does not
+    return Foo(value: 1)
+
+  doAssert newFoo1().value == 1
+  doAssert newFoo2().value == 1
+
+{.pop.}
diff --git a/tests/objects/mobject_default_value.nim b/tests/objects/mobject_default_value.nim
new file mode 100644
index 000000000..224549501
--- /dev/null
+++ b/tests/objects/mobject_default_value.nim
@@ -0,0 +1,15 @@
+type
+  Clean = object
+    mem: int
+  Default* = object
+    poi: int = 12
+    clc: Clean
+    se*: range[0'i32 .. high(int32)]
+
+  NonDefault* = object
+    poi: int
+
+  PrellDeque*[T] = object
+    pendingTasks*: range[0'i32 .. high(int32)]
+    head: T
+    tail: T
diff --git a/tests/objects/t20972.nim b/tests/objects/t20972.nim
new file mode 100644
index 000000000..6383dc9b1
--- /dev/null
+++ b/tests/objects/t20972.nim
@@ -0,0 +1,15 @@
+discard """
+  matrix: "--mm:refc -d:release; --mm:orc -d:release"
+"""
+
+{.passC: "-fsanitize=undefined -fsanitize-undefined-trap-on-error -Wall -Wextra -pedantic -flto".}
+{.passL: "-fsanitize=undefined -fsanitize-undefined-trap-on-error -flto".}
+
+# bug #20972
+type ForkedEpochInfo = object
+  case kind: bool
+  of true, false: discard
+var info = ForkedEpochInfo(kind: true)
+doAssert info.kind
+info.kind = false
+doAssert not info.kind
diff --git a/tests/objects/t22301.nim b/tests/objects/t22301.nim
new file mode 100644
index 000000000..8746bf584
--- /dev/null
+++ b/tests/objects/t22301.nim
@@ -0,0 +1,17 @@
+discard """
+  errormsg: "branch initialization with a runtime discriminator is not supported for a branch whose fields have default values."
+"""
+
+# bug #22301
+type
+  Enum = enum A, B
+  Object = object
+    case a: Enum
+    of A:
+      integer: int = 200
+    of B:
+      time: string
+
+let x = A
+let s = Object(a: x)
+echo s
\ No newline at end of file
diff --git a/tests/objects/tdefaultfieldscheck.nim b/tests/objects/tdefaultfieldscheck.nim
new file mode 100644
index 000000000..8a05439d9
--- /dev/null
+++ b/tests/objects/tdefaultfieldscheck.nim
@@ -0,0 +1,16 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout:
+'''
+tdefaultfieldscheck.nim(14, 17) Error: type mismatch: got <string> but expected 'int'
+'''
+"""
+
+
+type
+  Date* = object
+    goal: float = 7
+    name: int = "string"
+
+echo default(Date)
diff --git a/tests/objects/tdefaultrangetypescheck.nim b/tests/objects/tdefaultrangetypescheck.nim
new file mode 100644
index 000000000..71e7ac59b
--- /dev/null
+++ b/tests/objects/tdefaultrangetypescheck.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "cannot convert 0 to range 1..5(int)"
+  line: 9
+"""
+
+type
+  Point = object
+    y: int
+    x: range[1..5] = 0
+
+echo default(Point)
diff --git a/tests/objects/tillegal_recursion.nim b/tests/objects/tillegal_recursion.nim
index 222139101..428c2e445 100644
--- a/tests/objects/tillegal_recursion.nim
+++ b/tests/objects/tillegal_recursion.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "inheritance only works with non-final objects"
+  errormsg: "Cannot inherit from: 'Foo:ObjectType'"
   line: 7
 """
 # bug #1691
diff --git a/tests/objects/tillegal_recursion2.nim b/tests/objects/tillegal_recursion2.nim
new file mode 100644
index 000000000..ce2279f04
--- /dev/null
+++ b/tests/objects/tillegal_recursion2.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "Cannot inherit from: 'Foo'"
+  line: 6
+"""
+type
+  Foo = object of Foo
diff --git a/tests/objects/tillegal_recursion3.nim b/tests/objects/tillegal_recursion3.nim
new file mode 100644
index 000000000..c80f29e28
--- /dev/null
+++ b/tests/objects/tillegal_recursion3.nim
@@ -0,0 +1,6 @@
+discard """
+  errormsg: "Cannot inherit from: 'Foo'"
+  line: 6
+"""
+type
+  Foo = object of ref Foo
diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim
index 543a86376..a185bebcb 100644
--- a/tests/objects/tobject.nim
+++ b/tests/objects/tobject.nim
@@ -1,4 +1,3 @@
-import unittest
 
 type Obj = object
   foo: int
@@ -10,9 +9,9 @@ block: # object basic methods
   block: # it should convert an object to a string
     var obj = makeObj(1)
     # Should be "obj: (foo: 1)" or similar.
-    check($obj == "(foo: 1)")
+    doAssert($obj == "(foo: 1)")
   block: # it should test equality based on fields
-    check(makeObj(1) == makeObj(1))
+    doAssert(makeObj(1) == makeObj(1))
 
 # bug #10203
 
@@ -58,3 +57,18 @@ block: # bug #14698
     x1: int
     x3: seq[int]
   doAssert t[].sizeof == Foo1.sizeof
+
+# bug #147
+type
+  TValue* {.pure, final.} = object of RootObj
+    a: int
+  PValue = ref TValue
+  PPValue = ptr PValue
+
+
+var x: PValue
+new x
+var sp: PPValue = addr x
+
+sp.a = 2
+doAssert sp.a == 2
diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim
new file mode 100644
index 000000000..0cd05e4f3
--- /dev/null
+++ b/tests/objects/tobject_default_value.nim
@@ -0,0 +1,780 @@
+discard """
+  matrix: "-d:nimPreviewRangeDefault --mm:refc; -d:nimPreviewRangeDefault --warningAsError:ProveInit --mm:orc"
+  targets: "c cpp js"
+"""
+
+import std/[times, macros, tables]
+
+type
+  Guess = object
+    poi: DateTime
+
+  GuessDistinct = distinct Guess
+
+block:
+  var x: Guess
+  discard Guess()
+
+  var y: GuessDistinct
+
+  discard y
+
+  discard GuessDistinct(x)
+
+
+import mobject_default_value
+
+block:
+  let x = Default()
+  doAssert x.se == 0'i32
+
+block:
+  let x = default(Default)
+  doAssert x.se == 0'i32
+# echo Default(poi: 12)
+# echo Default(poi: 17)
+
+# echo NonDefault(poi: 77)
+
+block:
+  var x: Default
+  doAssert x.se == 0'i32
+
+type
+  ObjectBase = object of RootObj
+    value = 12
+
+  ObjectBaseDistinct = distinct ObjectBase
+
+  DinstinctInObject = object
+    data: ObjectBaseDistinct
+
+  Object = object of ObjectBase
+    time: float = 1.2
+    date: int
+    scale: range[1..10]
+
+  Object2 = object
+    name: Object
+
+  Object3 = object
+    obj: Object2
+
+  ObjectTuple = tuple
+    base: ObjectBase
+    typ: int
+    obj: Object
+
+  TupleInObject = object
+    size = 777
+    data: ObjectTuple
+
+  Ref = ref object of ObjectBase
+
+  RefInt = ref object of Ref
+    data = 73
+
+  Ref2 = ref object of ObjectBase
+
+  RefInt2 = ref object of Ref
+    data = 73
+
+var t {.threadvar.}: Default
+# var m1, m2 {.threadvar.}: Default
+
+block:
+  doAssert t.se == 0'i32
+
+block: # ARC/ORC cannot bind destructors twice, so it cannot
+      # be moved into main
+  block:
+    var x: Ref
+    new(x)
+    doAssert x.value == 12, "Ref.value = " & $x.value
+
+    var y: RefInt
+    new(y)
+    doAssert y.value == 12
+    doAssert y.data == 73
+
+  block:
+    var x: Ref2
+    new(x, proc (x: Ref2) {.nimcall.} = discard "call Ref")
+    doAssert x.value == 12, "Ref.value = " & $x.value
+
+    proc call(x: RefInt2) =
+      discard "call RefInt"
+
+    var y: RefInt2
+    new(y, call)
+    doAssert y.value == 12
+    doAssert y.data == 73
+
+template main {.dirty.} =
+  block: # bug #16744
+    type
+      R = range[1..10]
+      Obj = object
+        r: R
+
+    var
+      rVal: R = default(R) # Works fine
+      objVal = default(Obj)
+
+    doAssert rVal == 1
+    doAssert objVal.r == 1
+
+  block: # bug #16744
+    type
+      R = range[1..10]
+      Obj = object
+        r: R
+
+    var
+      rVal: R = default(R) # Works fine
+      objVal = Obj()
+
+    doAssert rVal == 1 # it should be 1
+    doAssert objVal.r == 1
+
+  block: # bug #3608
+    type
+      abc = ref object
+        w: range[2..100]
+
+    proc createABC(): abc =
+      new(result)
+      result.w = 20
+
+    doAssert createABC().w == 20
+
+  block:
+    var x = new ObjectBase
+    doAssert x.value == 12
+
+    proc hello(): ref ObjectBase =
+      new result
+
+    let z = hello()
+    doAssert z.value == 12
+
+  block:
+    var base = ObjectBase()
+    var x: ObjectBaseDistinct = ObjectBaseDistinct(base)
+    doAssert ObjectBase(x).value == 12
+    let y = ObjectBaseDistinct(default(ObjectBase))
+    doAssert ObjectBase(y).value == 12
+
+    let m = ObjectBaseDistinct(ObjectBase())
+    doAssert ObjectBase(m).value == 12
+
+    proc hello(): ObjectBaseDistinct =
+      result = ObjectBaseDistinct(default(ObjectBase))
+
+    let z = hello()
+    doAssert ObjectBase(z).value == 12
+
+  block:
+    var x: DinstinctInObject
+    x.data = ObjectBaseDistinct(default(ObjectBase))
+
+    doAssert ObjectBase(x.data).value == 12
+
+  block:
+    var x = Object()
+    doAssert x.value == 12
+    doAssert x.time == 1.2
+    doAssert x.scale == 1
+
+    let y = default(Object)
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+    var x1, x2, x3 = default(Object)
+    doAssert x1.value == 12
+    doAssert x1.time == 1.2
+    doAssert x1.scale == 1
+    doAssert x2.value == 12
+    doAssert x2.time == 1.2
+    doAssert x2.scale == 1
+    doAssert x3.value == 12
+    doAssert x3.time == 1.2
+    doAssert x3.scale == 1
+
+  block:
+    var x = new Object
+    doAssert x[] == default(Object)
+
+  block:
+    var x = default(Object2)
+    doAssert x.name.value == 12
+    doAssert x.name.time == 1.2
+    doAssert x.name.scale == 1
+
+  block:
+    let x = Object2()
+    doAssert x.name.value == 12
+    doAssert x.name.time == 1.2
+    doAssert x.name.scale == 1
+
+  block:
+    var x: ref Object2
+    new x
+    doAssert x[] == default(Object2)
+
+  block:
+    var x = default(Object3)
+    doAssert x.obj.name.value == 12
+    doAssert x.obj.name.time == 1.2
+    doAssert x.obj.name.scale == 1
+
+  block:
+    var x = Object3()
+    doAssert x.obj.name.value == 12
+    doAssert x.obj.name.time == 1.2
+    doAssert x.obj.name.scale == 1
+
+  when nimvm:
+    # todo
+    discard "fixme"
+  else:
+    when defined(gcArc) or defined(gcOrc):
+      block: #seq
+        var x = newSeq[Object](10)
+        let y = x[0]
+        doAssert y.value == 12
+        doAssert y.time == 1.2
+        doAssert y.scale == 1
+
+      block:
+        var x: seq[Object]
+        setLen(x, 5)
+        let y = x[^1]
+        doAssert y.value == 12
+        doAssert y.time == 1.2
+        doAssert y.scale == 1
+
+      block:
+        var my = @[1, 2, 3, 4, 5]
+        my.setLen(0)
+        my.setLen(5)
+        doAssert my == @[0, 0, 0, 0, 0]
+
+      block:
+        var my = "hello"
+        my.setLen(0)
+        my.setLen(5)
+        doAssert $(@my) == """@['\x00', '\x00', '\x00', '\x00', '\x00']"""
+
+  block: # array
+    var x: array[10, Object] = default(array[10, Object])
+    let y = x[0]
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+  block: # array
+    var x {.noinit.}: array[10, Object]
+    discard x
+
+  block: # tuple
+    var x = default(ObjectTuple)
+    doAssert x.base.value == 12
+    doAssert x.typ == 0
+    doAssert x.obj.time == 1.2
+    doAssert x.obj.date == 0
+    doAssert x.obj.scale == 1
+    doAssert x.obj.value == 12
+
+  block: # tuple in object
+    var x = default(TupleInObject)
+    doAssert x.data.base.value == 12
+    doAssert x.data.typ == 0
+    doAssert x.data.obj.time == 1.2
+    doAssert x.data.obj.date == 0
+    doAssert x.data.obj.scale == 1
+    doAssert x.data.obj.value == 12
+    doAssert x.size == 777
+
+  type
+    ObjectArray = object
+      data: array[10, Object]
+
+  block:
+    var x = default(ObjectArray)
+    let y = x.data[0]
+    doAssert y.value == 12
+    doAssert y.time == 1.2
+    doAssert y.scale == 1
+
+  block:
+    var x: PrellDeque[int]
+    doAssert x.pendingTasks == 0
+
+  type
+    Color = enum
+      Red, Blue, Yellow
+
+    ObjectVarint = object
+      case kind: Color
+      of Red:
+        data: int = 10
+      of Blue:
+        fill = "123"
+      of Yellow:
+        time = 1.8'f32
+
+    ObjectVarint1 = object
+      case kind: Color = Blue
+      of Red:
+        data1: int = 10
+      of Blue:
+        fill2 = "123"
+        cry: float
+      of Yellow:
+        time3 = 1.8'f32
+        him: int
+
+  block:
+    var x = ObjectVarint(kind: Red)
+    doAssert x.kind == Red
+    doAssert x.data == 10
+
+  block:
+    var x = ObjectVarint(kind: Blue)
+    doAssert x.kind == Blue
+    doAssert x.fill == "123"
+
+  block:
+    var x = ObjectVarint(kind: Yellow)
+    doAssert x.kind == Yellow
+    doAssert typeof(x.time) is float32
+
+  block:
+    var x = default(ObjectVarint1)
+    doAssert x.kind == Blue
+    doAssert x.fill2 == "123"
+    x.cry = 326
+
+  type
+    ObjectVarint2 = object
+      case kind: Color
+      of Red:
+        data: int = 10
+      of Blue:
+        fill = "123"
+      of Yellow:
+        time = 1.8'f32
+
+  block:
+    var x = ObjectVarint2(kind: Blue)
+    doAssert x.fill == "123"
+
+  block:
+    type
+      Color = enum
+        Red, Blue, Yellow
+  
+    type
+      ObjectVarint3 = object
+        case kind: Color = Blue
+        of Red:
+          data1: int = 10
+        of Blue:
+          case name: Color = Blue
+          of Blue:
+            go = 12
+          else:
+            temp = 66
+          fill2 = "123"
+          cry: float
+        of Yellow:
+          time3 = 1.8'f32
+          him: int
+
+    block:
+      var x = default(ObjectVarint3)
+      doAssert x.kind == Blue
+      doAssert x.name == Blue
+      doAssert x.go == 12
+
+    block:
+      var x = ObjectVarint3(kind: Blue, name: Red, temp: 99)
+      doAssert x.kind == Blue
+      doAssert x.name == Red
+      doAssert x.temp == 99
+
+  block:
+    type
+      Default = tuple
+        id: int = 1
+        obj: ObjectBase
+        name: string
+
+      Class = object
+        def: Default
+
+      Member = object
+        def: Default = (id: 777, obj: ObjectBase(), name: "fine")
+
+    block:
+      var x = default(Default)
+      doAssert x.id == 1
+      doAssert x.obj == default(ObjectBase)
+      doAssert x.name == ""
+
+    block:
+      var x = default(Class)
+      doAssert x.def == default(Default)
+      doAssert x.def.id == 1
+      doAssert x.def.obj == default(ObjectBase)
+      doAssert x.def.name == ""
+
+    block:
+      var x = default(Member)
+      doAssert x.def.id == 777
+      doAssert x.def.obj == default(ObjectBase)
+      doAssert x.def.name == "fine"
+
+  block:
+    var x {.noinit.} = 12
+    doAssert x == 12
+
+    type
+      Pure = object
+        id: int = 12
+
+    var y {.noinit.}: Pure
+    doAssert y.id == 0
+
+    var z {.noinit.}: Pure = Pure(id: 77)
+    doAssert z.id == 77
+
+  block: # bug #20681
+    type A = object
+      d: DateTime = DateTime()
+
+    let x = default(A)
+    doAssert $x == "(d: Uninitialized DateTime)"
+
+  block: # bug #20715
+    block:
+      type
+        Foo = enum
+          A
+          B
+
+        Bar = object
+          case foo: Foo
+          of A:
+            t: range[-1..2]
+          else: discard
+
+      var d = default(Bar)
+      doAssert d.t == -1
+
+    block:
+      type
+        Foo = enum
+          A
+          B
+
+        Bar = object
+          case foo: Foo
+          of A:
+            t: range[0..2]
+          else: discard
+
+      var d = default(Bar)
+      doAssert d.t == 0
+
+    block: # bug #20740
+      block:
+        proc foo(x: static DateTime = Datetime()) =
+          discard
+
+        foo()
+
+      block:
+        macro foo(x: static DateTime) =
+          discard x
+
+        macro foo2: untyped =
+          var x = DateTime()
+
+          result = quote do:
+            foo(`x`)
+
+        foo2()
+
+
+  block: # issue #20699
+    type
+      Either[A,B] = object
+        case kind:bool
+        of false:
+          b: B
+        of true:
+            a: A
+      O = object of RootRef
+
+    proc oToEither(o:O):Either[O,void] =
+      Either[O,void](kind:true,a: o)
+
+    discard oToEither(O())
+
+  block: # bug #20695
+    type
+      Default = object
+        tabs: Table[string, int] = initTable[string, int]()
+
+    let d = default(Default)
+    doAssert d.tabs.len == 0
+
+  block:
+    type
+      Default = object
+        tabs: Table[string, int] = Table[string, int]()
+
+    let d = default(Default)
+    doAssert d.tabs.len == 0
+
+
+  block:
+    type DjangoDateTime = distinct DateTime
+
+    type Default = object
+      data: DjangoDateTime = DjangoDateTime(DateTime())
+
+    let x = default(Default)
+    doAssert x.data is DjangoDateTime
+
+  block:
+    type DjangoDateTime = distinct DateTime
+
+    type Default = object
+      data = DjangoDateTime(DateTime())
+
+    let x = default(Default)
+    doAssert x.data is DjangoDateTime
+
+  block:
+    type
+      Result2 = object
+        case o: bool
+        of false:
+          e: float
+        of true:
+          v {.requiresInit.} : int = 1
+
+    proc startSessionSync(): Result2 =
+      return Result2(o: true)
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.v == 1
+
+    mainSync()
+
+  block:
+    type
+      Result2 = object
+        v {.requiresInit.} : int = 1
+
+    proc startSessionSync(): Result2 =
+      return Result2()
+
+    proc mainSync =
+      let ff = startSessionSync()
+      doAssert ff.v == 1
+
+    mainSync()
+
+  block: # bug #21801
+    func evaluate(i: int): float =
+      0.0
+
+    func evaluate(): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation: proc(): float = evaluate
+
+  block:
+    func evaluate(): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation: proc(): float = evaluate
+
+  block:
+    func evaluate(i: int): float =
+      0.0
+
+    type SearchOptions = object
+        evaluation = evaluate
+  block:
+    type
+      Result[T, E] = object
+        when T is void:
+          when E is void:
+            oResultPrivate: bool
+          else:
+            case oResultPrivate: bool
+            of false:
+              eResultPrivate: E
+            of true:
+              discard
+        else:
+          when E is void:
+            case oResultPrivate: bool
+            of false:
+              discard
+            of true:
+              vResultPrivate: T
+          else:
+            case oResultPrivate: bool
+            of false:
+              eResultPrivate: E
+            of true:
+              vResultPrivate: T
+
+
+    template `?`[T, E](self: Result[T, E]): auto =
+      let v = (self)
+      if not v.oResultPrivate:
+        when compiles(`assignResult?`(default(typeof(result)))):
+          when typeof(result) is typeof(v):
+            `assignResult?`(v)
+          elif E is void:
+            `assignResult?`(err(typeof(result)))
+          else:
+            `assignResult?`(err(typeof(result), v.eResultPrivate))
+          return
+        else:
+          return
+            when typeof(result) is typeof(v):
+              v
+            elif E is void:
+              err(typeof(result))
+            else:
+              err(typeof(result), v.eResultPrivate)
+
+      when not(T is void):
+        v.vResultPrivate
+        
+    type R = Result[int, string]
+
+    proc testAssignResult() =
+      var assigned: bool
+      template `assignResult?`(v: Result) =
+        assigned = true
+        result = v
+
+      proc failed(): Result[int, string] =
+        discard
+
+      proc calling(): Result[int, string] =
+        let _ = ? failed()
+        doAssert false
+
+      let r = calling()
+      doAssert assigned
+
+    when nimvm:
+      when not defined(js):
+        testAssignResult()
+    else:
+      testAssignResult()
+
+  block: # bug #22123
+    type Thing = object
+      x: float32 = 1
+
+    type ThingWithArray = object
+        arr: array[256, float32]
+        n: float32 = 1
+
+    type Container = ref object
+        thing: array[5, Thing]
+        thing_with_array: array[5, ThingWithArray]
+
+    var foo = new Container
+    doAssert int(foo.thing[0].x) == 1
+
+  block: # bug #22613
+    type
+      K = enum
+        A = "a"
+        B = "b"
+      T = object
+        case kind: K = B
+        of A:
+          a: int
+        of B:
+          b: float
+
+    doAssert T().kind == B
+
+  block: # bug #22926
+    type
+      Direction = enum
+        North
+        South
+        East
+        West
+
+      ArrayObj1 = object
+        list: array[Direction, int]
+
+      ArrayObj2 = object
+        list: array[Direction, int] = [1, 2, 3, 4]
+
+    block:
+      var a: ArrayObj1
+      doAssert a.list[West] == 0
+      var b = default ArrayObj1
+      doAssert b.list[North] == 0
+
+
+    block:
+      var a: ArrayObj2
+      doAssert a.list[West] == 0
+      var b = default ArrayObj2
+      doAssert b.list[North] == 1
+
+  block:
+    type limited_float = range[1.2..20.0]
+    doAssert default(limited_float) == 1.2
+
+
+  block:
+    type
+      range1 = range[1..10]
+      range2 = range[-1..10]
+
+    proc foo =
+      doAssert default(range1) == 1
+      doAssert default(range2) == -1
+
+      let s = default(array[5, range1])
+      doAssert s == [range1 1, 1, 1, 1, 1]
+
+    foo()
+
+  block:
+    type
+      Object = object
+        id: range[1.2..29.3]
+
+    var s = default(Object)
+    doAssert s.id == 1.2
+
+  block: # bug #23943
+    type limited_int = range[1..20]
+    var d: limited_int;
+    doAssert d == 1
+
+static: main()
+main()
diff --git a/tests/objects/tobjects_various.nim b/tests/objects/tobjects_various.nim
index 8ec090f42..55db9312e 100644
--- a/tests/objects/tobjects_various.nim
+++ b/tests/objects/tobjects_various.nim
@@ -105,3 +105,16 @@ block t7244:
 
   proc test(foo: var Foo) = discard
   proc test(bar: var Bar) = test(Foo(bar))
+
+
+import std/macros
+
+#bug #20856
+macro ensureImplWorksOnConstr(t: typed): untyped =
+  expectKind(t, nnkObjConstr)
+  doAssert t[0].getTypeInst.getImpl.repr == "A = object"
+  doAssert t[0].getImpl.repr == "A = object"
+
+type A = object
+
+ensureImplWorksOnConstr(A())
diff --git a/tests/objects/trequireinit.nim b/tests/objects/trequireinit.nim
new file mode 100644
index 000000000..202667b02
--- /dev/null
+++ b/tests/objects/trequireinit.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "The MPlayerObj type doesn't have a default value. The following fields must be initialized: foo."
+"""
+
+type
+  MPlayerObj* {.requiresInit.} = object
+    foo: range[5..10] = 5
+
+var a: MPlayerObj
+echo a.foo
\ No newline at end of file
diff --git a/tests/objects/tunsafenew.nim b/tests/objects/tunsafenew.nim
new file mode 100644
index 000000000..6c1b33cd9
--- /dev/null
+++ b/tests/objects/tunsafenew.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "conversion from int literal(-1) to Natural is invalid"
+"""
+
+type
+  Obj = object
+    case b: bool
+    else: discard
+var o: ref Obj
+unsafeNew(o, -1)
\ No newline at end of file
diff --git a/tests/objects/tunsafenew2.nim b/tests/objects/tunsafenew2.nim
new file mode 100644
index 000000000..83112bcfc
--- /dev/null
+++ b/tests/objects/tunsafenew2.nim
@@ -0,0 +1,15 @@
+discard """
+valgrind: "leaks"
+matrix: "-d:useMalloc"
+targets: "c cpp"
+"""
+
+type
+  Obj = object
+    case b: bool
+    else: discard
+    a: UncheckedArray[byte]
+
+var o: ref Obj
+unsafeNew(o, sizeof(Obj) + 512)
+zeroMem(addr o.a, 512)
diff --git a/tests/objects/twhen1.nim b/tests/objects/twhen1.nim
index 5b8eea3f4..fe072a46b 100644
--- a/tests/objects/twhen1.nim
+++ b/tests/objects/twhen1.nim
@@ -55,3 +55,35 @@ type
     x: int
     when (NimMajor, NimMinor) >= (1, 1):
       y: int
+discard MyObject(x: 100, y: 200)
+
+block: # Ensure when evaluates properly in objects
+  type X[bits: static int] = object #22474
+    when bits >= 256:
+     data32: byte
+    else:
+     data16: byte
+
+  static:
+    discard X[255]().data16
+    discard X[256]().data32
+
+
+  type ComplexExprObject[S: static string, I: static int, Y: static auto] = object
+    when 'h' in S and I < 10 and Y isnot float:
+      a: int
+    elif I > 30:
+      b: int
+    elif typeof(Y) is float:
+      c: int
+    else:
+      d: int
+
+  static:
+    discard ComplexExprObject["hello", 9, 300i32]().a
+    discard ComplexExprObject["", 40, 30f]().b
+    discard ComplexExprObject["", 20, float 30]().c
+    discard ComplexExprObject["", 20, ""]().d
+
+
+
diff --git a/tests/objvariant/tconstobjvariant.nim b/tests/objvariant/tconstobjvariant.nim
new file mode 100644
index 000000000..45a647707
--- /dev/null
+++ b/tests/objvariant/tconstobjvariant.nim
@@ -0,0 +1,18 @@
+# This is a sample code, the first echo statement prints out the error
+type
+  A = object
+    case w: uint8
+    of 1:
+      n: int
+    else:
+      other: string
+
+const
+  a = A(w: 1, n: 5)
+
+proc foo =
+
+  let c = [a]
+  doAssert c[0].n == 5
+
+foo()
\ No newline at end of file
diff --git a/tests/openarray/topenarray.nim b/tests/openarray/topenarray.nim
index aec5a4cbf..25b983651 100644
--- a/tests/openarray/topenarray.nim
+++ b/tests/openarray/topenarray.nim
@@ -48,6 +48,41 @@ proc main =
       doAssert testing(mySeq) == mySeq
       doAssert testing(mySeq[2..^2]) == mySeq[2..^2]
 
+  block: # bug #23321
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      var d = new array[1, int]
+      foo d[].toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): var array[1, int] =
+        result = x
+      var d: array[1, int]
+      foo task(d).toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): lent array[1, int] =
+        result = x
+      var d: array[1, int]
+      foo task(d).toOpenArray(0, 0)
+
+    block:
+      proc foo(x: openArray[int]) =
+        doAssert x[0] == 0
+
+      proc task(x: var array[1, int]): ptr array[1, int] =
+        result = addr x
+      var d: array[1, int]
+      foo task(d)[].toOpenArray(0, 0)
+
 
 main()
-# static: main() # xxx bug #15952: Error: cannot generate code for: mSlice
+static: main()
diff --git a/tests/openarray/tuncheckedarray.nim b/tests/openarray/tuncheckedarray.nim
new file mode 100644
index 000000000..c8ca9d2d4
--- /dev/null
+++ b/tests/openarray/tuncheckedarray.nim
@@ -0,0 +1,19 @@
+discard """
+  exitcode: 0
+  targets: "c cpp"
+"""
+
+proc main =
+  block: # issue 19171
+    var a = ['A']
+    proc mutB(x: var openArray[char]) =
+      x[0] = 'B'
+    mutB(toOpenArray(cast[ptr UncheckedArray[char]](addr a), 0, 0))
+    doAssert a[0] == 'B'
+    proc mutC(x: var openArray[char]; c: char) =
+      x[0] = c
+    let p = cast[ptr UncheckedArray[char]](addr a)
+    mutC(toOpenArray(p, 0, 0), 'C')
+    doAssert p[0] == 'C'
+
+main()
diff --git a/tests/options/tnimbasepattern.nim b/tests/options/tnimbasepattern.nim
new file mode 100644
index 000000000..1237af5c5
--- /dev/null
+++ b/tests/options/tnimbasepattern.nim
@@ -0,0 +1,26 @@
+discard """
+  cmd: "nim cpp --nimbasepattern:test.h --cincludes:./tests/options $file "
+  output:'''
+(a: 1)
+'''
+"""
+const header = """
+#pragma once
+#include "nimbase.h"
+struct Foo {
+  int a;
+};
+"""
+
+import os
+static:
+  const dir = "./tests/options/"
+  createDir(dir)
+  writeFile(dir / "test.h", header)
+
+type 
+  Foo {.importc.} = object
+    a: int32 = 1
+  
+
+echo $Foo()
\ No newline at end of file
diff --git a/tests/osproc/tnoexe.nim b/tests/osproc/tnoexe.nim
new file mode 100644
index 000000000..19a3cca67
--- /dev/null
+++ b/tests/osproc/tnoexe.nim
@@ -0,0 +1,27 @@
+discard """
+  output: '''true
+true'''
+"""
+
+import std/osproc
+
+const command = "lsaaa -lah"
+
+try:
+  let process = startProcess(command, options = {poUsePath})
+  discard process.waitForExit()
+except OSError as e:
+  echo e.errorCode != 0
+
+# `poEvalCommand`, invokes the system shell to run the specified command
+try:
+  let process = startProcess(command, options = {poUsePath, poEvalCommand})
+  # linux
+  let exitCode = process.waitForExit()
+  echo exitCode != 0
+except OSError as e:
+  # Because the implementation of `poEvalCommand` on different platforms is inconsistent, 
+  # Linux will not throw an exception, but Windows will throw an exception
+
+  # windows
+  echo e.errorCode != 0
diff --git a/tests/osproc/twaitforexit.nim b/tests/osproc/twaitforexit.nim
new file mode 100644
index 000000000..535faca63
--- /dev/null
+++ b/tests/osproc/twaitforexit.nim
@@ -0,0 +1,38 @@
+import std/[osproc, os, times]
+
+block: # bug #5091
+    when defined(linux):
+        const filename = "false"
+        var p = startProcess(filename, options = {poStdErrToStdOut, poUsePath})
+        os.sleep(1000) # make sure process has exited already
+
+        let atStart = getTime()
+        const msWait = 2000
+
+        try:
+            discard waitForExit(p, msWait)
+        except OSError:
+            discard
+
+        # check that we don't have to wait msWait milliseconds
+        doAssert(getTime() <  atStart + milliseconds(msWait))
+
+block: # bug #23825
+
+    # the sleep command might not be available in all Windows installations
+
+    when defined(linux):
+
+        var thr: array[0..99, Thread[int]]
+
+        proc threadFunc(i: int) {.thread.} =
+            let sleepTime = float(i) / float(thr.len + 1)
+            doAssert sleepTime < 1.0
+            let p = startProcess("sleep", workingDir = "", args = @[$sleepTime], options = {poUsePath, poParentStreams})
+            # timeout = 1_000_000 seconds ~= 278 hours ~= 11.5 days
+            doAssert p.waitForExit(timeout=1_000_000_000) == 0
+
+        for i in low(thr)..high(thr):
+            createThread(thr[i], threadFunc, i)
+
+        joinThreads(thr) 
diff --git a/tests/overflw/tdistinct_range.nim b/tests/overflow/tdistinct_range.nim
index f53515d45..f53515d45 100644
--- a/tests/overflw/tdistinct_range.nim
+++ b/tests/overflow/tdistinct_range.nim
diff --git a/tests/overflow/toverflow.nim b/tests/overflow/toverflow.nim
new file mode 100644
index 000000000..972f929c6
--- /dev/null
+++ b/tests/overflow/toverflow.nim
@@ -0,0 +1,82 @@
+discard """
+  output: "ok"
+  matrix: "--overflowChecks:off; --overflowChecks:off --b:js"
+"""
+# Tests nim's ability to detect overflows
+
+{.push overflowChecks: on.}
+
+var
+  a = high(int)
+  b = -2
+  overflowDetected = false
+
+try:
+  echo(b - a)
+except OverflowDefect:
+  overflowDetected = true
+
+{.pop.} # overflow check
+
+doAssert(overflowDetected)
+
+block: # Overflow checks in a proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks in a forward declared proc
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  proc foo()
+
+  {.push overflowChecks: on.}
+  proc foo() =
+    let c = b - a
+  {.pop.}
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(overflowDetected)
+
+block: # Overflow checks doesn't affect fwd declaration
+  var
+    a = high(int)
+    b = -2
+    overflowDetected = false
+
+  {.push overflowChecks: on.}
+  proc foo()
+  {.pop.}
+
+  proc foo() =
+    let c = b - a
+
+  try:
+    foo()
+  except OverflowDefect:
+    overflowDetected = true
+
+  doAssert(not overflowDetected)
+
+
+echo "ok"
diff --git a/tests/overflw/toverflw2.nim b/tests/overflow/toverflow2.nim
index c06b35c6d..c06b35c6d 100644
--- a/tests/overflw/toverflw2.nim
+++ b/tests/overflow/toverflow2.nim
diff --git a/tests/overflw/toverflw.nim b/tests/overflow/toverflow_reorder.nim
index 164e16e5c..fcf7b0c82 100644
--- a/tests/overflw/toverflw.nim
+++ b/tests/overflow/toverflow_reorder.nim
@@ -1,3 +1,5 @@
+{.experimental: "codeReordering".}
+
 discard """
   output: "ok"
   cmd: "nim $target --overflowChecks:off $options $file"
@@ -12,7 +14,7 @@ var
   overflowDetected = false
 
 try:
-  writeLine(stdout, b - a)
+  echo b - a
 except OverflowDefect:
   overflowDetected = true
 
diff --git a/tests/overflw/tovfint.nim b/tests/overflow/tovfint.nim
index 5c440a540..5c440a540 100644
--- a/tests/overflw/tovfint.nim
+++ b/tests/overflow/tovfint.nim
diff --git a/tests/misc/trangechecks.nim b/tests/overflow/trangechecks.nim
index e48b1272b..e48b1272b 100644
--- a/tests/misc/trangechecks.nim
+++ b/tests/overflow/trangechecks.nim
diff --git a/tests/overflow/twronginference.nim b/tests/overflow/twronginference.nim
new file mode 100644
index 000000000..34a982976
--- /dev/null
+++ b/tests/overflow/twronginference.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "cannot convert 256 to int8"
+  line: 9
+"""
+
+# issue #23177
+
+var x: int8
+x = 256
+echo x # 0
diff --git a/tests/overload/issue22142/tfail_implicit_ambiguous.nim b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
new file mode 100644
index 000000000..2586e0877
--- /dev/null
+++ b/tests/overload/issue22142/tfail_implicit_ambiguous.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+type
+  A[T] = object
+  C = object
+
+proc test[T: A](param: T): bool = false
+proc test(param: A): bool = true
+doAssert test(A[C]()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_nested_pointers.nim b/tests/overload/issue22142/tfail_nested_pointers.nim
new file mode 100644
index 000000000..1603d98cb
--- /dev/null
+++ b/tests/overload/issue22142/tfail_nested_pointers.nim
@@ -0,0 +1,12 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type
+  A[T] = object
+  C = object
+    x:int
+proc p[T: A[ptr]](x:ptr[T]):bool = false
+proc p(x: ptr[A[ptr]]):bool = true
+var a: A[ptr[C]]
+doAssert p(a.addr) == true
diff --git a/tests/overload/issue22142/tfail_object_is_generic.nim b/tests/overload/issue22142/tfail_object_is_generic.nim
new file mode 100644
index 000000000..b46795bd5
--- /dev/null
+++ b/tests/overload/issue22142/tfail_object_is_generic.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+#[
+As of the time of writing `object` needs some special
+treament in order to be considered "generic" in the right
+context when used implicitly
+]#
+
+type
+  C = object
+
+proc test[T: object](param: T): bool = false
+proc test(param: object): bool = true  
+doAssert test(C()) == true  # previously would pass
diff --git a/tests/overload/issue22142/tfail_typeclass_var_invar.nim b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
new file mode 100644
index 000000000..07db65fef
--- /dev/null
+++ b/tests/overload/issue22142/tfail_typeclass_var_invar.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "ambiguous call"
+"""
+
+type C = object
+proc test[T: ptr](param: var T): bool = false
+proc test(param: var ptr): bool = true
+var d: ptr[C]
+doAssert test(d) == true  # previously would pass
diff --git a/tests/overload/issue22142/tissue22142_shouldpass.nim b/tests/overload/issue22142/tissue22142_shouldpass.nim
new file mode 100644
index 000000000..90d4efe51
--- /dev/null
+++ b/tests/overload/issue22142/tissue22142_shouldpass.nim
@@ -0,0 +1,68 @@
+type
+  A[T] = object of RootObj
+  B[T] = object
+  C = object
+    x:int
+
+# change (previously true)
+block:
+  proc test[J;H: A[J];T: B[H]](param: T): bool = false
+  proc test[T](param: B[T]): bool = true
+  doAssert test(B[A[int]]()) == false
+block:  # object is more specific then `T`
+  proc p[H:object;T:ptr[H]](param:T):bool = false
+  proc p[T](param:ptr[T]):bool= true
+  var l: ptr[C]
+  doAssert p(l) == false
+block:
+  proc p[T:A[object]](param:T):bool = false
+  proc p[T](param: A[T]):bool= true
+  doAssert p(A[C]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: A): bool = true
+  doAssert test(A[C]()) == false
+
+# change (previously ambiguous)
+block:
+  proc p[T](a: A[T]): bool = false
+  proc p[T: object](a: T): bool = true
+  doAssert p(A[int]()) == false
+block:  # A is more specific than `object`
+  proc test[T: A](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[T: A](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[int]()) == false
+block:
+  proc test[H;T: A[H]](param: T): bool = false
+  proc test(param: object): bool = true
+  doAssert test(A[C]()) == false
+block:
+  proc test[H;T: A[B[H]]](param: T): bool = false
+  proc test[T: object](param: T): bool = true
+  doAssert test(A[B[int]]()) == false
+block:
+  #[
+  This was referenced in the nim compiler source (`sumGeneric`) as a case
+  that was supposed to not be ambiguous, yet it was
+  ]#
+  proc test[J;H:A[J]; T: A[H]](param: T): bool = false
+  proc test[H;T: A[H]](param: T): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test[T](param: A[T]): bool = true
+  doAssert test(A[A[C]]()) == false
+block:
+  proc test[T](param: A[T]): bool = false
+  proc test[T: object](param: A[T]): bool = true
+  doAssert test(A[C]()) == true
+
+
+block: #anti-regression (object is more specific then `T`)
+  proc test[J;T:A[J]](param: A[T]): bool = false
+  proc test(param: A[A[object]]): bool = true
+  doAssert test(A[A[C]]()) == true
\ No newline at end of file
diff --git a/tests/overload/m19737.nim b/tests/overload/m19737.nim
new file mode 100644
index 000000000..7f7ac98e2
--- /dev/null
+++ b/tests/overload/m19737.nim
@@ -0,0 +1,10 @@
+type
+  UInt128* = object
+    lo, hi: uint64
+
+func `<`*(x, y: UInt128): bool =
+  (x.hi < y.hi) or ((x.hi == y.hi) and (x.lo < y.lo))
+
+when not defined(works):
+  func `>`*(x, y: UInt128): bool =
+    (x.hi > y.hi) or ((x.hi == y.hi) and (x.lo > y.lo))
diff --git a/tests/overload/mvaruintconv.nim b/tests/overload/mvaruintconv.nim
new file mode 100644
index 000000000..b889c90cf
--- /dev/null
+++ b/tests/overload/mvaruintconv.nim
@@ -0,0 +1,145 @@
+import
+  std/[macros, tables, hashes]
+
+export
+  macros
+
+type
+  FieldDescription* = object
+    name*: NimNode
+    isPublic*: bool
+    isDiscriminator*: bool
+    typ*: NimNode
+    pragmas*: NimNode
+    caseField*: NimNode
+    caseBranch*: NimNode
+
+{.push raises: [].}
+
+func isTuple*(t: NimNode): bool =
+  t.kind == nnkBracketExpr and t[0].kind == nnkSym and eqIdent(t[0], "tuple")
+
+macro isTuple*(T: type): untyped =
+  newLit(isTuple(getType(T)[1]))
+
+proc collectFieldsFromRecList(result: var seq[FieldDescription],
+                              n: NimNode,
+                              parentCaseField: NimNode = nil,
+                              parentCaseBranch: NimNode = nil,
+                              isDiscriminator = false) =
+  case n.kind
+  of nnkRecList:
+    for entry in n:
+      collectFieldsFromRecList result, entry,
+                               parentCaseField, parentCaseBranch
+  of nnkRecWhen:
+    for branch in n:
+      case branch.kind:
+      of nnkElifBranch:
+        collectFieldsFromRecList result, branch[1],
+                                 parentCaseField, parentCaseBranch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0],
+                                 parentCaseField, parentCaseBranch
+      else:
+        doAssert false
+
+  of nnkRecCase:
+    collectFieldsFromRecList result, n[0],
+                             parentCaseField,
+                             parentCaseBranch,
+                             isDiscriminator = true
+
+    for i in 1 ..< n.len:
+      let branch = n[i]
+      case branch.kind
+      of nnkOfBranch:
+        collectFieldsFromRecList result, branch[^1], n[0], branch
+      of nnkElse:
+        collectFieldsFromRecList result, branch[0], n[0], branch
+      else:
+        doAssert false
+
+  of nnkIdentDefs:
+    let fieldType = n[^2]
+    for i in 0 ..< n.len - 2:
+      var field: FieldDescription
+      field.name = n[i]
+      field.typ = fieldType
+      field.caseField = parentCaseField
+      field.caseBranch = parentCaseBranch
+      field.isDiscriminator = isDiscriminator
+
+      if field.name.kind == nnkPragmaExpr:
+        field.pragmas = field.name[1]
+        field.name = field.name[0]
+
+      if field.name.kind == nnkPostfix:
+        field.isPublic = true
+        field.name = field.name[1]
+
+      result.add field
+
+  of nnkSym:
+    result.add FieldDescription(
+      name: n,
+      typ: getType(n),
+      caseField: parentCaseField,
+      caseBranch: parentCaseBranch,
+      isDiscriminator: isDiscriminator)
+
+  of nnkNilLit, nnkDiscardStmt, nnkCommentStmt, nnkEmpty:
+    discard
+
+  else:
+    doAssert false, "Unexpected nodes in recordFields:\n" & n.treeRepr
+
+proc collectFieldsInHierarchy(result: var seq[FieldDescription],
+                              objectType: NimNode) =
+  var objectType = objectType
+
+  objectType.expectKind {nnkObjectTy, nnkRefTy}
+
+  if objectType.kind == nnkRefTy:
+    objectType = objectType[0]
+
+  objectType.expectKind nnkObjectTy
+
+  var baseType = objectType[1]
+  if baseType.kind != nnkEmpty:
+    baseType.expectKind nnkOfInherit
+    baseType = baseType[0]
+    baseType.expectKind nnkSym
+    baseType = getImpl(baseType)
+    baseType.expectKind nnkTypeDef
+    baseType = baseType[2]
+    baseType.expectKind {nnkObjectTy, nnkRefTy}
+    collectFieldsInHierarchy result, baseType
+
+  let recList = objectType[2]
+  collectFieldsFromRecList result, recList
+
+proc recordFields*(typeImpl: NimNode): seq[FieldDescription] =
+  if typeImpl.isTuple:
+    for i in 1 ..< typeImpl.len:
+      result.add FieldDescription(typ: typeImpl[i], name: ident("Field" & $(i - 1)))
+    return
+
+  let objectType = case typeImpl.kind
+    of nnkObjectTy: typeImpl
+    of nnkTypeDef: typeImpl[2]
+    else:
+      macros.error("object type expected", typeImpl)
+      return
+
+  collectFieldsInHierarchy(result, objectType)
+
+macro field*(obj: typed, fieldName: static string): untyped =
+  newDotExpr(obj, ident fieldName)
+
+proc skipPragma*(n: NimNode): NimNode =
+  if n.kind == nnkPragmaExpr: n[0]
+  else: n
+
+
+{.pop.}
diff --git a/tests/overload/t19737.nim b/tests/overload/t19737.nim
new file mode 100644
index 000000000..b33ba9d8b
--- /dev/null
+++ b/tests/overload/t19737.nim
@@ -0,0 +1,15 @@
+# issue #19737
+
+import ./m19737
+
+var m: seq[uint64]
+
+proc foo(x: bool) = discard
+
+proc test[T: uint64|uint32](s: var seq[T]) =
+  var tmp = newSeq[T](1)
+  s = newSeq[T](1)
+
+  foo s[0] > tmp[0]
+
+test(m)
diff --git a/tests/overload/t23249.nim b/tests/overload/t23249.nim
new file mode 100644
index 000000000..f4657833b
--- /dev/null
+++ b/tests/overload/t23249.nim
@@ -0,0 +1,17 @@
+# issue #23249
+
+type Control* = object
+proc onAction*(c: Control, handler: proc(e: int) {.gcsafe.}) = discard
+proc onAction*(c: Control, handler: proc() {.gcsafe.}) = discard
+
+template setControlHandlerBlock(c: Control, p: untyped, a: untyped) =
+    when compiles(c.p(nil)):
+        c.p() do() {.gcsafe.}: a
+    else:
+        c.p = proc() {.gcsafe.} =
+            a
+
+proc mkLayout() =
+  var b: Control
+  setControlHandlerBlock(b, onAction):
+    echo "hi"
diff --git a/tests/overload/t23755.nim b/tests/overload/t23755.nim
new file mode 100644
index 000000000..de338a2ce
--- /dev/null
+++ b/tests/overload/t23755.nim
@@ -0,0 +1,62 @@
+type
+  BigInt[bits: static int] = object
+    limbs: array[8, uint64]
+
+block:
+  proc view[N](a: array[N, uint64]) =
+    discard
+
+  proc view[N](a: var array[N, uint64]) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+type Limbs[N: static int] = array[N, uint64]
+
+block:
+  proc view(a: Limbs) =
+    discard
+
+  proc view(a: var Limbs) =
+    discard
+
+  var r: BigInt[64]
+  r.limbs.view()
+
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc p[T](a: IntArray[T]): bool= true
+  proc p(a: IntArray[5]): bool= false
+
+  var s: IntArray[5]
+  doAssert s.p == false
+
+block:
+  type IntArray[N: static[int]] = array[N, int]
+
+  proc `$`(a: IntArray): string =
+    return "test"
+
+  var s: IntArray[5] = [1,1,1,1,1]
+  doAssert `$`(s) == "test"
+
+block: 
+  proc p[n:static[int]](a: array[n, char]):bool=true
+  proc p[T, IDX](a: array[IDX, T]):bool=false
+
+  var g: array[32, char]
+  doAssert p(g)
+
+block:  # issue #23823
+  func p[N,T](a, b: array[N,T]) =
+    discard
+
+  func p[N: static int; T](x, y: array[N, T]) =
+    discard
+
+  var a: array[5, int]
+  p(a,a)
diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim
index fe0dbf2bf..85d87f136 100644
--- a/tests/overload/t8829.nim
+++ b/tests/overload/t8829.nim
@@ -2,7 +2,7 @@ block:
   let txt = "Hello World"
 
   template `[]`[T](p: ptr T, span: Slice[int]): untyped =
-    toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b)
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
 
   doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
                 "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
@@ -12,7 +12,7 @@ block:
   let txt = "Hello World"
 
   template `[]`[T](p: ptr T, span: Slice[int]): untyped =
-    toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b)
+    toOpenArray(cast[ptr UncheckedArray[T]](p), span.a, span.b)
 
   doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] == 
                 "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
diff --git a/tests/overload/tgenericalias.nim b/tests/overload/tgenericalias.nim
new file mode 100644
index 000000000..50a44bd32
--- /dev/null
+++ b/tests/overload/tgenericalias.nim
@@ -0,0 +1,13 @@
+block: # issue #13799
+  type
+    X[A, B] = object
+      a: A
+      b: B
+
+    Y[A] = X[A, int]
+  template s(T: type X): X = T()
+  template t[A, B](T: type X[A, B]): X[A, B] = T()
+  proc works1(): Y[int] = s(X[int, int])
+  proc works2(): Y[int] = t(X[int, int])
+  proc works3(): Y[int] = t(Y[int])
+  proc broken(): Y[int] = s(Y[int])
diff --git a/tests/overload/tor_isnt_better.nim b/tests/overload/tor_isnt_better.nim
index 5ef8bc7c4..bee125386 100644
--- a/tests/overload/tor_isnt_better.nim
+++ b/tests/overload/tor_isnt_better.nim
@@ -1,18 +1,41 @@
-discard """
-  errormsg: "ambiguous call;"
-  line: 16
-"""
-
-# bug #8568
-
 type
   D[T] = object
   E[T] = object
 
-proc g(a: D|E): string = "foo D|E"
-proc g(a: D): string = "foo D"
+block: # PR #22261
+  proc d(x: D):bool= false
+  proc d(x: int | D[SomeInteger]):bool= true
+  doAssert d(D[5]()) == false
 
-proc test() =
-  let x = g D[int]()
+block: # bug #8568
+#[
+  Since PR #22261 and amendment has been made. Since D is a subset of D | E but
+  not the other way around `checkGeneric` should favor proc g(a: D) instead
+  of asserting ambiguity
+]#
+  proc g(a: D|E): string = "foo D|E"
+  proc g(a: D): string = "foo D"
+  doAssert g(D[int]()) == "foo D"
 
-test()
+type Obj1[T] = object
+  v: T
+converter toObj1[T](t: T): Obj1[T] = return Obj1[T](v: t)
+block: # issue #10019
+  proc fun1[T](elements: seq[T]): string = "fun1 seq"
+  proc fun1(o: object|tuple): string = "fun1 object|tuple"
+  proc fun2[T](elements: openArray[T]): string = "fun2 openarray"
+  proc fun2(o: object): string = "fun2 object"
+  proc fun_bug[T](elements: openArray[T]): string = "fun_bug openarray"
+  proc fun_bug(o: object|tuple):string = "fun_bug object|tuple"
+  proc main() =
+    var x = @["hello", "world"]
+    block:
+      # no ambiguity error shown here even though this would compile if we remove either 1st or 2nd overload of fun1
+      doAssert fun1(x) == "fun1 seq"
+    block:
+      # ditto
+      doAssert fun2(x) == "fun2 openarray"
+    block:
+      # Error: ambiguous call; both t0065.fun_bug(elements: openarray[T])[declared in t0065.nim(17, 5)] and t0065.fun_bug(o: object or tuple)[declared in t0065.nim(20, 5)] match for: (array[0..1, string])
+      doAssert fun_bug(x) == "fun_bug openarray"
+  main()
diff --git a/tests/overload/toverl4.nim b/tests/overload/toverl4.nim
index 537925674..21cedaa96 100644
--- a/tests/overload/toverl4.nim
+++ b/tests/overload/toverl4.nim
@@ -1,5 +1,6 @@
 discard """
-  output: '''true'''
+  output: '''true
+5.0'''
 """
 
 #bug #592
@@ -75,3 +76,26 @@ proc add*[TKey, TData](root: var PElement[TKey, TData], key: TKey, data: TData)
 var tree = PElement[int, int](kind: ElementKind.inner, key: 0, left: nil, right: nil)
 let result = add(tree, 1, 1)
 echo(result)
+
+# bug #3748
+type
+  Foo = object
+    bar: int
+
+proc bar(cur: Foo, val: int, s:seq[string]) =
+  discard cur.bar
+
+proc does_fail(): Foo =
+  let a = @["a"]
+  result.bar(5, a)
+
+doAssert does_fail().bar == 0
+
+# bug #20645
+
+type Zzz[Gen] = object
+
+proc testZ(z: Zzz) =
+  echo z.Gen(5)
+
+testZ(Zzz[float]())
diff --git a/tests/overload/toverload_issues.nim b/tests/overload/toverload_issues.nim
index 5db7b54fa..26bf89091 100644
--- a/tests/overload/toverload_issues.nim
+++ b/tests/overload/toverload_issues.nim
@@ -77,27 +77,30 @@ testPred(1)
 
 
 
-# bug #6526
-type
-  BaseObj = ref object of RootObj
-  DerivedObj = ref object of BaseObj
-  OtherDerivate = ref object of BaseObj
-
-proc `==`*[T1, T2: BaseObj](a: T1, b: T2): bool =
-  echo "baseobj =="
-  return true
-
-let a = DerivedObj()
-let b = DerivedObj()
-echo a == b
-
-proc `==`*[T1, T2: OtherDerivate](a: T1, b: T2): bool =
-  echo "even better! =="
-  return true
-
-let a2 = OtherDerivate()
-let b2 = OtherDerivate()
-echo a2 == b2
+block: # bug #6526
+  type
+    BaseObj = ref object of RootObj
+    DerivedObj = ref object of BaseObj
+    OtherDerivate = ref object of BaseObj
+
+  proc p[T](a: T, b: T): bool =
+    assert false
+
+  proc p[T1, T2: BaseObj](a: T1, b: T2): bool =
+    echo "baseobj =="
+    return true
+
+  let a = DerivedObj()
+  let b = DerivedObj()
+  echo p(a,b)
+
+  proc p[T1, T2: OtherDerivate](a: T1, b: T2): bool =
+    echo "even better! =="
+    return true
+
+  let a2 = OtherDerivate()
+  let b2 = OtherDerivate()
+  echo p(a2, b2)
 
 
 
diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim
index 0741fce60..d195a069d 100644
--- a/tests/overload/toverload_various.nim
+++ b/tests/overload/toverload_various.nim
@@ -506,3 +506,63 @@ block:
   doAssert(p2(F(float,1.0),F(float,2)) == 3.0)
   doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0)
   #doAssert(p2(F(float,1),F(int,2.0)) == 3.0)
+
+block: # PR #23870
+  type
+    A {.inheritable.} = object
+    B = object of A
+    C = object of B
+
+  proc p[T: A](x: T): int = 0
+  proc p[T: B](x: T): int = 1
+
+  proc d(x: A): int = 0
+  proc d(x: B): int = 1
+  
+  proc g[T:A](x: typedesc[T]): int = 0
+  proc g[T: B](x: typedesc[T]): int = 1
+  
+  proc f[T](x: typedesc[T]): int = 0
+  proc f[T:B](x: typedesc[T]): int = 1
+
+  assert p(C()) == 1
+  assert d(C()) == 1
+  assert g(C) == 1
+  assert f(C) == 1
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    PT = proc(ev: A) {.closure.}
+    sdt = seq[(PT, PT)]
+
+  proc encap() =
+    proc p(a: A) {.closure.} =
+      discard
+
+    var s: sdt
+    s.add (p, nil)
+
+  encap()
+
+block: # PR #23870
+  type
+    A = object of RootObj
+    B = object of A
+    C = object of B
+
+  proc p(a: B | RootObj): int =
+    0
+
+  proc p(a: A | A): int =
+    1
+
+  assert p(C()) == 0
+
+  proc d(a: RootObj | B): int =
+    0
+
+  proc d(a: A | A): int =
+    1
+
+  assert d(C()) == 0
diff --git a/tests/overload/tproc_types_dont_like_subtypes.nim b/tests/overload/tproc_types_dont_like_subtypes.nim
index d322f41be..6774be156 100644
--- a/tests/overload/tproc_types_dont_like_subtypes.nim
+++ b/tests/overload/tproc_types_dont_like_subtypes.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "got <B, proc (b: B){.closure, gcsafe, locks: 0.}>"
+  errormsg: "got <B, proc (b: B){.closure, gcsafe.}>"
   line: 20
 """
 
diff --git a/tests/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim
index e830e8a22..2bc1dfaab 100644
--- a/tests/overload/tstatic_with_converter.nim
+++ b/tests/overload/tstatic_with_converter.nim
@@ -1,7 +1,6 @@
 discard """
 output: '''
 9.0
-
 '''
 """
 
@@ -39,12 +38,11 @@ proc `^`(x: vfloat, exp: static[float]): vfloat =
   when exp == 0.5:
     sqrt(x)
   else:
-   pow(x, exp)
+    pow(x, exp)
  
 proc `$`(x: vfloat): string =
   let y = cast[ptr float](addr x)
-  # xxx not sure if intentional in this issue, but this returns ""
-  echo y[]
+  result = $y[]
  
 let x = set1(9.0)
 echo x^0.5
diff --git a/tests/overload/tuntypedarg.nim b/tests/overload/tuntypedarg.nim
new file mode 100644
index 000000000..9aa4fad3b
--- /dev/null
+++ b/tests/overload/tuntypedarg.nim
@@ -0,0 +1,19 @@
+import macros
+
+block: # issue #7385
+  type CustomSeq[T] = object
+    data: seq[T]
+  macro `[]`[T](s: CustomSeq[T], args: varargs[untyped]): untyped =
+    ## The end goal is to replace the joker "_" by something else
+    result = newIntLitNode(10)
+  proc foo1(): CustomSeq[int] =
+    result.data.newSeq(10)
+    # works since no overload matches first argument with type `CustomSeq`
+    # except magic `[]`, which always matches without checking arguments
+    doAssert result[_] == 10
+  doAssert foo1() == CustomSeq[int](data: newSeq[int](10))
+  proc foo2[T](): CustomSeq[T] =
+    result.data.newSeq(10)
+    # works fine with generic return type
+    doAssert result[_] == 10
+  doAssert foo2[int]() == CustomSeq[int](data: newSeq[int](10))
diff --git a/tests/overload/tvartypeclass.nim b/tests/overload/tvartypeclass.nim
new file mode 100644
index 000000000..04f3f5a91
--- /dev/null
+++ b/tests/overload/tvartypeclass.nim
@@ -0,0 +1,11 @@
+# issue #13302
+
+proc foo(x: object): int = x.i*2
+proc foo(x: var object) = x.i*=2
+type Foo = object
+  i: int
+let x = Foo(i: 3)
+var y = Foo(i: 4)
+doAssert foo(x) == 6
+foo(y)
+doAssert y.i == 8
diff --git a/tests/overload/tvaruintconv.nim b/tests/overload/tvaruintconv.nim
new file mode 100644
index 000000000..87ebd285d
--- /dev/null
+++ b/tests/overload/tvaruintconv.nim
@@ -0,0 +1,207 @@
+discard """
+  action: compile
+"""
+
+# https://github.com/status-im/nimbus-eth2/pull/6554#issuecomment-2354977102
+# failed with "for a 'var' type a variable needs to be passed; but 'uint64(result)' is immutable"
+
+import
+  std/[typetraits, macros]
+
+type
+  DefaultFlavor = object
+
+template serializationFormatImpl(Name: untyped) {.dirty.} =
+  type Name = object
+
+template serializationFormat(Name: untyped) =
+  serializationFormatImpl(Name)
+
+template setReader(Format, FormatReader: distinct type) =
+  when arity(FormatReader) > 1:
+    template Reader(T: type Format, F: distinct type = DefaultFlavor): type = FormatReader[F]
+  else:
+    template ReaderType(T: type Format): type = FormatReader
+    template Reader(T: type Format): type = FormatReader
+
+template useDefaultReaderIn(T: untyped, Flavor: type) =
+  mixin Reader
+
+  template readValue(r: var Reader(Flavor), value: var T) =
+    mixin readRecordValue
+    readRecordValue(r, value)
+
+import mvaruintconv
+
+type
+  FieldTag[RecordType: object; fieldName: static string] = distinct void
+
+func declval*(T: type): T {.compileTime.} =
+  default(ptr T)[]
+
+macro enumAllSerializedFieldsImpl(T: type, body: untyped): untyped =
+  var typeAst = getType(T)[1]
+  var typeImpl: NimNode
+  let isSymbol = not typeAst.isTuple
+
+  if not isSymbol:
+    typeImpl = typeAst
+  else:
+    typeImpl = getImpl(typeAst)
+  result = newStmtList()
+
+  var i = 0
+  for field in recordFields(typeImpl):
+    let
+      fieldIdent = field.name
+      realFieldName = newLit($fieldIdent.skipPragma)
+      fieldName = realFieldName
+      fieldIndex = newLit(i)
+
+    let fieldNameDefs =
+      if isSymbol:
+        quote:
+          const fieldName {.inject, used.} = `fieldName`
+          const realFieldName {.inject, used.} = `realFieldName`
+      else:
+        quote:
+          const fieldName {.inject, used.} = $`fieldIndex`
+          const realFieldName {.inject, used.} = $`fieldIndex`
+
+    let field =
+      if isSymbol:
+        quote do: declval(`T`).`fieldIdent`
+      else:
+        quote do: declval(`T`)[`fieldIndex`]
+
+    result.add quote do:
+      block:
+        `fieldNameDefs`
+
+        template FieldType: untyped {.inject, used.} = typeof(`field`)
+
+        `body`
+
+  # echo repr(result)
+
+template enumAllSerializedFields(T: type, body): untyped =
+  enumAllSerializedFieldsImpl(T, body)
+
+type
+  FieldReader[RecordType, Reader] = tuple[
+    fieldName: string,
+    reader: proc (rec: var RecordType, reader: var Reader)
+                 {.gcsafe, nimcall.}
+  ]
+
+proc totalSerializedFieldsImpl(T: type): int =
+  mixin enumAllSerializedFields
+  enumAllSerializedFields(T): inc result
+
+template totalSerializedFields(T: type): int =
+  (static(totalSerializedFieldsImpl(T)))
+
+template GetFieldType(FT: type FieldTag): type =
+  typeof field(declval(FT.RecordType), FT.fieldName)
+
+proc makeFieldReadersTable(RecordType, ReaderType: distinct type,
+                           numFields: static[int]):
+                           array[numFields, FieldReader[RecordType, ReaderType]] =
+  mixin enumAllSerializedFields, handleReadException
+  var idx = 0
+
+  enumAllSerializedFields(RecordType):
+    proc readField(obj: var RecordType, reader: var ReaderType)
+                  {.gcsafe, nimcall.} =
+
+      mixin readValue
+
+      type F = FieldTag[RecordType, realFieldName]
+      field(obj, realFieldName) = reader.readValue(GetFieldType(F))
+
+    result[idx] = (fieldName, readField)
+    inc idx
+
+proc fieldReadersTable(RecordType, ReaderType: distinct type): auto =
+  mixin readValue
+  type T = RecordType
+  const numFields = totalSerializedFields(T)
+  var tbl {.threadvar.}: ref array[numFields, FieldReader[RecordType, ReaderType]]
+  if tbl == nil:
+    tbl = new typeof(tbl)
+    tbl[] = makeFieldReadersTable(RecordType, ReaderType, numFields)
+  return addr(tbl[])
+
+proc readValue(reader: var auto, T: type): T =
+  mixin readValue
+  reader.readValue(result)
+
+template decode(Format: distinct type,
+                 input: string,
+                 RecordType: distinct type): auto =
+  mixin Reader
+  block:  # https://github.com/nim-lang/Nim/issues/22874
+    var reader: Reader(Format)
+    reader.readValue(RecordType)
+
+template readValue(Format: type,
+                    ValueType: type): untyped =
+  mixin Reader, init, readValue
+  var reader: Reader(Format)
+  readValue reader, ValueType
+
+template parseArrayImpl(numElem: untyped,
+                        actionValue: untyped) =
+  actionValue
+
+serializationFormat Json
+template createJsonFlavor(FlavorName: untyped,
+                           skipNullFields = false) {.dirty.} =
+  type FlavorName = object
+
+  template Reader(T: type FlavorName): type = Reader(Json, FlavorName)
+type
+  JsonReader[Flavor = DefaultFlavor] = object
+
+Json.setReader JsonReader
+
+template parseArray(r: var JsonReader; body: untyped) =
+  parseArrayImpl(idx): body
+
+template parseArray(r: var JsonReader; idx: untyped; body: untyped) =
+  parseArrayImpl(idx): body
+
+proc readRecordValue[T](r: var JsonReader, value: var T) =
+  type
+    ReaderType {.used.} = type r
+    T = type value
+
+  discard T.fieldReadersTable(ReaderType)
+
+proc readValue[T](r: var JsonReader, value: var T) =
+  mixin readValue
+
+  when value is seq:
+    r.parseArray:
+      readValue(r, value[0])
+
+  elif value is object:
+    readRecordValue(r, value)
+
+type
+  RemoteSignerInfo = object
+    id: uint32
+  RemoteKeystore = object
+
+proc readValue(reader: var JsonReader, value: var RemoteKeystore) =
+  discard reader.readValue(seq[RemoteSignerInfo])
+
+createJsonFlavor RestJson
+useDefaultReaderIn(RemoteSignerInfo, RestJson)
+proc readValue(reader: var JsonReader[RestJson], value: var uint64) =
+  discard reader.readValue(string)
+
+discard Json.decode("", RemoteKeystore)
+block:  # https://github.com/nim-lang/Nim/issues/22874
+  var reader: Reader(RestJson)
+  discard reader.readValue(RemoteSignerInfo)
diff --git a/tests/parallel/t10913.nim b/tests/parallel/t10913.nim
index d8459ecd0..191939100 100644
--- a/tests/parallel/t10913.nim
+++ b/tests/parallel/t10913.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
   errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter"
 """
 
diff --git a/tests/parallel/t7535.nim b/tests/parallel/t7535.nim
index 052dcdc3a..7817a1c9e 100644
--- a/tests/parallel/t7535.nim
+++ b/tests/parallel/t7535.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
   errormsg: "'spawn' takes a call expression; got: proc (x: uint32) = echo [x]"
 """
 
diff --git a/tests/parallel/t9691.nim b/tests/parallel/t9691.nim
index bbf2b1bc7..254f03416 100644
--- a/tests/parallel/t9691.nim
+++ b/tests/parallel/t9691.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
   errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter"
 """
 
diff --git a/tests/parallel/tblocking_channel.nim b/tests/parallel/tblocking_channel.nim
index eb5fcb715..f3ccd166a 100644
--- a/tests/parallel/tblocking_channel.nim
+++ b/tests/parallel/tblocking_channel.nim
@@ -1,8 +1,8 @@
 discard """
 output: ""
-disabled: "freebsd"
+disabled: "freebsd" # see #15725
 """
-# disabled pending bug #15725
+
 import threadpool, os
 
 var chan: Channel[int]
diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim
index b1eb7e7c5..f4c92319b 100644
--- a/tests/parallel/tguard1.nim
+++ b/tests/parallel/tguard1.nim
@@ -5,7 +5,7 @@ output: "90"
 
 when false:
   template lock(a, b: ptr Lock; body: stmt) =
-    if cast[ByteAddress](a) < cast[ByteAddress](b):
+    if cast[int](a) < cast[int](b):
       pthread_mutex_lock(a)
       pthread_mutex_lock(b)
     else:
diff --git a/tests/parallel/tlet_spawn.nim b/tests/parallel/tlet_spawn.nim
index 62341d8f0..853ffc443 100644
--- a/tests/parallel/tlet_spawn.nim
+++ b/tests/parallel/tlet_spawn.nim
@@ -4,7 +4,7 @@ done999 999
 '''
 """
 
-import threadpool
+import std/[threadpool, os]
 
 proc foo(): int = 999
 
@@ -17,3 +17,12 @@ proc main =
   echo "done", f, " ", b
 
 main()
+
+# bug #13781
+proc thread(): string =
+  os.sleep(1000)
+  return "ok"
+
+var fv = spawn thread()
+sync()
+doAssert ^fv == "ok"
diff --git a/tests/parallel/tsendtwice.nim b/tests/parallel/tsendtwice.nim
index 6bf5e5ebb..9f4a2e06e 100644
--- a/tests/parallel/tsendtwice.nim
+++ b/tests/parallel/tsendtwice.nim
@@ -1,18 +1,10 @@
 discard """
-  output: '''ob2 @[]
-ob @[]
-ob3 @[]
-3
-ob2 @[]
-ob @[]
-ob3 @[]
-'''
   matrix: "--mm:refc"
 """
 
 # bug #4776
 
-import tables
+import tables, algorithm
 
 type
   Base* = ref object of RootObj
@@ -35,20 +27,21 @@ globalTable.add("ob", d)
 globalTable.add("ob2", d)
 globalTable.add("ob3", d)
 
+proc `<`(x, y: seq[int]): bool = x.len < y.len
+proc kvs(t: TableRef[string, Base]): seq[(string, seq[int])] =
+  for k, v in t.pairs: result.add (k, v.someSeq)
+  result.sort
+
 proc testThread(channel: ptr TableChannel) {.thread.} =
   globalTable = channel[].recv()
-  for k, v in pairs globaltable:
-    echo k, " ", v.someSeq
   var myObj: Base
   deepCopy(myObj, globalTable["ob"])
   myObj.someSeq = newSeq[int](100)
   let table = channel[].recv() # same table
-  echo table.len
-  for k, v in mpairs table:
-    echo k, " ", v.someSeq
   assert(table.contains("ob")) # fails!
   assert(table.contains("ob2")) # fails!
   assert(table.contains("ob3")) # fails!
+  assert table.kvs == globalTable.kvs # Last to see above spot checks first
 
 var channel: TableChannel
 
diff --git a/tests/parallel/tsimple_array_checks.nim b/tests/parallel/tsimple_array_checks.nim
index 650b809e0..ab292f935 100644
--- a/tests/parallel/tsimple_array_checks.nim
+++ b/tests/parallel/tsimple_array_checks.nim
@@ -61,3 +61,15 @@ maino() # Doesn't work outside a proc
 
 when true:
   main()
+
+block two:
+  proc f(a: openArray[int]) =
+    discard
+
+  proc main() =
+    var a: array[0..9, int] = [0,1,2,3,4,5,6,7,8,9]
+    parallel:
+      spawn f(a[0..2])
+
+
+  main()
\ No newline at end of file
diff --git a/tests/parallel/tuseafterdef.nim b/tests/parallel/tuseafterdef.nim
index e73f1b794..64f835a1b 100644
--- a/tests/parallel/tuseafterdef.nim
+++ b/tests/parallel/tuseafterdef.nim
@@ -1,5 +1,6 @@
 discard """
   matrix: "--mm:refc"
+  disabled: true
   errormsg: "(k)..(k) not disjoint from (k)..(k)"
   line: 24
   action: compile
diff --git a/tests/parser/t19430.nim b/tests/parser/t19430.nim
new file mode 100644
index 000000000..c1aa6a92d
--- /dev/null
+++ b/tests/parser/t19430.nim
@@ -0,0 +1,3 @@
+let x = proc() = ## abc
+let y = 3 #[tt.Error
+^ invalid indentation]#
diff --git a/tests/parser/t20922.nim b/tests/parser/t20922.nim
new file mode 100644
index 000000000..110610fb1
--- /dev/null
+++ b/tests/parser/t20922.nim
@@ -0,0 +1,35 @@
+discard """
+  cmd: "nim check $options --verbosity:0 --hints:off $file"
+  action: "reject"
+  nimout: '''
+t20922.nim(26, 5) Error: expression expected, but found ':'
+t20922.nim(34, 7) Error: expression expected, but found ':'
+t20922.nim(35, 5) Error: ':' or '=' expected, but got 'keyword of'
+Error: in expression ' '+'': identifier expected, but found ''
+t20922.nim(26, 7) Error: attempting to call undeclared routine: '<Error>'
+Error: in expression ' '+'': identifier expected, but found ''
+t20922.nim(26, 7) Error: attempting to call undeclared routine: '<Error>'
+t20922.nim(26, 7) Error: expression '' cannot be called
+t20922.nim(26, 7) Error: expression '' has no type (or is ambiguous)
+t20922.nim(26, 7) Error: VM problem: dest register is not set
+'''
+"""
+# original test case issue #20922
+type Token = enum
+  incDataPtr,
+  incDataPtrByte
+
+proc mapInstrToToken(instr: char): Token =
+  case instr:
+  of '>':
+    incDataPtr
+  of: '+':
+    incDataPtrByte
+
+# same issue with `of` in object branches (different parser procs calling `exprList`)
+type
+  Bar = enum A, B
+  Foo = object
+    case kind: Bar
+    of: x: int
+    of B: y: float
diff --git a/tests/parser/tbinarynotindented.nim b/tests/parser/tbinarynotindented.nim
new file mode 100644
index 000000000..8d124aade
--- /dev/null
+++ b/tests/parser/tbinarynotindented.nim
@@ -0,0 +1,3 @@
+type Foo = ref int
+  not nil #[tt.Error
+  ^ invalid indentation]#
diff --git a/tests/parser/tbinarynotsameline.nim b/tests/parser/tbinarynotsameline.nim
new file mode 100644
index 000000000..ca417e023
--- /dev/null
+++ b/tests/parser/tbinarynotsameline.nim
@@ -0,0 +1,10 @@
+# issue #23565
+
+func foo: bool =
+  true
+
+const bar = block:
+  type T = int
+  not foo()
+
+doAssert not bar
diff --git a/tests/parser/tcommand_as_expr.nim b/tests/parser/tcommand_as_expr.nim
index b25ec4bd8..f37c34f63 100644
--- a/tests/parser/tcommand_as_expr.nim
+++ b/tests/parser/tcommand_as_expr.nim
@@ -36,3 +36,12 @@ echo f -4
 
 echo int -1 # doesn't compile
 echo int `-` 1 # compiles
+
+var num = 1
+num += int 2
+doAssert num == 3
+
+import options
+var opt = some some none int
+opt = some some none int
+opt = some none Option[int]
diff --git a/tests/parser/tcommandequals.nim b/tests/parser/tcommandequals.nim
new file mode 100644
index 000000000..f41b318ac
--- /dev/null
+++ b/tests/parser/tcommandequals.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''
+5
+'''
+"""
+
+proc foo(a, b: int) =
+  echo a + b
+
+foo a = 2, b = 3
+
+import macros
+
+macro bar(args: varargs[untyped]): untyped =
+  doAssert args[0].kind == nnkExprEqExpr
+
+bar "a" = 1
diff --git a/tests/parser/tcommandindent.nim b/tests/parser/tcommandindent.nim
new file mode 100644
index 000000000..449c218db
--- /dev/null
+++ b/tests/parser/tcommandindent.nim
@@ -0,0 +1,16 @@
+when false: # parse the following
+  let foo = Obj(
+    field1: proc (src: pointer, srcLen: Natural)
+                    {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      var file = FileOutputStream(s).file
+
+      implementWrites s.buffers, src, srcLen, "FILE",
+                      writeStartAddr, writeLen,
+        file.writeBuffer(writeStartAddr, writeLen)
+    ,
+    field2: proc {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      flushFile FileOutputStream(s).file
+    ,
+    field3: proc () {.nimcall, gcsafe, raises: [IOError, Defect].} =
+      close FileOutputStream(s).file
+  )
diff --git a/tests/misc/trfc_341.nim b/tests/parser/tdotlikeoperators.nim
index 37cf675c6..c2d23bd15 100644
--- a/tests/misc/trfc_341.nim
+++ b/tests/parser/tdotlikeoperators.nim
@@ -1,3 +1,6 @@
+discard """
+  matrix: "-d:nimPreviewDotLikeOps"
+"""
 # test for https://github.com/nim-lang/RFCs/issues/341
 import std/json
 import std/jsonutils
diff --git a/tests/parser/tdoublenotnil.nim b/tests/parser/tdoublenotnil.nim
new file mode 100644
index 000000000..c61008c54
--- /dev/null
+++ b/tests/parser/tdoublenotnil.nim
@@ -0,0 +1,3 @@
+when false:
+  type Foo = Bar not nil not nil #[tt.Error
+                         ^ invalid indentation]#
diff --git a/tests/parser/tifextracolon.nim b/tests/parser/tifextracolon.nim
new file mode 100644
index 000000000..171569111
--- /dev/null
+++ b/tests/parser/tifextracolon.nim
@@ -0,0 +1,8 @@
+# issue #21982
+
+if true:
+  if true:
+    discard default int:
+else: #[tt.Error
+^ invalid indentation]#
+  discard
diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim
index d272c712f..6cd4a8350 100644
--- a/tests/parser/tpostexprblocks.nim
+++ b/tests/parser/tpostexprblocks.nim
@@ -498,6 +498,13 @@ StmtList
         StmtList
           DiscardStmt
             Empty
+
+  Call
+    Ident "foo"
+    Finally
+      StmtList
+        DiscardStmt
+          Empty
 '''
 """
 
@@ -655,3 +662,7 @@ dumpTree:
     discard
   finally:
     discard
+
+  foo:
+  finally:
+    discard
diff --git a/tests/parser/tprocexprasstmt.nim b/tests/parser/tprocexprasstmt.nim
new file mode 100644
index 000000000..22fb4a7c8
--- /dev/null
+++ b/tests/parser/tprocexprasstmt.nim
@@ -0,0 +1,14 @@
+func r(): auto =
+  func(): int = 2
+doAssert r()() == 2
+
+block: # issue #11726
+  let foo = block:
+    var x: int
+    proc = inc x # "identifier expected, but got '='"
+
+  template paint(): untyped =
+    proc (s: string): string = s
+
+  let s = paint()
+  doAssert s("abc") == "abc"
diff --git a/tests/parser/ttry.nim b/tests/parser/ttry.nim
new file mode 100644
index 000000000..190b0b8dc
--- /dev/null
+++ b/tests/parser/ttry.nim
@@ -0,0 +1,27 @@
+# bug #21144
+block:
+  try:
+    let c = try:
+        10
+      except ValueError as exc:
+        10
+  except ValueError as exc:
+    discard
+
+if true:
+  block:
+    let c = try:
+          10
+        except ValueError as exc:
+          10
+    except OSError:
+      99
+
+
+try:
+  let c = try:
+    10
+  except ValueError as exc:
+    10
+except ValueError as exc:
+  discard
\ No newline at end of file
diff --git a/tests/parser/ttupleunpack.nim b/tests/parser/ttupleunpack.nim
index c7ab9ea15..993501fbb 100644
--- a/tests/parser/ttupleunpack.nim
+++ b/tests/parser/ttupleunpack.nim
@@ -27,3 +27,68 @@ proc main() =
 
 main()
 main2()
+
+block: # nested unpacking
+  block: # simple let
+    let (a, (b, c), d) = (1, (2, 3), 4)
+    doAssert (a, b, c, d) == (1, 2, 3, 4)
+    let foo = (a, (b, c), d)
+    let (a2, (b2, c2), d2) = foo
+    doAssert (a, b, c, d) == (a2, b2, c2, d2)
+
+  block: # var and assignment
+    var (x, (y, z), t) = ('a', (true, @[123]), "abc")
+    doAssert (x, y, z, t) == ('a', true, @[123], "abc")
+    (x, (y, z), t) = ('b', (false, @[456]), "def")
+    doAssert (x, y, z, t) == ('b', false, @[456], "def")
+
+  block: # very nested
+    let (_, (_, (_, (_, (_, a))))) = (1, (2, (3, (4, (5, 6)))))
+    doAssert a == 6
+
+  block: # const
+    const (a, (b, c), d) = (1, (2, 3), 4)
+    doAssert (a, b, c, d) == (1, 2, 3, 4)
+    const foo = (a, (b, c), d)
+    const (a2, (b2, c2), d2) = foo
+    doAssert (a, b, c, d) == (a2, b2, c2, d2)
+  
+  block: # evaluation semantics preserved between literal and not literal
+    var s: seq[string]
+    block: # literal
+      let (a, (b, c), d) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      doAssert (a, b, c, d) == (1, 2, 3, 4)
+      doAssert s == @["a", "b", "c", "d"]
+    block: # underscore
+      s = @[]
+      let (a, (_, c), _) = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      doAssert (a, c) == (1, 3)
+      doAssert s == @["a", "b", "c", "d"]
+    block: # temp
+      s = @[]
+      let foo = ((s.add("a"); 1), ((s.add("b"); 2), (s.add("c"); 3)), (s.add("d"); 4))
+      let (a, (b, c), d) = foo
+      doAssert (a, b, c, d) == (1, 2, 3, 4)
+      doAssert s == @["a", "b", "c", "d"]
+
+block: # unary assignment unpacking
+  var a: int
+  (a,) = (1,)
+  doAssert a == 1
+
+block: # type annotations
+  block: # basic
+    let (a, b): (int, int) = (1, 2)
+    doAssert (a, b) == (1, 2)
+  block: # type inference
+    let (a, b): (byte, float) = (1, 2)
+    doAssert (a, b) == (1.byte, 2.0)
+  block: # type mismatch
+    doAssert not (compiles do:
+      let (a, b): (int, string) = (1, 2))
+  block: # nested
+    let (a, (b, c)): (int, (int, int)) = (1, (2, 3))
+    doAssert (a, b, c) == (1, 2, 3)
+  block: # nested type inference
+    let (a, (b, c)): (byte, (float, cstring)) = (1, (2, "abc"))
+    doAssert (a, b, c) == (1.byte, 2.0, cstring"abc")
diff --git a/tests/parser/ttypeexprobject.nim b/tests/parser/ttypeexprobject.nim
new file mode 100644
index 000000000..6895f1731
--- /dev/null
+++ b/tests/parser/ttypeexprobject.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "invalid indentation"
+  line: 10
+  column: 14
+"""
+
+type
+  A = (object | tuple | int)
+  B = int | object | tuple
+  C = object | tuple | int # issue #8846
diff --git a/tests/parser/ttypeexprs.nim b/tests/parser/ttypeexprs.nim
new file mode 100644
index 000000000..e40efc7d9
--- /dev/null
+++ b/tests/parser/ttypeexprs.nim
@@ -0,0 +1,25 @@
+proc foo[T: ptr int | ptr string](x: T) = discard
+var x = "abc"
+foo(addr x)
+
+let n = 3'u32
+type Double = (
+  when n.sizeof == 4: uint64
+  elif n.sizeof == 2: uint32
+  else: uint16
+)
+
+type
+  A = (ref | ptr | pointer)
+  B = pointer | ptr | ref
+  C = ref | ptr | pointer
+
+template `+`(a, b): untyped = (b, a)
+template `*`(a, b): untyped = (a, b)
+
+doAssert (ref int + ref float * ref string + ref bool) is
+  (ref bool, ((ref float, ref string), ref int))
+type X = ref int + ref float * ref string + ref bool
+doAssert X is (ref bool, ((ref float, ref string), ref int))
+
+type SomePointer = proc | ref | ptr | pointer
diff --git a/tests/pragmas/monoff1.nim b/tests/pragmas/monoff1.nim
new file mode 100644
index 000000000..85d6c57b3
--- /dev/null
+++ b/tests/pragmas/monoff1.nim
@@ -0,0 +1 @@
+proc on*() = discard
diff --git a/tests/pragmas/mqualifiedmacro.nim b/tests/pragmas/mqualifiedmacro.nim
new file mode 100644
index 000000000..908973206
--- /dev/null
+++ b/tests/pragmas/mqualifiedmacro.nim
@@ -0,0 +1,10 @@
+template t*(x:untyped): untyped = 
+  echo "template t"
+
+import macros
+macro m*(name: static string, x: untyped): untyped =
+  let newName = ident(name)
+  result = quote do:
+    type `newName` = object
+  if result.kind == nnkStmtList:
+    result = result[^1]
diff --git a/tests/pragmas/t22713.nim b/tests/pragmas/t22713.nim
new file mode 100644
index 000000000..3d3384632
--- /dev/null
+++ b/tests/pragmas/t22713.nim
@@ -0,0 +1,12 @@
+import std/macros
+
+
+template myPragma(x: int) {.pragma.}
+
+type
+  A = object
+    x: int64
+
+  B {.myPragma(sizeof(A)).} = object
+
+doAssert B.getCustomPragmaVal(myPragma) == 8
\ No newline at end of file
diff --git a/tests/pragmas/t8741.nim b/tests/pragmas/t8741.nim
index 221c732b0..bf97b0e29 100644
--- a/tests/pragmas/t8741.nim
+++ b/tests/pragmas/t8741.nim
@@ -1,7 +1,7 @@
 discard """
   cmd: "nim check --hint:processing:off $file"
   errormsg: "3 is not two"
-  nimout: '''t8741.nim(13, 9) Error: cannot attach a custom pragma to 'a'
+  nimout: '''t8741.nim(13, 9) Error: invalid pragma: foobar
 t8741.nim(29, 15) template/generic instantiation of `onlyTwo` from here
 t8741.nim(25, 12) Error: 3 is not two
 '''
diff --git a/tests/pragmas/tcompile_missing_file.nim b/tests/pragmas/tcompile_missing_file.nim
new file mode 100644
index 000000000..fd90bd57d
--- /dev/null
+++ b/tests/pragmas/tcompile_missing_file.nim
@@ -0,0 +1,5 @@
+discard """
+  joinable: false
+  errormsg: "cannot find: noexist.c"
+"""
+{.compile: "noexist.c".}
diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim
index b624f32ba..11a6df813 100644
--- a/tests/pragmas/tcustom_pragma.nim
+++ b/tests/pragmas/tcustom_pragma.nim
@@ -399,12 +399,39 @@ block:
 
   discard Hello(a: 1.0, b: 12)
 
-# custom pragma on iterators
+# test routines
+block:
+  template prag {.pragma.}
+  proc hello {.prag.} = discard
+  iterator hello2: int {.prag.} = discard
+  template hello3(x: int): int {.prag.} = x
+  macro hello4(x: int): int {.prag.} = x
+  func hello5(x: int): int {.prag.} = x
+  doAssert hello.hasCustomPragma(prag)
+  doAssert hello2.hasCustomPragma(prag)
+  doAssert hello3.hasCustomPragma(prag)
+  doAssert hello4.hasCustomPragma(prag)
+  doAssert hello5.hasCustomPragma(prag)
+
+# test push doesn't break
 block:
   template prag {.pragma.}
   {.push prag.}
   proc hello = discard
   iterator hello2: int = discard
+  template hello3(x: int): int = x
+  macro hello4(x: int): int = x
+  func hello5(x: int): int = x
+  type
+    Foo = enum a
+    Bar[T] = ref object of RootObj
+      x: T
+      case y: bool
+      of false: discard
+      else:
+        when true: discard
+  for a in [1]: discard a
+  {.pop.}
 
 # issue #11511
 when false:
@@ -423,6 +450,31 @@ when false:
 
   doAssert hasMyAttr(TObj)
 
+
+# bug #11415
+template noserialize() {.pragma.}
+
+type
+  Point[T] = object
+    x, y: T
+
+  ReplayEventKind = enum
+    FoodAppeared, FoodEaten, DirectionChanged
+
+  ReplayEvent = object
+    case kind: ReplayEventKind
+    of FoodEaten, FoodAppeared: # foodPos is in multiple branches
+      foodPos {.noserialize.}: Point[float]
+    of DirectionChanged:
+      playerPos: float
+let ev = ReplayEvent(
+    kind: FoodEaten,
+    foodPos: Point[float](x: 5.0, y: 1.0)
+  )
+
+doAssert ev.foodPos.hasCustomPragma(noserialize)
+
+
 when false:
   # misc
   {.pragma: haha.}
@@ -439,3 +491,50 @@ when false:
 
   # left-to-right priority/override order for getCustomPragmaVal
   assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he")
+
+{.experimental: "dynamicBindSym".}
+
+# const
+block:
+  template myAttr() {.pragma.}
+  template myAttr2(x: int) {.pragma.}
+  template myAttr3(x: string) {.pragma.}
+
+  type
+    MyObj2 = ref object
+
+  const a {.myAttr,myAttr2(2),myAttr3:"test".}: int = 0
+  const b {.myAttr,myAttr2(2),myAttr3:"test".} = 0
+
+  macro forceHasCustomPragma(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(hasCustomPragma(x, y))
+
+  macro forceGetCustomPragmaVal(x: untyped, y: typed): untyped =
+    var x = bindSym(x.repr)
+    for c in x:
+      if c.symKind == nskConst:
+        x = c
+        break
+    result = getAst(getCustomPragmaVal(x, y))
+
+  template check(s: untyped) =
+    doAssert forceHasCustomPragma(s, myAttr)
+    doAssert forceHasCustomPragma(s, myAttr2)
+    doAssert forceGetCustomPragmaVal(s, myAttr2) == 2
+    doAssert forceHasCustomPragma(s, myAttr3)
+    doAssert forceGetCustomPragmaVal(s, myAttr3) == "test"
+
+  check(a)
+  check(b)
+
+block: # https://forum.nim-lang.org/t/12522, backticks
+  template `mypragma`() {.pragma.}
+  # Error: invalid pragma: `mypragma`
+  type Test = object
+    field {.`mypragma`.}: int
+  doAssert Test().field.hasCustomPragma(mypragma)
diff --git a/tests/pragmas/tinvalid_user_pragma.nim b/tests/pragmas/tinvalid_user_pragma.nim
new file mode 100644
index 000000000..3081db842
--- /dev/null
+++ b/tests/pragmas/tinvalid_user_pragma.nim
@@ -0,0 +1,9 @@
+discard """
+cmd: "nim check $file"
+"""
+
+{.pragma test: foo.} #[tt.Error
+^ invalid pragma:  {.pragma, test: foo.} ]#
+
+{.pragma: 1.} #[tt.Error
+^ invalid pragma:  {.pragma: 1.} ]#
diff --git a/tests/pragmas/tinvalidcustompragma.nim b/tests/pragmas/tinvalidcustompragma.nim
new file mode 100644
index 000000000..a31695809
--- /dev/null
+++ b/tests/pragmas/tinvalidcustompragma.nim
@@ -0,0 +1,23 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+# issue #21652
+type Foo = object
+template foo() {.tags:[Foo].} = #[tt.Error
+                     ^ invalid pragma: tags: [Foo]]#
+  discard
+
+{.foobar.} #[tt.Error
+  ^ invalid pragma: foobar]#
+type A = enum a {.foobar.} #[tt.Error
+                  ^ invalid pragma: foobar]#
+for b {.foobar.} in [1]: discard #[tt.Error
+        ^ invalid pragma: foobar]#
+template foobar {.pragma.}
+{.foobar.} #[tt.Error
+  ^ cannot attach a custom pragma to 'tinvalidcustompragma'; custom pragmas are not supported for modules]#
+type A = enum a {.foobar.} #[tt.Error
+                  ^ cannot attach a custom pragma to 'a'; custom pragmas are not supported for enum fields]#
+for b {.foobar.} in [1]: discard #[tt.Error
+        ^ cannot attach a custom pragma to 'b'; custom pragmas are not supported for `for` loop variables]#
diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim
index 6c2a9f9e9..5d6fcdd9c 100644
--- a/tests/pragmas/tlocks.nim
+++ b/tests/pragmas/tlocks.nim
@@ -1,7 +1,3 @@
-discard """
-  matrix: "--mm:arc; --mm:refc"
-"""
-
 type SomeBase* = ref object of RootObj
 type SomeDerived* = ref object of SomeBase
   memberProc*: proc ()
diff --git a/tests/pragmas/tonoff1.nim b/tests/pragmas/tonoff1.nim
new file mode 100644
index 000000000..20ba7def2
--- /dev/null
+++ b/tests/pragmas/tonoff1.nim
@@ -0,0 +1,8 @@
+# issue #23002
+
+import monoff1
+
+proc test() =
+  {.warning[ProveInit]: on.}
+
+test()
diff --git a/tests/pragmas/tonoff2.nim b/tests/pragmas/tonoff2.nim
new file mode 100644
index 000000000..9dff5ef11
--- /dev/null
+++ b/tests/pragmas/tonoff2.nim
@@ -0,0 +1,14 @@
+discard """
+    action: compile
+"""
+
+# issue #22841
+
+import unittest
+
+proc on() =
+    discard
+
+suite "some suite":
+    test "some test":
+        discard
diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim
index 6dc2e6b80..adb7e73c3 100644
--- a/tests/pragmas/tpragmas_misc.nim
+++ b/tests/pragmas/tpragmas_misc.nim
@@ -68,3 +68,8 @@ block: # issue #10994
 
   proc a {.bar.} = discard # works
   proc b {.bar, foo.} = discard # doesn't
+
+block: # issue #22525
+  macro catch(x: typed) = x
+  proc thing {.catch.} = discard
+  thing()
diff --git a/tests/pragmas/tpragmas_reorder.nim b/tests/pragmas/tpragmas_reorder.nim
new file mode 100644
index 000000000..c4b1a6b0a
--- /dev/null
+++ b/tests/pragmas/tpragmas_reorder.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--experimental:codeReordering"
+"""
+
+runnableExamples:
+  import strtabs
+  var t = newStringTable()
+  t["name"] = "John"
+  t["city"] = "Monaco"
+  doAssert t.len == 2
+  doAssert t.hasKey "name"
+  doAssert "name" in t
+
+include "system/inclrtl"
+
+{.pragma: rtlFunc, rtl.}
+
+proc hasKey*(): bool {.rtlFunc.} =
+  discard
\ No newline at end of file
diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim
index 5ecfe9704..9c6b85c4e 100644
--- a/tests/pragmas/tpush.nim
+++ b/tests/pragmas/tpush.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c js"
+"""
+
 # test the new pragmas
 
 {.push warnings: off, hints: off.}
@@ -13,3 +17,128 @@ proc WarnMe() =
     x: int
   echo(x)
 
+# bug #11852
+proc foo(x: string, y: int, res: int) =
+  {.push checks: off}
+  var a: ptr char = unsafeAddr(x[y])
+  {.pop.}
+  if x.len > y:
+    doAssert ord(a[]) == 51
+  else:
+    doAssert x.len + 48 == res
+
+foo("", 0, 48)
+foo("abc", 40, 51)
+
+# bug #22362
+{.push staticBoundChecks: on.}
+proc main(): void =
+  {.pop.}
+  discard
+  {.push staticBoundChecks: on.}
+
+main()
+
+
+proc timnFoo[T](obj: T) {.noSideEffect.} = discard # BUG
+
+{.push exportc.}
+proc foo1() =
+  var s1 = "bar"
+  timnFoo(s1)
+  var s2 = @[1]
+  timnFoo(s2)
+{.pop.}
+
+
+block: # bug #22913
+  block:
+    type r = object
+
+    template std[T](x: T) =
+      let ttt {.used.} = x
+      result = $ttt
+
+    proc bar[T](x: T): string =
+      std(x)
+
+    {.push exportc: "$1".}
+    proc foo(): r =
+      let s = bar(123)
+    {.pop.}
+
+    discard foo()
+
+  block:
+    type r = object
+    {.push exportc: "$1".}
+    proc foo2(): r =
+      let s = $result
+    {.pop.}
+
+    discard foo2()
+
+block: # bug #23019
+  proc f(x: bool)
+
+  proc a(x: int) =
+    if false: f(true)
+
+  proc f(x: bool) =
+    if false: a(0)
+
+  proc k(r: int|int) {.inline.} =  # seems to require being generic and inline
+    if false: a(0)
+
+
+  # {.push tags: [].}
+  {.push raises: [].}
+
+  {.push warning[ObservableStores]:off.}  # can be any warning, off or on
+  let w = 0
+  k(w)
+  {.pop.}
+  {.pop.}
+
+{.push exportC.}
+
+block:
+  proc foo11() =
+    const factor = [1, 2, 3, 4]
+    doAssert factor[0] == 1
+  proc foo21() =
+    const factor = [1, 2, 3, 4]
+    doAssert factor[0] == 1
+
+  foo11()
+  foo21()
+
+template foo31() =
+  let factor = [1, 2, 3, 4]
+  doAssert factor[0] == 1
+template foo41() =
+  let factor = [1, 2, 3, 4]
+  doAssert factor[0] == 1
+
+foo31()
+foo41()
+
+{.pop.}
+
+import macros
+
+block:
+  {.push deprecated.}
+  template test() = discard
+  test()
+  {.pop.}
+  macro foo(): bool =
+    let ast = getImpl(bindSym"test")
+    var found = false
+    if ast[4].kind == nnkPragma:
+      for x in ast[4]:
+        if x.eqIdent"deprecated":
+          found = true
+          break
+    result = newLit(found)
+  doAssert foo()
diff --git a/tests/pragmas/tpushnotes.nim b/tests/pragmas/tpushnotes.nim
new file mode 100644
index 000000000..27ba0bec4
--- /dev/null
+++ b/tests/pragmas/tpushnotes.nim
@@ -0,0 +1,13 @@
+discard """
+  matrix: "--warningAsError:HoleEnumConv"
+"""
+
+type
+  e = enum
+    a = 0
+    b = 2
+
+var i: int
+{.push warning[HoleEnumConv]:off.}
+discard i.e
+{.pop.}
diff --git a/tests/pragmas/tqualifiedmacro.nim b/tests/pragmas/tqualifiedmacro.nim
new file mode 100644
index 000000000..bc95ec1ea
--- /dev/null
+++ b/tests/pragmas/tqualifiedmacro.nim
@@ -0,0 +1,14 @@
+discard """
+  output: '''
+template t
+'''
+"""
+
+# issue #12696
+
+import mqualifiedmacro
+proc p() {. mqualifiedmacro.t .} = # errors with identifier expected but a.t found
+  echo "proc p"
+
+type Foo {. mqualifiedmacro.m("Bar") .} = object
+doAssert Bar is object
diff --git a/tests/pragmas/tuserpragmaargs.nim b/tests/pragmas/tuserpragmaargs.nim
new file mode 100644
index 000000000..791d703ac
--- /dev/null
+++ b/tests/pragmas/tuserpragmaargs.nim
@@ -0,0 +1,5 @@
+var foo {.exportc: "abc".} = 123
+{.pragma: importc2, importc.}
+var bar {.importc2: "abc".}: int #[tt.Error
+                  ^ user pragma cannot have arguments]#
+echo bar
diff --git a/tests/defaultprocparam/mdefaultprocparam.nim b/tests/proc/mdefaultprocparam.nim
index 4a17277c0..4a17277c0 100644
--- a/tests/defaultprocparam/mdefaultprocparam.nim
+++ b/tests/proc/mdefaultprocparam.nim
diff --git a/tests/proc/t15949.nim b/tests/proc/t15949.nim
index bc3fddc84..6467ed5d3 100644
--- a/tests/proc/t15949.nim
+++ b/tests/proc/t15949.nim
@@ -1,14 +1,9 @@
-# bug #15949
+# bug #15949 and RFC #480
 
-discard """
-errormsg: "parameter 'a' requires a type"
-nimout: '''
-t15949.nim(20, 14) Error: parameter 'a' requires a type'''
-"""
+proc procWarn(a, b = 1): (int, int) = (a, b) #[tt.Warning
+              ^ a, b all have default value '1', this may be unintentional, either use ';' (semicolon) or explicitly write each default value [ImplicitDefaultValue]]#
 
-
-# line 10
-proc procGood(a, b = 1): (int, int) = (a, b)
+proc procGood(a = 1, b = 1): (int, int) = (a, b)
 
 doAssert procGood() == (1, 1)
 doAssert procGood(b = 3) == (1, 3)
@@ -17,4 +12,5 @@ doAssert procGood(a = 5, b = 6) == (5, 6)
 
 # The type (and default value propagation breaks in the below example
 # as semicolon is used instead of comma.
-proc procBad(a; b = 1): (int, int) = (a, b)
+proc procBad(a; b = 1): (int, int) = (a, b) #[tt.Error
+             ^ parameter 'a' requires a type]#
diff --git a/tests/proc/t17157.nim b/tests/proc/t17157.nim
index 2927eeee8..020e93fce 100644
--- a/tests/proc/t17157.nim
+++ b/tests/proc/t17157.nim
@@ -1,6 +1,5 @@
 discard """
   errormsg: "'untyped' is only allowed in templates and macros or magic procs"
-  disabled: true
 """
 
 template something(op: proc (v: untyped): void): void =
diff --git a/tests/proc/t19795.nim b/tests/proc/t19795.nim
new file mode 100644
index 000000000..677ec0a63
--- /dev/null
+++ b/tests/proc/t19795.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--mm:arc"
+"""
+
+# bug #19795
+# bug #21085
+
+type Vector = seq[int]
+
+var vect: Vector = newSeq[int](5)
+doAssert vect == @[0, 0, 0, 0, 0]
+
+# Needed to get the problem. Could also use "var".
+let vectCopy = vect
+
+# Then some procedure definition is needed to get the problem.
+proc p(): int = 3
+
+doAssert vect == @[0, 0, 0, 0, 0]
\ No newline at end of file
diff --git a/tests/proc/t23874.nim b/tests/proc/t23874.nim
new file mode 100644
index 000000000..940bc4ac8
--- /dev/null
+++ b/tests/proc/t23874.nim
@@ -0,0 +1,26 @@
+block:
+  type Head[T] = object
+    wasc: bool
+
+  proc `=destroy`[T](x: var Head[T]) =
+    discard
+
+  proc `=copy`[T](x: var Head[T], y: Head[T]) =
+    x.wasc = true
+
+  proc `=dup`[T](x: Head[T]): Head[T] =
+    result.wasc = true
+
+  proc update(h: var Head) =
+    discard
+
+  proc digest(h: sink Head) =
+    assert h.wasc
+
+  var h = Head[int](wasc: false)
+  h.digest() # sink h
+  h.update() # use after sink
+
+block:
+  proc two(a: sink auto) =discard
+  assert typeof(two[int]) is proc(a: sink int) {.nimcall.}
diff --git a/tests/misc/tcolonisproc.nim b/tests/proc/tcolonisproc.nim
index c10dabcf1..c10dabcf1 100644
--- a/tests/misc/tcolonisproc.nim
+++ b/tests/proc/tcolonisproc.nim
diff --git a/tests/defaultprocparam/tdefaultprocparam.nim b/tests/proc/tdefaultprocparam.nim
index 90edfa8c9..90edfa8c9 100644
--- a/tests/defaultprocparam/tdefaultprocparam.nim
+++ b/tests/proc/tdefaultprocparam.nim
diff --git a/tests/proc/texplicitgenericcount.nim b/tests/proc/texplicitgenericcount.nim
new file mode 100644
index 000000000..8654a1d13
--- /dev/null
+++ b/tests/proc/texplicitgenericcount.nim
@@ -0,0 +1,24 @@
+discard """
+  cmd: "nim check -d:testsConciseTypeMismatch $file"
+"""
+
+proc foo[T, U](x: T, y: U): (T, U) = (x, y)
+
+let x = foo[int](1, 2) #[tt.Error
+                ^ type mismatch
+Expression: foo[int](1, 2)
+  [1] 1: int literal(1)
+  [2] 2: int literal(2)
+
+Expected one of (first mismatch at [position]):
+[2] proc foo[T, U](x: T; y: U): (T, U)
+  missing generic parameter: U]#
+let y = foo[int, float, string](1, 2) #[tt.Error
+                               ^ type mismatch
+Expression: foo[int, float, string](1, 2)
+  [1] 1: int literal(1)
+  [2] 2: int literal(2)
+
+Expected one of (first mismatch at [position]):
+[3] proc foo[T, U](x: T; y: U): (T, U)
+  extra generic param given]#
diff --git a/tests/proc/texplicitgenericcountverbose.nim b/tests/proc/texplicitgenericcountverbose.nim
new file mode 100644
index 000000000..76228eeaf
--- /dev/null
+++ b/tests/proc/texplicitgenericcountverbose.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim check $file"
+"""
+
+proc foo[T, U](x: T, y: U): (T, U) = (x, y)
+
+let x = foo[int](1, 2) #[tt.Error
+                ^ type mismatch: got <int literal(1), int literal(2)>
+but expected one of:
+proc foo[T, U](x: T; y: U): (T, U)
+  first type mismatch at position: 2 in generic parameters
+  missing generic parameter: U
+
+expression: foo[int](1, 2)]#
+let y = foo[int, float, string](1, 2) #[tt.Error
+                               ^ type mismatch: got <int literal(1), int literal(2)>
+but expected one of:
+proc foo[T, U](x: T; y: U): (T, U)
+  first type mismatch at position: 3 in generic parameters
+  extra generic param given
+
+expression: foo[int, float, string](1, 2)]#
diff --git a/tests/proc/texplicitgenerics.nim b/tests/proc/texplicitgenerics.nim
new file mode 100644
index 000000000..833d77b3b
--- /dev/null
+++ b/tests/proc/texplicitgenerics.nim
@@ -0,0 +1,55 @@
+block: # issue #16376
+  type
+    Matrix[T] = object
+      data: T
+  proc randMatrix[T](m, n: int, max: T): Matrix[T] = discard
+  proc randMatrix[T](m, n: int, x: Slice[T]): Matrix[T] = discard
+  template randMatrix[T](m, n: int): Matrix[T] = randMatrix[T](m, n, T(1.0))
+  let B = randMatrix[float32](20, 10)
+
+block: # different generic param counts 
+  type
+    Matrix[T] = object
+      data: T
+  proc randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0))
+  proc randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U))
+  let b = randMatrix[float32](20, 10)
+  doAssert b == Matrix[float32](data: 1.0)
+
+block: # above for templates 
+  type
+    Matrix[T] = object
+      data: T
+  template randMatrix[T](m: T, n: T): Matrix[T] = Matrix[T](data: T(1.0))
+  template randMatrix[T; U: not T](m: T, n: U): (Matrix[T], U) = (Matrix[T](data: T(1.0)), default(U))
+  let b = randMatrix[float32](20, 10)
+  doAssert b == Matrix[float32](data: 1.0)
+
+block: # sigmatch can't handle this without pre-instantiating the type:
+  # minimized from numericalnim
+  type Foo[T] = proc (x: T)
+  proc foo[T](x: T) = discard
+  proc bar[T](f: Foo[T]) = discard
+  bar[int](foo)
+
+block: # ditto but may be wrong minimization
+  # minimized from measuremancer
+  type Foo[T] = object
+  proc foo[T](): Foo[T] = Foo[T]()
+  # this is the actual issue but there are other instantiation problems
+  proc bar[T](x = foo[T]()) = discard
+  bar[int](Foo[int]())
+  bar[int]()
+  # alternative version, also causes instantiation issue
+  proc baz[T](x: typeof(foo[T]())) = discard
+  baz[int](Foo[int]())
+
+block: # issue #21346
+  type K[T] = object
+  template s[T](x: int) = doAssert T is K[K[int]]
+  proc b1(n: bool | bool) = s[K[K[int]]](3)
+  proc b2(n: bool)        = s[K[K[int]]](3)
+  template b3(n: bool)    = s[K[K[int]]](3)
+  b1(false)     # Error: cannot instantiate K; got: <T> but expected: <T>
+  b2(false)     # Builds, on its own
+  b3(false)
diff --git a/tests/proc/tgenericdefaultparam.nim b/tests/proc/tgenericdefaultparam.nim
new file mode 100644
index 000000000..7bce591ce
--- /dev/null
+++ b/tests/proc/tgenericdefaultparam.nim
@@ -0,0 +1,98 @@
+block: # issue #16700
+  type MyObject[T] = object
+    x: T
+  proc initMyObject[T](value = T.default): MyObject[T] =
+    MyObject[T](x: value)
+  var obj = initMyObject[int]()
+
+block: # issue #20916
+  type
+    SomeX = object
+      v: int
+  var val = 0
+  proc f(_: type int, x: SomeX, v = x.v) =
+    doAssert v == 42
+    val = v
+  proc a(): proc() =
+    let v = SomeX(v: 42)
+    var tmp = proc() =
+      int.f(v)
+    tmp
+  a()()
+  doAssert val == 42
+
+import std/typetraits
+
+block: # issue #24099, original example
+  type
+    ColorRGBU = distinct array[3, uint8] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e
+
+block: # issue #24099, modified to actually work
+  type
+    ColorRGBU = distinct array[3, uint8] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, uint8] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template arrayType[I, T](t: typedesc[array[I, T]]): typedesc =
+    T
+  template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i]
+  proc abs(a: uint8): uint8 = a
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) <= e and abs(a[1] - b[1]) <= e and abs(a[2] - b[2]) <= e
+  doAssert ColorRGBU([1.uint8, 1, 1]) ~= ColorRGBU([1.uint8, 1, 1])
+
+block: # issue #24099, modified to work but using float32
+  type
+    ColorRGBU = distinct array[3, float32] ## RGB range 0..255
+    ColorRGBAU = distinct array[4, float32] ## RGB range 0..255
+    ColorRGBUAny = ColorRGBU | ColorRGBAU
+  template arrayType[I, T](t: typedesc[array[I, T]]): typedesc =
+    T
+  template `[]`(a: ColorRGBUAny, i: untyped): untyped = distinctBase(a)[i]
+  template componentType(t: typedesc[ColorRGBUAny]): typedesc =
+    ## Returns component type of a given color type.
+    arrayType distinctBase t
+  func `~=`[T: ColorRGBUAny](a, b: T, e = componentType(T)(1.0e-11)): bool =
+    ## Compares colors with given accuracy.
+    abs(a[0] - b[0]) < e and abs(a[1] - b[1]) < e and abs(a[2] - b[2]) < e
+  doAssert ColorRGBU([1.float32, 1, 1]) ~= ColorRGBU([1.float32, 1, 1])
+
+block: # issue #13270
+  type
+    A = object
+    B = object
+  proc f(a: A) = discard
+  proc g[T](value: T, cb: (proc(a: T)) = f) =
+    cb value
+  g A()
+  # This should fail because there is no f(a: B) overload available
+  doAssert not compiles(g B())
+
+block: # issue #24121
+  type
+    Foo = distinct int
+    Bar = distinct int
+    FooBar = Foo | Bar
+
+  proc foo[T: distinct](x: T): string = "a"
+  proc foo(x: Foo): string = "b"
+  proc foo(x: Bar): string = "c"
+
+  proc bar(x: FooBar, y = foo(x)): string = y
+  doAssert bar(Foo(123)) == "b"
+  doAssert bar(Bar(123)) == "c"
+
+  proc baz[T: FooBar](x: T, y = foo(x)): string = y
+  doAssert baz(Foo(123)) == "b"
+  doAssert baz(Bar(123)) == "c"
diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim
index 799cad53a..1076f7f75 100644
--- a/tests/proc/tillegalreturntype.nim
+++ b/tests/proc/tillegalreturntype.nim
@@ -1,5 +1,5 @@
 discard """
-  cmd: "nim check $file"
+  cmd: "nim check --hints:off $file"
   errormsg: ""
   nimout: '''
 tillegalreturntype.nim(11, 11) Error: return type 'typed' is only valid for macros and templates
diff --git a/tests/proc/tinferlambdareturn.nim b/tests/proc/tinferlambdareturn.nim
new file mode 100644
index 000000000..e9e592871
--- /dev/null
+++ b/tests/proc/tinferlambdareturn.nim
@@ -0,0 +1,36 @@
+import std/[sugar, sequtils]
+
+block: # issue #23200
+  proc dosomething(iter: int -> (iterator: int)) =
+    discard
+  proc dosomething(iter: int -> seq[int]) =
+    discard
+  proc makeSeq(x: int): seq[int] =
+    @[x]
+  # Works fine with 1.6.12 and 1.6.14
+  dosomething(makeSeq)
+  # Works with 1.6.12, fails with 1.6.14
+  dosomething((y) => makeSeq(y))
+  dosomething(proc (y: auto): auto = makeSeq(y))
+  proc foo(y: auto): auto = makeSeq(y)
+  dosomething(foo)
+
+block: # issue #18866
+  proc somefn[T](list: openarray[T], op: proc (v: T): float) =
+    discard op(list[0])
+
+  type TimeD = object
+    year:  Natural
+    month: 1..12
+    day:   1..31
+
+  doAssert not compiles(@[TimeD()].somefn(proc (v: auto): auto =
+    v
+  ))
+  @[TimeD()].somefn(proc (v: auto): auto =
+    v.year.float
+  )
+  proc foo(v: auto): auto = v
+  doAssert not compiles(@[TimeD()].somefn(foo))
+  proc bar(v: auto): auto = v.year.float
+  @[TimeD()].somefn(bar)
diff --git a/tests/misc/tlambdadonotation.nim b/tests/proc/tlambdadonotation.nim
index af51efdbf..3160c0972 100644
--- a/tests/misc/tlambdadonotation.nim
+++ b/tests/proc/tlambdadonotation.nim
@@ -67,7 +67,7 @@ proc main2() =
   proc foo() =
     subscriber.consume()
 
-  emitter.on_event() do:
+  emitter.on_event() do ():
     subscriber.consume()
 
   # this works
diff --git a/tests/namedparams/tnamedparams.nim b/tests/proc/tnamedparams.nim
index d0774f0d8..d0774f0d8 100644
--- a/tests/namedparams/tnamedparams.nim
+++ b/tests/proc/tnamedparams.nim
diff --git a/tests/namedparams/tnamedparams2.nim b/tests/proc/tnamedparams2.nim
index 9acdeed87..9acdeed87 100644
--- a/tests/namedparams/tnamedparams2.nim
+++ b/tests/proc/tnamedparams2.nim
diff --git a/tests/namedparams/tnamedparams3.nim b/tests/proc/tnamedparams3.nim
index e736c338c..e736c338c 100644
--- a/tests/namedparams/tnamedparams3.nim
+++ b/tests/proc/tnamedparams3.nim
diff --git a/tests/misc/tparamsindefault.nim b/tests/proc/tparamsindefault.nim
index 3fe917f2b..3fe917f2b 100644
--- a/tests/misc/tparamsindefault.nim
+++ b/tests/proc/tparamsindefault.nim
diff --git a/tests/procvar/tprocvar.nim b/tests/proc/tprocvar.nim
index 14f24efdc..14f24efdc 100644
--- a/tests/procvar/tprocvar.nim
+++ b/tests/proc/tprocvar.nim
diff --git a/tests/procvar/tprocvarmismatch.nim b/tests/proc/tprocvarmismatch.nim
index 4d6be9be6..4d6be9be6 100644
--- a/tests/procvar/tprocvarmismatch.nim
+++ b/tests/proc/tprocvarmismatch.nim
diff --git a/tests/proc/tstaticsignature.nim b/tests/proc/tstaticsignature.nim
new file mode 100644
index 000000000..25aa09c5d
--- /dev/null
+++ b/tests/proc/tstaticsignature.nim
@@ -0,0 +1,268 @@
+block: # issue #4228
+  template seqType(t: typedesc): typedesc =
+    when t is int:
+      seq[int]
+    else:
+      seq[string]
+
+  proc mkSeq[T: int|string](v: T): seqType(T) =
+    result = newSeq[T](1)
+    result[0] = v
+
+  doAssert mkSeq("a") == @["a"]
+  doAssert mkSeq(1) == @[1]
+
+block: # expanded version of t8545
+  template bar(a: static[bool]): untyped =
+    when a:
+      int
+    else:
+      float
+
+  proc main() =
+    proc foo1(a: static[bool]): auto = 1
+    doAssert foo1(true) == 1
+
+    proc foo2(a: static[bool]): bar(a) = 1
+    doAssert foo2(true) == 1
+    doAssert foo2(true) is int
+    doAssert foo2(false) == 1.0
+    doAssert foo2(false) is float
+
+    proc foo3(a: static[bool]): bar(cast[bool](a)) = 1
+    doAssert foo3(true) == 1
+    doAssert foo3(true) is int
+    doAssert foo3(false) == 1.0
+    doAssert foo3(false) is float
+
+    proc foo4(a: static[bool]): bar(static(a)) = 1
+    doAssert foo4(true) == 1
+    doAssert foo4(true) is int
+    doAssert foo4(false) == 1.0
+    doAssert foo4(false) is float
+
+  static: main()
+  main()
+
+block: # issue #8406
+  macro f(x: static[int]): untyped = discard
+  proc g[X: static[int]](v: f(X)) = discard
+
+import macros
+
+block: # issue #8551
+  macro distinctBase2(T: typedesc): untyped =
+    let typeNode = getTypeImpl(T)
+    expectKind(typeNode, nnkBracketExpr)
+    if typeNode[0].typeKind != ntyTypeDesc:
+      error "expected typeDesc, got " & $typeNode[0]
+    var typeSym = typeNode[1]
+
+    typeSym = getTypeImpl(typeSym)
+
+    if typeSym.typeKind != ntyDistinct:
+      error "type is not distinct: " & $typeSym.typeKind
+
+    typeSym = typeSym[0]
+    typeSym
+
+  func distinctBase[T](a: T): distinctBase2(T) = distinctBase2(T)(a)
+
+  type T = distinct int
+  doAssert distinctBase(T(0)) is int
+
+block:
+  type Foo[T] = object
+    x: T
+
+  proc foo(x: Foo): Foo[x.T] =
+    doAssert typeof(result) is typeof(x)
+
+  var a: Foo[int]
+  let b: Foo[int] = foo(a)
+  doAssert b.x is int
+
+block:
+  type Foo[T: static int] = object
+    x: array[T, int]
+  
+  proc double(x: int): int = x * 2
+
+  proc foo[T: static int](x: Foo[T]): Foo[T.double] =
+    doAssert typeof(result).T == double(typeof(x).T)
+
+  var a: Foo[3]
+  let b: Foo[6] = foo(a)
+  doAssert $typeof(foo(a)) == "Foo[6]"
+
+block:
+  type Foo[T: static int] = object
+    x: array[T, int]
+
+  proc foo(x: Foo): Foo[x.T] =
+    doAssert typeof(result).T == typeof(x).T
+    doAssert typeof(result) is typeof(x)
+
+  var a: Foo[3]
+  let b: Foo[3] = foo(a)
+  doAssert $typeof(foo(a)) == "Foo[3]"
+
+block: # issue #7006
+  type
+    Node[T] = object
+      val: T
+      next: ref Node[T]
+    HHSet[T, Key] = object
+      data: seq[Node[T]]
+  proc rawGet(hhs:HHSet; key: hhs.Key): ptr Node[hhs.T] =
+    return nil # body doesn't matter
+  var hhs: HHSet[string, cstring]
+  discard hhs.rawGet("hello".cstring)
+
+block: # issue #7008
+  type Node[T] = object
+    val: T
+  # Compiles fine
+  proc concreteProc(s: Node[cstring]; key: s.T) = discard
+  # Also fine
+  proc implicitGenericProc1(s: Node; key: s.T) = discard
+  # still fine
+  proc explicitGenericProc1[T](s: Node[T]; key: T) = discard
+  # Internal Compiler Error!
+  proc explicitGenericProc2[T](s: Node[T]; key: s.T) = discard
+  let n = Node[int](val: 5)
+  implicitGenericProc1(n, 5) # works
+  explicitGenericProc1(n, 5) # works
+  explicitGenericProc2(n, 5) # doesn't
+
+block: # issue #20027
+  block:
+    type Test[T] = object
+    proc run(self: Test): self.T = discard
+    discard run(Test[int]())
+  block:
+    type Test[T] = object
+    proc run[T](self: Test[T]): self.T = discard
+    discard run(Test[int]())
+  block:
+    type Test[T] = object
+    proc run(self: Test[auto]): self.T = discard
+    discard run(Test[int]())
+
+block: # issue #11112
+  proc foo[A, B]: type(A.default + B.default) =
+    discard
+  doAssert foo[int, int]() is int
+
+block: # tyStatic and tyFromExpr instantiation mid-match
+  proc bar(x: int): int = x * 3
+  proc bar2(x: static int): int = x * 4
+  type Foo[T: static int] = distinct array[T, int]
+  proc foo[T: static int](x: Foo[T], y: Foo[bar(T)]) = discard
+  proc foo2[T: static int](x: Foo[T], y: Foo[bar2(T)]) = discard
+  foo(Foo[1]([1]), Foo[3]([1, 2, 3]))
+  foo2(Foo[1]([1]), Foo[4]([1, 2, 3, 4]))
+
+block: # issue #4990
+  type Foo[I: static[int], A: static[array[I, int]]] = object
+    curIndex: int
+
+  proc next[I: static[int], A: static[array[I, int]]](f: Foo[I, A]): string =
+    discard
+  const arr = [1, 2, 3]
+  var f: Foo[arr.len, arr]
+  discard next(f)
+
+block: # issue #4990 comment
+  type
+    Foo[A: static[int], B: static[int], TokenType: enum, EofToken: static[TokenType]] = object
+      curIndex: int
+    MyEnum = enum
+      meA, meB
+    Bar = Foo[2, 3, MyEnum, meA]
+  proc next[A: static[int], B: static[int], TokenType: enum,
+            EofToken: static[TokenType]](f: Foo[A, B, TokenType, EofToken],
+      a: static[(array[A, int], array[B, int])]): TokenType =
+    TokenType(a[0][f.curIndex])
+  const
+    a = [1, 2]
+    b = [3, 4, 5]
+  template next(bar: Bar): MyEnum =
+    next(Foo[2, 3, MyEnum, meA](bar), (a, b))
+  let bar = Bar(curIndex: 0)
+  doAssert bar.next() == meB
+
+block: # issue #14053
+  template returnType(value: static[int]): typedesc =
+    when value == 1:
+      int
+    else:
+      float
+  proc fun(value: static[int]): returnType(value) = discard
+  doAssert fun(1) is int
+  template returnType2(value: static[int]): typedesc =
+    int
+  proc fun2(value: static[int]): returnType2(value) = discard
+  doAssert fun2(1) is int
+
+block: # issue #7547
+  macro foo(N: static[int]): untyped =
+    result = getType(int)
+  type
+    Foo[N: static[int]] = foo(N)
+    ContainsFoo[N: static[int]] = object
+      Ffoo: Foo[N]
+  proc initFoo(N: static[int]): Foo[N] = discard
+  proc initContainsFoo(size: static[int]): ContainsFoo[size] = discard
+  var a: Foo[10] # Works
+  doAssert a is int
+  let b = initFoo(10) # Works
+  doAssert b is int
+  let c = ContainsFoo[5]() # Works
+  doAssert c.Ffoo is int
+  let z = initContainsFoo(5) # Error: undeclared identifier: 'N'
+  doAssert z.Ffoo is int
+
+block: # issue #22607, needs nkWhenStmt to be handled like nkRecWhen
+  proc test[x: static bool](
+    t: (
+      when x:
+        int
+      else:
+        float
+      )
+  ) = discard
+  test[true](1.int)
+  test[false](1.0)
+  doAssert not compiles(test[])
+
+block: # `when` in static signature
+  template ctAnd(a, b): bool =
+    when a:
+      when b: true
+      else: false
+    else: false
+  template test(): untyped =
+    when ctAnd(declared(SharedTable), typeof(result) is SharedTable):
+      result = SharedTable()
+    else:
+      result = 123
+  proc foo[T](): T = test()
+  proc bar[T](x = foo[T]()): T = x
+  doAssert bar[int]() == 123
+
+block: # issue #22276
+  type Foo = enum A, B
+  macro test(y: static[Foo]): untyped =
+    if y == A:
+      result = parseExpr("proc (x: int)")
+    else:
+      result = parseExpr("proc (x: float)")
+  proc foo(y: static[Foo], x: test(y)) = # We want to make the type of `x` depend on what `y` is
+    x(9)
+  foo(A, proc (x: int) = doAssert x == 9)
+  var a: int
+  foo(A, proc (x: int) =
+    a = x * 2)
+  doAssert a == 18
+  foo(B, proc (x: float) = doAssert x == 9)
diff --git a/tests/proc/tunderscoreparam.nim b/tests/proc/tunderscoreparam.nim
new file mode 100644
index 000000000..8d60603f1
--- /dev/null
+++ b/tests/proc/tunderscoreparam.nim
@@ -0,0 +1,122 @@
+discard """
+  targets: "c cpp js"
+"""
+
+import std/[assertions, sequtils]
+
+proc test() =
+  block:
+    proc ok(_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doassert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    proc ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+    
+  block:
+    proc ok[T](_, _, a: T): T =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+    doAssert ok("a", "b", "c") == "c"
+    doAssert not compiles(ok(1, 2, "a"))
+  
+  block:
+    let ok = proc (_, _, a: int): int =
+      doAssert not compiles(_)
+      a
+    doAssert ok(4, 2, 5) == 5
+  
+  block:
+    proc foo(lam: proc (_, _, a: int): int): int =
+      lam(4, 2, 5)
+    doAssert foo(proc (_, _, a: auto): auto =
+      doAssert not compiles(_)
+      a) == 5
+    
+  block:
+    iterator fn(_, _: int, c: int): int = yield c
+    doAssert toSeq(fn(1,2,3)) == @[3]
+
+  block:
+    template ok(_, _, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    doAssert not (compiles do:
+      template bad(_: int): int = _
+      discard bad(3))
+
+  block:
+    template ok(_: int, _: int, a: int): int = a
+    doAssert ok(4, 2, 5) == 5
+
+  block:
+    template ok(_: int, _: float, a: int): int = a
+    doAssert ok(1, 2.0, 5) == 5
+
+  block:
+    template ok(_: int, _: float, _: string, a: int): int = a
+    doAssert ok(1, 2.6, "5", 5) == 5
+  
+  block:
+    template main2() =
+      iterator fn(_, _: int, c: int): int = yield c
+    main2()
+
+  block:
+    template main =
+      proc foo(_: int) =
+        let a = _
+    doAssert not compiles(main())
+  
+  block: # generic params
+    doAssert not (compiles do:
+      proc foo[_](t: typedesc[_]): seq[_] = @[default(_)]
+      doAssert foo[int]() == 0)
+  
+  block:
+    proc foo[_, _](): int = 123
+    doAssert foo[int, bool]() == 123
+  
+  block:
+    proc foo[T; U](_: typedesc[T]; _: typedesc[U]): (T, U) = (default(T), default(U))
+    doAssert foo(int, bool) == (0, false)
+
+proc closureTest() =
+  var x = 0
+
+  block:
+    proc foo(_, _: int) = x += 5
+
+    foo(1, 2)
+    doAssert x == 5
+
+  block:
+    proc foo(_: int, _: float) = x += 5
+
+    foo(1, 2)
+    doAssert x == 10
+
+  block:
+    proc foo(_: int, _: float, _: string) = x += 5
+
+    foo(1, 2, "5")
+    doAssert x == 15
+
+static: test()
+test()
+
+when not defined(js):
+  static: closureTest()
+closureTest()
diff --git a/tests/proc/twrongdefaultvalue.nim b/tests/proc/twrongdefaultvalue.nim
new file mode 100644
index 000000000..2c36c2247
--- /dev/null
+++ b/tests/proc/twrongdefaultvalue.nim
@@ -0,0 +1,25 @@
+discard """
+  cmd: "nim check $file"
+  action: reject
+  nimout: '''
+twrongdefaultvalue.nim(20, 12) template/generic instantiation of `doit` from here
+twrongdefaultvalue.nim(17, 37) Error: type mismatch: got <proc (p: int): Item[initItem.T]> but expected 'Item[system.string]'
+twrongdefaultvalue.nim(25, 3) template/generic instantiation of `foo` from here
+twrongdefaultvalue.nim(23, 33) Error: type mismatch: got <string> but expected 'int'
+'''
+"""
+
+block: # issue #21258
+  type Item[T] = object
+    pos: int
+  proc initItem[T](p:int=10000) : Item[T] = 
+    result = Item[T](p)
+  proc doit[T](x:Item[T], s:Item[T]=initItem) : string = 
+    return $x.pos
+  let x = Item[string](pos:100)
+  echo doit(x)
+
+block: # issue #21258, reduced case
+  proc foo[T](x: seq[T], y: T = "foo") =
+    discard
+  foo @[1, 2, 3]
diff --git a/tests/range/t19678.nim b/tests/range/t19678.nim
new file mode 100644
index 000000000..88f7eff89
--- /dev/null
+++ b/tests/range/t19678.nim
@@ -0,0 +1,17 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t19678.nim(13, 13) Error: range of string is invalid
+
+
+
+'''
+"""
+
+case "5":
+  of "0" .. "9":
+    discard
+  else:
+    discard
+
diff --git a/tests/range/tcompiletime_range_checks.nim b/tests/range/tcompiletime_range_checks.nim
index 29e2c48a8..2d3f292ec 100644
--- a/tests/range/tcompiletime_range_checks.nim
+++ b/tests/range/tcompiletime_range_checks.nim
@@ -1,8 +1,8 @@
 discard """
   cmd: "nim check --hint:Processing:off --hint:Conf:off $file"
   errormsg: "18446744073709551615 can't be converted to int8"
-  nimout: '''tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32
-tcompiletime_range_checks.nim(37, 23) Error: -1 can't be converted to uint64
+  nimout: '''
+tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32
 tcompiletime_range_checks.nim(38, 34) Error: 255 can't be converted to FullNegativeRange
 tcompiletime_range_checks.nim(39, 34) Error: 18446744073709551615 can't be converted to HalfNegativeRange
 tcompiletime_range_checks.nim(40, 34) Error: 300 can't be converted to FullPositiveRange
diff --git a/tests/range/texplicitvarconv.nim b/tests/range/texplicitvarconv.nim
new file mode 100644
index 000000000..8da8a8878
--- /dev/null
+++ b/tests/range/texplicitvarconv.nim
@@ -0,0 +1,13 @@
+# related to issue #24032
+
+proc `++`(n: var int) =
+    n += 1
+
+type
+    r = range[ 0..15 ]
+
+var a: r = 14
+
+++int(a) # this should be mutable
+
+doAssert a == 15
diff --git a/tests/range/toutofrangevarconv.nim b/tests/range/toutofrangevarconv.nim
new file mode 100644
index 000000000..1ee4d340e
--- /dev/null
+++ b/tests/range/toutofrangevarconv.nim
@@ -0,0 +1,14 @@
+discard """
+  outputsub: "value out of range: 5 notin 0 .. 3 [RangeDefect]"
+  exitcode: "1"
+"""
+
+# make sure out of bounds range conversion is detected for `var` conversions
+
+type R = range[0..3]
+
+proc foo(x: var R) =
+  doAssert x in 0..3
+
+var x = 5
+foo(R(x))
diff --git a/tests/range/trange.nim b/tests/range/trange.nim
index 92ab5ea86..abfa7d474 100644
--- a/tests/range/trange.nim
+++ b/tests/range/trange.nim
@@ -74,37 +74,37 @@ block tn8vsint16:
 
 import strutils
 block tcolors:
-  type TColor = distinct int32
+  type TColor = distinct uint32
 
   proc rgb(r, g, b: range[0..255]): TColor =
     result = TColor(r or g shl 8 or b shl 16)
   proc `$`(c: TColor): string =
-    result = "#" & toHex(int32(c), 6)
+    result = "#" & toHex(uint32(c), 6)
   echo rgb(34, 55, 255)
 
-  when false:
+  block:
     type
-      TColor = distinct int32
-      TColorComponent = distinct int8
+      TColor = distinct uint32
+      TColorComponent = distinct uint8
 
     proc red(a: TColor): TColorComponent =
-      result = TColorComponent(int32(a) and 0xff'i32)
+      result = TColorComponent(uint32(a) and 0xff'u32)
     proc green(a: TColor): TColorComponent =
-      result = TColorComponent(int32(a) shr 8'i32 and 0xff'i32)
+      result = TColorComponent(uint32(a) shr 8'u32 and 0xff'u32)
     proc blue(a: TColor): TColorComponent =
-      result = TColorComponent(int32(a) shr 16'i32 and 0xff'i32)
+      result = TColorComponent(uint32(a) shr 16'u32 and 0xff'u32)
     proc rgb(r, g, b: range[0..255]): TColor =
       result = TColor(r or g shl 8 or b shl 8)
 
     proc `+!` (a, b: TColorComponent): TColorComponent =
       ## saturated arithmetic:
-      result = TColorComponent(min(ze(int8(a)) + ze(int8(b)), 255))
+      result = TColorComponent(min(int(uint8(a)) + int(uint8(b)), 255))
 
     proc `+` (a, b: TColor): TColor =
       ## saturated arithmetic for colors makes sense, I think:
-      return rgb(red(a) +! red(b), green(a) +! green(b), blue(a) +! blue(b))
+      return rgb(int(red(a) +! red(b)), int(green(a) +! green(b)), int(blue(a) +! blue(b)))
 
-    rgb(34, 55, 255)
+    discard rgb(34, 55, 255)
 
 block:
   type
diff --git a/tests/readme.md b/tests/readme.md
index b67ec55db..f638ddc10 100644
--- a/tests/readme.md
+++ b/tests/readme.md
@@ -2,14 +2,14 @@ This directory contains the test cases.
 
 Each test must have a filename of the form: ``t*.nim``
 
-**Note:** Testament is only aware of tests under a directory (eg `tests/foo/`) and will ignore
+**Note:** [Testament](https://nim-lang.github.io/Nim/testament.html) is only aware of tests under a directory (eg `tests/foo/`) and will ignore
 top-level tests like `tests/tbar.nim`.
 
 # Specs
 
 Each test can contain a spec in a ``discard """ ... """`` block.
 
-**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L158) in the ``specs`` module for a full and reliable reference**
+**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L315) in the ``specs`` module for a full and reliable reference**
 
 ## action
 
diff --git a/tests/refc/tsinkbug.nim b/tests/refc/tsinkbug.nim
new file mode 100644
index 000000000..de2ec98a5
--- /dev/null
+++ b/tests/refc/tsinkbug.nim
@@ -0,0 +1,26 @@
+discard """
+  matrix: "--gc:refc; --gc:arc"
+  output: '''
+Value is: 42
+Value is: 42'''
+"""
+
+type AnObject* = object of RootObj
+  value*: int
+
+proc mutate(a: sink AnObject) =
+  a.value = 1
+
+var obj = AnObject(value: 42)
+echo "Value is: ", obj.value
+mutate(obj)
+echo "Value is: ", obj.value
+
+proc p(x: sink string) = 
+  var y = move(x)
+  doAssert x.len == 0
+  doAssert y.len == 4
+
+p("1234")
+var s = "oooo"
+p(s)
diff --git a/tests/sets/t13764.nim b/tests/sets/t13764.nim
new file mode 100644
index 000000000..1634f113d
--- /dev/null
+++ b/tests/sets/t13764.nim
@@ -0,0 +1,6 @@
+discard """
+errormsg: "conversion from int literal(1000000) to range 0..255(int) is invalid"
+line: 6
+"""
+
+let a = {1_000_000} # Compiles
diff --git a/tests/sets/t20997.nim b/tests/sets/t20997.nim
new file mode 100644
index 000000000..b320eee1a
--- /dev/null
+++ b/tests/sets/t20997.nim
@@ -0,0 +1,18 @@
+discard """
+  joinable: false
+"""
+
+{.passC: "-flto".}
+{.passL: "-flto".}
+
+template f(n: int) = discard card(default(set[range[0 .. (1 shl n) - 1]]))
+f( 7)
+f( 8)
+f( 9)
+f(10)
+f(11)
+f(12)
+f(13)
+f(14)
+f(15)
+f(16)
diff --git a/tests/sets/thugeset.nim b/tests/sets/thugeset.nim
new file mode 100644
index 000000000..1d82ebede
--- /dev/null
+++ b/tests/sets/thugeset.nim
@@ -0,0 +1,10 @@
+let x = 20_000
+let s = {x, 123} #[tt.Warning
+        ^ type 'int' is too big to be a `set` element, assuming a range of 0..65535, explicitly write this range to get rid of warning [AboveMaxSizeSet]]#
+doAssert x in s
+doAssert 20_000 in s
+{.push warningAsError[AboveMaxSizeSet]: on.}
+let s2 = {range[0..65535](x), 123}
+doAssert x in s
+doAssert 20_000 in s
+{.pop.}
diff --git a/tests/misc/tnewsets.nim b/tests/sets/tnewsets.nim
index f239d4aa2..f239d4aa2 100644
--- a/tests/misc/tnewsets.nim
+++ b/tests/sets/tnewsets.nim
diff --git a/tests/sets/trangeincompatible.nim b/tests/sets/trangeincompatible.nim
new file mode 100644
index 000000000..554a50235
--- /dev/null
+++ b/tests/sets/trangeincompatible.nim
@@ -0,0 +1,32 @@
+block: # issue #20142
+  let
+    s1: set['a' .. 'g'] = {'a', 'e'}
+    s2: set['a' .. 'g'] = {'b', 'c', 'd', 'f'} # this works fine
+    s3 = {'b', 'c', 'd', 'f'}
+
+  doAssert s1 != s2
+  doAssert s1 == {range['a'..'g'] 'a', 'e'}
+  doAssert s2 == {range['a'..'g'] 'b', 'c', 'd', 'f'}
+  # literal conversion:
+  doAssert s1 == {'a', 'e'}
+  doAssert s2 == {'b', 'c', 'd', 'f'}
+  doAssert s3 == {'b', 'c', 'd', 'f'}
+  doAssert not compiles(s1 == s3)
+  doAssert not compiles(s2 == s3)
+  # can't convert literal 'z', overload match fails
+  doAssert not compiles(s1 == {'a', 'z'})
+
+block: # issue #18396
+  var s1: set[char] = {'a', 'b'}
+  var s2: set['a'..'z'] = {'a', 'b'}
+  doAssert s1 == {'a', 'b'}
+  doAssert s2 == {range['a'..'z'] 'a', 'b'}
+  doAssert s2 == {'a', 'b'}
+  doAssert not compiles(s1 == s2)
+
+block: # issue #16270
+  var s1: set[char] = {'a', 'b'}
+  var s2: set['a'..'z'] = {'a', 'c'}
+  doAssert not (compiles do: s2 = s2 + s1)
+  s2 = s2 + {'a', 'b'}
+  doAssert s2 == {'a', 'b', 'c'}
diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim
index 517c0b89c..6125a3715 100644
--- a/tests/sets/tsets.nim
+++ b/tests/sets/tsets.nim
@@ -1,3 +1,7 @@
+discard """
+  targets: "c cpp"
+"""
+
 # Test builtin sets
 
 # xxx these tests are not very good, this should be revisited.
@@ -21,6 +25,10 @@ block:
       k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249
     FakeTokTypeRange = range[k2..k101]
     FakeTokTypes = set[FakeTokTypeRange]
+    Foo = object
+      field: set[Bar] #Bug: 6259
+    Bar = enum
+      bar1, bar2, bar3
 
   const toktypes: FakeTokTypes = {FakeTokTypeRange(k2)..pred(k64), k72..k74}
 
@@ -89,3 +97,37 @@ block:
 
   doAssert k99 notin s1
   doAssert k99 notin s2
+
+block: # bug #23422
+  block:
+    var a: set[uint8] = {1'u8}
+
+    proc printLen(x: set[uint8]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    proc printLenVar(x: var set[uint8]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    doAssert a.len == 1
+    doAssert printLen(a) == 1
+    doAssert printLenVar(a) == card(a)
+
+  block:
+    type Fruit = enum
+      Apple, Banana, Melon
+
+    var a: set[Fruit] = {Apple}
+
+    proc printLen(x: set[Fruit]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    proc printLenVar(x: var set[Fruit]): int =
+      doAssert x.len == card(x)
+      result = card(x)
+
+    doAssert a.len == 1
+    doAssert printLen(a) == 1
+    doAssert printLenVar(a) == card(a)
diff --git a/tests/sets/twrongenumrange.nim b/tests/sets/twrongenumrange.nim
new file mode 100644
index 000000000..a8d64ac44
--- /dev/null
+++ b/tests/sets/twrongenumrange.nim
@@ -0,0 +1,50 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+# issue #17848
+
+block:
+  # generate with:
+  # var a = ""
+  # for i in 0..<80: a.add "k" & $i & ", "
+  # echo a
+  type
+    TMsgKind = enum
+      k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79
+  type
+    TNoteKind = range[k10..k79]
+    Conf = ref object
+      notes: set[TNoteKind]
+  proc bad(conf: Conf, noteSet: set[TMsgKind]) =
+    conf.notes = noteSet #[tt.Error
+                 ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
+  var conf = Conf()
+  bad(conf, {k10..k60})
+
+block:
+  type
+    TMsgKind = enum k0, k1, k2, k3
+    TNoteKind = range[k1..k2]
+    TNoteKinds = set[TNoteKind]
+  type Conf = ref object
+    notes: TNoteKinds
+  proc fn(conf: Conf, b: set[TMsgKind]) =
+    conf.notes = b #[tt.Error
+                 ^ type mismatch: got <set[TMsgKind]> but expected 'TNoteKinds = set[TNoteKind]']#
+  var conf = Conf()
+  conf.fn({k0..k3}) # BUG: this should give error
+  echo conf.notes # {k1, k2}
+
+block:
+  #[
+  compiler/bitsets.nim(43, 9) `elem >= 0`  [AssertionDefect]
+  ]#
+  type
+    TMsgKind = enum k0, k1, k2, k3
+    TNoteKind = range[k1..k2]
+  var notes: set[TNoteKind]
+  notes = {k0} #[tt.Error
+          ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
+  notes = {k0..k3} #[tt.Error
+          ^ type mismatch: got <set[TMsgKind]> but expected 'set[TNoteKind]']#
diff --git a/tests/showoff/tgenericmacrotypes.nim b/tests/showoff/tgenericmacrotypes.nim
new file mode 100644
index 000000000..cc07f4355
--- /dev/null
+++ b/tests/showoff/tgenericmacrotypes.nim
@@ -0,0 +1,55 @@
+# issue #7974
+
+import macros
+
+macro genTypeA(arg: typed): untyped =
+  if arg.typeKind != ntyTypeDesc:
+    error("expected typedesc", arg)
+
+  result = arg.getTypeInst[1]
+
+macro genTypeB(arg: typed): untyped =
+  if arg.typeKind != ntyTypeDesc:
+    error("expected typedesc", arg)
+
+
+  let typeSym = arg.getTypeInst[1]
+  result =
+    nnkTupleTy.newTree(
+      nnkIdentDefs.newTree(
+        ident"a", typeSym, newEmptyNode()
+      )
+    )
+
+type
+  # this is the trivial case, MyTypeA[T] is basically just T, nothing else. But it works.
+  MyTypeA[T] = genTypeA(T)
+  # in this case I generate `tuple[a: T]`. This this is something the compiler does not want
+  MyTypeB[T] = genTypeB(T)
+
+# these are just alias types for int32 and float32, nothing really happens, but it works
+var a1: MyTypeA[int32]
+doAssert a1 is MyTypeA[int32]
+doAssert a1 is int32
+a1 = 0'i32
+var a2: MyTypeA[float32]
+doAssert a2 is MyTypeA[float32]
+doAssert a2 is float32
+a2 = 0'f32
+var a3: MyTypeA[float32]
+doAssert a3 is MyTypeA[float32]
+doAssert a3 is float32
+a3 = 0'f32
+
+var b1: MyTypeB[int32]   # cannot generate VM code fur tuple[a: int32]
+doAssert b1 is MyTypeB[int32]
+doAssert b1 is tuple[a: int32]
+b1 = (a: 0'i32)
+var b2: MyTypeB[float32]
+doAssert b2 is MyTypeB[float32]
+doAssert b2 is tuple[a: float32]
+b2 = (a: 0'f32)
+var b3: MyTypeB[float32]
+doAssert b3 is MyTypeB[float32]
+doAssert b3 is tuple[a: float32]
+b3 = (a: 0'f32)
diff --git a/tests/slice/tdistinct.nim b/tests/slice/tdistinct.nim
deleted file mode 100644
index d99b529a7..000000000
--- a/tests/slice/tdistinct.nim
+++ /dev/null
@@ -1,2 +0,0 @@
-type Foo = distinct uint64
-const slice = 0 ..< 42.Foo
diff --git a/tests/specialops/tcallops.nim b/tests/specialops/tcallops.nim
index 0508a37a1..c541a0c1d 100644
--- a/tests/specialops/tcallops.nim
+++ b/tests/specialops/tcallops.nim
@@ -37,3 +37,13 @@ doAssert a(b) == "(12)"
 doAssert a.b(c) == `()`(b, a, c)
 doAssert (a.b)(c) == `()`(a.b, c)
 doAssert `()`(a.b, c) == `()`(`()`(b, a), c)
+
+block: # bug #1072
+  var x: int
+
+  proc foo(some:int):int = some
+  proc `()`(l,r:string): string = discard
+
+  block:
+    var foo = 42
+    doAssert x.foo == 0
diff --git a/tests/specialops/terrmsgs.nim b/tests/specialops/terrmsgs.nim
new file mode 100644
index 000000000..081bca451
--- /dev/null
+++ b/tests/specialops/terrmsgs.nim
@@ -0,0 +1,76 @@
+discard """
+action: reject
+cmd: '''nim check $options $file'''
+matrix: "; -d:testWithout; --mm:refc"
+"""
+
+when not defined(testWithout): # test for same errors before and after
+  {.experimental: "dotOperators".}
+  {.experimental: "callOperator".}
+
+# issue #13063
+
+block:
+  type Foo = object
+  type Bar = object
+    x1: int
+  var b: Bar
+  block:
+    template `.`(a: Foo, b: untyped): untyped = 123
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+  block:
+    template `.()`(a: Foo, b: untyped): untyped = 123
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+  block:
+    template `.=`(a: Foo, b: untyped, c: untyped) = b = c
+    b.x = 123 #[tt.Error
+        ^ undeclared field: 'x=' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    # yeah it says x= but does it matter in practice
+  block:
+    template `()`(a: Foo, b: untyped, c: untyped) = echo "something"
+
+    # completely undeclared::
+    xyz(123) #[tt.Error
+    ^ undeclared identifier: 'xyz']#
+
+    # already declared routine:
+    min(123) #[tt.Error
+       ^ type mismatch: got <int literal(123)>]#
+
+    # non-routine type shows `()` overloads:
+    b(123) #[tt.Error
+     ^ attempting to call routine: 'b']#
+
+    echo b.x #[tt.Error
+          ^ undeclared field: 'x' for type terrmsgs.Bar [type declared in terrmsgs.nim(15, 8)]]#
+    echo b.x() #[tt.Error
+          ^ attempting to call undeclared routine: 'x']#
+
+# issue #7777
+
+import macros
+
+block:
+  type TestType = object
+    private_field: string
+
+  when false:
+    template getField(obj, field: untyped): untyped = obj.field
+
+  macro `.`(obj: TestType, field: untyped): untyped =
+    let private = newIdentNode("private_" & $field)
+    result = quote do:
+      `obj`.getField(`private`) #[tt.Error
+           ^ attempting to call undeclared routine: 'getField']#
+
+  var tt: TestType
+  discard tt.field
+
+block: # related to issue #6981
+  proc `()`(a:string, b:string):string = a & b
+  proc mewSeq[T](a,b:int)=discard
+  proc mewSeq[T](c:int)= discard
+  mewSeq[int]() #[tt.Error
+             ^ type mismatch: got <>]#
diff --git a/tests/specialops/tnewseq.nim b/tests/specialops/tnewseq.nim
new file mode 100644
index 000000000..970a5a8c2
--- /dev/null
+++ b/tests/specialops/tnewseq.nim
@@ -0,0 +1,22 @@
+# issue #6981
+
+import std/assertions
+
+{.experimental: "callOperator".}
+
+block: # issue #6981
+  proc `()`(a:string, b:string):string = a & b
+
+  var s = newSeq[int](3)
+
+  doAssert s == @[0, 0, 0]
+
+block: # generalized example from #6981
+  proc mewSeq[T](a: int)=discard
+  proc mewSeq[T]()= discard
+  mewSeq[int]()
+
+block: # issue #9831
+  type Foo = object
+  proc `()`(foo: Foo) = discard
+  let x = newSeq[int]()
diff --git a/tests/specialops/tsetter.nim b/tests/specialops/tsetter.nim
new file mode 100644
index 000000000..6175cbec4
--- /dev/null
+++ b/tests/specialops/tsetter.nim
@@ -0,0 +1,10 @@
+block: # ensure RHS of setter statement is treated as call operand
+  proc `b=`(a: var int, c: proc (x: int): int) =
+    a = c(a)
+
+  proc foo(x: int): int = x + 1
+  proc foo(x: float): float = x - 1
+
+  var a = 123
+  a.b = foo
+  doAssert a == 124
diff --git a/tests/statictypes/t20148.nim b/tests/statictypes/t20148.nim
new file mode 100644
index 000000000..d74a7755e
--- /dev/null
+++ b/tests/statictypes/t20148.nim
@@ -0,0 +1,8 @@
+type Percent = range[0.0 .. 1.0]
+# type Percent = float # using unlimited `float` works fine
+
+proc initColor*(alpha: Percent): bool =
+  echo alpha
+
+const moduleInstanceStyle = initColor(1)
+# let moduleInstanceStyle = initColor(1) # using runtime conversion works fine
diff --git a/tests/statictypes/t5780.nim b/tests/statictypes/t5780.nim
new file mode 100644
index 000000000..85548aaad
--- /dev/null
+++ b/tests/statictypes/t5780.nim
@@ -0,0 +1,3 @@
+type StringArray[N:int] = array[N, string]
+let a = ["one", "two"]
+doAssert a is StringArray
diff --git a/tests/statictypes/t9255.nim b/tests/statictypes/t9255.nim
index 99ca2a23c..aec2fbaf5 100644
--- a/tests/statictypes/t9255.nim
+++ b/tests/statictypes/t9255.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: '''
-type mismatch: got <proc (a0: int): string{.noSideEffect, gcsafe, locks: 0.}>
+type mismatch: got <proc (a0: int): string{.noSideEffect, gcsafe.}>
 '''
   line: 13
 """
diff --git a/tests/statictypes/tgenericcomputedrange.nim b/tests/statictypes/tgenericcomputedrange.nim
new file mode 100644
index 000000000..9e3a49ae0
--- /dev/null
+++ b/tests/statictypes/tgenericcomputedrange.nim
@@ -0,0 +1,125 @@
+import math
+
+block: # issue #19916
+  type
+    Test[S: static[Natural]] = object
+      a: array[ceilDiv(S, 8), uint8]
+
+  let a = Test[32]()
+  doAssert a.a.len == 4
+
+block: # issue #20514
+  type Foo[S:static[array[2, int]]] = object
+    values: array[prod(S), float]
+
+  doAssert Foo[[4,5]]().values.len == 20
+
+block: # issue #20937
+  type
+    Vec3[T: SomeNumber] {.bycopy.} = tuple[x, y, z: T]
+
+  func volume[T](v: Vec3[T]): T =
+    when T is SomeUnsignedInt:
+      v.x * v.y * v.z
+    else:
+      abs (v.x * v.y * v.z)
+
+  type
+    Matrix3[C: static Vec3[uint], T] = object
+      cells: array[C.volume, T]
+
+  let m = Matrix3[(1.uint, 1.uint, 1.uint), uint](cells: [0.uint])
+  doAssert m.cells.len == 1
+  let m2 = Matrix3[(4.uint, 3.uint, 5.uint), uint]()
+  doAssert m2.cells.len == 60
+
+block:  # issue #19284
+  type Board[N, M: static Slice[int]] = array[len(N)*len(M), int8]
+
+  var t: Board[0..4, 0..4]
+  doAssert t.len == 25
+
+block: # minimal issue #19284
+  proc foo[T](x: T): int =
+    result = 0
+  type Foo[N: static int] = array[0..foo(N), int]
+
+  var t: Foo[5]
+  doAssert t.len == 1
+
+block:
+  type Foo[T; U: static T] = range[T(0) .. U]
+
+  block:
+    var x: array[Foo[int, 1], int]
+    x[0] = 1
+    x[1] = 2
+    doAssert x == [0: 1, 1: 2]
+    doAssert x is array[0 .. 1, int]
+
+  block:
+    type Bar = enum a, b, c
+    var x: array[Foo[Bar, c], int]
+    x[a] = 1
+    x[b] = 2
+    x[c] = 3
+    doAssert x == [a: 1, b: 2, c: 3]
+    doAssert x is array[a .. c, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. U, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    doAssert x == [0: 1, 1: 2]
+    doAssert x is array[0 .. 1, int]
+
+  block:
+    type Bar = enum a, b, c
+    var x: Foo[Bar, c]
+    x[a] = 1
+    x[b] = 2
+    x[c] = 3
+    doAssert x == [a: 1, b: 2, c: 3]
+    doAssert x is array[a .. c, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. U + 1, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    x[2] = 3
+    doAssert x == [0: 1, 1: 2, 2: 3]
+    doAssert x is array[0 .. 2, int]
+
+block:
+  type Foo[T; U: static T] = array[T(0) .. (U * 2) + 1, int]
+
+  block:
+    var x: Foo[int, 1]
+    x[0] = 1
+    x[1] = 2
+    x[2] = 3
+    x[3] = 4
+    doAssert x == [0: 1, 1: 2, 2: 3, 3: 4]
+    doAssert x is array[0 .. 3, int]
+
+block: # issue #22187
+  template m(T: type, s: int64): int64 = s
+  func p(n: int64): int = int(n)
+  type F[T; s: static int64] = object
+    k: array[p(m(T, s)), int64]
+  var x: F[int, 3]
+  doAssert x.k is array[3, int64]
+
+block: # issue #22490
+  proc log2trunc(x: uint64): int =
+    if x == 0: int(0) else: int(0)
+  template maxChunkIdx(T: typedesc): int64 = 0'i64
+  template layer(vIdx: int64): int = log2trunc(0'u64)
+  type HashList[T] = object
+    indices: array[int(layer(maxChunkIdx(T))), int]
diff --git a/tests/statictypes/tstaticgenericparam.nim b/tests/statictypes/tstaticgenericparam.nim
new file mode 100644
index 000000000..6acd05b70
--- /dev/null
+++ b/tests/statictypes/tstaticgenericparam.nim
@@ -0,0 +1,24 @@
+# static types that depend on a generic parameter
+
+block: # issue #19365
+  var ss: seq[string]
+  proc f[T](x: static T) =
+    ss.add($x & ": " & $T)
+
+  f(123)
+  doAssert ss == @["123: int"]
+  f("abc")
+  doAssert ss == @["123: int", "abc: string"]
+
+block: # issue #7209
+  type Modulo[A; M: static[A]] = distinct A
+
+  proc `$`[A; M: static[A]](x: Modulo[A, M]): string =
+    $(A(x)) & " mod " & $(M)
+
+  proc modulo[A](a: A, M: static[A]): Modulo[A, M] = Modulo[A, M](a %% M)
+
+  proc `+`[A; M: static[A]](x, y: Modulo[A, M]): Modulo[A, M] =
+    (A(x) + A(y)).modulo(M)
+
+  doAssert $(3.modulo(7) + 5.modulo(7)) == "1 mod 7"
diff --git a/tests/statictypes/tstaticprocparams.nim b/tests/statictypes/tstaticprocparams.nim
new file mode 100644
index 000000000..75d8288ac
--- /dev/null
+++ b/tests/statictypes/tstaticprocparams.nim
@@ -0,0 +1,16 @@
+proc consumer[T: static proc(i: int): int{.nimcall.}](i: int): int = T(i)
+proc consumer(T: static proc(i: int): int{.nimcall.}, i: int): int = T(i)
+
+proc addIt(i: int): int = i + i
+proc add(i: int): int = i + i # Checks if we can use overloads
+proc squareIt(i: int): int = i * i
+
+assert consumer[addIt](10) == 20
+assert consumer[add](10) == 20
+assert consumer[squareIt](30) == 900
+assert consumer[proc(i: int): int{.nimcall.} = i * i + i](10) == 110
+
+assert consumer(addIt, 10) == 20
+assert consumer(add, 10) == 20
+assert consumer(squareIt, 30) == 900
+assert consumer(proc(i: int): int{.nimcall.} = i * i + i, 10) == 110
diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim
index 5df3f35fd..572b8c4a5 100644
--- a/tests/statictypes/tstatictypes.nim
+++ b/tests/statictypes/tstatictypes.nim
@@ -22,7 +22,7 @@ heyho
 Val1
 Val1
 '''
-matrix: "--hints:off"
+matrix: "--hints:off --mm:orc; --hints:off --mm:refc"
 """
 
 import macros
@@ -391,3 +391,69 @@ var sorted = newSeq[int](1000)
 for i in 0..<sorted.len: sorted[i] = i*2
 doAssert isSorted2(sorted, compare)
 doAssert isSorted2(sorted, proc (a, b: int): bool {.inline.} = a < b)
+
+
+block: # Ensure static descriminated objects compile
+  type
+    ObjKind = enum
+      KindA, KindB, KindC
+
+    MyObject[kind: static[ObjKind]] = object of RootObj
+      myNumber: int
+      when kind != KindA:
+        driverType: int
+        otherField: int
+      elif kind == KindC:
+        driverType: uint
+        otherField: int
+
+  var instance: MyObject[KindA]
+  discard instance
+  discard MyObject[KindC]()
+
+block: # more cases of above, issue #8446
+  type
+    Color = enum
+      red, green, blue
+    Blah[color: static[Color]] = object
+      when color == red:
+        a: string
+      else:
+        b: int
+
+  proc foo(blah: Blah) = discard
+  foo(Blah[red](a: "abc"))
+
+  type
+    Mytype[K: static[int]] = object
+      when K < 16:
+        data: uint8
+      else:
+        data: uint64
+  proc usingMyt(k: Mytype) = discard  # triggers Error: cannot generate code for: K
+
+block: # bug #22600
+  proc f(n: static int): int = n * 2 # same for template
+
+  type
+    a[N: static int] = object
+      field : array[N, uint8]
+
+    b[N: static int] = object
+      field : a[N]
+
+    c[N: static int] = object
+      f0    : a[N     ]         # works
+      f1    : a[N + 1 ]         # asserts
+      f2    : a[f(N)  ]         # asserts
+
+      f3    : b[N     ]         # works
+      f4    : b[N + 1 ]         # asserts
+      f5    : b[f(N)  ]         # asserts
+
+  proc init[N: static int](x : var a[N]) = discard
+  proc init[N: static int](x : var b[N]) = discard
+  proc init[N: static int](x : var c[N]) = x.f1.init() # this is needed
+
+  var x: c[2]
+  x.init()
diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim
index 9cfdce83d..08f2e7d3e 100644
--- a/tests/stdlib/concurrency/tatomics.nim
+++ b/tests/stdlib/concurrency/tatomics.nim
@@ -1,3 +1,9 @@
+discard """
+  # test C with -d:nimUseCppAtomics as well to check nothing breaks
+  matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
+  targets: "c cpp"
+"""
+
 # test atomic operations
 
 import std/[atomics, bitops]
diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim
index 7b43787fb..f64adb308 100644
--- a/tests/stdlib/concurrency/tatomics_size.nim
+++ b/tests/stdlib/concurrency/tatomics_size.nim
@@ -1,4 +1,6 @@
 discard """
+  # test C with -d:nimUseCppAtomics as well to check nothing breaks
+  matrix: "--mm:refc; --mm:orc; --mm:refc -d:nimUseCppAtomics; --mm:orc -d:nimUseCppAtomics"
   targets: "c cpp"
 """
 import std/atomics
@@ -16,4 +18,4 @@ block testSize: # issue 12726
       f: AtomicFlag
   static:
     doAssert sizeof(Node) == sizeof(pointer)
-    doAssert sizeof(MyChannel) == sizeof(pointer) * 2
\ No newline at end of file
+    doAssert sizeof(MyChannel) == sizeof(pointer) * 2
diff --git a/tests/stdlib/config.nims b/tests/stdlib/config.nims
index ea5d738e2..dffae2812 100644
--- a/tests/stdlib/config.nims
+++ b/tests/stdlib/config.nims
@@ -1,3 +1,5 @@
 switch("styleCheck", "usages")
 switch("styleCheck", "error")
-switch("define", "nimPreviewSlimSystem")
\ No newline at end of file
+switch("define", "nimPreviewSlimSystem")
+switch("define", "nimPreviewCstringConversion")
+switch("define", "nimPreviewProcConversion")
diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim
index e89d58d27..678d9ec02 100644
--- a/tests/stdlib/mimportutils.nim
+++ b/tests/stdlib/mimportutils.nim
@@ -26,6 +26,12 @@ type
   H1*[T] = ref H2[T]
   H*[T] = H1[T]
 
+  Pity[T] = object
+    a: T
+  PityRef*[T] = ref Pity[T]
+  Hope*[T] = ref object
+    a: T
+
 type BAalias* = typeof(B.default)
   # typeof is not a transparent abstraction, creates a `tyAlias`
 
diff --git a/tests/stdlib/t15663.nim b/tests/stdlib/t15663.nim
index 1ad5677fd..8e8bfd9a8 100644
--- a/tests/stdlib/t15663.nim
+++ b/tests/stdlib/t15663.nim
@@ -3,5 +3,7 @@ discard """
   output: "Test"
 """
 
+import std/widestrs
+
 let ws = newWideCString("Test")
-echo ws
\ No newline at end of file
+echo ws
diff --git a/tests/stdlib/t19304.nim b/tests/stdlib/t19304.nim
new file mode 100644
index 000000000..5e8795ac5
--- /dev/null
+++ b/tests/stdlib/t19304.nim
@@ -0,0 +1,7 @@
+import times
+
+type DjangoDateTime* = distinct DateTime
+
+# proc toTime*(x: DjangoDateTime): Time  {.borrow.} # <-- works
+proc format*(x: DjangoDateTime, f: TimeFormat,
+    loc: DateTimeLocale = DefaultLocale): string {.borrow.}
diff --git a/tests/stdlib/t20023.nim b/tests/stdlib/t20023.nim
new file mode 100644
index 000000000..8f12f8993
--- /dev/null
+++ b/tests/stdlib/t20023.nim
@@ -0,0 +1,10 @@
+import std/[tables, hashes, assertions]
+
+
+let t = ()
+var a = toTable({t:t})
+del(a,t)
+let b = default(typeof(a))
+
+doAssert a==b , "tables are not equal"
+doAssert hash(a) == hash(b), "table hashes are not equal"
diff --git a/tests/stdlib/t21251.nim b/tests/stdlib/t21251.nim
new file mode 100644
index 000000000..4402e9b7e
--- /dev/null
+++ b/tests/stdlib/t21251.nim
@@ -0,0 +1,6 @@
+import std / [tables, sets, sharedtables]
+
+var shared: SharedTable[int, int]
+shared.init
+
+shared[1] = 1
diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim
new file mode 100644
index 000000000..86bf7b0c7
--- /dev/null
+++ b/tests/stdlib/t21406.nim
@@ -0,0 +1,7 @@
+import std/[times, strformat]
+import std/assertions
+
+let aTime = getTime()
+doAssert fmt"{aTime}" == $aTime
+let aNow = now()
+doAssert fmt"{aNow}" == $aNow
diff --git a/tests/stdlib/t21564.nim b/tests/stdlib/t21564.nim
new file mode 100644
index 000000000..0a5777d12
--- /dev/null
+++ b/tests/stdlib/t21564.nim
@@ -0,0 +1,32 @@
+discard """
+targets: "c js"
+"""
+
+import bitops
+import std/assertions
+
+proc main() =
+  block: # bug #21564
+    # tesk `bitops.bitsliced` patch
+    doAssert(0x17.bitsliced(4..7) == 0x01)
+    doAssert(0x17.bitsliced(0..3) == 0x07)
+
+  block:
+    # test in-place `bitops.bitslice`
+    var t = 0x12F4
+    t.bitslice(4..7)
+
+    doAssert(t == 0xF)
+
+  block:
+    # test `bitops.toMask` patch via bitops.masked
+    doAssert(0x12FFFF34.masked(8..23) == 0x00FFFF00)
+
+  block: # bug #22687
+    var a: uint8 = 0b1111_1111
+    doAssert a.bitsliced(4..7).int == 15
+
+main()
+
+static:
+  main()
diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim
index 83a84f956..e2024df0c 100644
--- a/tests/stdlib/talgorithm.nim
+++ b/tests/stdlib/talgorithm.nim
@@ -1,5 +1,6 @@
 discard """
   targets: "c js"
+  matrix: "--mm:refc; --mm:orc"
   output:'''@["3", "2", "1"]
 '''
 """
diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim
index a69334e71..0a6dd1fcf 100644
--- a/tests/stdlib/tarithmetics.nim
+++ b/tests/stdlib/tarithmetics.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 import std/assertions
diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim
index 8cf0d0ced..5a7e2da40 100644
--- a/tests/stdlib/tasynchttpserver.nim
+++ b/tests/stdlib/tasynchttpserver.nim
@@ -109,6 +109,7 @@ proc testCustomContentLength() {.async.} =
     doAssert(body == "")
     doAssert(response.headers.hasKey("Content-Length"))
     doAssert(response.headers["Content-Length"] == "0")
+    doAssert contentLength(response) == 0 # bug #22778
 
   runTest(handler, request, test)
 
diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim
index dae87be82..886ba0f33 100644
--- a/tests/stdlib/tasynchttpserver_transferencoding.nim
+++ b/tests/stdlib/tasynchttpserver_transferencoding.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger; --threads:on"
+  matrix: "--mm:arc; --mm:arc -d:danger; --mm:refc"
   disabled: "freebsd"
 """
 
diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim
index 60fa3865d..c3bfb818e 100644
--- a/tests/stdlib/tbase64.nim
+++ b/tests/stdlib/tbase64.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 import std/assertions
@@ -17,6 +18,8 @@ template main() =
   doAssert encode("") == ""
   doAssert decode("") == ""
 
+  doAssert decode(" ") == ""
+
   const testInputExpandsTo76 = "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++"
   const testInputExpands = "++++++++++++++++++++++++++++++"
   const longText = """Man is distinguished, not only by his reason, but by this
@@ -52,5 +55,9 @@ template main() =
     doAssert encode("", safe = true) == ""
     doAssert encode("the quick brown dog jumps over the lazy fox", safe = true) == "dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA=="
 
+func mainNoSideEffects() = main()
+
 static: main()
 main()
+static: mainNoSideEffects()
+mainNoSideEffects()
diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim
index c90943a93..3ecab2c64 100644
--- a/tests/stdlib/tbitops.nim
+++ b/tests/stdlib/tbitops.nim
@@ -1,5 +1,6 @@
 discard """
   nimout: "OK"
+  matrix: "--mm:refc; --mm:orc"
   output: '''
 OK
 '''
diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim
index 7a64ea68d..e3f96fecc 100644
--- a/tests/stdlib/tbitops_utils.nim
+++ b/tests/stdlib/tbitops_utils.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/private/bitops_utils
 import std/assertions
 
diff --git a/tests/stdlib/tcasts.nim b/tests/stdlib/tcasts.nim
new file mode 100644
index 000000000..e01c7e940
--- /dev/null
+++ b/tests/stdlib/tcasts.nim
@@ -0,0 +1,26 @@
+import std/[strutils]
+import std/[assertions, objectdollar]
+
+# bug #19101
+type
+  Small = object
+    a: int
+
+  Big = object
+    a, b, c, d: int
+
+proc main =
+  var
+    n = 1'i8
+    f = 2.0
+    s = Small(a: 1)
+    b = Big(a: 12345, b: 23456, c: 34567, d: 45678)
+
+  doAssert $cast[int](f).toBin(64) == "0100000000000000000000000000000000000000000000000000000000000000"
+  f = cast[float](n)
+  doAssert $cast[int](f).toBin(64) == "0000000000000000000000000000000000000000000000000000000000000001"
+
+  doAssert $b == "(a: 12345, b: 23456, c: 34567, d: 45678)"
+  b = cast[Big](s)
+  doAssert $b == "(a: 1, b: 0, c: 0, d: 0)"
+main()
diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim
index 7a52dc89b..ef39450da 100644
--- a/tests/stdlib/tcgi.nim
+++ b/tests/stdlib/tcgi.nim
@@ -1,3 +1,7 @@
+discard """

+  matrix: "--mm:refc; --mm:orc"

+"""

+

 import std/unittest

 import std/[cgi, strtabs, sugar]

 import std/assertions

diff --git a/tests/stdlib/tclosures.nim b/tests/stdlib/tclosures.nim
new file mode 100644
index 000000000..84b033fa8
--- /dev/null
+++ b/tests/stdlib/tclosures.nim
@@ -0,0 +1,47 @@
+discard """
+  targets: "c js"
+"""
+
+import std/assertions
+
+block: # bug #4299
+  proc scopeProc() =
+    proc normalProc() =
+      discard
+
+    proc genericProc[T]() =
+      normalProc()
+
+    genericProc[string]()
+
+  scopeProc()
+
+block: # bug #12492
+  proc foo() =
+    var i = 0
+    proc bar() =
+      inc i
+
+    bar()
+    doAssert i == 1
+
+  foo()
+  static:
+    foo()
+
+block: # bug #10849
+  type
+    Generic[T] = ref object
+      getState: proc(): T
+
+  proc newGeneric[T](): Generic[T] =
+    var state: T
+
+    proc getState[T](): T =
+      state
+
+    Generic[T](getState: getState)
+
+  let g = newGeneric[int]()
+  let state = g.getState()
+  doAssert state == 0
diff --git a/tests/stdlib/tcmdline.nim b/tests/stdlib/tcmdline.nim
index 5c0f71772..8b428900b 100644
--- a/tests/stdlib/tcmdline.nim
+++ b/tests/stdlib/tcmdline.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
   joinable: false
 """
diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim
index c7666be84..ca83314b9 100644
--- a/tests/stdlib/tcomplex.nim
+++ b/tests/stdlib/tcomplex.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/[complex, math]
 import std/assertions
 
@@ -80,6 +84,9 @@ let t = polar(a)
 doAssert(rect(t.r, t.phi) =~ a)
 doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817))
 
+doAssert(almostEqual(a, a + complex(1e-16, 1e-16)))
+doAssert(almostEqual(a, a + complex(2e-15, 2e-15), unitsInLastPlace = 5))
+
 
 let
   i64: Complex32 = complex(0.0f, 1.0f)
diff --git a/tests/stdlib/tcookies.nim b/tests/stdlib/tcookies.nim
index 4fe104dfc..3ff0f3bae 100644
--- a/tests/stdlib/tcookies.nim
+++ b/tests/stdlib/tcookies.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim
index 0c2e1d6fa..e6282f045 100644
--- a/tests/stdlib/tcritbits.nim
+++ b/tests/stdlib/tcritbits.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim
index ec2b8596c..e73b2b681 100644
--- a/tests/stdlib/tcstrutils.nim
+++ b/tests/stdlib/tcstrutils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
diff --git a/tests/stdlib/tdb.nim b/tests/stdlib/tdb.nim
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/stdlib/tdb.nim
diff --git a/tests/stdlib/tdb.nims b/tests/stdlib/tdb.nims
new file mode 100644
index 000000000..d31d0b26f
--- /dev/null
+++ b/tests/stdlib/tdb.nims
@@ -0,0 +1 @@
+--styleCheck:off
\ No newline at end of file
diff --git a/tests/stdlib/tdb_mysql.nim b/tests/stdlib/tdb_mysql.nim
index d97358e9f..e69de29bb 100644
--- a/tests/stdlib/tdb_mysql.nim
+++ b/tests/stdlib/tdb_mysql.nim
@@ -1,5 +0,0 @@
-import std/db_mysql
-import std/assertions
-
-doAssert dbQuote("SELECT * FROM foo WHERE col1 = 'bar_baz'") == "'SELECT * FROM foo WHERE col1 = \\'bar_baz\\''"
-doAssert dbQuote("SELECT * FROM foo WHERE col1 LIKE '%bar_baz%'") == "'SELECT * FROM foo WHERE col1 LIKE \\'%bar_baz%\\''"
diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim
index 5cf352cfb..42dc646f2 100644
--- a/tests/stdlib/tdecls.nim
+++ b/tests/stdlib/tdecls.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 import std/assertions
@@ -13,18 +14,18 @@ template fun() =
   var b {.byaddr.}: int = s[0]
   doAssert a.addr == b.addr
 
-  when false:
-    # template specific redeclaration issue
-    # see https://github.com/nim-lang/Nim/issues/8275
-    doAssert not compiles(block:
-      # redeclaration not allowed
-      var foo = 0
-      var foo {.byaddr.} = s[0])
-
-    doAssert not compiles(block:
-      # ditto
-      var foo {.byaddr.} = s[0]
-      var foo {.byaddr.} = s[0])
+  {.push warningAsError[ImplicitTemplateRedefinition]: on.}
+  # in the future ImplicitTemplateRedefinition will be an error anyway
+  doAssert not compiles(block:
+    # redeclaration not allowed
+    var foo = 0
+    var foo {.byaddr.} = s[0])
+
+  doAssert not compiles(block:
+    # ditto
+    var foo {.byaddr.} = s[0]
+    var foo {.byaddr.} = s[0])
+  {.pop.}
 
   block:
     var b {.byaddr.} = s[1] # redeclaration ok in sub scope
diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim
index 8a788d337..39ff996d1 100644
--- a/tests/stdlib/tdeques.nim
+++ b/tests/stdlib/tdeques.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--gc:refc; --gc:orc"
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
@@ -183,6 +183,61 @@ proc main() =
     clear(a)
     doAssert len(a) == 0
 
+  block: # bug #21278
+    var a = [10, 20, 30, 40].toDeque
+
+    a.shrink(fromFirst = 0, fromLast = 1)
+    doAssert $a == "[10, 20, 30]"
+
+  block:
+    var a, b: Deque[int]
+    for i in 1 .. 256:
+      a.addLast(i)
+    for i in 1 .. 255:
+      a.popLast
+    b.addLast(1)
+    doAssert a == b
+
+  block:
+    # Issue 23275
+    # Test `==`.
+    block:
+      var a, b = initDeque[int]()
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.addFirst(1)
+      doAssert a != b
+      doAssert a.hash != b.hash
+      b.addLast(1)
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.popFirst
+      b.popLast
+      doAssert a == b
+      doAssert a.hash == b.hash
+      a.addLast 2
+      doAssert a != b
+      doAssert a.hash != b.hash
+      b.addFirst 2
+      doAssert a == b
+      doAssert a.hash == b.hash
+
+    block:
+      var a, b = initDeque[int]()
+      for i in countDown(100, 1):
+        a.addFirst(i)
+      for i in 1..100:
+        b.addLast(i)
+      doAssert a == b
+      for i in 1..99:
+        a.popLast
+      let a1 = [1].toDeque
+      doAssert a == a1
+      doAssert a.hash == a1.hash
+      var c = initDeque[int]()
+      c.addLast(1)
+      doAssert a == c
+      doAssert a.hash == c.hash
 
 static: main()
 main()
diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim
index cb9cebb3a..132f7120b 100644
--- a/tests/stdlib/tdiff.nim
+++ b/tests/stdlib/tdiff.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/distros/tdistros_detect.nim b/tests/stdlib/tdistros_detect.nim
index 9639cfeb9..1176c8993 100644
--- a/tests/distros/tdistros_detect.nim
+++ b/tests/stdlib/tdistros_detect.nim
@@ -1,9 +1,4 @@
-import distros
-
-discard """
-  exitcode: 0
-  output: ""
-"""
+import std/[assertions, distros]
 
 when defined(windows):
     doAssert detectOs(Windows) == true
diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim
index 15d538891..4d532b5d0 100644
--- a/tests/stdlib/tdochelpers.nim
+++ b/tests/stdlib/tdochelpers.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''
 
 [Suite] Integration with Nim
@@ -202,3 +203,19 @@ suite "Integration with Nim":
                               name: "Copyflag")
     check inputRst.fromRst == expected
     check inputMd.fromMd == expected
+
+  test "prefixed module":
+    let inputRst = "`module std / paths`_"
+    let inputMd = "[module std / paths]"
+    let expected = LangSymbol(symKind: "module",
+                              name: "std/paths")
+    check inputRst.fromRst == expected
+    check inputMd.fromMd == expected
+
+  test "postfixed module":
+    let inputRst = "`std / paths module`_"
+    let inputMd = "[std / paths module]"
+    let expected = LangSymbol(symKind: "module",
+                              name: "std/paths")
+    check inputRst.fromRst == expected
+    check inputMd.fromMd == expected
diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim
index b3b323647..14ba6df97 100644
--- a/tests/stdlib/teditdistance.nim
+++ b/tests/stdlib/teditdistance.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/editdistance
 import std/assertions
 
diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim
index 10d79f5d0..2f4daaba3 100644
--- a/tests/stdlib/tencodings.nim
+++ b/tests/stdlib/tencodings.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/encodings
 import std/assertions
 
@@ -97,3 +101,7 @@ block:
   doAssert orig == "\195\182\195\164\195\188\195\159"
   doAssert ibm850 == "\148\132\129\225"
   doAssert convert(ibm850, current, "ibm850") == orig
+
+block: # fixes about #23481
+  doAssertRaises EncodingError:
+    discard open(destEncoding="this is a invalid enc")
diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim
index b15b9e2db..2789ebe3a 100644
--- a/tests/stdlib/tenumerate.nim
+++ b/tests/stdlib/tenumerate.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/enumerate
 import std/assertions
 
diff --git a/tests/stdlib/tenumutils.nim b/tests/stdlib/tenumutils.nim
index 63c563739..2662a660d 100644
--- a/tests/stdlib/tenumutils.nim
+++ b/tests/stdlib/tenumutils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
@@ -34,5 +35,15 @@ template main =
     doAssert $b == "kb0"
     static: doAssert B.high.symbolName == "b2"
 
+  block:
+    type
+      Color = enum
+        Red = "red", Yellow = "yellow", Blue = "blue"
+
+    var s = Red
+    doAssert symbolName(s) == "Red"
+    var x: range[Red..Blue] = Yellow
+    doAssert symbolName(x) == "Yellow"
+
 static: main()
 main()
diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim
index 47c1ad24a..1a07f02b8 100644
--- a/tests/stdlib/tenvvars.nim
+++ b/tests/stdlib/tenvvars.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
   joinable: false
   targets: "c js cpp"
 """
@@ -7,7 +7,10 @@ discard """
 import std/envvars
 from std/sequtils import toSeq
 import stdtest/testutils
-import std/assertions
+import std/[assertions]
+
+when not defined(js):
+  import std/typedthreads
 
 # "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386)
 const unicodeUtf8 = "\xc3\x86"
@@ -46,9 +49,11 @@ template main =
       doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE")
       doAssert not existsEnv("NIM_TESTS_TOSENV_PUT")
 
+static: main()
 main()
 
 when defined(windows):
+  import std/widestrs
   proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".}
 proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
 
@@ -68,7 +73,7 @@ when not defined(js) and not defined(nimscript):
 
     doAssertRaises(OSError): delEnv("foo=bar")
 
-when defined(windows):
+when defined(windows) and not defined(nimscript):
   import std/encodings
 
   proc c_putenv(env: cstring): int32 {.importc: "putenv", header: "<stdlib.h>".}
diff --git a/tests/stdlib/texitprocs.nim b/tests/stdlib/texitprocs.nim
index 9d5378fe8..ea29d8f58 100644
--- a/tests/stdlib/texitprocs.nim
+++ b/tests/stdlib/texitprocs.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 targets: "c cpp js"
 output: '''
 ok4
diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim
index 1ac746e48..272a7507c 100644
--- a/tests/stdlib/tfdleak.nim
+++ b/tests/stdlib/tfdleak.nim
@@ -1,7 +1,7 @@
 discard """
   exitcode: 0
   output: ""
-  matrix: "; -d:nimInheritHandles"
+  matrix: "; -d:nimInheritHandles; --mm:refc"
   joinable: false
 """
 
diff --git a/tests/stdlib/tfilesanddirs.nim b/tests/stdlib/tfilesanddirs.nim
new file mode 100644
index 000000000..a1920d4f2
--- /dev/null
+++ b/tests/stdlib/tfilesanddirs.nim
@@ -0,0 +1,36 @@
+import std/[paths, files, dirs, appdirs]
+
+from stdtest/specialpaths import buildDir
+import std/[syncio, assertions]
+
+block fileOperations:
+  let files = @[Path"these.txt", Path"are.x", Path"testing.r", Path"files.q"]
+  let dirs = @[Path"some", Path"created", Path"test", Path"dirs"]
+
+  let dname = Path"__really_obscure_dir_name"
+
+  createDir(dname.Path)
+  doAssert dirExists(Path(dname))
+ 
+  # Test creating files and dirs
+  for dir in dirs:
+    createDir(Path(dname/dir))
+    doAssert dirExists(Path(dname/dir))
+
+  for file in files:
+    let fh = open(string(dname/file), fmReadWrite) # createFile
+    fh.close()
+    doAssert fileExists(Path(dname/file))
+
+block: # getCacheDir
+  doAssert getCacheDir().dirExists
+
+block: # moveFile
+  let tempDir = getTempDir() / Path("D20221022T151608")
+  createDir(tempDir)
+  defer: removeDir(tempDir)
+
+block: # moveDir
+  let tempDir = getTempDir() / Path("D20220609T161443")
+  createDir(tempDir)
+  defer: removeDir(tempDir)
diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim
index 6b4c3b6d3..aa734ddac 100644
--- a/tests/stdlib/tfrexp1.nim
+++ b/tests/stdlib/tfrexp1.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "js c cpp"
 """
 
diff --git a/tests/stdlib/tgetaddrinfo.nim b/tests/stdlib/tgetaddrinfo.nim
index a8bcecb0c..3a90034c8 100644
--- a/tests/stdlib/tgetaddrinfo.nim
+++ b/tests/stdlib/tgetaddrinfo.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   exitcode: 0
   output: ""
 """
diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim
index 0f21622d0..ae1480a4c 100644
--- a/tests/stdlib/tgetfileinfo.nim
+++ b/tests/stdlib/tgetfileinfo.nim
@@ -1,10 +1,11 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: "pcDir\npcFile\npcLinkToDir\npcLinkToFile\n"
   joinable: false
 """
 
 import os, strutils
-import std/syncio
+import std/[syncio, assertions]
 # Cases
 #  1 - String : Existing File : Symlink true
 #  2 - String : Existing File : Symlink false
@@ -127,10 +128,37 @@ proc testGetFileInfo =
       echo pcLinkToDir
       echo pcLinkToFile
 
+    doAssert dirInfo.isSpecial == false
+    doAssert fileInfo.isSpecial == false
+    when defined(posix):
+      doAssert linkDirInfo.isSpecial == false
+      doAssert linkFileInfo.isSpecial == false
+
     removeDir(dirPath)
     removeFile(filePath)
     when defined(posix):
       removeFile(linkDirPath)
       removeFile(linkFilePath)
 
+  # Test that `isSpecial` is set correctly
+  block:
+    when defined(posix):
+      let
+        tmp = getTempDir()
+        fifoPath     = tmp / "test-fifo"
+        linkFifoPath = tmp / "test-link-fifo"
+
+      doAssert execShellCmd("mkfifo " & fifoPath) == 0
+      createSymlink(fifoPath, linkFifoPath)
+
+      let
+        fifoInfo = getFileInfo(fifoPath)
+        linkFifoInfo = getFileInfo(linkFifoPath)
+
+      doAssert fifoInfo.isSpecial == true
+      doAssert linkFifoInfo.isSpecial == true
+
+      removeFile(fifoPath)
+      removeFile(linkFifoPath)
+
 testGetFileInfo()
diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim
index e524510b2..1fc060ffe 100644
--- a/tests/stdlib/tgetprotobyname.nim
+++ b/tests/stdlib/tgetprotobyname.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import nativesockets
 import std/assertions
 
diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim
index 69ff31938..4aa21992c 100644
--- a/tests/stdlib/tglobs.nim
+++ b/tests/stdlib/tglobs.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/private/globs
 import std/assertions
 
diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim
index caae79213..4555fbcb3 100644
--- a/tests/stdlib/thashes.nim
+++ b/tests/stdlib/thashes.nim
@@ -1,5 +1,5 @@
 discard """
-  targets: "c cpp js"
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:on; --backend:c -d:nimStringHash2; --backend:cpp -d:nimStringHash2; --backend:js -d:nimStringHash2"
 """
 
 import std/hashes
@@ -29,6 +29,10 @@ block hashes:
     const wy123 = hashWangYi1(123)
     doAssert wy123 != 0
     doAssert hashWangYi1(123) == wy123
+    const wyNeg123 = hashWangYi1(-123)
+    doAssert wyNeg123 != 0
+    when not defined(js): # TODO: fixme it doesn't work for JS
+      doAssert hashWangYi1(-123) == wyNeg123
 
 
   # "hashIdentity value incorrect at 456"
@@ -42,20 +46,31 @@ block hashes:
     else:
       doAssert hashWangYi1(456) == -6421749900419628582
 
+template jsNoInt64: untyped =
+  when defined js:
+    when compiles(compileOption("jsbigint64")):
+      when not compileOption("jsbigint64"): true
+      else: false
+    else: false
+  else: false
+const sHash2 = (when defined(nimStringHash2) or jsNoInt64(): true else: false)
+
 block empty:
+  const emptyStrHash = # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash]
+    when sHash2: 0 else: cast[Hash](-7286425919675154353i64)
   var
     a = ""
     b = newSeq[char]()
     c = newSeq[int]()
     d = cstring""
     e = "abcd"
-  doAssert hash(a) == 0
-  doAssert hash(b) == 0
+  doAssert hash(a) == emptyStrHash
+  doAssert hash(b) == emptyStrHash
   doAssert hash(c) == 0
-  doAssert hash(d) == 0
+  doAssert hash(d) == emptyStrHash
   doAssert hashIgnoreCase(a) == 0
   doAssert hashIgnoreStyle(a) == 0
-  doAssert hash(e, 3, 2) == 0
+  doAssert hash(e, 3, 2) == emptyStrHash
 
 block sameButDifferent:
   doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13)
@@ -89,7 +104,10 @@ block largeSize: # longer than 4 characters
 proc main() =
   doAssert hash(0.0) == hash(0)
   # bug #16061
-  doAssert hash(cstring"abracadabra") == 97309975
+  when not sHash2: # Hash=int=4B on js even w/--jsbigint64:on => cast[Hash]
+    doAssert hash(cstring"abracadabra") == cast[Hash](-1119910118870047694i64)
+  else:
+    doAssert hash(cstring"abracadabra") == 97309975
   doAssert hash(cstring"abracadabra") == hash("abracadabra")
 
   when sizeof(int) == 8 or defined(js):
diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim
index bb40b6f93..afb09c7e3 100644
--- a/tests/stdlib/theapqueue.nim
+++ b/tests/stdlib/theapqueue.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/heapqueue
 import std/assertions
 
diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim
index 5134215c1..0cd334254 100644
--- a/tests/stdlib/thighlite.nim
+++ b/tests/stdlib/thighlite.nim
@@ -1,6 +1,10 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
 
 import unittest, strutils
 import ../../lib/packages/docutils/highlite
+import std/objectdollar
 
 block: # Nim tokenizing
   test "string literals and escape seq":
@@ -30,3 +34,12 @@ block: # Cmd (shell) tokenizing
         ("file.nim", gtIdentifier), ("\n", gtWhitespace),
         ("out: file [SuccessX]", gtProgramOutput)
       ])
+
+block: # bug #21232
+  let code = "/"
+  var toknizr: GeneralTokenizer
+
+  initGeneralTokenizer(toknizr, code)
+
+  getNextToken(toknizr, langC)
+  check $toknizr == """(kind: gtOperator, start: 0, length: 1, buf: "/", pos: 1, state: gtEof, lang: langC)"""
diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim
index a27d41fe6..853a1c0cc 100644
--- a/tests/stdlib/thtmlparser.nim
+++ b/tests/stdlib/thtmlparser.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
   output: '''
 true
diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim
index d2fec6eec..0bd479670 100644
--- a/tests/stdlib/thttpclient.nim
+++ b/tests/stdlib/thttpclient.nim
@@ -15,6 +15,7 @@ from net import TimeoutError
 import nativesockets, os, httpclient, asyncdispatch
 
 import std/[assertions, syncio]
+from stdtest/testutils import enableRemoteNetworking
 
 const manualTests = false
 
@@ -52,12 +53,13 @@ proc asyncTest() {.async.} =
   doAssert("<title>Example Domain</title>" in body)
 
   resp = await client.request("http://example.com/404")
-  doAssert(resp.code.is4xx)
-  doAssert(resp.code == Http404)
-  doAssert(resp.status == $Http404)
+  doAssert(resp.code.is4xx or resp.code.is5xx)
+  doAssert(resp.code == Http404 or resp.code == Http500)
+  doAssert(resp.status == $Http404 or resp.status == $Http500)
 
-  resp = await client.request("https://google.com/")
-  doAssert(resp.code.is2xx or resp.code.is3xx)
+  when false: # occasionally does not give success code 
+    resp = await client.request("https://google.com/")
+    doAssert(resp.code.is2xx or resp.code.is3xx)
 
   # getContent
   try:
@@ -113,12 +115,13 @@ proc syncTest() =
   doAssert("<title>Example Domain</title>" in resp.body)
 
   resp = client.request("http://example.com/404")
-  doAssert(resp.code.is4xx)
-  doAssert(resp.code == Http404)
-  doAssert(resp.status == $Http404)
+  doAssert(resp.code.is4xx or resp.code.is5xx)
+  doAssert(resp.code == Http404 or resp.code == Http500)
+  doAssert(resp.status == $Http404 or resp.status == $Http500)
 
-  resp = client.request("https://google.com/")
-  doAssert(resp.code.is2xx or resp.code.is3xx)
+  when false: # occasionally does not give success code
+    resp = client.request("https://google.com/")
+    doAssert(resp.code.is2xx or resp.code.is3xx)
 
   # getContent
   try:
@@ -178,5 +181,7 @@ proc ipv6Test() =
   client.close()
 
 ipv6Test()
-syncTest()
-waitFor(asyncTest())
+
+when enableRemoteNetworking:
+  syncTest()
+  waitFor(asyncTest())
diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim
index feacd3e57..6b963f029 100644
--- a/tests/stdlib/thttpclient_ssl.nim
+++ b/tests/stdlib/thttpclient_ssl.nim
@@ -13,7 +13,10 @@ discard """
 ## Test with:
 ## ./bin/nim c -d:ssl -p:. --threads:on -r tests/stdlib/thttpclient_ssl.nim
 
-when not defined(windows):
+
+from stdtest/testutils import disableSSLTesting
+
+when not defined(windows) and not disableSSLTesting():
   # Disabled on Windows due to old OpenSSL version
   import std/[formatfloat, syncio]
   import
diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim
index 3b6b1efa0..93e7d85c6 100644
--- a/tests/stdlib/thttpcore.nim
+++ b/tests/stdlib/thttpcore.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import httpcore, strutils
 import std/assertions
 
diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim
index be912e702..672092282 100644
--- a/tests/stdlib/timportutils.nim
+++ b/tests/stdlib/timportutils.nim
@@ -1,4 +1,8 @@
-import std/importutils
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[importutils, assertions]
 import stdtest/testutils
 import mimportutils
 
@@ -134,5 +138,14 @@ template main =
           privateAccess PtA
           a.ha1 == 0
 
+    block:
+      privateAccess PityRef
+      let x = PityRef[int](a: 1)  # works
+      doAssert x.a == 1
+
+      privateAccess Hope
+      let y = Hope[int](a: 1)
+      doAssert y.a == 1
+
 static: main()
 main()
diff --git a/tests/stdlib/tio.nim b/tests/stdlib/tio.nim
index 0e20d6495..80a119763 100644
--- a/tests/stdlib/tio.nim
+++ b/tests/stdlib/tio.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 # xxx move to here other tests that belong here; io is a proper module
 
 import std/os
@@ -36,3 +40,21 @@ block: # readChars
         break
     doAssert n2s == @[2,2,2,1,0]
     doAssert s2 == s
+
+
+import std/strutils
+
+block: # bug #21273
+  let FILE = buildDir / "D20220119T134305.txt"
+
+  let hex = "313632313920313632343720313632353920313632363020313632393020323035363520323037323120323131353020323239393820323331303520323332313020323332343820323332363820"
+
+
+  writeFile FILE, parseHexStr(hex)
+
+  doAssert readFile(FILE).toHex == hex
+
+  let f = open(FILE)
+  var s = newString(80)
+  while f.readLine(s):
+    doAssert s.toHex == hex
diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim
index a60d45aab..e425501f6 100644
--- a/tests/stdlib/tjson.nim
+++ b/tests/stdlib/tjson.nim
@@ -1,6 +1,5 @@
 discard """
-  matrix: "--mm:refc"
-  targets: "c cpp js"
+  matrix: "; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
 """
 
 
@@ -9,6 +8,7 @@ Note: Macro tests are in tests/stdlib/tjsonmacro.nim
 ]#
 
 import std/[json,parsejson,strutils]
+import std/private/jsutils
 from std/math import isNaN
 when not defined(js):
   import std/streams
@@ -51,7 +51,7 @@ for i in 0 .. 10000:
   except:
     discard
 # memory diff should less than 4M
-doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) # todo fixme doesn;t work for ORC
+doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024)
 
 
 # test `$`
@@ -314,7 +314,8 @@ block: # bug #17383
   else:
     testRoundtrip(int.high): "9223372036854775807"
     testRoundtrip(uint.high): "18446744073709551615"
-  when not defined(js):
+  whenJsNoBigInt64: discard
+  do:
     testRoundtrip(int64.high): "9223372036854775807"
     testRoundtrip(uint64.high): "18446744073709551615"
 
diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim
index 9c1fa833d..5a1b4b294 100644
--- a/tests/stdlib/tjsonmacro.nim
+++ b/tests/stdlib/tjsonmacro.nim
@@ -1,5 +1,6 @@
 discard """
   output: ""
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
@@ -436,11 +437,7 @@ proc testJson() =
   block:
     let s = """{"a": 1, "b": 2}"""
     let t = parseJson(s).to(Table[string, int])
-    when not defined(js):
-      # For some reason on the JS backend `{"b": 2, "a": 0}` is
-      # sometimes the value of `t`. This needs investigation. I can't
-      # reproduce it right now in an isolated test.
-      doAssert t["a"] == 1
+    doAssert t["a"] == 1
     doAssert t["b"] == 2
 
   block:
diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim
index 54cb69560..9acf4c9e5 100644
--- a/tests/stdlib/tjsonutils.nim
+++ b/tests/stdlib/tjsonutils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
@@ -60,8 +61,7 @@ template fn() =
         testRoundtrip(pointer(nil)): """0"""
         testRoundtrip(cast[pointer](nil)): """0"""
 
-    # causes workaround in `fromJson` potentially related to
-    # https://github.com/nim-lang/Nim/issues/12282
+    # refs bug #9423
     testRoundtrip(Foo(1.5)): """1.5"""
 
   block: # OrderedTable
@@ -410,6 +410,39 @@ template fn() =
       doAssert foo.c == 0
       doAssert foo.c0 == 42
 
+
+    block testInvalidTupleLength:
+      let json = parseJson("[0]")
+      # Should raise ValueError instead of index error
+      doAssertRaises(ValueError):
+        discard json.jsonTo((int, int))
+
+    type
+      InnerEnum = enum
+        A
+        B
+        C
+      InnerObject = object
+        x: string
+        y: InnerEnum
+
+    block testOptionsArePassedWhenDeserialising:
+      let json = parseJson("""{"x": "hello"}""")
+      let inner = json.jsonTo(Option[InnerObject], Joptions(allowMissingKeys: true))
+      doAssert inner.isSome()
+      doAssert inner.get().x == "hello"
+      doAssert inner.get().y == A
+
+    block testOptionsArePassedWhenSerialising:
+      let inner = some InnerObject(x: "hello", y: A)
+      let json = inner.toJson(ToJsonOptions(enumMode: joptEnumSymbol))
+      doAssert $json == """{"x":"hello","y":"A"}"""
+
+    block: # bug #21638
+      type Something = object
+
+      doAssert "{}".parseJson.jsonTo(Something) == Something()
+
     when false:
       ## TODO: Implement support for nested variant objects allowing the tests
       ## bellow to pass.
diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim
index 701fb7974..5993278c7 100644
--- a/tests/stdlib/tlists.nim
+++ b/tests/stdlib/tlists.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim
index 9ce9afd13..1c5f67119 100644
--- a/tests/stdlib/tlocks.nim
+++ b/tests/stdlib/tlocks.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp js"
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
 """
 
 #bug #6049
diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim
index 7ec2fed9f..06a9a9c27 100644
--- a/tests/stdlib/tmacros.nim
+++ b/tests/stdlib/tmacros.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 #[
 xxx macros tests need to be reorganized to makes sure each API is tested once
 See also:
@@ -157,3 +161,189 @@ block: # bug #19020
   static:
     doAssert $bar.getCustomPragmaVal(typ) == "foo"
   doAssert $bar.getCustomPragmaVal(typ) == "foo"
+
+block hasCustomPragmaGeneric:
+  template examplePragma() {.pragma.}
+  type
+    Foo[T] {.examplePragma.} = object
+      x {.examplePragma.}: T
+  var f: Foo[string]
+  doAssert f.hasCustomPragma(examplePragma)
+  doAssert f.x.hasCustomPragma(examplePragma)
+
+block getCustomPragmaValGeneric:
+  template examplePragma(x: int) {.pragma.}
+  type
+    Foo[T] {.examplePragma(42).} = object
+      x {.examplePragma(25).}: T
+  var f: Foo[string]
+  doAssert f.getCustomPragmaVal(examplePragma) == 42
+  doAssert f.x.getCustomPragmaVal(examplePragma) == 25
+
+block: # bug #21326
+  macro foo(body: untyped): untyped =
+    let a = body.lineInfoObj()
+    let aLit = a.newLit
+    result = quote do:
+      doAssert $`a` == $`aLit`
+
+  foo:
+    let c = 1
+
+  template name(a: LineInfo): untyped =
+    discard a # `aLit` works though
+
+  macro foo3(body: untyped): untyped =
+    let a = body.lineInfoObj()
+    # let ax = newLit(a)
+    result = getAst(name(a))
+
+  foo3:
+    let c = 1
+
+block: # bug #7375
+  macro fails(b: static[bool]): untyped =
+    doAssert b == false
+    result = newStmtList()
+
+  macro foo(): untyped =
+
+    var b = false
+
+    ## Fails
+    result = quote do:
+      fails(`b`)
+
+  foo()
+
+  macro someMacro(): untyped =
+    template tmpl(boolean: bool) =
+      when boolean:
+        discard "it's true!"
+      else:
+        doAssert false
+    result = getAst(tmpl(true))
+
+  someMacro()
+
+block:
+  macro foo(): untyped =
+    result = quote do: `littleEndian`
+
+  doAssert littleEndian == foo()
+
+block:
+  macro eqSym(x, y: untyped): untyped =
+    let eq = $x == $y # Unfortunately eqIdent compares to string.
+    result = quote do: `eq`
+
+  var r, a, b: int
+
+  template fma(result: var int, a, b: int, op: untyped) =
+    # fused multiple-add
+    when eqSym(op, `+=`):
+      discard "+="
+    else:
+      discard "+"
+
+  fma(r, a, b, `+=`)
+
+block:
+  template test(boolArg: bool) =
+    static:
+      doAssert typeof(boolArg) is bool
+    let x: bool = boolArg # compile error here, because boolArg became an int
+
+  macro testWrapped1(boolArg: bool): untyped =
+    # forwarding boolArg directly works
+    result = getAst(test(boolArg))
+
+  macro testWrapped2(boolArg: bool): untyped =
+    # forwarding boolArg via a local variable also works
+    let b = boolArg
+    result = getAst(test(b))
+
+  macro testWrapped3(boolArg: bool): untyped =
+    # but using a literal `true` as a local variable will be converted to int
+    let b = true
+    result = getAst(test(b))
+
+  test(true) # ok
+  testWrapped1(true) # ok
+  testWrapped2(true) # ok
+  testWrapped3(true) 
+
+block:
+  macro foo(): untyped =
+    var s = { 'a', 'b' }
+    quote do:              
+      let t = `s`         
+      doAssert $typeof(t) == "set[char]"
+
+  foo()
+
+block: # bug #9607
+  proc fun1(info:LineInfo): string = "bar"
+  proc fun2(info:int): string = "bar"
+
+  macro echoL(args: varargs[untyped]): untyped =
+    let info = args.lineInfoObj
+    let fun1 = bindSym"fun1"
+    let fun2 = bindSym"fun2"
+
+    # this would work instead
+    # result = newCall(bindSym"fun2", info.line.newLit)
+
+    result = quote do:
+
+      # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
+      `fun1`(`info`)
+
+  macro echoM(args: varargs[untyped]): untyped =
+    let info = args.lineInfoObj
+    let fun1 = bindSym"fun1"
+    let fun2 = bindSym"fun2"
+
+    # this would work instead
+    # result = newCall(bindSym"fun2", info.line.newLit)
+
+    result = quote do:
+
+      # BUG1: ???(0, 0) Error: internal error: genLiteral: ty is nil
+      `fun2`(`info`.line)
+
+
+  doAssert echoL() == "bar"
+  doAssert echoM() == "bar"
+
+block:
+  macro hello[T](x: T): untyped =
+    result = quote do:
+      let m: `T` = `x`
+      discard m
+
+  hello(12)
+
+block:
+  proc hello(x: int, y: typedesc) =
+    discard
+
+  macro main =
+    let x = 12
+    result = quote do:
+      `hello`(12, type(x))
+
+  main()
+
+block: # bug #22947
+  macro bar[N: static int](a: var array[N, int]) =
+    result = quote do:
+      for i in 0 ..< `N`:
+        `a`[i] = i
+
+  func foo[N: static int](a: var array[N, int]) =
+    bar(a)
+
+
+  var a: array[4, int]
+  foo(a)
diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim
index f972332a2..32991ccc9 100644
--- a/tests/stdlib/tmarshal.nim
+++ b/tests/stdlib/tmarshal.nim
@@ -3,7 +3,7 @@ discard """
 """
 
 import std/marshal
-import std/[assertions, objectdollar]
+import std/[assertions, objectdollar, streams]
 
 # TODO: add static tests
 
@@ -166,6 +166,46 @@ block:
   let a: ref A = new(B)
   doAssert $$a[] == "{}" # not "{f: 0}"
 
+# bug #16496
+block:
+  type
+    A = ref object
+      data: seq[int]
+
+    B = ref object
+      x: A
+  let o = A(data: @[1, 2, 3, 4])
+  let s1 = @[B(x: o), B(x: o)]
+  let m  = $$ s1
+  let s2 = to[seq[B]](m)
+  doAssert s2[0].x.data == s2[1].x.data
+  doAssert s1[0].x.data == s2[1].x.data
+
+
+block:
+  type
+    Obj = ref object
+      i: int
+      b: bool
+
+  let
+    strm = newStringStream()
+
+  var
+    o = Obj(i: 1, b: false)
+    t1 = @[o, o]
+    t2: seq[Obj]
+
+  doAssert t1[0] == t1[1]
+
+  strm.store(t1)
+  strm.setPosition(0)
+  strm.load(t2)
+  strm.close()
+
+  doAssert t2[0] == t2[1]
+
+
 template checkMarshal(data: typed) =
   let orig = data
   let m = $$orig
diff --git a/tests/stdlib/tmarshalsegfault.nim b/tests/stdlib/tmarshalsegfault.nim
new file mode 100644
index 000000000..71f2766c8
--- /dev/null
+++ b/tests/stdlib/tmarshalsegfault.nim
@@ -0,0 +1,54 @@
+# issue #12405
+
+import std/[marshal, streams, times, tables, os, assertions]
+
+type AiredEpisodeState * = ref object
+    airedAt * : DateTime
+    tvShowId * : string
+    seasonNumber * : int
+    number * : int
+    title * : string
+
+type ShowsWatchlistState * = ref object
+    aired * : seq[AiredEpisodeState]
+
+type UiState * = ref object
+    shows: ShowsWatchlistState
+
+# Helpers to marshal and unmarshal
+proc load * ( state : var UiState, file : string ) =
+    var strm = newFileStream( file, fmRead )
+
+    strm.load( state )
+
+    strm.close()
+
+proc store * ( state : UiState, file : string ) =
+    var strm = newFileStream( file, fmWrite )
+
+    strm.store( state )
+
+    strm.close()
+
+# 1. We fill the state initially
+var state : UiState = UiState( shows: ShowsWatchlistState( aired: @[] ) )
+
+# VERY IMPORTANT: For some reason, small numbers (like 2 or 3) don't trigger the bug. Anything above 7 or 8 on my machine triggers though
+for i in 0..30:
+    var episode = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
+
+    state.shows.aired.add( episode )
+
+# 2. Store it in a file with the marshal module, and then load it back up
+store( state, "tmarshalsegfault_data" )
+load( state, "tmarshalsegfault_data" )
+removeFile("tmarshalsegfault_data")
+
+# 3. VERY IMPORTANT: Without this line, for some reason, everything works fine
+state.shows.aired[ 0 ] = AiredEpisodeState( airedAt: now(), tvShowId: "1", seasonNumber: 1, number: 1, title: "string" )
+
+# 4. And formatting the airedAt date will now trigger the exception
+for ep in state.shows.aired:
+    let x = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
+    let y = $ep.seasonNumber & "x" & $ep.number & " (" & $ep.airedAt & ")"
+    doAssert x == y
diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim
index 66c1f8ca0..22e5f7d88 100644
--- a/tests/stdlib/tmath.nim
+++ b/tests/stdlib/tmath.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp js"
-  matrix:"; -d:danger"
+  matrix:"; -d:danger; --mm:refc"
 """
 
 # xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`,
@@ -186,7 +186,22 @@ template main() =
     when not defined(nimTmathCase2):
       doAssert classify(trunc(f_nan.float32)) == fcNan
     doAssert classify(trunc(0.0'f32)) == fcZero
-
+  
+  block: # divmod
+    doAssert divmod(int.high, 1) == (int.high, 0)
+    doAssert divmod(-1073741823, 17) == (-63161283, -12)
+    doAssert divmod(int32.high, 1.int32) == (int32.high, 0.int32)
+    doAssert divmod(1073741823.int32, 5.int32) == (214748364.int32, 3.int32)
+    doAssert divmod(4611686018427387903.int64, 5.int64) == (922337203685477580.int64, 3.int64)
+    when not defined(js) and (not compileOption("panics")) and compileOption("overflowChecks"):
+      when nimvm:
+        discard # cannot catch OverflowDefect here
+      else:
+        doAssertRaises(OverflowDefect, (discard divmod(cint.low, -1.cint)))
+        doAssertRaises(OverflowDefect, (discard divmod(clong.low, -1.clong)))
+        doAssertRaises(OverflowDefect, (discard divmod(clonglong.low, -1.clonglong)))
+        doAssertRaises(DivByZeroDefect, (discard divmod(1, 0)))
+  
   block: # log
     doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0)
     doAssert log2(8.0'f64) == 3.0'f64
@@ -439,6 +454,20 @@ template main() =
       doAssert lgamma(-0.0) == Inf
       doAssert lgamma(-1.0) == Inf
 
-
 static: main()
 main()
+
+when not defined(js) and not defined(danger):
+  block: # bug #21792
+    block:
+      type Digit = 0..9
+      var x = [Digit 4, 7]
+
+      doAssertRaises(RangeDefect):
+        discard sum(x)
+
+    block:
+      var x = [int8 124, 127]
+
+      doAssertRaises(OverflowDefect):
+        discard sum(x)
diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim
deleted file mode 100644
index 254eefea9..000000000
--- a/tests/stdlib/tmd5.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-  targets: "c cpp js"
-"""
-
-import md5
-import std/assertions
-
-proc main() {.raises: [].} =
-  doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
-    "a3cca2b2aa1e3b5b3b5aad99a8529074")
-  doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
-    "7e716d0e702df0505fc72e2b89467910")
-  doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e")
-
-main()
-
-static: main()
diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim
index 6fee3c1ae..c79f85ebf 100644
--- a/tests/stdlib/tmemfiles2.nim
+++ b/tests/stdlib/tmemfiles2.nim
@@ -15,8 +15,9 @@ var
 
 if fileExists(fn): removeFile(fn)
 
-# Create a new file, data all zeros
-mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20)
+# Create a new file, data all zeros, starting at size 10
+mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 10, allowRemap=true)
+mm.resize 20  # resize up to 20
 mm.close()
 
 # read, change
diff --git a/tests/stdlib/tmget.nim b/tests/stdlib/tmget.nim
index 52e61fd24..f41963f02 100644
--- a/tests/stdlib/tmget.nim
+++ b/tests/stdlib/tmget.nim
@@ -1,16 +1,21 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
+2
 Can't access 6
 10
 11
+2
 0
 10
 11
@@ -40,6 +45,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = newTable[int, int]()
@@ -52,6 +60,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = initOrderedTable[int, int]()
@@ -64,6 +75,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = newOrderedTable[int, int]()
@@ -76,6 +90,9 @@ block:
   x[5] += 1
   var c = x[5]
   echo c
+  x.mgetOrPut(7).inc
+  x.mgetOrPut(7).inc
+  echo x[7]
 
 block:
   var x = initCountTable[int]()
diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim
index 8263e37fd..fd66ebd97 100644
--- a/tests/stdlib/tmimetypes.nim
+++ b/tests/stdlib/tmimetypes.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
@@ -10,10 +11,18 @@ template main() =
   var m = newMimetypes()
   doAssert m.getMimetype("mp4") == "video/mp4"
   doAssert m.getExt("application/json") == "json"
+  doAssert m.getMimetype("json") == "application/json"
   m.register("foo", "baa")
   doAssert m.getMimetype("foo") == "baa"
+  doAssert m.getMimetype("txt") == "text/plain"
+  doAssert m.getExt("text/plain") == "txt"
   # see also `runnableExamples`.
   # xxx we should have a way to avoid duplicating code between runnableExamples and tests
 
+  doAssert m.getMimetype("nim") == "text/nim"
+  doAssert m.getMimetype("nimble") == "text/nimble"
+  doAssert m.getMimetype("nimf") == "text/nim"
+  doAssert m.getMimetype("nims") == "text/nim"
+
 static: main()
 main()
diff --git a/tests/stdlib/tmisc_issues.nim b/tests/stdlib/tmisc_issues.nim
index b5a02e614..86dcf4162 100644
--- a/tests/stdlib/tmisc_issues.nim
+++ b/tests/stdlib/tmisc_issues.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
@@ -17,3 +18,22 @@ type
 
 var x: Object = Object(data: Test(Data(id: 12)))
 doAssert Data(x.data).id == 12
+
+block: # bug #16771
+  type A = object
+    n: int
+
+  proc foo(a, b: var A) =
+    swap a, b
+
+  var a, b: A
+  a.n = 42
+  b.n = 1
+  doAssert a.n == 42
+  doAssert b.n == 1
+  a.swap b
+  doAssert a.n == 1
+  doAssert b.n == 42
+  a.foo b
+  doAssert a.n == 42
+  doAssert b.n == 1
diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim
index 171604e33..cc515a175 100644
--- a/tests/stdlib/tmitems.nim
+++ b/tests/stdlib/tmitems.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''@[11, 12, 13]
 @[11, 12, 13]
 @[1, 3, 5]
diff --git a/tests/stdlib/tmonotimes.nim b/tests/stdlib/tmonotimes.nim
index f10fef591..1366dbfe9 100644
--- a/tests/stdlib/tmonotimes.nim
+++ b/tests/stdlib/tmonotimes.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim
index b1bbf32c2..8242beb83 100644
--- a/tests/stdlib/tnativesockets.nim
+++ b/tests/stdlib/tnativesockets.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/nativesockets
 import stdtest/testutils
 import std/assertions
diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim
index 06ff44c3d..27a6ac49c 100644
--- a/tests/stdlib/tnet.nim
+++ b/tests/stdlib/tnet.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 outputsub: ""
 """
 
diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim
index 2d340cea8..199946482 100644
--- a/tests/stdlib/tnet_ll.nim
+++ b/tests/stdlib/tnet_ll.nim
@@ -1,5 +1,6 @@
 discard """
   action: run
+  matrix: "--mm:refc; --mm:orc"
   output: '''
 
 [Suite] inet_ntop tests
@@ -29,7 +30,7 @@ suite "inet_ntop tests":
     check: ip4.s_addr == 0x10111213'u32
 
     var buff: array[0..255, char]
-    let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), buff[0].addr, buff.len.int32)
+    let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), cast[cstring](buff[0].addr), buff.len.int32)
     let res = if r == nil: "" else: $r
     check: res == "19.18.17.16"
       
@@ -41,7 +42,7 @@ suite "inet_ntop tests":
           
     var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001]
     var buff: array[0..255, char]
-    let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), buff[0].addr, buff.len.int32)
+    let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), cast[cstring](buff[0].addr), buff.len.int32)
     let res = if r == nil: "" else: $r
     check: not ipv6Support or res == "10:110:20:120:30:130:40:140"
 
diff --git a/tests/stdlib/tnetbind.nim b/tests/stdlib/tnetbind.nim
index 734b6c5e7..84f9ac464 100644
--- a/tests/stdlib/tnetbind.nim
+++ b/tests/stdlib/tnetbind.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 joinable: false
 """
 
diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim
index 3a80df19f..ae654aed9 100644
--- a/tests/stdlib/tnetconnect.nim
+++ b/tests/stdlib/tnetconnect.nim
@@ -1,9 +1,11 @@
 discard """
+  disabled: "i386"
   matrix: "-d:ssl"
 """
 
 import std/net
 from std/strutils import `%`
+from stdtest/testutils import enableRemoteNetworking
 
 # bug #15215
 proc test() =
@@ -24,4 +26,5 @@ proc test() =
   except TimeoutError, OSError:
     fn("www.google.com")
 
-test()
+when enableRemoteNetworking:
+  test()
diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim
index 3b8276d6f..a1e147ad5 100644
--- a/tests/stdlib/tnetdial.nim
+++ b/tests/stdlib/tnetdial.nim
@@ -5,7 +5,7 @@ discard """
 """
 
 import os, net, nativesockets, asyncdispatch
-import std/[assertions]
+import std/[assertions, typedthreads]
 
 ## Test for net.dial
 
diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim
index f13c16052..3b40e9e83 100644
--- a/tests/stdlib/tnre.nim
+++ b/tests/stdlib/tnre.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 # Since the tests for nre are all bundled together we treat failure in one test as an nre failure
 # When running 'testament/tester' a failed check() in the test suite will cause the exit
 # codes to differ and be reported as a failure
diff --git a/tests/stdlib/tntpath.nim b/tests/stdlib/tntpath.nim
index dce0cf6f8..8efdd6bd0 100644
--- a/tests/stdlib/tntpath.nim
+++ b/tests/stdlib/tntpath.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/private/ntpath
 import std/assertions
 
diff --git a/tests/stdlib/toids.nim b/tests/stdlib/toids.nim
index 95161415d..dd5b84c51 100644
--- a/tests/stdlib/toids.nim
+++ b/tests/stdlib/toids.nim
@@ -7,7 +7,7 @@ import std/assertions
 
 block: # genOid
   let x = genOid()
-  doAssert ($x).len == 32
+  doAssert ($x).len == 24
 
 block:
   let x = genOid()
diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim
index 3209437de..af259627f 100644
--- a/tests/stdlib/topenssl.nim
+++ b/tests/stdlib/topenssl.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/wordwrap
 import openssl
 import std/assertions
diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim
index 6065425b9..63a10e746 100644
--- a/tests/stdlib/toptions.nim
+++ b/tests/stdlib/toptions.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
@@ -195,6 +196,12 @@ proc main() =
         doAssert x.isNone
         doAssert $x == "none(cstring)"
 
-
 static: main()
 main()
+
+when not defined(js):
+  block: # bug #22932
+    var it = iterator: int {.closure.} = discard
+    doAssert it.option.isSome # Passes.
+    it = nil
+    doAssert it.option.isNone # Passes.
diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim
index bd91a3de9..611659fdb 100644
--- a/tests/stdlib/tos.nim
+++ b/tests/stdlib/tos.nim
@@ -22,13 +22,13 @@ __really_obscure_dir_name/test
 Raises
 Raises
 '''
+  matrix: "--mm:refc; --mm:orc"
   joinable: false
 """
 # test os path creation, iteration, and deletion
 
-import os, strutils, pathnorm
 from stdtest/specialpaths import buildDir
-import std/[syncio, assertions]
+import std/[syncio, assertions, osproc, os, strutils, pathnorm]
 
 block fileOperations:
   let files = @["these.txt", "are.x", "testing.r", "files.q"]
@@ -160,6 +160,18 @@ block fileOperations:
   # createDir should not fail if `dir` is empty
   createDir("")
 
+
+  when defined(linux): # bug #24174
+    createDir("a/b")
+    open("a/file.txt", fmWrite).close
+
+    if not fileExists("a/fifoFile"):
+      doAssert execCmd("mkfifo -m 600 a/fifoFile") == 0
+
+    copyDir("a/", "../dest/a/", skipSpecial = true)
+    copyDirWithPermissions("a/", "../dest2/a/", skipSpecial = true)
+    removeDir("a")
+
   # Symlink handling in `copyFile`, `copyFileWithPermissions`, `copyFileToDir`,
   # `copyDir`, `copyDirWithPermissions`, `moveFile`, and `moveDir`.
   block:
@@ -344,6 +356,8 @@ block walkDirRec:
 
   removeDir("walkdir_test")
 
+import std/sequtils
+
 block: # walkDir
   doAssertRaises(OSError):
     for a in walkDir("nonexistent", checkDir = true): discard
@@ -358,6 +372,21 @@ block: # walkDir
         doAssert k == pcLinkToDir
       removeDir("walkdir_test")
 
+  when defined(posix):
+    block walkDirSpecial:
+      createDir("walkdir_test")
+      doAssert execShellCmd("mkfifo walkdir_test/fifo") == 0
+      createSymlink("fifo", "walkdir_test/fifo_link")
+      let withSpecialFiles = toSeq(walkDir("walkdir_test", relative = true))
+      doAssert (withSpecialFiles.len == 2 and
+                (pcFile, "fifo") in withSpecialFiles and
+                (pcLinkToFile, "fifo_link") in withSpecialFiles)
+      # now Unix special files are excluded from walkdir output:
+      let skipSpecialFiles = toSeq(walkDir("walkdir_test", relative = true,
+                                           skipSpecial = true))
+      doAssert skipSpecialFiles.len == 0
+      removeDir("walkdir_test")
+
 block normalizedPath:
   doAssert normalizedPath("") == ""
   block relative:
@@ -672,32 +701,6 @@ block: # normalizePathEnd
     doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\"
     doAssert "/".normalizePathEnd == r"\"
 
-block: # isValidFilename
-  # Negative Tests.
-  doAssert not isValidFilename("abcd", maxLen = 2)
-  doAssert not isValidFilename("0123456789", maxLen = 8)
-  doAssert not isValidFilename("con")
-  doAssert not isValidFilename("aux")
-  doAssert not isValidFilename("prn")
-  doAssert not isValidFilename("OwO|UwU")
-  doAssert not isValidFilename(" foo")
-  doAssert not isValidFilename("foo ")
-  doAssert not isValidFilename("foo.")
-  doAssert not isValidFilename("con.txt")
-  doAssert not isValidFilename("aux.bat")
-  doAssert not isValidFilename("prn.exe")
-  doAssert not isValidFilename("nim>.nim")
-  doAssert not isValidFilename(" foo.log")
-  # Positive Tests.
-  doAssert isValidFilename("abcd", maxLen = 42.Positive)
-  doAssert isValidFilename("c0n")
-  doAssert isValidFilename("foo.aux")
-  doAssert isValidFilename("bar.prn")
-  doAssert isValidFilename("OwO_UwU")
-  doAssert isValidFilename("cron")
-  doAssert isValidFilename("ux.bat")
-  doAssert isValidFilename("nim.nim")
-  doAssert isValidFilename("foo.log")
 
 import sugar
 
@@ -716,7 +719,23 @@ block: # isAdmin
   # In Azure on POSIX tests run as a normal user
   if isAzure and defined(posix): doAssert not isAdmin()
 
-import std/sequtils
+
+import sugar
+
+block: # normalizeExe
+  doAssert "".dup(normalizeExe) == ""
+  when defined(posix):
+    doAssert "foo".dup(normalizeExe) == "./foo"
+    doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
+  when defined(windows):
+    doAssert "foo".dup(normalizeExe) == "foo"
+
+block: # isAdmin
+  let isAzure = existsEnv("TF_BUILD") # xxx factor with testament.specs.isAzure
+  # In Azure on Windows tests run as an admin user
+  if isAzure and defined(windows): doAssert isAdmin()
+  # In Azure on POSIX tests run as a normal user
+  if isAzure and defined(posix): doAssert not isAdmin()
 
 when doslikeFileSystem:
   import std/private/ntpath
@@ -793,3 +812,64 @@ else:
     doAssert parentDirs("/home/user", fromRoot=false).toSeq == @["/home/user", "/home", "/"]
     doAssert parentDirs("home/user", fromRoot=true).toSeq == @["home/", "home/user"]
     doAssert parentDirs("home/user", fromRoot=false).toSeq == @["home/user", "home"]
+
+
+# https://github.com/nim-lang/Nim/pull/19643#issuecomment-1235102314
+block:  # isValidFilename
+  # Negative Tests.
+  doAssert not isValidFilename("abcd", maxLen = 2)
+  doAssert not isValidFilename("0123456789", maxLen = 8)
+  doAssert not isValidFilename("con")
+  doAssert not isValidFilename("aux")
+  doAssert not isValidFilename("prn")
+  doAssert not isValidFilename("OwO|UwU")
+  doAssert not isValidFilename(" foo")
+  doAssert not isValidFilename("foo ")
+  doAssert not isValidFilename("foo.")
+  doAssert not isValidFilename("con.txt")
+  doAssert not isValidFilename("aux.bat")
+  doAssert not isValidFilename("prn.exe")
+  doAssert not isValidFilename("nim>.nim")
+  doAssert not isValidFilename(" foo.log")
+  # Positive Tests.
+  doAssert isValidFilename("abcd", maxLen = 42.Positive)
+  doAssert isValidFilename("c0n")
+  doAssert isValidFilename("foo.aux")
+  doAssert isValidFilename("bar.prn")
+  doAssert isValidFilename("OwO_UwU")
+  doAssert isValidFilename("cron")
+  doAssert isValidFilename("ux.bat")
+  doAssert isValidFilename("nim.nim")
+  doAssert isValidFilename("foo.log")
+
+block: # searchExtPos
+  doAssert "foo.nim".searchExtPos == 3
+  doAssert "/foo.nim".searchExtPos == 4
+  doAssert "".searchExtPos == -1
+  doAssert "/".searchExtPos == -1
+  doAssert "a.b/foo".searchExtPos == -1
+  doAssert ".".searchExtPos == -1
+  doAssert "foo.".searchExtPos == 3
+  doAssert "foo..".searchExtPos == 4
+  doAssert "..".searchExtPos == -1
+  doAssert "...".searchExtPos == -1
+  doAssert "./".searchExtPos == -1
+  doAssert "../".searchExtPos == -1
+  doAssert "/.".searchExtPos == -1
+  doAssert "/..".searchExtPos == -1
+  doAssert ".b".searchExtPos == -1
+  doAssert "..b".searchExtPos == -1
+  doAssert "/.b".searchExtPos == -1
+  doAssert "a/.b".searchExtPos == -1
+  doAssert ".a.b".searchExtPos == 2
+  doAssert "a/.b.c".searchExtPos == 4
+  doAssert "a/..b".searchExtPos == -1
+  doAssert "a/b..c".searchExtPos == 4
+
+  when doslikeFileSystem:
+    doAssert "c:a.b".searchExtPos == 3
+    doAssert "c:.a".searchExtPos == -1
+    doAssert r"c:\.a".searchExtPos == -1
+    doAssert "c:..a".searchExtPos == -1
+    doAssert r"c:\..a".searchExtPos == -1
+    doAssert "c:.a.b".searchExtPos == 4
diff --git a/tests/stdlib/tos_unc.nim b/tests/stdlib/tos_unc.nim
index fc74a4b9d..194deeb42 100644
--- a/tests/stdlib/tos_unc.nim
+++ b/tests/stdlib/tos_unc.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   disabled: "posix"
 """
 
diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim
index f7b3bb9d6..17e397987 100644
--- a/tests/stdlib/tosenv.nim
+++ b/tests/stdlib/tosenv.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads"
+  matrix: "--mm:refc; --mm:arc"
   joinable: false
   targets: "c js cpp"
 """
@@ -7,7 +7,9 @@ discard """
 import std/os
 from std/sequtils import toSeq
 import stdtest/testutils
-import std/assertions
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions]
 
 # "LATIN CAPITAL LETTER AE" in UTF-8 (0xc386)
 const unicodeUtf8 = "\xc3\x86"
@@ -50,10 +52,13 @@ static: main()
 main()
 
 when defined(windows):
+  import std/widestrs
   proc c_wgetenv(env: WideCString): WideCString {.importc: "_wgetenv", header: "<stdlib.h>".}
 proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "<stdlib.h>".}
 
 when not defined(js) and not defined(nimscript):
+  when defined(nimPreviewSlimSystem):
+    import std/typedthreads
   block: # bug #18533
     var thr: Thread[void]
     proc threadFunc {.thread.} = putEnv("foo", "fooVal2")
diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim
index 47fec2567..da4f6252d 100644
--- a/tests/stdlib/tosproc.nim
+++ b/tests/stdlib/tosproc.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 joinable: false
 """
 
@@ -93,9 +94,7 @@ else: # main driver
   const sourcePath = currentSourcePath()
   let dir = getCurrentDir() / "tests" / "osproc"
 
-  template deferScoped(cleanup, body) =
-    # pending https://github.com/nim-lang/RFCs/issues/236#issuecomment-646855314
-    # xxx move to std/sugar or (preferably) some low level module
+  template deferring(cleanup, body) =
     try: body
     finally: cleanup
 
@@ -120,7 +119,10 @@ else: # main driver
     runTest("exit_0", 0)
     runTest("exitnow_139", 139)
     runTest("c_exit2_139", 139)
-    runTest("quit_139", 139)
+    when defined(posix):
+      runTest("quit_139", 127) # The quit value gets saturated to 127
+    else:
+      runTest("quit_139", 139)
 
   block execCmdTest:
     let output = compileNimProg("-d:release -d:case_testfile", "D20220705T221100")
@@ -246,14 +248,14 @@ else: # main driver
     var x = newStringOfCap(120)
     block: # startProcess stdout poStdErrToStdOut (replaces old test `tstdout` + `ta_out`)
       var p = startProcess(output, dir, options={poStdErrToStdOut})
-      deferScoped: p.close()
+      deferring: p.close()
       do:
         var sout: seq[string]
         while p.outputStream.readLine(x): sout.add x
         doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stderr", "to stderr", "to stdout", "to stdout", "end ta_out"]
     block: # startProcess stderr (replaces old test `tstderr` + `ta_out`)
       var p = startProcess(output, dir, options={})
-      deferScoped: p.close()
+      deferring: p.close()
       do:
         var serr, sout: seq[string]
         while p.errorStream.readLine(x): serr.add x
diff --git a/tests/stdlib/tosprocterminate.nim b/tests/stdlib/tosprocterminate.nim
index 08b379569..93b0317f7 100644
--- a/tests/stdlib/tosprocterminate.nim
+++ b/tests/stdlib/tosprocterminate.nim
@@ -1,7 +1,7 @@
 discard """
   cmd: "nim $target $options -r $file"
   targets: "c cpp"
-  matrix: "--threads:on; "
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import os, osproc, times, std / monotimes
diff --git a/tests/stdlib/tpackedsets.nim b/tests/stdlib/tpackedsets.nim
index 2c69f6b1b..f519c08a7 100644
--- a/tests/stdlib/tpackedsets.nim
+++ b/tests/stdlib/tpackedsets.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/packedsets
 import std/sets
 
diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim
index 16f12bc9e..2600d6f66 100644
--- a/tests/stdlib/tparsecfg.nim
+++ b/tests/stdlib/tparsecfg.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim
index a879019f6..5a1e41bce 100644
--- a/tests/stdlib/tparsecsv.nim
+++ b/tests/stdlib/tparsecsv.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 include parsecsv
 import strutils, os
 import std/assertions
diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim
index 9b9c464c7..31ec4ecfb 100644
--- a/tests/stdlib/tparseipv6.nim
+++ b/tests/stdlib/tparseipv6.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: "all ok"
 """
 
diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim
index cfd8ad148..cd582551d 100644
--- a/tests/stdlib/tparsesql.nim
+++ b/tests/stdlib/tparsesql.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 import parsesql
diff --git a/tests/stdlib/tparseuints.nim b/tests/stdlib/tparseuints.nim
index ef8c782b3..9c71a27d6 100644
--- a/tests/stdlib/tparseuints.nim
+++ b/tests/stdlib/tparseuints.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import unittest, strutils
 
 block: # parseutils
diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim
index 084a85dac..b69900864 100644
--- a/tests/stdlib/tparseutils.nim
+++ b/tests/stdlib/tparseutils.nim
@@ -1,53 +1,112 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+  targets: "c cpp"
+"""
+
 import std/[parseutils, sequtils, sugar, formatfloat]
 import std/assertions
 
+proc test() =
+  let input = "$test{}  $this is ${an{  example}}  "
+  let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
+                    (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
+  doAssert toSeq(interpolatedFragments(input)) == expected
+
+  var value = 0
+  discard parseHex("0x38", value)
+  doAssert value == 56
+
+  value = -1
+  doAssert(parseSaturatedNatural("848", value) == 3)
+  doAssert value == 848
+
+  value = -1
+  discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
+  doAssert value == high(int)
 
-let input = "$test{}  $this is ${an{  example}}  "
-let expected = @[(ikVar, "test"), (ikStr, "{}  "), (ikVar, "this"),
-                  (ikStr, " is "), (ikExpr, "an{  example}"), (ikStr, "  ")]
-doAssert toSeq(interpolatedFragments(input)) == expected
+  value = -1
+  discard parseSaturatedNatural("9223372036854775808", value)
+  doAssert value == high(int)
 
-var value = 0
-discard parseHex("0x38", value)
-doAssert value == 56
+  value = -1
+  discard parseSaturatedNatural("9223372036854775807", value)
+  doAssert value == high(int)
 
-value = -1
-doAssert(parseSaturatedNatural("848", value) == 3)
-doAssert value == 848
+  value = -1
+  discard parseSaturatedNatural("18446744073709551616", value)
+  doAssert value == high(int)
 
-value = -1
-discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
-doAssert value == high(int)
+  value = -1
+  discard parseSaturatedNatural("18446744073709551615", value)
+  doAssert value == high(int)
 
-value = -1
-discard parseSaturatedNatural("9223372036854775808", value)
-doAssert value == high(int)
+  value = -1
+  doAssert(parseSaturatedNatural("1_000_000", value) == 9)
+  doAssert value == 1_000_000
 
-value = -1
-discard parseSaturatedNatural("9223372036854775807", value)
-doAssert value == high(int)
+  var i64Value: int64
+  discard parseBiggestInt("9223372036854775807", i64Value)
+  doAssert i64Value == 9223372036854775807
 
-value = -1
-discard parseSaturatedNatural("18446744073709551616", value)
-doAssert value == high(int)
+  block:
+    var f: float
+    let res = collect:
+      for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]:
+        (parseFloat(x, f, 0), $f)
+    doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"),
+                      (17, "9.123456789012344"), (17, "8.123456789012344"),
+                      (16, "9.12345678901234"), (17, "9.123456789012344")]
 
-value = -1
-discard parseSaturatedNatural("18446744073709551615", value)
-doAssert value == high(int)
+test()
+static: test()
 
-value = -1
-doAssert(parseSaturatedNatural("1_000_000", value) == 9)
-doAssert value == 1_000_000
+block:  # With this included, static: test() crashes the compiler (from a
+        # VM problem with parseSize calling parseFloat).
+  var sz: int64
+  template checkParseSize(s, expectLen, expectVal) =
+    if (let got = parseSize(s, sz); got != expectLen):
+      raise newException(IOError, "got len " & $got & " != " & $expectLen)
+    if sz != expectVal:
+      raise newException(IOError, "got sz " & $sz & " != " & $expectVal)
+  #              STRING    LEN SZ
+  # Good, complete parses
+  checkParseSize "1  b"   , 4, 1
+  checkParseSize "1  B"   , 4, 1
+  checkParseSize "1k"     , 2, 1000
+  checkParseSize "1 kib"  , 5, 1024
+  checkParseSize "1 ki"   , 4, 1024
+  checkParseSize "1mi"    , 3, 1048576
+  checkParseSize "1 mi"   , 4, 1048576
+  checkParseSize "1 mib"  , 5, 1048576
+  checkParseSize "1 Mib"  , 5, 1048576
+  checkParseSize "1 MiB"  , 5, 1048576
+  checkParseSize "1.23GiB", 7, 1320702444 # 1320702443.52 rounded
+  checkParseSize "0.001k" , 6, 1
+  checkParseSize "0.0004k", 7, 0
+  checkParseSize "0.0006k", 7, 1
+  # Incomplete parses
+  checkParseSize "1  "    , 1, 1          # Trailing white IGNORED
+  checkParseSize "1  B "  , 4, 1          # Trailing white IGNORED
+  checkParseSize "1  B/s" , 4, 1          # Trailing junk IGNORED
+  checkParseSize "1 kX"   , 3, 1000
+  checkParseSize "1 kiX"  , 4, 1024
+  checkParseSize "1j"     , 1, 1          # Unknown prefix IGNORED
+  checkParseSize "1 jib"  , 2, 1          # Unknown prefix post space
+  checkParseSize "1  ji"  , 3, 1
+  # Bad parses; `sz` should stay last good|incomplete value
+  checkParseSize "-1b"    , 0, 1          # Negative numbers
+  checkParseSize "abc"    , 0, 1          # Non-numeric
+  checkParseSize " 12"    , 0, 1          # Leading white
+  # Value Edge cases
+  checkParseSize "9223372036854775807", 19, int64.high
 
-var i64Value: int64
-discard parseBiggestInt("9223372036854775807", i64Value)
-doAssert i64Value == 9223372036854775807
+block: # bug #23936
+  func parsePyFloat(
+      a: openArray[char],  # here must be openArray instead of string to reproduce this bug
+      res: var BiggestFloat): int =
+    result = parseFloat(a, res)
 
-block:
-  var f: float
-  let res = collect:
-    for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]:
-      (parseFloat(x, f, 0), $f)
-  doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"),
-                    (17, "9.123456789012344"), (17, "8.123456789012344"),
-                    (16, "9.12345678901234"), (17, "9.123456789012344")]
+  static:
+    var f = 0.0
+    doAssert "1.0".parsePyFloat(f) == 3
+    doAssert f == 1.0
diff --git a/tests/stdlib/tpathnorm.nim b/tests/stdlib/tpathnorm.nim
index 1cd913084..3dd287a77 100644
--- a/tests/stdlib/tpathnorm.nim
+++ b/tests/stdlib/tpathnorm.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import std/os
diff --git a/tests/stdlib/tpaths.nim b/tests/stdlib/tpaths.nim
new file mode 100644
index 000000000..edb56209a
--- /dev/null
+++ b/tests/stdlib/tpaths.nim
@@ -0,0 +1,238 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/paths
+import std/assertions
+import pathnorm
+from std/private/ospaths2 {.all.} import joinPathImpl
+import std/[sugar, sets]
+
+
+proc normalizePath*(path: Path; dirSep = DirSep): Path =
+  result = Path(pathnorm.normalizePath(path.string, dirSep))
+
+func joinPath*(parts: varargs[Path]): Path =
+  var estimatedLen = 0
+  var state = 0
+  for p in parts: estimatedLen += p.string.len
+  var res = newStringOfCap(estimatedLen)
+  for i in 0..high(parts):
+    joinPathImpl(res, state, parts[i].string)
+  result = Path(res)
+
+
+func joinPath(head, tail: Path): Path {.inline.} =
+  head / tail
+
+block absolutePath:
+  doAssertRaises(ValueError): discard absolutePath(Path"a", Path"b")
+  doAssert absolutePath(Path"a") == getCurrentDir() / Path"a"
+  doAssert absolutePath(Path"a", Path"/b") == Path"/b" / Path"a"
+  when defined(posix):
+    doAssert absolutePath(Path"a", Path"/b/") == Path"/b" / Path"a"
+    doAssert absolutePath(Path"a", Path"/b/c") == Path"/b/c" / Path"a"
+    doAssert absolutePath(Path"/a", Path"b/") == Path"/a"
+
+block splitFile:
+  doAssert splitFile(Path"") == (Path"", Path"", "")
+  doAssert splitFile(Path"abc/") == (Path"abc", Path"", "")
+  doAssert splitFile(Path"/") == (Path"/", Path"", "")
+  doAssert splitFile(Path"./abc") == (Path".", Path"abc", "")
+  doAssert splitFile(Path".txt") == (Path"", Path".txt", "")
+  doAssert splitFile(Path"abc/.txt") == (Path"abc", Path".txt", "")
+  doAssert splitFile(Path"abc") == (Path"", Path"abc", "")
+  doAssert splitFile(Path"abc.txt") == (Path"", Path"abc", ".txt")
+  doAssert splitFile(Path"/abc.txt") == (Path"/", Path"abc", ".txt")
+  doAssert splitFile(Path"/foo/abc.txt") == (Path"/foo", Path"abc", ".txt")
+  doAssert splitFile(Path"/foo/abc.txt.gz") == (Path"/foo", Path"abc.txt", ".gz")
+  doAssert splitFile(Path".") == (Path"", Path".", "")
+  doAssert splitFile(Path"abc/.") == (Path"abc", Path".", "")
+  doAssert splitFile(Path"..") == (Path"", Path"..", "")
+  doAssert splitFile(Path"a/..") == (Path"a", Path"..", "")
+  doAssert splitFile(Path"/foo/abc....txt") == (Path"/foo", Path"abc...", ".txt")
+
+# execShellCmd is tested in tosproc
+
+block ospaths:
+  doAssert unixToNativePath(Path"") == Path""
+  doAssert unixToNativePath(Path".") == Path($CurDir)
+  doAssert unixToNativePath(Path"..") == Path($ParDir)
+  doAssert isAbsolute(unixToNativePath(Path"/"))
+  doAssert isAbsolute(unixToNativePath(Path"/", Path"a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a", Path"a"))
+  doAssert isAbsolute(unixToNativePath(Path"/a/b"))
+  doAssert isAbsolute(unixToNativePath(Path"/a/b", Path"a"))
+  doAssert unixToNativePath(Path"a/b") == joinPath(Path"a", Path"b")
+
+  when defined(macos):
+    doAssert unixToNativePath(Path"./") == Path":"
+    doAssert unixToNativePath(Path"./abc") == Path":abc"
+    doAssert unixToNativePath(Path"../abc") == Path"::abc"
+    doAssert unixToNativePath(Path"../../abc") == Path":::abc"
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path"abc"
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"abc:def"
+  elif doslikeFileSystem:
+    doAssert unixToNativePath(Path"./") == Path(".\\")
+    doAssert unixToNativePath(Path"./abc") == Path(".\\abc")
+    doAssert unixToNativePath(Path"../abc") == Path("..\\abc")
+    doAssert unixToNativePath(Path"../../abc") == Path("..\\..\\abc")
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path("a:\\abc")
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path("a:\\abc\\def")
+  else:
+    #Tests for unix
+    doAssert unixToNativePath(Path"./") == Path"./"
+    doAssert unixToNativePath(Path"./abc") == Path"./abc"
+    doAssert unixToNativePath(Path"../abc") == Path"../abc"
+    doAssert unixToNativePath(Path"../../abc") == Path"../../abc"
+    doAssert unixToNativePath(Path"/abc", Path"a") == Path"/abc"
+    doAssert unixToNativePath(Path"/abc/def", Path"a") == Path"/abc/def"
+
+  block extractFilenameTest:
+    doAssert extractFilename(Path"") == Path""
+    when defined(posix):
+      doAssert extractFilename(Path"foo/bar") == Path"bar"
+      doAssert extractFilename(Path"foo/bar.txt") == Path"bar.txt"
+      doAssert extractFilename(Path"foo/") == Path""
+      doAssert extractFilename(Path"/") == Path""
+    when doslikeFileSystem:
+      doAssert extractFilename(Path(r"foo\bar")) == Path"bar"
+      doAssert extractFilename(Path(r"foo\bar.txt")) == Path"bar.txt"
+      doAssert extractFilename(Path(r"foo\")) == Path""
+      doAssert extractFilename(Path(r"C:\")) == Path""
+
+  block lastPathPartTest:
+    doAssert lastPathPart(Path"") == Path""
+    when defined(posix):
+      doAssert lastPathPart(Path"foo/bar.txt") == Path"bar.txt"
+      doAssert lastPathPart(Path"foo/") == Path"foo"
+      doAssert lastPathPart(Path"/") == Path""
+    when doslikeFileSystem:
+      doAssert lastPathPart(Path(r"foo\bar.txt")) == Path"bar.txt"
+      doAssert lastPathPart(Path(r"foo\")) == Path"foo"
+
+  template canon(x): Path = normalizePath(Path(x), '/')
+  doAssert canon"/foo/../bar" == Path"/bar"
+  doAssert canon"foo/../bar" == Path"bar"
+
+  doAssert canon"/f/../bar///" == Path"/bar"
+  doAssert canon"f/..////bar" == Path"bar"
+
+  doAssert canon"../bar" == Path"../bar"
+  doAssert canon"/../bar" == Path"/../bar"
+
+  doAssert canon("foo/../../bar/") == Path"../bar"
+  doAssert canon("./bla/blob/") == Path"bla/blob"
+  doAssert canon(".hiddenFile") == Path".hiddenFile"
+  doAssert canon("./bla/../../blob/./zoo.nim") == Path"../blob/zoo.nim"
+
+  doAssert canon("C:/file/to/this/long") == Path"C:/file/to/this/long"
+  doAssert canon("") == Path""
+  doAssert canon("foobar") == Path"foobar"
+  doAssert canon("f/////////") == Path"f"
+
+  doAssert relativePath(Path"/foo/bar//baz.nim", Path"/foo", '/') == Path"bar/baz.nim"
+  doAssert normalizePath(Path"./foo//bar/../baz", '/') == Path"foo/baz"
+
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other/bad", '/') == Path"../../me/bar/z.nim"
+
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/other", '/') == Path"../me/bar/z.nim"
+
+  # `//` is a UNC path, `/` is the current working directory's drive, so can't
+  # run this test on Windows.
+  when not doslikeFileSystem:
+    doAssert relativePath(Path"/Users///me/bar//z.nim", Path"//Users/", '/') == Path"me/bar/z.nim"
+  doAssert relativePath(Path"/Users/me/bar/z.nim", Path"/Users/me", '/') == Path"bar/z.nim"
+  doAssert relativePath(Path"", Path"/users/moo", '/') == Path""
+  doAssert relativePath(Path"foo", Path"", '/') == Path"foo"
+  doAssert relativePath(Path"/foo", Path"/Foo", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".")
+  doAssert relativePath(Path"/Foo", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../Foo" else: Path".")
+  doAssert relativePath(Path"/foo", Path"/fOO", '/') == (when FileSystemCaseSensitive: Path"../foo" else: Path".")
+  doAssert relativePath(Path"/foO", Path"/foo", '/') == (when FileSystemCaseSensitive: Path"../foO" else: Path".")
+
+  doAssert relativePath(Path"foo", Path".", '/') == Path"foo"
+  doAssert relativePath(Path".", Path".", '/') == Path"."
+  doAssert relativePath(Path"..", Path".", '/') == Path".."
+
+  doAssert relativePath(Path"foo", Path"foo") == Path"."
+  doAssert relativePath(Path"", Path"foo") == Path""
+  doAssert relativePath(Path"././/foo", Path"foo//./") == Path"."
+
+  doAssert relativePath(getCurrentDir() / Path"bar", Path"foo") == Path"../bar".unixToNativePath
+  doAssert relativePath(Path"bar", getCurrentDir() / Path"foo") == Path"../bar".unixToNativePath
+
+  when doslikeFileSystem:
+    doAssert relativePath(r"c:\foo.nim".Path, r"C:\".Path) == r"foo.nim".Path
+    doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"c:\foo".Path) == r"bar\baz.nim".Path
+    doAssert relativePath(r"c:\foo\bar\baz.nim".Path, r"d:\foo".Path) == r"c:\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\foo\baz.nim".Path, r"\foo".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\foo\bar\baz.nim".Path, r"\bar".Path) == r"..\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\bar".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foO\bar".Path) == r"baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\bar\bar".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\foo\car".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\\goo\bar".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"c:\".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"\\foo\bar\baz.nim".Path, r"\foo".Path) == r"\\foo\bar\baz.nim".Path
+    doAssert relativePath(r"c:\foo.nim".Path, r"\foo".Path) == r"c:\foo.nim".Path
+
+  doAssert joinPath(Path"usr", Path"") == unixToNativePath(Path"usr")
+  doAssert joinPath(Path"usr", Path"") == (Path"usr").dup(add Path"")
+  doAssert joinPath(Path"", Path"lib") == Path"lib"
+  doAssert joinPath(Path"", Path"lib") == Path"".dup(add Path"lib")
+  doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib")
+  doAssert joinPath(Path"", Path"/lib") == unixToNativePath(Path"/lib")
+  doAssert joinPath(Path"usr/", Path"/lib") == Path"usr/".dup(add Path"/lib")
+  doAssert joinPath(Path"", Path"") == unixToNativePath(Path"") # issue #13455
+  doAssert joinPath(Path"", Path"") == Path"".dup(add Path"")
+  doAssert joinPath(Path"", Path"/") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"", Path"/") == Path"".dup(add Path"/")
+  doAssert joinPath(Path"/", Path"/") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"/", Path"/") == Path"/".dup(add Path"/")
+  doAssert joinPath(Path"/", Path"") == unixToNativePath(Path"/")
+  doAssert joinPath(Path"/" / Path"") == unixToNativePath(Path"/") # weird test case...
+  doAssert joinPath(Path"/", Path"/a/b/c") == unixToNativePath(Path"/a/b/c")
+  doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo/", Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert joinPath(Path"foo//./", Path"abc/.//") == unixToNativePath(Path"foo/abc/")
+  doAssert Path"foo//./".dup(add Path"abc/.//") == unixToNativePath(Path"foo/abc/")
+  doAssert joinPath(Path"foo", Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert Path"foo".dup(add Path"abc") == unixToNativePath(Path"foo/abc")
+  doAssert joinPath(Path"", Path"abc") == unixToNativePath(Path"abc")
+
+  doAssert joinPath(Path"zook/.", Path"abc") == unixToNativePath(Path"zook/abc")
+
+  # controversial: inconsistent with `joinPath("zook/.","abc")`
+  # on linux, `./foo` and `foo` are treated a bit differently for executables
+  # but not `./foo/bar` and `foo/bar`
+  doAssert joinPath(Path".", Path"/lib") == unixToNativePath(Path"./lib")
+  doAssert joinPath(Path".", Path"abc") == unixToNativePath(Path"./abc")
+
+  # cases related to issue #13455
+  doAssert joinPath(Path"foo", Path"", Path"") == Path"foo"
+  doAssert joinPath(Path"foo", Path"") == Path"foo"
+  doAssert joinPath(Path"foo/", Path"") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo/", Path".") == Path"foo"
+  doAssert joinPath(Path"foo", Path"./") == unixToNativePath(Path"foo/")
+  doAssert joinPath(Path"foo", Path"", Path"bar/") == unixToNativePath(Path"foo/bar/")
+
+  # issue #13579
+  doAssert joinPath(Path"/foo", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/.", Path"../a") == unixToNativePath(Path"/a")
+  doAssert joinPath(Path"/foo/.b", Path"../a") == unixToNativePath(Path"/foo/a")
+  doAssert joinPath(Path"/foo///", Path"..//a/") == unixToNativePath(Path"/a/")
+  doAssert joinPath(Path"foo/", Path"../a") == unixToNativePath(Path"a")
+
+  when doslikeFileSystem:
+    doAssert joinPath(Path"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\Common7\\Tools\\", Path"..\\..\\VC\\vcvarsall.bat") == r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat".Path
+    doAssert joinPath(Path"C:\\foo", Path"..\\a") == r"C:\a".Path
+    doAssert joinPath(Path"C:\\foo\\", Path"..\\a") == r"C:\a".Path
+
+
+block: # bug #23663
+  var s: HashSet[Path]
+  s.incl("/a/b/c/..".Path)
+  doAssert "/a/b/".Path in s
+  doAssert "/a/b/c".Path notin s
diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim
index cbc8fe205..da3fc14b7 100644
--- a/tests/stdlib/tpegs.nim
+++ b/tests/stdlib/tpegs.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
   output: '''
 PEG AST traversal output
@@ -106,9 +107,9 @@ block:
 
 block:
   var
-    pStack: seq[string] = @[]
-    valStack: seq[float] = @[]
-    opStack = ""
+    pStack {.threadvar.}: seq[string]
+    valStack {.threadvar.}: seq[float]
+    opStack {.threadvar.}: string
   let
     parseArithExpr = pegAst.eventParser:
       pkNonTerminal:
@@ -328,7 +329,16 @@ call()
       doAssert program.len == program.rawMatch(grammar, 0, c)
       doAssert c.ml == 1
 
+    block:
+      # bug #21632
+
+      let p = peg"""
+        atext <- \w / \d
+      """
+
+      doAssert "a".match(p)
+      doAssert "1".match(p)
+
   pegsTest()
   static:
     pegsTest()
-
diff --git a/tests/stdlib/tposix.nim b/tests/stdlib/tposix.nim
index ea0472c31..060482229 100644
--- a/tests/stdlib/tposix.nim
+++ b/tests/stdlib/tposix.nim
@@ -1,5 +1,6 @@
 discard """
-outputsub: ""
+  matrix: "--mm:refc; --mm:orc"
+  disabled: windows
 """
 
 # Test Posix interface
@@ -7,7 +8,7 @@ outputsub: ""
 when not defined(windows):
 
   import posix
-  import std/syncio
+  import std/[assertions, syncio]
 
   var
     u: Utsname
@@ -18,3 +19,70 @@ when not defined(windows):
   writeLine(stdout, u.nodename)
   writeLine(stdout, u.release)
   writeLine(stdout, u.machine)
+
+  when not (defined(nintendoswitch) or defined(macos) or defined(macosx)):
+    block:
+      type Message = object
+        value: int
+
+      const MQ_PATH: cstring = "/top_level_file"
+      const MQ_PRIORITY: cuint = 170
+      const MQ_MESSAGE_SIZE: csize_t = csize_t(sizeof(Message))
+
+      let mqd_a: posix.MqAttr = MqAttr(mq_maxmsg: 10, mq_msgsize: clong(MQ_MESSAGE_SIZE))
+      let writable: posix.Mqd = posix.mq_open(
+        MQ_PATH,
+        posix.O_CREAT or posix.O_WRONLY or posix.O_NONBLOCK,
+        posix.S_IRWXU,
+        addr(mqd_a)
+      )
+      let readable: posix.Mqd = posix.mq_open(
+        MQ_PATH,
+        posix.O_RDONLY or posix.O_NONBLOCK,
+        posix.S_IRWXU,
+        addr(mqd_a)
+      )
+
+      let sent: Message = Message(value: 88)
+      block:
+        let success: int = writable.mq_send(
+          cast[cstring](sent.addr),
+          MQ_MESSAGE_SIZE,
+          MQ_PRIORITY
+        )
+        doAssert success == 0, $success
+
+      block:
+        var buffer: Message
+        var priority: cuint
+        let bytesRead: int = readable.mq_receive(
+          cast[cstring](buffer.addr),
+          MQ_MESSAGE_SIZE,
+          priority
+        )
+        doAssert buffer == sent
+        doAssert bytesRead == int(MQ_MESSAGE_SIZE)
+
+  block:
+    var rl: RLimit
+    var res = getrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
+
+    # save old value
+    let oldrlim = rl.rlim_cur
+
+    # set new value
+    rl.rlim_cur = rl.rlim_max - 1
+    res = setrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
+
+    # get new value
+    var rl1: RLimit
+    res = getrlimit(RLIMIT_STACK, rl1)
+    doAssert res == 0
+    doAssert rl1.rlim_cur == rl.rlim_max - 1
+
+    # restore old value
+    rl.rlim_cur = oldrlim
+    res = setrlimit(RLIMIT_STACK, rl)
+    doAssert res == 0
diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim
deleted file mode 100644
index dea6b2dc6..000000000
--- a/tests/stdlib/tpunycode.nim
+++ /dev/null
@@ -1,206 +0,0 @@
-import punycode, std/unicode
-import std/assertions
-
-doAssert(decode(encode("", "bücher")) == "bücher")
-doAssert(decode(encode("münchen")) == "münchen")
-doAssert encode("xn--", "münchen") == "xn--mnchen-3ya"
-
-# source: https://datatracker.ietf.org/doc/html/rfc3492.html#section-7
-let punycode_testcases = [
-  # (A) Arabic (Egyptian):
-  (
-    input:
-      "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" &
-      "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F",
-    output:
-      "egbpdaj6bu4bxfgehfvwxn"
-  ),
-
-  # (B) Chinese (simplified):
-  (
-    input:
-      "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587",
-    output:
-      "ihqwcrb4cv8a8dqg056pqjye"
-  ),
-
-  # (C) Chinese (traditional):
-  (
-    input:
-      "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587",
-    output:
-      "ihqwctvzc91f659drss3x8bo0yb"
-  ),
-
-  # (D) Czech: Pro<ccaron>prost<ecaron>nemluv<iacute><ccaron>esky
-  (
-    input:
-      "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" &
-      "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" &
-      "\u0065\u0073\u006B\u0079",
-    output:
-      "Proprostnemluvesky-uyb24dma41a"
-  ),
-
-  # (E) Hebrew:
-  (
-    input:
-      "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" &
-      "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" &
-      "\u05D1\u05E8\u05D9\u05EA",
-    output:
-      "4dbcagdahymbxekheh6e0a7fei0b"
-  ),
-
-  # (F) Hindi (Devanagari):
-  (
-    input:
-      "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" &
-      "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" &
-      "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" &
-      "\u0939\u0948\u0902",
-    output:
-      "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd"
-  ),
-
-  # (G) Japanese (kanji and hiragana):
-  (
-    input:
-      "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" &
-      "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B",
-    output:
-      "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa"
-  ),
-
-  # (H) Korean (Hangul syllables):
-  (
-    input:
-      "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" &
-      "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" &
-      "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C",
-    output:
-      "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c"
-  ),
-
-  # (I) Russian (Cyrillic):
-  (
-    input:
-      "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" &
-      "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" &
-      "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" &
-      "\u0438",
-    output:
-      "b1abfaaepdrnnbgefbaDotcwatmq2g4l"
-  ),
-
-  # (J) Spanish: Porqu<eacute>nopuedensimplementehablarenEspa<ntilde>ol
-  (
-    input:
-      "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" &
-      "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" &
-      "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" &
-      "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" &
-      "\u0061\u00F1\u006F\u006C",
-    output:
-      "PorqunopuedensimplementehablarenEspaol-fmd56a"
-  ),
-
-  # (K) Vietnamese:
-  #  T<adotbelow>isaoh<odotbelow>kh<ocirc>ngth<ecirchookabove>ch\
-  #   <ihookabove>n<oacute>iti<ecircacute>ngVi<ecircdotbelow>t
-  (
-    input:
-      "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" &
-      "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" &
-      "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" &
-      "\u0056\u0069\u1EC7\u0074",
-    output:
-      "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g"
-  ),
-
-  # (L) 3<nen>B<gumi><kinpachi><sensei>
-  (
-    input:
-      "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F",
-    output:
-      "3B-ww4c5e180e575a65lsy2b"
-  ),
-
-  # (M) <amuro><namie>-with-SUPER-MONKEYS
-  (
-    input:
-      "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" &
-      "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" &
-      "\u004F\u004E\u004B\u0045\u0059\u0053",
-    output:
-      "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n"
-  ),
-
-  # (N) Hello-Another-Way-<sorezore><no><basho>
-  (
-    input:
-      "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" &
-      "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" &
-      "\u305D\u308C\u305E\u308C\u306E\u5834\u6240",
-    output:
-      "Hello-Another-Way--fc4qua05auwb3674vfr0b"
-  ),
-
-  # (O) <hitotsu><yane><no><shita>2
-  (
-    input:
-      "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032",
-    output:
-      "2-u9tlzr9756bt3uc0v"
-  ),
-
-  # (P) Maji<de>Koi<suru>5<byou><mae>
-  (
-    input:
-      "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" &
-      "\u308B\u0035\u79D2\u524D",
-    output:
-      "MajiKoi5-783gue6qz075azm5e"
-  ),
-
-    # (Q) <pafii>de<runba>
-  (
-    input:
-      "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0",
-    output:
-      "de-jg4avhby1noc0d"
-  ),
-
-  # (R) <sono><supiido><de>
-  (
-    input:
-      "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067",
-    output:
-      "d9juau41awczczp"
-  ),
-
-  # (S) -> $1.00 <-
-  (
-    input:
-      "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" &
-      "\u003C\u002D",
-    output:
-      "-> $1.00 <--"
-  )
-]
-
-block encodeTests:
-  for (uni, puny) in punycode_testcases:
-    # Need to convert both strings to lower case, since
-    # some of the extended encodings use upper case, but our
-    # code produces only lower case. Converting just puny to
-    # lower is also insufficient, since some of the input characters
-    # are upper case.
-    doAssert encode(uni).toLower() == puny.toLower()
-
-block decodeTests:
-  for (uni, puny) in punycode_testcases:
-    doAssert decode(puny) == uni
-
-block decodeInvalidTest:
-  doAssertRaises(PunyError): discard decode("xn--w&")
diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim
deleted file mode 100644
index 3a7db4d4e..000000000
--- a/tests/stdlib/tquit.nim
+++ /dev/null
@@ -1,17 +0,0 @@
-discard """
-output: '''
-just exiting...
-'''
-joinable: false
-"""
-
-# Test `addQuitProc` (now deprecated by `addExitProc`)
-
-import std/syncio
-
-proc myExit() {.noconv.} =
-  write(stdout, "just exiting...\n")
-
-{.push warning[deprecated]: off.}
-addQuitProc(myExit)
-{.pop.}
diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim
index ef71c3442..eb32f7757 100644
--- a/tests/stdlib/trandom.nim
+++ b/tests/stdlib/trandom.nim
@@ -1,9 +1,10 @@
 discard """
   joinable: false # to avoid messing with global rand state
-  targets: "c js"
+  matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
 """
 import std/[assertions, formatfloat]
 import std/[random, math, stats, sets, tables]
+import std/private/jsutils
 when not defined(js):
   import std/os
 
@@ -46,6 +47,8 @@ block:
   type DiceRoll = range[0..6]
   when not defined(js):
     doAssert rand(DiceRoll).int == 3
+  elif compileOption("jsbigint64"):
+    doAssert rand(DiceRoll).int == 1
   else:
     doAssert rand(DiceRoll).int == 6
 
@@ -191,9 +194,7 @@ block: # bug #17467
       # This used to fail for each i in 0..<26844, i.e. the 1st produced value
       # was predictable and < 1e-4, skewing distributions.
 
-const withUint = false # pending exporting `proc rand[T: uint | uint64](r: var Rand; max: T): T =`
-
-block: # bug #16360
+block: # bug #16360, Natural overload
   var r = initRand()
   template test(a) =
     let a2 = a
@@ -205,23 +206,38 @@ block: # bug #16360
       let a3 = rand(a2)
       doAssert a3 <= a2
       doAssert a3.type is a2.type
-  when withUint:
-    test cast[uint](int.high)
-    test cast[uint](int.high) + 1
-    when not defined(js):
-      # pending bug #16411
-      test uint64.high
-      test uint64.high - 1
-    test uint.high - 2
-    test uint.high - 1
-    test uint.high
   test int.high
   test int.high - 1
   test int.high - 2
   test 0
-  when withUint:
-    test 0'u
-    test 0'u64
+
+block: # same as above but use slice overload
+  var r = initRand()
+  template test[T](a: T) =
+    let a2: T = a
+    block:
+      let a3 = r.rand(T(0) .. a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+    block:
+      let a3 = rand(T(0) .. a2)
+      doAssert a3 <= a2
+      doAssert a3.type is a2.type
+  test cast[uint](int.high)
+  test cast[uint](int.high) + 1
+  whenJsNoBigInt64: discard
+  do:
+    test uint64.high
+    test uint64.high - 1
+  test uint.high - 2
+  test uint.high - 1
+  test uint.high
+  test int.high
+  test int.high - 1
+  test int.high - 2
+  test 0
+  test 0'u
+  test 0'u64
 
 block: # bug #16296
   var r = initRand()
@@ -239,16 +255,12 @@ block: # bug #16296
   test(int.low .. -1)
   test(int.low .. 1)
   test(int64.low .. 1'i64)
-  when not defined(js):
-    # pending bug #16411
-    test(10'u64 .. uint64.high)
+  test(10'u64 .. uint64.high)
 
 block: # bug #17670
-  when not defined(js):
-    # pending bug #16411
-    type UInt48 = range[0'u64..2'u64^48-1]
-    let x = rand(UInt48)
-    doAssert x is UInt48
+  type UInt48 = range[0'u64..2'u64^48-1]
+  let x = rand(UInt48)
+  doAssert x is UInt48
 
 block: # bug #17898
   # Checks whether `initRand()` generates unique states.
@@ -272,3 +284,27 @@ block: # bug #17898
       for j in 0..<numRepeat:
         discard rands[i].next
         doAssert rands[i] notin randSet
+
+block: # bug #22360
+  const size = 1000
+  var fc = 0
+  var tc = 0
+
+  for _ in 1..size:
+    let s = rand(bool)
+
+    if s:
+      inc tc
+    else:
+      inc fc
+
+  when defined(js) and not compileOption("jsbigint64"):
+    doAssert (tc, fc) == (515, 485), $(tc, fc)
+  else:
+    doAssert (tc, fc) == (510, 490), $(tc, fc)
+
+block:
+  when defined(js) and not compileOption("jsbigint64"):
+    doAssert rand(int32.high) == 335507522
+  else:
+    doAssert rand(int32.high) == 607539621
diff --git a/tests/rational/trat_float.nim b/tests/stdlib/trat_float.nim
index 663973bf9..663973bf9 100644
--- a/tests/rational/trat_float.nim
+++ b/tests/stdlib/trat_float.nim
diff --git a/tests/rational/trat_init.nim b/tests/stdlib/trat_init.nim
index 2be0c0099..2be0c0099 100644
--- a/tests/rational/trat_init.nim
+++ b/tests/stdlib/trat_init.nim
diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim
index cf2e92003..22d7f5c2d 100644
--- a/tests/stdlib/trationals.nim
+++ b/tests/stdlib/trationals.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/[rationals, math]
 import std/assertions
 
@@ -6,6 +10,7 @@ template main() =
     z = Rational[int](num: 0, den: 1)
     o = initRational(num = 1, den = 1)
     a = initRational(1, 2)
+    u = 3u // 2
     b = -1 // -2
     m1 = -1 // 1
     tt = 10 // 2
@@ -100,5 +105,13 @@ template main() =
   when sizeof(int) == 8:
     doAssert almostEqual(PI.toRational.toFloat, PI)
 
+  # unsigned
+  doAssert u == u
+  doAssert u + u == 3u // 1
+  doAssert 3u.toRational - u == u
+  doAssert u * 2 == 3u // 1
+
+
+
 static: main()
 main()
diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim
index 3986934c4..39637434d 100644
--- a/tests/stdlib/tre.nim
+++ b/tests/stdlib/tre.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/re
 import std/assertions
 
diff --git a/tests/misc/treadln.nim b/tests/stdlib/treadln.nim
index b716c4711..4a070e848 100644
--- a/tests/misc/treadln.nim
+++ b/tests/stdlib/treadln.nim
@@ -6,6 +6,8 @@ Macintosh, Unix or Windows text format.
 '''
 """
 
+import std/syncio
+
 # test the improved readline handling that does not care whether its
 # Macintosh, Unix or Windows text format.
 
@@ -13,7 +15,7 @@ var
   inp: File
   line: string
 
-if open(inp, "tests/misc/treadln.nim"):
+if open(inp, "tests/stdlib/treadln.nim"):
   while not endOfFile(inp):
     line = readLine(inp)
     if line.len >= 2 and line[0] == '#' and line[1] == ' ':
diff --git a/tests/stdlib/tregex.nim b/tests/stdlib/tregex.nim
index cf80f8122..9dd66cd60 100644
--- a/tests/stdlib/tregex.nim
+++ b/tests/stdlib/tregex.nim
@@ -1,5 +1,6 @@
 discard """
   output: "key: keyAYes!"
+  matrix: "--mm:refc; --mm:orc"
 """
 # Test the new regular expression module
 # which is based on the PCRE library
diff --git a/tests/stdlib/tregistry.nim b/tests/stdlib/tregistry.nim
index 4956f8196..25aed8df8 100644
--- a/tests/stdlib/tregistry.nim
+++ b/tests/stdlib/tregistry.nim
@@ -1,6 +1,6 @@
 discard """
   disabled: "unix"
-  matrix: "--gc:refc; --gc:arc"
+  matrix: "--mm:refc; --mm:orc"
 """
 
 when defined(windows):
diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim
index c85ae2b2a..3956b98f9 100644
--- a/tests/stdlib/trepr.nim
+++ b/tests/stdlib/trepr.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp js"
-  matrix: ";--gc:arc"
+  matrix: "--mm:refc;--mm:arc"
 """
 
 # if excessive, could remove 'cpp' from targets
@@ -40,7 +40,7 @@ template main() =
   #[
   BUG:
   --gc:arc returns `"abc"`
-  regular gc returns with address, e.g. 0x1068aae60"abc", but only 
+  regular gc returns with address, e.g. 0x1068aae60"abc", but only
   for c,cpp backends (not js, vm)
   ]#
   block:
@@ -271,5 +271,58 @@ func fn2(): int =
   ## comment
   result = 1"""
 
+  block: # block calls
+    let a = deb:
+      foo(a, b, (c, d)):
+        e
+        f
+      do: g
+      of h: i
+      elif j: k
+      except m: n
+      do () -> u: v
+      finally: o
+
+      a + b:
+        c
+        d
+      do:
+        e
+        f
+      else: g
+
+      *a: b
+      do: c
+
+    doAssert a == """foo(a, b, (c, d)):
+  e
+  f
+do:
+  g
+of h:
+  i
+elif j:
+  k
+except m:
+  n
+do -> u:
+  v
+finally:
+  o
+a + b:
+  c
+  d
+do:
+  e
+  f
+else:
+  g
+*a:
+  b
+do:
+  c"""
+
+  doAssert repr(1..2) == "1 .. 2"
+
 static: main()
 main()
diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim
index 6d41e9e44..eb0edc364 100644
--- a/tests/stdlib/tropes.nim
+++ b/tests/stdlib/tropes.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim
index 818f8b8dc..ceab34bc9 100644
--- a/tests/stdlib/trst.nim
+++ b/tests/stdlib/trst.nim
@@ -7,6 +7,8 @@ discard """
 
 [Suite] RST indentation
 
+[Suite] Markdown indentation
+
 [Suite] Warnings
 
 [Suite] RST include directive
@@ -14,7 +16,10 @@ discard """
 [Suite] RST escaping
 
 [Suite] RST inline markup
+
+[Suite] Misc isssues
 '''
+matrix: "--mm:refc; --mm:orc"
 """
 
 # tests for rst module
@@ -26,7 +31,9 @@ import os
 import std/[assertions, syncio]
 
 const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
+# legacy nimforum / old default mode:
 const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled}
+const pureRst = {roNimFile, roSandboxDisabled}
 
 proc toAst(input: string,
             rstOptions: RstParseOptions = preferMarkdown,
@@ -58,13 +65,52 @@ proc toAst(input: string,
       result = ""
 
     var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit,
-                               rstOptions, myFindFile, testMsgHandler)
+                               rstOptions, myFindFile, nil, testMsgHandler)
     result = treeRepr(rst)
   except EParseError as e:
     if e.msg != "":
       result = e.msg
 
 suite "RST parsing":
+  test "Standalone punctuation is not parsed as heading overlines":
+    check(dedent"""
+        Paragraph
+
+        !""".toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Paragraph'
+          rnParagraph
+            rnLeaf  '!'
+      """)
+
+    check(dedent"""
+        Paragraph1
+
+        ...
+
+        Paragraph2""".toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Paragraph1'
+          rnParagraph
+            rnLeaf  '...'
+          rnParagraph
+            rnLeaf  'Paragraph2'
+      """)
+
+    check(dedent"""
+        ---
+        Paragraph""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  '---'
+          rnLeaf  ' '
+          rnLeaf  'Paragraph'
+      """)
+
   test "References are whitespace-neutral and case-insensitive":
     # refname is 'lexical-analysis', the same for all the 3 variants:
     check(dedent"""
@@ -124,12 +170,12 @@ suite "RST parsing":
     check(dedent"""
         Paragraph::
 
-        >x""".toAst == expected)
+        >x""".toAst(rstOptions = preferRst) == expected)
 
     check(dedent"""
         Paragraph::
 
-            >x""".toAst == expected)
+            >x""".toAst(rstOptions = preferRst) == expected)
 
   test "RST quoted literal blocks, :: at a separate line":
     let expected =
@@ -148,7 +194,7 @@ suite "RST parsing":
         ::
 
         >x
-        >>y""".toAst == expected)
+        >>y""".toAst(rstOptions = preferRst) == expected)
 
     check(dedent"""
         Paragraph
@@ -156,7 +202,7 @@ suite "RST parsing":
         ::
 
           >x
-          >>y""".toAst == expected)
+          >>y""".toAst(rstOptions = preferRst) == expected)
 
   test "Markdown quoted blocks":
     check(dedent"""
@@ -482,11 +528,77 @@ suite "RST parsing":
             rnFieldBody
               rnLeaf  'Nim'
         rnLiteralBlock
-          rnLeaf  '
-      let a = 1
+          rnLeaf  'let a = 1
       ```'
       """
 
+  test "Markdown footnotes":
+    # Testing also 1) correct order of manually-numbered and automatically-
+    # numbered footnotes; 2) no spaces between references (html & 3 below):
+
+    check(dedent"""
+        Paragraph [^1] [^html-hyphen][^3] and [^latex]
+
+        [^1]: footnote1
+
+        [^html-hyphen]: footnote2
+           continuation2
+
+        [^latex]: footnote4
+
+        [^3]: footnote3
+            continuation3
+        """.toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'Paragraph'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '1'
+              rnLeaf  'footnote-1'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '2'
+              rnLeaf  'footnote-htmlminushyphen'
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '3'
+              rnLeaf  'footnote-3'
+            rnLeaf  ' '
+            rnLeaf  'and'
+            rnLeaf  ' '
+            rnFootnoteRef
+              rnInner
+                rnLeaf  '4'
+              rnLeaf  'footnote-latex'
+          rnFootnoteGroup
+            rnFootnote  anchor='footnote-1'
+              rnInner
+                rnLeaf  '1'
+              rnLeaf  'footnote1'
+            rnFootnote  anchor='footnote-htmlminushyphen'
+              rnInner
+                rnLeaf  '2'
+              rnInner
+                rnLeaf  'footnote2'
+                rnLeaf  ' '
+                rnLeaf  'continuation2'
+            rnFootnote  anchor='footnote-latex'
+              rnInner
+                rnLeaf  '4'
+              rnLeaf  'footnote4'
+            rnFootnote  anchor='footnote-3'
+              rnInner
+                rnLeaf  '3'
+              rnInner
+                rnLeaf  'footnote3'
+                rnLeaf  ' '
+                rnLeaf  'continuation3'
+      """)
+
   test "Markdown code blocks with more > 3 backticks":
     check(dedent"""
         ````
@@ -526,8 +638,7 @@ suite "RST parsing":
                 rnLeaf  'test'
               rnFieldBody
           rnLiteralBlock
-            rnLeaf  '
-        let a = 1'
+            rnLeaf  'let a = 1'
         """)
 
     check(dedent"""
@@ -550,8 +661,7 @@ suite "RST parsing":
               rnFieldBody
                 rnLeaf  '1'
           rnLiteralBlock
-            rnLeaf  '
-        let a = 1'
+            rnLeaf  'let a = 1'
         """)
 
   test "additional indentation < 4 spaces is handled fine":
@@ -571,8 +681,7 @@ suite "RST parsing":
                 rnLeaf  'nim'
               [nil]
               rnLiteralBlock
-                rnLeaf  '
-          let a = 1'
+                rnLeaf  '  let a = 1'
       """)
       # | |
       # |  \ indentation of exactly two spaces before 'let a = 1'
@@ -606,8 +715,7 @@ suite "RST parsing":
                   rnFieldBody
                     rnLeaf  'Nim'
               rnLiteralBlock
-                rnLeaf  '
-        CodeBlock()'
+                rnLeaf  'CodeBlock()'
             rnLeaf  ' '
             rnLeaf  'Other'
             rnLeaf  ' '
@@ -687,6 +795,32 @@ suite "RST parsing":
                 rnLeaf  'desc2'
       """)
 
+  test "definition lists work correctly with additional indentation in Markdown":
+    check(dedent"""
+        Paragraph:
+          -c  desc1
+          -b  desc2
+        """.toAst() ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'Paragraph'
+            rnLeaf  ':'
+          rnOptionList
+            rnOptionListItem  order=1
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'c'
+              rnDescription
+                rnLeaf  'desc1'
+            rnOptionListItem  order=2
+              rnOptionGroup
+                rnLeaf  '-'
+                rnLeaf  'b'
+              rnDescription
+                rnLeaf  'desc2'
+      """)
+
   test "RST comment":
     check(dedent"""
         .. comment1
@@ -779,7 +913,7 @@ suite "RST parsing":
 
         code
 
-      """.toAst ==
+      """.toAst(rstOptions = preferRst) ==
       dedent"""
         rnInner
           rnLeaf  'Check'
@@ -788,6 +922,32 @@ suite "RST parsing":
             rnLeaf  'code'
       """)
 
+  test "Markdown indented code blocks":
+    check(dedent"""
+      See
+
+          some code""".toAst ==
+      dedent"""
+        rnInner
+          rnInner
+            rnLeaf  'See'
+          rnLiteralBlock
+            rnLeaf  'some code'
+      """)
+
+    # not a code block -- no blank line before:
+    check(dedent"""
+      See
+          some code""".toAst ==
+      dedent"""
+        rnInner
+          rnLeaf  'See'
+          rnLeaf  ' '
+          rnLeaf  'some'
+          rnLeaf  ' '
+          rnLeaf  'code'
+      """)
+
 suite "RST tables":
 
   test "formatting in tables works":
@@ -823,10 +983,31 @@ suite "RST tables":
         ======   ======
          Inputs  Output
         ======   ======
-        """.toAst(error=error) == "")
+        """.toAst(rstOptions = pureRst, error = error) == "")
     check(error[] == "input(2, 2) Error: Illformed table: " &
                      "this word crosses table column from the right")
 
+    # In nimforum compatibility mode & Markdown we raise a warning instead:
+    let expected = dedent"""
+      rnTable  colCount=2
+        rnTableRow
+          rnTableDataCell
+            rnLeaf  'Inputs'
+          rnTableDataCell
+            rnLeaf  'Output'
+      """
+    for opt in [preferRst, preferMarkdown]:
+      var warnings = new seq[string]
+
+      check(
+        dedent"""
+          ======   ======
+           Inputs  Output
+          ======   ======
+          """.toAst(rstOptions = opt, warnings = warnings) == expected)
+      check(warnings[] == @[
+        "input(2, 2) Warning: RST style: this word crosses table column from the right"])
+
   test "tables with slightly overflowed cells cause an error (2)":
     var error = new string
     check("" == dedent"""
@@ -835,7 +1016,7 @@ suite "RST tables":
       =====  =====  ======
       False  False  False
       =====  =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(2, 8) Error: Illformed table: " &
                      "this word crosses table column from the right")
 
@@ -847,7 +1028,7 @@ suite "RST tables":
       =====  =====  ======
       False  False  False
       =====  =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(2, 7) Error: Illformed table: " &
                      "this word crosses table column from the left")
 
@@ -860,7 +1041,7 @@ suite "RST tables":
       =====  ======
       False  False
       =====  =======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(5, 14) Error: Illformed table: " &
                      "end of table column #2 should end at position 13")
 
@@ -872,7 +1053,7 @@ suite "RST tables":
       =====  =======
       False  False
       =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(3, 14) Error: Illformed table: " &
                      "end of table column #2 should end at position 13")
 
@@ -1238,6 +1419,31 @@ suite "RST indentation":
                 rnLeaf  'term3definition2'
       """)
 
+suite "Markdown indentation":
+  test "Markdown paragraph indentation":
+    # Additional spaces (<=3) of indentation does not break the paragraph.
+    # TODO: in 2nd case de-indentation causes paragraph to break, this is
+    # reasonable but does not seem to conform the Markdown spec.
+    check(dedent"""
+      Start1
+        stop1
+
+        Start2
+      stop2
+      """.toAst ==
+      dedent"""
+        rnInner
+          rnParagraph
+            rnLeaf  'Start1'
+            rnLeaf  ' '
+            rnLeaf  'stop1'
+          rnParagraph
+            rnLeaf  'Start2'
+          rnParagraph
+            rnLeaf  'stop2'
+            rnLeaf  ' '
+      """)
+
 suite "Warnings":
   test "warnings for broken footnotes/links/substitutions":
     let input = dedent"""
@@ -1776,3 +1982,13 @@ suite "RST inline markup":
           rnLeaf  ')'
       """)
     check(warnings[] == @["input(1, 5) Warning: broken link 'f'"])
+
+suite "Misc isssues":
+  test "Markdown CodeblockFields in one line (lacking enclosing ```)":
+    let message = """
+    ```llvm-profdata merge first.profraw second.profraw third.profraw <more stuff maybe> -output data.profdata```"""
+
+    try:
+      echo rstgen.rstToHtml(message, {roSupportMarkdown}, nil)
+    except EParseError:
+      discard
diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim
index b33ee82a8..6253e7146 100644
--- a/tests/stdlib/trstgen.nim
+++ b/tests/stdlib/trstgen.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 outputsub: ""
 """
 
@@ -617,8 +618,8 @@ context2
 This is too short to be a transition:
 
 ---
-
 context2
+---
 """
     var error2 = new string
     let output2 = input2.toHtml(error=error2)
@@ -632,7 +633,7 @@ Test literal block
 ::
 
   check """
-    let output1 = input1.toHtml
+    let output1 = input1.toHtml(preferRst)
     doAssert "<pre>" in output1
 
   test "Markdown code block":
@@ -1127,7 +1128,7 @@ Test1
 
       Paragraph2 ref `internal anchor`_.
       """
-    let output9 = input9.toHtml
+    let output9 = input9.toHtml(preferRst)
     # _`internal anchor` got erased:
     check "href=\"#internal-anchor\"" notin output9
     check "href=\"#citation-another\"" in output9
@@ -1155,7 +1156,7 @@ Test1
     doAssert "<a href=\"#citation-third\">[Third]</a>" in output10
 
     let input11 = ".. [note]\n"  # should not crash
-    let output11 = input11.toHtml
+    let output11 = input11.toHtml(preferRst)
     doAssert "<a href=\"#citation-note\">[note]</a>" in output11
 
     # check that references to auto-numbered footnotes work
@@ -1245,7 +1246,7 @@ Test1
         "input(8, 4) Warning: language 'anotherLang' not supported"
         ])
     check(output == "<pre class = \"listing\">anything</pre>" &
-                    "<p><pre class = \"listing\">\nsomeCode</pre> </p>")
+                    "<p><pre class = \"listing\">someCode</pre> </p>")
 
   test "RST admonitions":
     # check that all admonitions are implemented
@@ -1442,7 +1443,7 @@ Test1
       Ref. target103_.
 
     """
-    let output2 = input2.toHtml
+    let output2 = input2.toHtml(preferRst)
     # "target101" should be erased and changed to "section-xyz":
     doAssert "href=\"#target300\"" notin output2
     doAssert "id=\"target300\""    notin output2
@@ -1684,3 +1685,8 @@ suite "local file inclusion":
     discard "```nim file = ./readme.md\n```".toHtml(error=error)
     check(error[] == "input(1, 23) Error: disabled directive: 'file'")
 
+proc documentToHtml*(doc: string, isMarkdown: bool = false): string {.gcsafe.} =
+  var options = {roSupportMarkdown}
+  if isMarkdown:
+    options.incl roPreferMarkdown
+  result = rstToHtml(doc, options, defaultConfig())
diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim
index 176c00214..1094ae233 100644
--- a/tests/stdlib/tsequtils.nim
+++ b/tests/stdlib/tsequtils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
@@ -388,6 +389,11 @@ block: # newSeqWith tests
   seq2D[0][1] = true
   doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]]
 
+block: # bug #21538
+  var x: seq[int] = @[2, 4]
+  var y = newSeqWith(x.pop(), true)
+  doAssert y == @[true, true, true, true]
+
 block: # mapLiterals tests
   let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int)
   doAssert x is array[4, int]
diff --git a/tests/stdlib/tsetutils.nim b/tests/stdlib/tsetutils.nim
index 037c696c1..c8498f23e 100644
--- a/tests/stdlib/tsetutils.nim
+++ b/tests/stdlib/tsetutils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tsha1.nim b/tests/stdlib/tsha1.nim
deleted file mode 100644
index c984d97bd..000000000
--- a/tests/stdlib/tsha1.nim
+++ /dev/null
@@ -1,24 +0,0 @@
-import std/sha1
-import std/assertions
-
-let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]")
-doAssert hash1 == hash1
-doAssert parseSecureHash($hash1) == hash1
-
-template checkVector(s, exp: string) =
-  doAssert secureHash(s) == parseSecureHash(exp)
-
-checkVector("", "da39a3ee5e6b4b0d3255bfef95601890afd80709")
-checkVector("abc", "a9993e364706816aba3e25717850c26c9cd0d89d")
-checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-            "84983e441c3bd26ebaae4aa1f95129e5e54670f1")
-
-proc testIsValidSha1Hash =
-  doAssert not isValidSha1Hash("")
-  doAssert not isValidSha1Hash("042D4BE2B90ED0672E717D71850ABDB0A2D19CD11")
-  doAssert not isValidSha1Hash("042G4BE2B90ED0672E717D71850ABDB0A2D19CD1")
-  doAssert isValidSha1Hash("042D4BE2B90ED0672E717D71850ABDB0A2D19CD1")
-  doAssert isValidSha1Hash("042d4be2b90ed0672e717d71850abdb0a2d19cd1")
-  doAssert isValidSha1Hash("042d4be2b90ed0672e717D71850ABDB0A2D19CD1")
-
-testIsValidSha1Hash()
diff --git a/tests/stdlib/tsharedlist.nim b/tests/stdlib/tsharedlist.nim
index 0bb3ad827..b91302d19 100644
--- a/tests/stdlib/tsharedlist.nim
+++ b/tests/stdlib/tsharedlist.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:orc; --mm:refc"
 """
 
 import std/sharedlist
diff --git a/tests/stdlib/tsharedtable.nim b/tests/stdlib/tsharedtable.nim
index 0022f7bb2..10ad5f658 100644
--- a/tests/stdlib/tsharedtable.nim
+++ b/tests/stdlib/tsharedtable.nim
@@ -1,5 +1,5 @@
 discard """
-cmd: "nim $target --threads:on $options $file"
+matrix: "--mm:refc; --mm:orc"
 output: '''
 '''
 """
diff --git a/tests/stdlib/tsince.nim b/tests/stdlib/tsince.nim
index 877a2bcda..a0a4229cb 100644
--- a/tests/stdlib/tsince.nim
+++ b/tests/stdlib/tsince.nim
@@ -27,7 +27,6 @@ doAssert ok
 since (99, 3):
   doAssert false
 
-when false:
-  # pending bug #15920
-  # Error: cannot attach a custom pragma to 'fun3'
-  template fun3(): int {.since: (1, 3).} = 12
+template fun3(): int {.since: (1, 3).} = 12
+
+doAssert declared(fun3)
diff --git a/tests/stdlib/tsocketstreams.nim b/tests/stdlib/tsocketstreams.nim
index 0cf952810..a37e7c34c 100644
--- a/tests/stdlib/tsocketstreams.nim
+++ b/tests/stdlib/tsocketstreams.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''
 OM
 NIM
diff --git a/tests/stdlib/tsortcall.nim b/tests/stdlib/tsortcall.nim
index 242e3fe4c..32e004921 100644
--- a/tests/stdlib/tsortcall.nim
+++ b/tests/stdlib/tsortcall.nim
@@ -1,5 +1,5 @@
 discard """
-outputsub: ""
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import algorithm
diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim
index 80d9df864..e69de29bb 100644
--- a/tests/stdlib/tsqlitebindatas.nim
+++ b/tests/stdlib/tsqlitebindatas.nim
@@ -1,79 +0,0 @@
-discard """
-  action: "run"
-  exitcode: 0
-"""
-import db_sqlite
-import random
-import os
-from stdtest/specialpaths import buildDir
-import std/assertions
-
-block tsqlitebindatas: ## db_sqlite binary data
-  const dbName = buildDir / "tsqlitebindatas.db"
-
-  let origName = "Bobby"
-  var orig = newSeq[float64](150)
-  randomize()
-  for x in orig.mitems:
-    x = rand(1.0)/10.0
-
-  discard tryRemoveFile(dbName)
-  let db = open(dbName, "", "", "")
-  let createTableStr = sql"""CREATE TABLE test(
-    id INTEGER NOT NULL PRIMARY KEY,
-    name TEXT,
-    data BLOB
-  )
-  """
-  db.exec(createTableStr)
-
-  var dbuf = newSeq[byte](orig.len*sizeof(float64))
-  copyMem(addr(dbuf[0]), addr(orig[0]), dbuf.len)
-
-  var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)")
-  insertStmt.bindParams(1, origName, dbuf)
-  let bres = db.tryExec(insertStmt)
-  doAssert(bres)
-
-  finalize(insertStmt)
-
-  var nameTest = db.getValue(sql"SELECT name FROM test WHERE id = ?", 1)
-  doAssert nameTest == origName
-
-  var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
-  let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
-  var res: seq[float64] = newSeq[float64](seqSize)
-  copyMem(addr(res[0]), addr(dataTest[0]), dataTest.len)
-  doAssert res.len == orig.len
-  doAssert res == orig
-
-  db.close()
-  doAssert tryRemoveFile(dbName)
-
-
-block:
-  block:
-    const dbName = buildDir / "db.sqlite3"
-    var db = db_sqlite.open(dbName, "", "", "")
-    var witness = false
-    try:
-      db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);"))
-      db.exec(sql("REPLACE INTO table (url, another_field) VALUES (?, '123');"))
-    except DbError as e:
-      witness = true
-      doAssert e.msg == "The number of \"?\" given exceeds the number of parameters present in the query."
-    finally:
-      db.close()
-      removeFile(dbName)
-
-    doAssert witness
-
-  block:
-    const dbName = buildDir / "db.sqlite3"
-    var db = db_sqlite.open(dbName, "", "", "")
-    try:
-      db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);"))
-      db.exec(sql("INSERT INTO table1 (url, other_field) VALUES (?, ?);"), "http://domain.com/test?param=1", 123)
-    finally:
-      db.close()
-      removeFile(dbName)
diff --git a/tests/stdlib/tsqlparser.nim b/tests/stdlib/tsqlparser.nim
index 11ee22e2b..6f123f21d 100644
--- a/tests/stdlib/tsqlparser.nim
+++ b/tests/stdlib/tsqlparser.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''true'''
 """
 
diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim
index 6d238e6c9..1628b9326 100644
--- a/tests/stdlib/tssl.nim
+++ b/tests/stdlib/tssl.nim
@@ -1,10 +1,12 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   joinable: false
-  disabled: "freebsd"
-  disabled: "openbsd"
+  disabled: "freebsd" # see #15713
+  disabled: "openbsd" # see #15713
+  disabled: "netbsd" # see #15713
 """
-# disabled: pending bug #15713
-import std/[net, nativesockets, assertions]
+
+import std/[net, nativesockets, assertions, typedthreads]
 
 when defined(posix): import os, posix
 else:
diff --git a/tests/stdlib/tstaticos.nim b/tests/stdlib/tstaticos.nim
new file mode 100644
index 000000000..41ab995dd
--- /dev/null
+++ b/tests/stdlib/tstaticos.nim
@@ -0,0 +1,8 @@
+import std/[assertions, staticos, os]
+
+block:
+  static:
+    doAssert staticDirExists("MISSINGFILE") == false
+    doAssert staticFileExists("MISSINGDIR") == false
+    doAssert staticDirExists(currentSourcePath().parentDir)
+    doAssert staticFileExists(currentSourcePath())
diff --git a/tests/stdlib/tstats.nim b/tests/stdlib/tstats.nim
index 3ed013005..728d93d09 100644
--- a/tests/stdlib/tstats.nim
+++ b/tests/stdlib/tstats.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/[stats, assertions]
 import std/math
 
diff --git a/tests/stdlib/tstdlib_issues.nim b/tests/stdlib/tstdlib_issues.nim
index 9db319603..b7b806db8 100644
--- a/tests/stdlib/tstdlib_issues.nim
+++ b/tests/stdlib/tstdlib_issues.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 output: '''
 02
 1
diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim
index ce9c9a7c5..bac5018fa 100644
--- a/tests/stdlib/tstdlib_various.nim
+++ b/tests/stdlib/tstdlib_various.nim
@@ -27,7 +27,6 @@ Hi Andreas! How do you feel, Rumpf?
 [2, 3, 4, 5]
 [2, 3, 4, 5, 6]
 [1, 2, 3, 4, 5, 6]
-true
 <h1><a href="http://force7.de/nim">Nim</a></h1>
 '''
 """
@@ -207,11 +206,7 @@ block tsplit2:
     s.add("#")
     s.add(w)
 
-  try:
-    discard "hello".split("")
-    echo "false"
-  except AssertionDefect:
-    echo "true"
+  doAssert "true".split("") == @["true"]
 
 
 
diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim
index 9a624fec3..a965ff15f 100644
--- a/tests/stdlib/tstrbasics.nim
+++ b/tests/stdlib/tstrbasics.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp js"
-  matrix: "--gc:refc; --gc:arc"
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import std/[strbasics, sugar, assertions]
diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim
index 08441a766..60c63b450 100644
--- a/tests/stdlib/tstreams.nim
+++ b/tests/stdlib/tstreams.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--gc:refc; --gc:arc"
+  matrix: "--mm:refc; --mm:orc"
   input: "Arne"
   output: '''
 Hello! What is your name?
@@ -50,6 +50,12 @@ block tstreams3:
       echo line
     s.close
 
+
+block:
+  let fs = newFileStream("amissingfile.txt")
+  defer: fs.close()
+  doAssert isNil(fs)
+
 # bug #12410
 
 var a = newStringStream "hehohihahuhyh"
@@ -86,6 +92,10 @@ static: # Ensure streams it doesnt break with nimscript on arc/orc #19716
   let s = newStringStream("a")
   doAssert s.data == "a"
 
+static: # issue #24054, readStr
+  var s = newStringStream("foo bar baz")
+  doAssert s.readStr(3) == "foo"
+
 template main =
   var strm = newStringStream("abcde")
   var buffer = "12345"
diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim
index b44a11e68..ff406f898 100644
--- a/tests/stdlib/tstrformat.nim
+++ b/tests/stdlib/tstrformat.nim
@@ -1,4 +1,6 @@
-# xxx: test js target
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
 
 import genericstrformat
 import std/[strformat, strutils, times, tables, json]
@@ -475,15 +477,17 @@ proc main() =
 
     # Note: times.format adheres to the format protocol. Test that this
     # works:
+    when nimvm:
+      discard
+    else:
+      var dt = dateTime(2000, mJan, 01, 00, 00, 00)
+      check &"{dt:yyyy-MM-dd}", "2000-01-01"
 
-    var dt = initDateTime(01, mJan, 2000, 00, 00, 00)
-    check &"{dt:yyyy-MM-dd}", "2000-01-01"
+      var tm = fromUnix(0)
+      discard &"{tm}"
 
-    var tm = fromUnix(0)
-    discard &"{tm}"
-
-    var noww = now()
-    check &"{noww}", $noww
+      var noww = now()
+      check &"{noww}", $noww
 
     # Unicode string tests
     check &"""{"αβγ"}""", "αβγ"
@@ -558,5 +562,29 @@ proc main() =
     doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')"
     doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')"
     doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')"
-# xxx static: main()
+
+  block: # issue #20381
+    var ss: seq[string]
+    template myTemplate(s: string) =
+      ss.add s
+      ss.add s
+    proc foo() =
+      myTemplate fmt"hello"
+    foo()
+    doAssert ss == @["hello", "hello"]
+
+  block:
+    proc noraises() {.raises: [].} =
+      const
+        flt = 0.0
+        str = "str"
+
+      doAssert fmt"{flt} {str}" == "0.0 str"
+
+    noraises()
+
+  block:
+    doAssert not compiles(fmt"{formatting errors detected at compile time")
+
+static: main()
 main()
diff --git a/tests/stdlib/tstrformatlineinfo.nim b/tests/stdlib/tstrformatlineinfo.nim
new file mode 100644
index 000000000..3a7bf0d33
--- /dev/null
+++ b/tests/stdlib/tstrformatlineinfo.nim
@@ -0,0 +1,8 @@
+# issue #21759
+
+{.hint[ConvFromXToItselfNotNeeded]: on.}
+
+import std/strformat
+
+echo fmt"{string ""abc""}" #[tt.Hint
+        ^ conversion from string to itself is pointless]#
diff --git a/tests/stdlib/tstrimpl.nim b/tests/stdlib/tstrimpl.nim
new file mode 100644
index 000000000..a8933e53f
--- /dev/null
+++ b/tests/stdlib/tstrimpl.nim
@@ -0,0 +1,12 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/private/strimpl
+
+import std/assertions
+
+doAssert find(cstring"Hello Nim", cstring"Nim") == 6
+doAssert find(cstring"Hello Nim", cstring"N") == 6
+doAssert find(cstring"Hello Nim", cstring"I") == -1
+doAssert find(cstring"Hello Nim", cstring"O") == -1
diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim
index 232211471..b9b3c78a3 100644
--- a/tests/stdlib/tstring.nim
+++ b/tests/stdlib/tstring.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--mm:refc"
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim
index 76b14d27a..b42f2e1fe 100644
--- a/tests/stdlib/tstrmiscs.nim
+++ b/tests/stdlib/tstrmiscs.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/strmisc
 import std/assertions
 
diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim
index e30c86279..ae7fd98ca 100644
--- a/tests/stdlib/tstrscans.nim
+++ b/tests/stdlib/tstrscans.nim
@@ -1,5 +1,5 @@
 discard """
-  output: ""
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import std/[strscans, strutils, assertions]
diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim
index feae66ff7..bbb6c2677 100644
--- a/tests/stdlib/tstrset.nim
+++ b/tests/stdlib/tstrset.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 # test a simple yet highly efficient set of strings
 
 type
@@ -6,7 +10,7 @@ type
   TRadixNode {.inheritable.} = object
     kind: TRadixNodeKind
   TRadixNodeLinear = object of TRadixNode
-    len: int8
+    len: uint8
     keys: array[0..31, char]
     vals: array[0..31, PRadixNode]
   TRadixNodeFull = object of TRadixNode
@@ -24,7 +28,7 @@ proc search(r: PRadixNode, s: string): PRadixNode =
     case r.kind
     of rnLinear:
       var x = PRadixNodeLinear(r)
-      for j in 0..ze(x.len)-1:
+      for j in 0..int(x.len)-1:
         if x.keys[j] == s[i]:
           if s[i] == '\0': return r
           r = x.vals[j]
@@ -63,9 +67,9 @@ proc excl*(r: var PRadixNode, s: string) =
   of rnFull: PRadixNodeFull(x).b['\0'] = nil
   of rnLinear:
     var x = PRadixNodeLinear(x)
-    for i in 0..ze(x.len)-1:
+    for i in 0..int(x.len)-1:
       if x.keys[i] == '\0':
-        swap(x.keys[i], x.keys[ze(x.len)-1])
+        swap(x.keys[i], x.keys[int(x.len)-1])
         dec(x.len)
         break
 
diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim
index 036287bfd..d261abe76 100644
--- a/tests/stdlib/tstrtabs.nim
+++ b/tests/stdlib/tstrtabs.nim
@@ -1,4 +1,5 @@
 discard """
+matrix: "--mm:refc; --mm:orc"
 sortoutput: true
 output: '''
 key1: value1
diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim
index f055b5d33..a4030ec77 100644
--- a/tests/stdlib/tstrtabs2.nim
+++ b/tests/stdlib/tstrtabs2.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
@@ -14,3 +15,18 @@ block:
   doAssert t["name"] == "John"
 
 m()
+
+proc fun()=
+  let ret = newStringTable(modeCaseSensitive)
+  ret["foo"] = "bar"
+
+  doAssert $ret == "{foo: bar}"
+
+  let b = ret["foo"]
+  doAssert b == "bar"
+
+proc main()=
+  static: fun()
+  fun()
+
+main()
diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim
index 0e6384d7e..35f6bc669 100644
--- a/tests/stdlib/tstrutils.nim
+++ b/tests/stdlib/tstrutils.nim
@@ -1,10 +1,11 @@
 discard """
-  targets: "c cpp js"
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
 """
 
 import std/strutils
 from stdtest/testutils import disableVm
 import std/assertions
+import std/private/jsutils
 # xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed
 
 template rejectParse(e) =
@@ -52,6 +53,15 @@ template main() =
     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  "]
+    # Empty string:
+    doAssert "".split() == @[""]
+    doAssert "".split(" ") == @[""]
+    doAssert "".split({' '}) == @[""]
+    # Empty separators:
+    doAssert "".split({}) == @[""]
+    doAssert "".split("") == @[""]
+    doAssert s.split({}) == @[s]
+    doAssert s.split("") == @[s]
 
   block: # splitLines
     let fixture = "a\nb\rc\r\nd"
@@ -62,12 +72,21 @@ template main() =
   block: # rsplit
     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 ", 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"]
+    # Empty string:
+    doAssert "".rsplit() == @[""]
+    doAssert "".rsplit(" ") == @[""]
+    doAssert "".rsplit({' '}) == @[""]
+    # Empty separators:
+    let s = " this is an example  "
+    doAssert "".rsplit({}) == @[""]
+    doAssert "".rsplit("") == @[""]
+    doAssert s.rsplit({}) == @[s]
+    doAssert s.rsplit("") == @[s]
 
   block: # splitWhitespace
     let s = " this is an example  "
@@ -433,6 +452,9 @@ template main() =
     x = "1e0"
     x.trimZeros()
     doAssert x == "1e0"
+    x = "1.23"
+    x.trimZeros()
+    doAssert x == "1.23"
 
   block: # countLines
     proc assertCountLines(s: string) = doAssert s.countLines == s.splitLines.len
@@ -505,8 +527,9 @@ template main() =
 
   block: # toHex
     doAssert(toHex(100i16, 32) == "00000000000000000000000000000064")
-    doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C")
-    when not defined js:
+    whenJsNoBigInt64: discard
+    do:
+      doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C")
       doAssert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF")
       doAssert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF")
       doAssert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF")
@@ -527,11 +550,12 @@ template main() =
     doAssert(spaces(0) == "")
 
   block: # toBin, toOct
-    block:# bug #11369
+    whenJsNoBigInt64: # bug #11369
+      discard
+    do:
       var num: int64 = -1
-      when not defined js:
-        doAssert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111"
-        doAssert num.toOct(24) == "001777777777777777777777"
+      doAssert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111"
+      doAssert num.toOct(24) == "001777777777777777777777"
 
   block: # replace
     doAssert "oo".replace("", "abc") == "oo"
@@ -655,11 +679,22 @@ template main() =
       doAssert b == f2
       doAssert c == f3
 
-  block: # parseEnum TODO: merge above
-    type MyEnum = enum enA, enB, enC, enuD, enE
-    doAssert parseEnum[MyEnum]("enu_D") == enuD
+    block:
+      type MyEnum = enum enA, enB, enC, enuD, enE
+      doAssert parseEnum[MyEnum]("enu_D") == enuD
 
-    doAssert parseEnum("invalid enum value", enC) == enC
+      doAssert parseEnum("invalid enum value", enC) == enC
+    
+    block: # issue #22726
+      type SomeEnum = enum A, B, C
+
+      proc assignEnum(dest: var enum, s: string) =
+        type ty = typeof(dest)
+        dest = parseEnum[ty](s)
+      
+      var v: SomeEnum
+      v.assignEnum("A")
+      doAssert v == A
 
   block: # indentation
     doAssert 0 == indentation """
@@ -738,7 +773,8 @@ bar
 
   block: # formatSize
     disableVm:
-      when not defined(js):
+      whenJsNoBigInt64: discard
+      do:
         doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231
       doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
       doAssert formatSize(4096) == "4KiB"
@@ -868,5 +904,10 @@ bar
     doAssert nimIdentNormalize("Foo_bar") == "Foobar"
     doAssert nimIdentNormalize("_Foo_bar") == "_foobar"
 
+  block: # bug #19500
+    doAssert "abc \0 def".find("def") == 6
+    doAssert "abc \0 def".find('d') == 6
+
+
 static: main()
 main()
diff --git a/tests/stdlib/tstrutils2.nim b/tests/stdlib/tstrutils2.nim
deleted file mode 100644
index 22a935ab8..000000000
--- a/tests/stdlib/tstrutils2.nim
+++ /dev/null
@@ -1,38 +0,0 @@
-discard """
-  matrix: "--gc:refc; --gc:orc"
-"""
-
-import "$lib/.." / compiler/strutils2
-import std/assertions
-
-block: # setLen
-  var a = "abc"
-  a.setLen 0
-  a.setLen 3, isInit = false
-  when defined(gcRefc): # bug #19763
-    doAssert a[1] == 'b'
-  a.setLen 0
-  a.setLen 3, isInit = true
-  doAssert a[1] == '\0'
-
-block: # forceCopy
-  var a: string
-  a = "foo"
-  shallow(a)
-  var b: string
-  b = a
-  doAssert b[0].addr == a[0].addr
-  var c: string
-  c.forceCopy a
-  doAssert c == a
-  doAssert c[0].addr != a[0].addr
-
-block: # toLowerAscii
-  var a = "fooBAr"
-  a.toLowerAscii
-  doAssert a == "foobar"
-
-block: # dataPointer
-  var a: string
-  discard a.dataPointer
-  # doAssert a.dataPointer == nil # not guaranteed
diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim
index 9c1213901..2ea96cfbb 100644
--- a/tests/stdlib/tsugar.nim
+++ b/tests/stdlib/tsugar.nim
@@ -1,11 +1,34 @@
 discard """
+  targets: "c js"
+  matrix: "--mm:refc; --mm:orc"
   output: '''
 x + y = 30
 '''
 """
-import std/[sugar, algorithm, random, sets, tables, strutils]
+import std/[sugar, algorithm, random, sets, tables, strutils, sequtils]
 import std/[syncio, assertions]
 
+type # for capture test, ref #20679
+  FooCapture = ref object
+    x: int
+
+proc mainProc() =
+  block: # bug #16967
+    var s = newSeq[proc (): int](5)
+    {.push exportc.}
+    proc bar() =
+      for i in 0 ..< s.len:
+        let foo = i + 1
+        capture foo:
+          s[i] = proc(): int = foo
+    {.pop.}
+
+    bar()
+
+    for i, p in s.pairs:
+      let foo = i + 1
+      doAssert p() == foo
+
 template main() =
   block: # `=>`
     block:
@@ -80,21 +103,61 @@ template main() =
             closure2 = () => (i, j)
     doAssert closure2() == (5, 3)
 
-    block: # bug #16967
-      var s = newSeq[proc (): int](5)
-      {.push exportc.}
-      proc bar() =
-        for i in 0 ..< s.len:
-          let foo = i + 1
-          capture foo:
-            s[i] = proc(): int = foo
-      {.pop.}
+    block: # issue #20679
+      # this should compile. Previously was broken as `var int` is an `nnkHiddenDeref`
+      # which was not handled correctly
 
-      bar()
+      block:
+        var x = 5
+        var s1 = newSeq[proc (): int](2)
+        proc function(data: var int) =
+          for i in 0 ..< 2:
+            data = (i+1) * data
+            capture data:
+              s1[i] = proc(): int = data
+        function(x)
+        doAssert s1[0]() == 5
+        doAssert s1[1]() == 10
 
-      for i, p in s.pairs:
-        let foo = i + 1
-        doAssert p() == foo
+
+      block:
+        var y = @[5, 10]
+        var s2 = newSeq[proc (): seq[int]](2)
+        proc functionS(data: var seq[int]) =
+          for i in 0 ..< 2:
+            data.add (i+1) * 5
+            capture data:
+              s2[i] = proc(): seq[int] = data
+        functionS(y)
+        doAssert s2[0]() == @[5, 10, 5]
+        doAssert s2[1]() == @[5, 10, 5, 10]
+
+
+      template typeT(typ, val: untyped): untyped =
+        var x = val
+        var s = newSeq[proc (): typ](2)
+
+        proc functionT[T](data: var T) =
+          for i in 0 ..< 2:
+            if i == 1:
+              data = default(T)
+            capture data:
+              s[i] = proc (): T = data
+
+        functionT(x)
+        doAssert s[0]() == val
+        doAssert s[1]() == x # == default
+        doAssert s[1]() == default(typ)
+
+      block:
+        var x = 1.1
+        typeT(float, x)
+      block:
+        var x = "hello"
+        typeT(string, x)
+      block:
+        var f = FooCapture(x: 5)
+        typeT(FooCapture, f)
 
   block: # dup
     block dup_with_field:
@@ -209,17 +272,16 @@ template main() =
         discard collect(newSeq, for i in 1..3: i)
       foo()
 
-proc mainProc() =
   block: # dump
     # symbols in templates are gensym'd
     let
-      x = 10
-      y = 20
+      x {.inject.} = 10
+      y {.inject.} = 20
     dump(x + y) # x + y = 30
 
   block: # dumpToString
     template square(x): untyped = x * x
-    let x = 10
+    let x {.inject.} = 10
     doAssert dumpToString(square(x)) == "square(x): x * x = 100"
     let s = dumpToString(doAssert 1+1 == 2)
     doAssert "failedAssertImpl" in s
@@ -227,8 +289,22 @@ proc mainProc() =
       doAssertRaises(AssertionDefect): doAssert false
     doAssert "except AssertionDefect" in s2
 
-static:
-  main()
+  block: # bug #20704
+    proc test() =
+      var xs, ys: seq[int]
+      for i in 0..5:
+        xs.add(i)
+
+      xs.apply(proc (d: auto) = ys.add(d))
+      # ^ can be turned into d => ys.add(d) when we can infer void return type, #16906
+      doAssert ys == @[0, 1, 2, 3, 4, 5]
+
+    test()
+
   mainProc()
+
+when not defined(js): # TODO fixme JS VM
+  static:
+    main()
+
 main()
-mainProc()
diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim
index 071e0b303..cf410cddf 100644
--- a/tests/stdlib/tsums.nim
+++ b/tests/stdlib/tsums.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/sums
 from math import pow
 import std/assertions
diff --git a/tests/stdlib/tsysrand.nim b/tests/stdlib/tsysrand.nim
index e6b65e70f..7b7a0fc34 100644
--- a/tests/stdlib/tsysrand.nim
+++ b/tests/stdlib/tsysrand.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp js"
-  matrix: "--experimental:vmopsDanger"
+  matrix: "--experimental:vmopsDanger; --experimental:vmopsDanger --mm:refc"
 """
 
 import std/sysrand
diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim
index 1a976f7a2..f634ce0c2 100644
--- a/tests/stdlib/tsystem.nim
+++ b/tests/stdlib/tsystem.nim
@@ -1,9 +1,10 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
 import stdtest/testutils
-import std/assertions
+import std/[assertions, formatfloat]
 
 # TODO: in future work move existing `system` tests here, where they belong
 
@@ -82,24 +83,36 @@ block:
     X = object
       a: string
       b: set[char]
+      c: int
+      d: float
+      e: int64
 
-  var y = X(b: {'a'})
 
-  reset(y)
+  var x = X(b: {'a'}, e: 10)
 
-  doAssert y.b == {}
+  var y = move x
 
-block:
-  type
-    X = object
-      a: string
-      b: int
-
-  var y = X(b: 1314)
+  doAssert x.a == ""
+  doAssert x.b == {}
+  doAssert x.c == 0
+  doAssert x.d == 0.0
+  doAssert x.e == 0
 
   reset(y)
 
-  doAssert y.b == 0
+  doAssert y.a == ""
+  doAssert y.b == {}
+  doAssert y.c == 0
+  doAssert y.d == 0.0
+  doAssert y.e == 0
+
+block:
+  var x = 2
+  var y = move x
+  doAssert y == 2
+  doAssert x == 0
+  reset y
+  doAssert y == 0
 
 block:
   type
@@ -162,3 +175,26 @@ block:
   reset(y)
   doAssert y.b == {}
 
+block: # bug #20516
+  type Foo = object
+    x {.bitsize:4.}: uint
+    y {.bitsize:4.}: uint
+
+  when not defined(js):
+    let a = create(Foo)
+
+block: # bug #6549
+  when not defined(js):
+    block:
+      const v = 18446744073709551615'u64
+
+      doAssert $v == "18446744073709551615"
+      doAssert $float32(v) == "1.8446744e+19", $float32(v)
+      doAssert $float64(v) == "1.8446744073709552e+19", $float64(v)
+
+    block:
+      let v = 18446744073709551615'u64
+
+      doAssert $v == "18446744073709551615"
+      doAssert $float32(v) == "1.8446744e+19"
+      doAssert $float64(v) == "1.8446744073709552e+19"
diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim
index ab6502411..c529aff9f 100644
--- a/tests/stdlib/ttables.nim
+++ b/tests/stdlib/ttables.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import tables, hashes
 import std/assertions
 
diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim
index 4889d49d9..ba65590d9 100644
--- a/tests/stdlib/ttasks.nim
+++ b/tests/stdlib/ttasks.nim
@@ -505,3 +505,57 @@ block:
       b.invoke()
 
     doAssert called == 36
+
+  block:
+    proc returnsSomething(a, b: int): int = a + b
+
+    proc noArgsButReturnsSomething(): string = "abcdef"
+
+    proc testReturnValues() =
+      let t = toTask returnsSomething(2233, 11)
+      var res: int
+      t.invoke(addr res)
+      doAssert res == 2233+11
+
+      let tb = toTask noArgsButReturnsSomething()
+      var resB: string
+      tb.invoke(addr resB)
+      doAssert resB == "abcdef"
+
+    testReturnValues()
+
+
+block: # bug #23635
+  block:
+    type
+      Store = object
+        run: proc (a: int) {.nimcall, gcsafe.}
+
+    block:
+      var count = 0
+      proc hello(a: int) =
+        inc count, a
+
+      var store = Store()
+      store.run = hello
+
+      let b = toTask store.run(13)
+      b.invoke()
+      doAssert count == 13
+
+  block:
+    type
+      Store = object
+        run: proc () {.nimcall, gcsafe.}
+
+    block:
+      var count = 0
+      proc hello() =
+        inc count, 1
+
+      var store = Store()
+      store.run = hello
+
+      let b = toTask store.run()
+      b.invoke()
+      doAssert count == 1
diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim
index 1159e08ef..352788c42 100644
--- a/tests/stdlib/ttempfiles.nim
+++ b/tests/stdlib/ttempfiles.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   joinable: false # not strictly necessary
 """
 
diff --git a/tests/stdlib/tthreadpool.nim b/tests/stdlib/tthreadpool.nim
index bc574faeb..1947074be 100644
--- a/tests/stdlib/tthreadpool.nim
+++ b/tests/stdlib/tthreadpool.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on --gc:arc"
+  matrix: "--mm:arc; --mm:refc"
   disabled: "freebsd"
   output: "42"
 """
diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim
index 4f396c735..0f04168dc 100644
--- a/tests/stdlib/ttimes.nim
+++ b/tests/stdlib/ttimes.nim
@@ -1,5 +1,5 @@
 discard """
-  targets: "c js"
+  matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:on; --backend:js --jsbigint64:off -d:nimStringHash2"
 """
 
 import times, strutils, unittest
@@ -11,12 +11,12 @@ when not defined(js):
 proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} =
   let offset = hours * 3600 + minutes * 60 + seconds
 
-  proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime {.locks: 0.} =
+  proc zonedTimeFromAdjTime(adjTime: Time): ZonedTime =
     result.isDst = false
     result.utcOffset = offset
     result.time = adjTime + initDuration(seconds = offset)
 
-  proc zonedTimeFromTime(time: Time): ZonedTime {.locks: 0.}=
+  proc zonedTimeFromTime(time: Time): ZonedTime =
     result.isDst = false
     result.utcOffset = offset
     result.time = time
@@ -71,7 +71,7 @@ template runTimezoneTests() =
       "2006-01-12T22:04:05Z", 11)
   # RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"
   parseTest("2006-01-12T15:04:05.999999999Z-07:00",
-      "yyyy-MM-dd'T'HH:mm:ss'.999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
+      "yyyy-MM-dd'T'HH:mm:ss.'999999999Z'zzz", "2006-01-12T22:04:05Z", 11)
   for tzFormat in ["z", "zz", "zzz"]:
     # formatting timezone as 'Z' for UTC
     parseTest("2001-01-12T22:04:05Z", "yyyy-MM-dd'T'HH:mm:ss" & tzFormat,
@@ -742,3 +742,43 @@ block: # ttimes
     doAssert getWeeksInIsoYear(2014.IsoYear) == 52
     doAssert getWeeksInIsoYear(2015.IsoYear) == 53
     doAssert getWeeksInIsoYear(2016.IsoYear) == 52
+
+  block: # parse and generate iso years
+    # short calendar week with text
+    parseTest("KW 23 2023", "'KW' VV GGGG",
+      "2023-06-05T00:00:00Z", 155)
+    parseTest("KW 5 2023", "'KW' V GGGG",
+      "2023-01-30T00:00:00Z", 29)
+    parseTest("KW 05 23 Saturday", "'KW' V GG dddd",
+      "2023-02-04T00:00:00Z", 34)
+    parseTest("KW 53 20 Fri", "'KW' VV GG ddd",
+      "2021-01-01T00:00:00Z", 0)
+
+    parseTestExcp("KW 23", "'KW' VV") # no year
+    parseTestExcp("KW 23", "'KW' V") # no year
+    parseTestExcp("KW 23", "'KW' GG") # no week
+    parseTestExcp("KW 2023", "'KW' GGGG") # no week
+    
+    var dt = initDateTime(5, mJan, 2023, 0, 0, 0, utc())
+    check dt.format("V") == "1"
+    check dt.format("VV") == "01"
+    check dt.format("GG") == "23"
+    check dt.format("GGGG") == "2023"
+    check dt.format("dddd 'KW'V GGGG") == "Thursday KW1 2023"
+
+  block: # Can be used inside gcsafe proc
+    proc test(): DateTime {.gcsafe.} =
+      result = "1970".parse("yyyy")
+    doAssert test().year == 1970
+
+  block: # test FormatLiterals
+    # since #23861
+    block:
+      let dt = dateTime(2024, mJul, 21, 17, 01, 02, 123_321_123, utc())
+      check dt.format("ss.fff") == "02.123"
+      check dt.format("fff.ffffff") == "123.123321"
+    block:
+      let dt = parse("2024.07.21", "yyyy.MM.dd")
+      check dt.year == 2024
+      check dt.month == mJul
+      check dt.monthday == 21
diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim
index 5e17c151a..9bbc2e92c 100644
--- a/tests/stdlib/ttypeinfo.nim
+++ b/tests/stdlib/ttypeinfo.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/typeinfo
 import std/assertions
 
@@ -70,3 +74,20 @@ block:
 
   doAssert getEnumOrdinal(y, "Hello") == 0
   doAssert getEnumOrdinal(y, "hello") == 1
+
+block: # bug #23556
+  proc test =
+    var
+      t: seq[int]
+      aseq = toAny(t)
+
+    invokeNewSeq(aseq, 0)
+
+    # Got random value only when loop 8 times.
+    for i in 1 .. 8:
+      extendSeq(aseq)
+
+    doAssert t == @[0, 0, 0, 0, 0, 0, 0, 0]
+
+  for i in 1 .. 7:
+    test()
diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim
index 574204da6..6851b9220 100644
--- a/tests/stdlib/ttypetraits.nim
+++ b/tests/stdlib/ttypetraits.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim
index 2b1cb2385..b9e68b15b 100644
--- a/tests/stdlib/tunicode.nim
+++ b/tests/stdlib/tunicode.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/unicode
 import std/assertions
 
@@ -53,6 +57,7 @@ doAssert isAlpha("r")
 doAssert isAlpha("α")
 doAssert isAlpha("ϙ")
 doAssert isAlpha("ஶ")
+doAssert isAlpha("网")
 doAssert(not isAlpha("$"))
 doAssert(not isAlpha(""))
 
@@ -62,6 +67,7 @@ doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽")
 doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει")
 doAssert isAlpha("Јамогујестистаклоитоминештети")
 doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ")
+doAssert isAlpha("编程语言")
 doAssert(not isAlpha("$Foo✓"))
 doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞"))
 
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index 8aa7f9fad..0442c7863 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -19,10 +19,12 @@ discard """
 
 [Suite] test name filtering
 '''
+matrix: "--mm:refc; --mm:orc"
 targets: "c js"
 """
 
 import std/[unittest, sequtils, assertions]
+from std/unittest {.all.} import matchFilter
 
 proc doThings(spuds: var int): int =
   spuds = 24
diff --git a/tests/stdlib/tunittestpass.nim b/tests/stdlib/tunittestpass.nim
index cff37a3b7..d8de277b7 100644
--- a/tests/stdlib/tunittestpass.nim
+++ b/tests/stdlib/tunittestpass.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c js"
 """
 
diff --git a/tests/stdlib/tunixsocket.nim b/tests/stdlib/tunixsocket.nim
new file mode 100644
index 000000000..636fd08c6
--- /dev/null
+++ b/tests/stdlib/tunixsocket.nim
@@ -0,0 +1,35 @@
+import std/[assertions, net, os, osproc]
+
+# XXX: Make this test run on Windows too when we add support for Unix sockets on Windows
+when defined(posix) and not defined(nimNetLite):
+  const nim = getCurrentCompilerExe()
+  let
+    dir = currentSourcePath().parentDir()
+    serverPath = dir / "unixsockettest"
+
+  let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "unixsockettest.nim"))
+  doAssert err == 0
+
+  let svproc = startProcess(serverPath, workingDir = dir)
+  doAssert svproc.running()
+  # Wait for the server to open the socket and listen from it
+  sleep(400)
+
+  block unixSocketSendRecv:
+    let
+      unixSocketPath = dir / "usox"
+      socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE)
+
+    socket.connectUnix(unixSocketPath)
+    # for a blocking Unix socket this should never fail
+    socket.send("data sent through the socket\c\l", maxRetries = 0)
+    var resp: string
+    socket.readLine(resp)
+    doAssert resp == "Hello from server"
+
+    socket.send("bye\c\l")
+    socket.readLine(resp)
+    doAssert resp == "bye"
+    socket.close()
+
+  svproc.close()
diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim
index 77ba02dd1..9c717c5b1 100644
--- a/tests/stdlib/turi.nim
+++ b/tests/stdlib/turi.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets:  "c js"
 """
 
diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim
index ba8ea050e..927077120 100644
--- a/tests/stdlib/tuserlocks.nim
+++ b/tests/stdlib/tuserlocks.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--threads:on"
+  matrix: "--mm:refc; --mm:orc"
 """
 
 import std/rlocks
diff --git a/tests/stdlib/tvarargs.nim b/tests/stdlib/tvarargs.nim
index 3207572b5..2edc26264 100644
--- a/tests/stdlib/tvarargs.nim
+++ b/tests/stdlib/tvarargs.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c js"
-  matrix: "--gc:refc; --gc:arc"
+  matrix: "--mm:refc; --mm:orc"
 """
 import std/assertions
 
diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim
index bb0d3d37f..f9624ee5b 100644
--- a/tests/stdlib/tvarints.nim
+++ b/tests/stdlib/tvarints.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/varints
 import std/assertions
 
@@ -29,7 +33,7 @@ block:
     doAssert cast[float64](got) == test
 
 block:
-  var hugeIntArray: array[50, byte]
+  var hugeIntArray: array[9, byte]
   var readedInt: uint64
 
   template chk(a) =
diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim
index f43557ad8..63804c136 100644
--- a/tests/stdlib/tvmutils.nim
+++ b/tests/stdlib/tvmutils.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   joinable: false
   nimout: '''
 0
@@ -19,7 +20,6 @@ tvmutils.nim(29, 14) [opcIndCall]       vmTrace(false)
 """
 # line 20 (only showing a subset of nimout to avoid making the test rigid)
 import std/vmutils
-
 proc main() =
   for i in 0..<7:
     echo i
diff --git a/tests/stdlib/tvolatile.nim b/tests/stdlib/tvolatile.nim
new file mode 100644
index 000000000..c097f9723
--- /dev/null
+++ b/tests/stdlib/tvolatile.nim
@@ -0,0 +1,15 @@
+import std/[volatile, assertions]
+
+var st: int
+var foo: ptr int = addr st
+volatileStore(foo, 12)
+doAssert volatileLoad(foo) == 12
+
+# bug #14623
+proc bar =
+  var st: int
+  var foo: ptr int = addr st
+  volatileStore(foo, 12)
+  doAssert volatileLoad(foo) == 12
+
+bar()
diff --git a/tests/stdlib/twchartoutf8.nim b/tests/stdlib/twchartoutf8.nim
index add104b07..e437177ac 100644
--- a/tests/stdlib/twchartoutf8.nim
+++ b/tests/stdlib/twchartoutf8.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   output: '''OK'''
 """
 
@@ -10,6 +11,7 @@ import std/[syncio, assertions]
 when not defined(windows):
   echo "OK"
 else:
+  import std/widestrs
   {.push gcsafe.}
 
   const CP_UTF8 = 65001'i32
diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim
index b2d72bd0c..ceadbe7bf 100644
--- a/tests/stdlib/twith.nim
+++ b/tests/stdlib/twith.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/with
 import std/[assertions, formatfloat]
 
@@ -22,3 +26,19 @@ with f:
 doAssert f.col == "(2, 3, 4)"
 doAssert f.pos == "(0.0, 1.0)"
 doAssert f.name == "bar"
+
+type
+  Baz* = object
+    a*, b*: int
+  Bar* = object
+    x*: int
+    baz*: Baz
+
+var bar: Bar
+with bar:
+  x = 1
+  with baz:
+    a = 2
+
+doAssert bar.x == 1
+doAssert bar.baz.a == 2
diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim
index a08e64cf9..5d49477d3 100644
--- a/tests/stdlib/twordwrap.nim
+++ b/tests/stdlib/twordwrap.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/wordwrap
 import std/assertions
 
diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim
index 5d5c1ab2d..3da230b5e 100644
--- a/tests/stdlib/twrapnils.nim
+++ b/tests/stdlib/twrapnils.nim
@@ -1,3 +1,7 @@
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
 import std/wrapnils
 from std/options import get, isSome
 import std/assertions
diff --git a/tests/stdlib/twrongstattype.nim b/tests/stdlib/twrongstattype.nim
new file mode 100644
index 000000000..4a1fc30c6
--- /dev/null
+++ b/tests/stdlib/twrongstattype.nim
@@ -0,0 +1,14 @@
+# issue #24076
+
+when defined(macosx) or defined(freebsd) or defined(openbsd) or defined(netbsd):
+  import std/posix
+  proc uid(x: uint32): Uid = Uid(x)
+  var y: uint32
+  let myUid = geteuid()
+  discard myUid == uid(y)
+  proc dev(x: uint32): Dev = Dev(x)
+  let myDev = 1.Dev
+  discard myDev == dev(y)
+  proc nlink(x: uint32): Nlink = Nlink(x)
+  let myNlink = 1.Nlink
+  discard myNlink == nlink(y)
diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim
index f6b7c62e8..add12a3fc 100644
--- a/tests/stdlib/txmltree.nim
+++ b/tests/stdlib/txmltree.nim
@@ -1,4 +1,8 @@
-import std/[xmltree, assertions]
+discard """
+  matrix: "--mm:refc; --mm:orc"
+"""
+
+import std/[xmltree, assertions, xmlparser]
 
 
 block:
@@ -83,3 +87,34 @@ block:
   x.add newElement("sonTag")
   x.add newEntity("my entity")
   doAssert $x == "<myTag>my text<sonTag />&my entity;</myTag>"
+
+block: # bug #21290
+  let x = newXmlTree("foo",[
+    newXmlTree("bar",[
+      newText("Hola"),
+      newXmlTree("qux",[
+        newXmlTree("plugh",[])
+      ])
+    ])
+  ])
+
+  let s = $x
+  doAssert $parseXml(s) == s
+  doAssert s == """<foo>
+  <bar>Hola<qux>    <plugh />  </qux></bar>
+</foo>"""
+
+block: #21541
+  let root = <>root()
+  root.add <>child(newText("hello"))
+  root.add <>more(newVerbatimText("hola"))
+  let s = $root
+  doAssert s == """<root>
+  <child>hello</child>
+  <more>hola</more>
+</root>"""
+
+  let temp = newVerbatimText("Hello!")
+  doAssert temp.text == "Hello!"
+  temp.text = "Hola!"
+  doAssert temp.text == "Hola!"
diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim
index 0cf52999c..f385ddd05 100644
--- a/tests/stdlib/tyield.nim
+++ b/tests/stdlib/tyield.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc; --mm:orc"
   targets: "c cpp js"
 """
 
diff --git a/tests/stdlib/unixsockettest.nim b/tests/stdlib/unixsockettest.nim
new file mode 100644
index 000000000..8f95d0808
--- /dev/null
+++ b/tests/stdlib/unixsockettest.nim
@@ -0,0 +1,26 @@
+import std/[assertions, net, os]
+
+let unixSocketPath = getCurrentDir() / "usox"
+
+removeFile(unixSocketPath)
+
+let socket = newSocket(AF_UNIX, SOCK_STREAM, IPPROTO_NONE)
+socket.bindUnix(unixSocketPath)
+socket.listen()
+
+var
+  clientSocket: Socket
+  data: string
+
+socket.accept(clientSocket)
+clientSocket.readLine(data)
+doAssert data == "data sent through the socket"
+clientSocket.send("Hello from server\c\l")
+
+clientSocket.readLine(data)
+doAssert data == "bye"
+clientSocket.send("bye\c\l")
+
+clientSocket.close()
+socket.close()
+removeFile(unixSocketPath)
diff --git a/tests/stmt/tforloop_tuple_multiple_underscore.nim b/tests/stmt/tforloop_tuple_multiple_underscore.nim
index 2afdb0b77..96804df18 100644
--- a/tests/stmt/tforloop_tuple_multiple_underscore.nim
+++ b/tests/stmt/tforloop_tuple_multiple_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 iterator iter(): (int, int, int) =
@@ -7,4 +7,4 @@ iterator iter(): (int, int, int) =
 
 
 for (_, i, _) in iter():
-  echo _
\ No newline at end of file
+  echo _
diff --git a/tests/stmt/tforloop_tuple_underscore.nim b/tests/stmt/tforloop_tuple_underscore.nim
index 8cbb0fc10..eda42d527 100644
--- a/tests/stmt/tforloop_tuple_underscore.nim
+++ b/tests/stmt/tforloop_tuple_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 iterator iter(): (int, int) =
diff --git a/tests/stmt/tforloop_underscore.nim b/tests/stmt/tforloop_underscore.nim
index c5b49da77..ce1c86386 100644
--- a/tests/stmt/tforloop_underscore.nim
+++ b/tests/stmt/tforloop_underscore.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "undeclared identifier: '_'"
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
 """
 
 for _ in ["a"]:
diff --git a/tests/stmt/tgenericsunderscore.nim b/tests/stmt/tgenericsunderscore.nim
new file mode 100644
index 000000000..be2b8ec78
--- /dev/null
+++ b/tests/stmt/tgenericsunderscore.nim
@@ -0,0 +1,4 @@
+# issue #21435
+
+proc foo[_](x: typedesc[_]): string = "BAR" #[tt.Error
+                        ^ the special identifier '_' is ignored in declarations and cannot be used]#
diff --git a/tests/stmt/tmiscunderscore.nim b/tests/stmt/tmiscunderscore.nim
new file mode 100644
index 000000000..c4bae1c3d
--- /dev/null
+++ b/tests/stmt/tmiscunderscore.nim
@@ -0,0 +1,15 @@
+import std/assertions
+
+block:
+  proc _() = echo "one"
+  doAssert not compiles(_())
+  proc _() = echo "two"
+  doAssert not compiles(_())
+
+block:
+  type _ = int
+  doAssert not (compiles do:
+    let x: _ = 3)
+  type _ = float
+  doAssert not (compiles do:
+    let x: _ = 3)
diff --git a/tests/stylecheck/foreign_package/foreign_package.nim b/tests/stylecheck/foreign_package/foreign_package.nim
new file mode 100644
index 000000000..f95be006c
--- /dev/null
+++ b/tests/stylecheck/foreign_package/foreign_package.nim
@@ -0,0 +1 @@
+include ../thint
\ No newline at end of file
diff --git a/tests/stylecheck/foreign_package/foreign_package.nimble b/tests/stylecheck/foreign_package/foreign_package.nimble
new file mode 100644
index 000000000..a2c49e389
--- /dev/null
+++ b/tests/stylecheck/foreign_package/foreign_package.nimble
@@ -0,0 +1,2 @@
+# See `tstyleCheck`
+# Needed to mark `mstyleCheck` as a foreign package.
diff --git a/tests/stylecheck/t20397_1.nim b/tests/stylecheck/t20397_1.nim
index 24f5791f8..76c03dca1 100644
--- a/tests/stylecheck/t20397_1.nim
+++ b/tests/stylecheck/t20397_1.nim
@@ -1,8 +1,8 @@
 discard """
-  matrix: "--styleCheck:off"
+  matrix: "--styleCheck:off --hint:Name:on"
 """
 
 {.hintAsError[Name]:on.}
 var a_b = 1
 discard a_b
-{.hintAsError[Name]:off.}
\ No newline at end of file
+{.hintAsError[Name]:off.}
diff --git a/tests/stylecheck/tforeign_package.nim b/tests/stylecheck/tforeign_package.nim
new file mode 100644
index 000000000..8594ad802
--- /dev/null
+++ b/tests/stylecheck/tforeign_package.nim
@@ -0,0 +1,16 @@
+discard """
+  matrix: "--errorMax:0 --styleCheck:error"
+  action: compile
+"""
+
+import foreign_package/foreign_package
+
+# This call tests that:
+#   - an instantiation of a generic in a foreign package doesn't raise errors
+#     when the generic body contains:
+#     - definition and usage violations
+#     - builtin pragma usage violations
+#     - user pragma usage violations
+#   - definition violations in foreign packages are ignored
+#   - usage violations in foreign packages are ignored
+genericProc[int]()
diff --git a/tests/stylecheck/thint.nim b/tests/stylecheck/thint.nim
new file mode 100644
index 000000000..c19aac1b8
--- /dev/null
+++ b/tests/stylecheck/thint.nim
@@ -0,0 +1,43 @@
+discard """
+  matrix: "--styleCheck:hint"
+  action: compile
+"""
+
+# Test violating ident definition:
+{.pragma: user_pragma.} #[tt.Hint
+        ^ 'user_pragma' should be: 'userPragma' [Name] ]#
+
+# Test violating ident usage style matches definition style:
+{.userPragma.} #[tt.Hint
+  ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
+
+# Test violating builtin pragma usage style:
+{.no_side_effect.}: #[tt.Hint
+  ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
+  discard 0
+
+# Test:
+#  - definition style violation
+#  - user pragma usage style violation
+#  - builtin pragma usage style violation
+proc generic_proc*[T] {.no_destroy, userPragma.} = #[tt.Hint
+     ^ 'generic_proc' should be: 'genericProc' [Name]; tt.Hint
+                        ^ 'no_destroy' should be: 'nodestroy' [Name]; tt.Hint
+                                    ^ 'userPragma' should be: 'user_pragma' [template declared in thint.nim(7, 9)] [Name] ]#
+  # Test definition style violation:
+  let snake_case = 0 #[tt.Hint
+      ^ 'snake_case' should be: 'snakeCase' [Name] ]#
+  # Test user pragma definition style violation:
+  {.pragma: another_user_pragma.} #[tt.Hint
+          ^ 'another_user_pragma' should be: 'anotherUserPragma' [Name] ]#
+  # Test user pragma usage style violation:
+  {.anotherUserPragma.} #[tt.Hint
+    ^ 'anotherUserPragma' should be: 'another_user_pragma' [template declared in thint.nim(31, 11)] [Name] ]#
+  # Test violating builtin pragma usage style:
+  {.no_side_effect.}: #[tt.Hint
+    ^ 'no_side_effect' should be: 'noSideEffect' [Name] ]#
+    # Test usage style violation:
+    discard snakeCase #[tt.Hint
+            ^ 'snakeCase' should be: 'snake_case' [let declared in thint.nim(28, 7)] [Name] ]#
+
+generic_proc[int]()
diff --git a/tests/stylecheck/treject.nim b/tests/stylecheck/treject.nim
index b9d97a58d..458a2d039 100644
--- a/tests/stylecheck/treject.nim
+++ b/tests/stylecheck/treject.nim
@@ -1,7 +1,7 @@
 discard """
   action: reject
   nimout: '''treject.nim(14, 13) Error: 'iD' should be: 'id' [field declared in treject.nim(9, 5)]'''
-  matrix: "--styleCheck:error --styleCheck:usages"
+  matrix: "--styleCheck:error --styleCheck:usages  --hint:Name:on"
 """
 
 type
@@ -14,4 +14,4 @@ template hello =
   echo name.iD
   echo iD
 
-hello()
\ No newline at end of file
+hello()
diff --git a/tests/stylecheck/tusages.nim b/tests/stylecheck/tusages.nim
index 2f99c70c5..b689dabb3 100644
--- a/tests/stylecheck/tusages.nim
+++ b/tests/stylecheck/tusages.nim
@@ -1,11 +1,9 @@
 discard """
   action: reject
-  nimout: '''tusages.nim(22, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(11, 6)]'''
-  matrix: "--styleCheck:error --styleCheck:usages"
+  nimout: '''tusages.nim(20, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(9, 6)]'''
+  matrix: "--styleCheck:error --styleCheck:usages --hint:all:off --hint:Name:on"
 """
 
-
-
 import strutils
 
 proc BADSTYLE(c: char) = discard
diff --git a/tests/magics/t10307.nim b/tests/system/t10307.nim
index b5a93c5c6..b5a93c5c6 100644
--- a/tests/magics/t10307.nim
+++ b/tests/system/t10307.nim
diff --git a/tests/system/t20938.nim b/tests/system/t20938.nim
new file mode 100644
index 000000000..7341cbb91
--- /dev/null
+++ b/tests/system/t20938.nim
@@ -0,0 +1,12 @@
+discard """
+  cmd: "nim c --mm:refc $file"
+  action: "compile"
+"""
+
+template foo(x: typed) =
+  discard x
+
+foo:
+  var x = "hello"
+  x.shallowCopy("test")
+  true
diff --git a/tests/system/tcomparisons.nim b/tests/system/tcomparisons.nim
new file mode 100644
index 000000000..a661b14a1
--- /dev/null
+++ b/tests/system/tcomparisons.nim
@@ -0,0 +1,51 @@
+discard """
+  targets: "c cpp js"
+"""
+
+template main =
+  block: # proc equality
+    var prc: proc(): int {.closure.}
+    prc = nil
+    doAssert prc == nil
+    doAssert prc.isNil
+    prc = proc(): int =
+      result = 123
+    doAssert prc != nil
+    doAssert not prc.isNil
+    doAssert prc == prc
+    let prc2 = prc
+    doAssert prc == prc2
+    doAssert prc2 != nil
+    doAssert not prc2.isNil
+    doAssert not prc.isNil
+    prc = proc(): int =
+      result = 456
+    doAssert prc != nil
+    doAssert not prc.isNil
+    doAssert prc != prc2
+  block: # iterator equality
+    when nimvm: discard # vm does not support closure iterators
+    else:
+      when not defined(js): # js also does not support closure iterators
+        var iter: iterator(): int {.closure.}
+        iter = nil
+        doAssert iter == nil
+        doAssert iter.isNil
+        iter = iterator(): int =
+          yield 123
+        doAssert iter != nil
+        doAssert not iter.isNil
+        doAssert iter == iter
+        let iter2 = iter
+        doAssert iter == iter2
+        doAssert iter2 != nil
+        doAssert not iter2.isNil
+        doAssert not iter.isNil
+        iter = iterator(): int =
+          yield 456
+        doAssert iter != nil
+        doAssert not iter.isNil
+        doAssert iter != iter2
+
+static: main()
+main()
diff --git a/tests/concat/tconcat.nim b/tests/system/tconcat.nim
index fdce3ea00..fdce3ea00 100644
--- a/tests/concat/tconcat.nim
+++ b/tests/system/tconcat.nim
diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim
index 93fa5cb9e..eabee81b3 100644
--- a/tests/system/tdollars.nim
+++ b/tests/system/tdollars.nim
@@ -1,5 +1,5 @@
 discard """
-  targets: "c cpp js"
+  matrix: "--mm:refc; --mm:orc; --backend:cpp; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
 """
 
 #[
@@ -12,6 +12,7 @@ duplication (which always results in weaker test coverage in practice).
 ]#
 
 import std/unittest
+import std/private/jsutils
 template test[T](a: T, expected: string) =
   check $a == expected
   var b = a
@@ -44,8 +45,7 @@ block: # `$`(SomeInteger)
 
   check $int8.low == "-128"
   check $int8(-128) == "-128"
-  when not defined js: # pending https://github.com/nim-lang/Nim/issues/14127
-    check $cast[int8](-128) == "-128"
+  check $cast[int8](-128) == "-128"
 
   var a = 12345'u16
   check $a == "12345"
@@ -66,7 +66,8 @@ block: # `$`(SomeInteger)
   testType int
   testType bool
 
-  when not defined(js): # requires BigInt support
+  whenJsNoBigInt64: discard
+  do:
     testType uint64
     testType int64
     testType BiggestInt
@@ -108,10 +109,10 @@ block:
     # if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test;
     # until then, this is the correct semantics.
     let a3 = $a2
-    doAssert a2 < 3
-    doAssert a3 == "-1"
+    doAssert a2 == 255'u8
+    doAssert a3 == "255"
     proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".}
-    doAssert $intToStr(a2) == "-1"
+    doAssert $intToStr(a2) == "255"
   else:
     block:
       let x = -1'i8
@@ -176,10 +177,10 @@ proc main()=
       res.addInt int64(i)
     doAssert res == "-9-8-7-6-5-4-3-2-10"
 
-    when not defined(js):
+    whenJsNoBigInt64: discard
+    do:
       test2 high(int64), "9223372036854775807"
       test2 low(int64), "-9223372036854775808"
-
     test2 high(int32), "2147483647"
     test2 low(int32), "-2147483648"
     test2 high(int16), "32767"
diff --git a/tests/misc/temptyecho.nim b/tests/system/temptyecho.nim
index a3c407897..a3c407897 100644
--- a/tests/misc/temptyecho.nim
+++ b/tests/system/temptyecho.nim
diff --git a/tests/system/tensuremove.nim b/tests/system/tensuremove.nim
new file mode 100644
index 000000000..668d5aed1
--- /dev/null
+++ b/tests/system/tensuremove.nim
@@ -0,0 +1,131 @@
+discard """
+  targets: "c js"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+block:
+  type
+    X = object
+      s: string
+
+  proc `=copy`(x: var X, y: X) =
+    x.s = "copied " & y.s
+
+  proc `=sink`(x: var X, y: X) =
+    `=destroy`(x)
+    wasMoved(x)
+    x.s = "moved " & y.s
+
+  proc consume(x: sink X) =
+    discard x.s
+
+  proc main =
+    let m = "abcdefg"
+    var x = X(s: ensureMove m)
+    consume(ensureMove x)
+
+  static: main()
+  main()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var s = String(id: "1")
+    var m = ensureMove s
+    doAssert m.id == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = String(id: ensureMove n)
+    var m = ensureMove s
+    doAssert m.id == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = [ensureMove n]
+    var m = ensureMove s
+    doAssert m[0] == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var n = "1"
+    var s = @[ensureMove n]
+    var m = ensureMove s
+    doAssert m[0] == "1"
+
+  hello()
+
+block:
+  type
+    String = object
+      id: string
+
+  proc hello =
+    var s = String(id: "1")
+    var m = ensureMove s.id
+    doAssert m == "1"
+
+  hello()
+
+block:
+  proc foo =
+    var x = 1
+    let y = ensureMove x # move
+    when not defined(js):
+      doAssert (y, x) == (1, 0) # (1, 0)
+  foo()
+
+block:
+  proc foo =
+    var x = 1
+    let y = ensureMove x # move
+    doAssert y == 1
+  foo()
+
+block:
+  proc foo =
+    var x = @[1, 2, 3]
+    let y = ensureMove x[0] # move
+    doAssert y == 1
+    when not defined(js):
+      doAssert x == @[0, 2, 3]
+  foo()
+
+block:
+  proc foo =
+    var x = [1, 2, 3]
+    let y = ensureMove x[0] # move
+    doAssert y == 1
+    when not defined(js):
+      doAssert x == @[0, 2, 3]
+  foo()
+
+block:
+  proc foo =
+    var x = @["1", "2", "3"]
+    let y = ensureMove x[0] # move
+    doAssert y == "1"
+  foo()
diff --git a/tests/system/tensuremove1.nim b/tests/system/tensuremove1.nim
new file mode 100644
index 000000000..b7e19c4fb
--- /dev/null
+++ b/tests/system/tensuremove1.nim
@@ -0,0 +1,16 @@
+discard """
+  errormsg: "cannot move 's', which introduces an implicit copy"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+type
+  String = object
+    id: string
+
+proc hello =
+  var s = String(id: "1")
+  var m = ensureMove s
+  discard m
+  discard s
+
+hello()
\ No newline at end of file
diff --git a/tests/system/tensuremove2.nim b/tests/system/tensuremove2.nim
new file mode 100644
index 000000000..39bbeb22e
--- /dev/null
+++ b/tests/system/tensuremove2.nim
@@ -0,0 +1,15 @@
+discard """
+  errormsg: "Nested expressions cannot be moved: 'if true: s else: String()'"
+"""
+
+type
+  String = object
+    id: string
+
+proc hello =
+  var s = String(id: "1")
+  var m = ensureMove(if true: s else: String())
+  discard m
+  discard s
+
+hello()
\ No newline at end of file
diff --git a/tests/system/tensuremove3.nim b/tests/system/tensuremove3.nim
new file mode 100644
index 000000000..eca032673
--- /dev/null
+++ b/tests/system/tensuremove3.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "cannot move 'x', passing 'x' to a sink parameter introduces an implicit copy"
+  matrix: "--cursorinference:on; --cursorinference:off"
+"""
+
+block:
+  type
+    X = object
+      s: string
+
+  proc `=copy`(x: var X, y: X) =
+    x.s = "copied " & y.s
+
+  proc `=sink`(x: var X, y: X) =
+    `=destroy`(x)
+    wasMoved(x)
+    x.s = "moved " & y.s
+
+  proc consume(x: sink X) =
+    discard x.s
+
+  proc main =
+    var s = "abcdefg"
+    var x = X(s: ensureMove s)
+    consume(ensureMove x)
+    discard x
+
+  main()
\ No newline at end of file
diff --git a/tests/fields/tfielditerator.nim b/tests/system/tfielditerator.nim
index d1fbf02f9..7e063c6cf 100644
--- a/tests/fields/tfielditerator.nim
+++ b/tests/system/tfielditerator.nim
@@ -109,4 +109,18 @@ block titerator2:
     echo key, ": ", val
 
   for val in fields(co):
-    echo val
\ No newline at end of file
+    echo val
+
+block:
+  type
+    Enum = enum A, B
+    Object = object
+      case a: Enum
+      of A:
+        integer: int
+      of B:
+        time: string
+
+  let x = A
+  let s = Object(a: x)
+  doAssert s.integer == 0
diff --git a/tests/fields/tfields.nim b/tests/system/tfields.nim
index 0bf3a4e1a..0bf3a4e1a 100644
--- a/tests/fields/tfields.nim
+++ b/tests/system/tfields.nim
diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim
index 47c6c6014..1ccb9e29c 100644
--- a/tests/system/tgcnone.nim
+++ b/tests/system/tgcnone.nim
@@ -1,6 +1,7 @@
 discard """
-  matrix: "--gc:none -d:useMalloc --threads:off"
+  matrix: "--mm:none -d:useMalloc"
 """
 # bug #15617
+# bug #22262
 let x = 4
 doAssert x == 4
diff --git a/tests/misc/tgcregions.nim b/tests/system/tgcregions.nim
index e14865be3..e14865be3 100644
--- a/tests/misc/tgcregions.nim
+++ b/tests/system/tgcregions.nim
diff --git a/tests/misc/tinc.nim b/tests/system/timmutableinc.nim
index 91f6223e2..e857800b3 100644
--- a/tests/misc/tinc.nim
+++ b/tests/system/timmutableinc.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: "type mismatch: got <int>"
-  file: "tinc.nim"
+  file: "timmutableinc.nim"
   line: 8
 """
 var x = 0
diff --git a/tests/misc/tnot.nim b/tests/system/tinvalidnot.nim
index a3669705b..df0291a8a 100644
--- a/tests/misc/tnot.nim
+++ b/tests/system/tinvalidnot.nim
@@ -1,6 +1,6 @@
 discard """
   errormsg: "type mismatch"
-  file: "tnot.nim"
+  file: "tinvalidnot.nim"
   line: 14
 """
 # BUG: following compiles, but should not:
diff --git a/tests/misc/tlocals.nim b/tests/system/tlocals.nim
index e59976102..e59976102 100644
--- a/tests/misc/tlocals.nim
+++ b/tests/system/tlocals.nim
diff --git a/tests/misc/tlowhigh.nim b/tests/system/tlowhigh.nim
index 6ae871255..6ae871255 100644
--- a/tests/misc/tlowhigh.nim
+++ b/tests/system/tlowhigh.nim
diff --git a/tests/magics/tmagics.nim b/tests/system/tmagics.nim
index e52912b74..6088c9600 100644
--- a/tests/magics/tmagics.nim
+++ b/tests/system/tmagics.nim
@@ -57,10 +57,6 @@ block t9442:
   GC_ref(v3)
   GC_unref(v3)
 
-block: # bug #6499
-  let x = (chr, 0)
-  doAssert x[1] == 0
-
 block: # bug #12229
   proc foo(T: typedesc) = discard
   foo(ref)
diff --git a/tests/stdlib/tmemory.nim b/tests/system/tmemory.nim
index 553037011..553037011 100644
--- a/tests/stdlib/tmemory.nim
+++ b/tests/system/tmemory.nim
diff --git a/tests/misc/tnew.nim b/tests/system/tnew.nim
index 41ef3fa19..c28c1187f 100644
--- a/tests/misc/tnew.nim
+++ b/tests/system/tnew.nim
@@ -1,5 +1,5 @@
 discard """
-matrix: "--mm:refc"
+matrix: "--mm:refc; --mm:orc"
 outputsub: '''
 Simple tree node allocation worked!
 Simple cycle allocation worked!
@@ -11,7 +11,6 @@ joinable: false
 # and the code generation for gc walkers
 # (and the garbage collector):
 
-## todo fixme it doesn't work for ORC
 type
   PNode = ref TNode
   TNode = object
@@ -26,7 +25,10 @@ proc finalizer(n: PNode) =
   write(stdout, " is now freed\n")
 
 proc newNode(data: int, le, ri: PNode): PNode =
-  new(result, finalizer)
+  when defined(gcDestructors): # using finalizer breaks the test for orc
+    new(result)
+  else:
+    new(result, finalizer)
   result.le = le
   result.ri = ri
   result.data = data
diff --git a/tests/misc/tnewderef.nim b/tests/system/tnewderef.nim
index 3394dbddf..3394dbddf 100644
--- a/tests/misc/tnewderef.nim
+++ b/tests/system/tnewderef.nim
diff --git a/tests/system/tnewstring_uninitialized.nim b/tests/system/tnewstring_uninitialized.nim
new file mode 100644
index 000000000..9bc0e1622
--- /dev/null
+++ b/tests/system/tnewstring_uninitialized.nim
@@ -0,0 +1,11 @@
+discard """
+  matrix: "--mm:refc;"
+"""
+
+# bug #22555
+var x = newStringUninit(10)
+doAssert x.len == 10
+for i in 0..<x.len:
+  x[i] = chr(ord('a') + i)
+
+doAssert x == "abcdefghij"
diff --git a/tests/system/tnilconcats.nim b/tests/system/tnilconcats.nim
index c1126405c..69fc3913c 100644
--- a/tests/system/tnilconcats.nim
+++ b/tests/system/tnilconcats.nim
@@ -26,7 +26,8 @@ when true:
 
   # casting an empty string as sequence with shallow() should not segfault
   var s2: string
-  shallow(s2)
+  when defined(gcRefc):
+    shallow(s2)
   s2 &= "foo"
   doAssert s2 == "foo"
 
diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim
index cae20865e..bb6e453fb 100644
--- a/tests/system/tostring.nim
+++ b/tests/system/tostring.nim
@@ -47,7 +47,7 @@ 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(addr arr) == "Hello World!"
+doAssert $cast[cstring](addr arr) == "Hello World!"
 
 proc takes(c: cstring) =
   doAssert c == cstring""
diff --git a/tests/system/trefs.nim b/tests/system/trefs.nim
new file mode 100644
index 000000000..8c36aec52
--- /dev/null
+++ b/tests/system/trefs.nim
@@ -0,0 +1,15 @@
+discard """
+  targets: "c js"
+"""
+
+# bug #21317
+proc parseHook*(v: var ref int) =
+  var a: ref int
+  new(a)
+  a[] = 123
+  v = a
+
+proc fromJson2*(): ref int =
+  parseHook(result)
+
+doAssert fromJson2()[] == 123
diff --git a/tests/misc/tslices.nim b/tests/system/tslices.nim
index 987b50de1..d0c68f8cb 100644
--- a/tests/misc/tslices.nim
+++ b/tests/system/tslices.nim
@@ -57,3 +57,9 @@ echo mystr
 var s = "abcdef"
 s[1 .. ^2] = "xyz"
 assert s == "axyzf"
+
+# issue mentioned in PR #19219
+type Foo = distinct uint64
+# < here calls `pred` which used to cause a codegen error
+# `pred` compiles because distinct ordinals are considered ordinal
+const slice = 0 ..< 42.Foo
diff --git a/tests/system/tslimsystem.nim b/tests/system/tslimsystem.nim
index 690f4ee9b..4815306b5 100644
--- a/tests/system/tslimsystem.nim
+++ b/tests/system/tslimsystem.nim
@@ -1,6 +1,6 @@
 discard """
   output: "123"
-  matrix: "-d:nimPreviewSlimSystem"
+  matrix: "-d:nimPreviewSlimSystem --mm:refc; -d:nimPreviewSlimSystem --mm:arc"
 """
 
 echo 123
\ No newline at end of file
diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim
index 7f5914725..1debb7c48 100644
--- a/tests/system/tsystem_misc.nim
+++ b/tests/system/tsystem_misc.nim
@@ -212,3 +212,16 @@ block:
   doAssert not compiles(echo p.rawProc.repr)
   doAssert not compiles(echo p.rawEnv.repr)
   doAssert not compiles(echo p.finished)
+
+proc bug23223 = # bug #23223
+  var stuff = "hello"
+  stuff.insert ""
+  doAssert stuff == "hello"
+
+bug23223()
+
+block: # bug #23894
+  let v = high(uint) div 2
+  let s = v + 1 # 9223372036854775808
+  let m = succ v
+  doAssert s == m
diff --git a/tests/system/tuse_version.nim b/tests/system/tuse_version.nim
deleted file mode 100644
index 116ef3596..000000000
--- a/tests/system/tuse_version.nim
+++ /dev/null
@@ -1,49 +0,0 @@
-discard """
-  matrix: "--useVersion:1.0"
-"""
-
-{.warning[UnusedImport]: off.}
-
-import std/[
-  # Core:
-  bitops, typetraits, lenientops, macros, volatile,
-
-  # Algorithms:
-  algorithm, sequtils,
-
-  # Collections:
-  critbits, deques, heapqueue, intsets, lists, options, sets,
-  sharedlist, tables,
-
-  # Strings:
-  editdistance, wordwrap, parseutils, ropes,
-  pegs, punycode, strformat, strmisc, strscans, strtabs,
-  strutils, unicode, unidecode,
-
-  # Generic operator system services:
-  os, streams,
-
-  # Math libraries:
-  complex, math, mersenne, random, rationals, stats, sums,
-
-  # Internet protocols:
-  httpcore, mimetypes, uri,
-
-  # Parsers:
-  htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml,
-
-  # XML processing:
-  xmltree, xmlparser,
-
-  # Generators:
-  htmlgen,
-
-  # Hashing:
-  base64, hashes,
-
-  # Miscellaneous:
-  colors, sugar, varints,
-]
-
-
-doAssert NimVersion == "1.0.100"
diff --git a/tests/system/tuse_version16.nim b/tests/system/tuse_version16.nim
index d5430db32..802b617ad 100644
--- a/tests/system/tuse_version16.nim
+++ b/tests/system/tuse_version16.nim
@@ -1,5 +1,5 @@
 discard """
-  matrix: "--useVersion:1.6"
+  matrix: "-d:NimMajor=1 -d:NimMinor=6 -d:NimPatch=100"
 """
 
 {.warning[UnusedImport]: off.}
@@ -17,7 +17,7 @@ import std/[
 
   # Strings:
   editdistance, wordwrap, parseutils, ropes,
-  pegs, punycode, strformat, strmisc, strscans, strtabs,
+  pegs, strformat, strmisc, strscans, strtabs,
   strutils, unicode, unidecode,
 
   # Generic operator system services:
diff --git a/tests/system/tvarargslen.nim b/tests/system/tvarargslen.nim
index a129aa5c2..24b54a1e0 100644
--- a/tests/system/tvarargslen.nim
+++ b/tests/system/tvarargslen.nim
@@ -1,8 +1,8 @@
 discard """
   output: '''
-tvarargslen.nim:35:9 (1, 2)
-tvarargslen.nim:36:9 12
-tvarargslen.nim:37:9 1
+tvarargslen.nim:35:2 (1, 2)
+tvarargslen.nim:36:2 12
+tvarargslen.nim:37:2 1
 tvarargslen.nim:38:8 
 done
 '''
diff --git a/tests/template/m1027a.nim b/tests/template/m1027a.nim
new file mode 100644
index 000000000..fad915a2f
--- /dev/null
+++ b/tests/template/m1027a.nim
@@ -0,0 +1 @@
+const version_str* = "mod a"
diff --git a/tests/template/m1027b.nim b/tests/template/m1027b.nim
new file mode 100644
index 000000000..5ff0b9714
--- /dev/null
+++ b/tests/template/m1027b.nim
@@ -0,0 +1 @@
+const version_str* = "mod b"
diff --git a/tests/template/m19277_1.nim b/tests/template/m19277_1.nim
new file mode 100644
index 000000000..840bd4767
--- /dev/null
+++ b/tests/template/m19277_1.nim
@@ -0,0 +1,2 @@
+template foo*(x: untyped) =
+  echo "got: ", x
diff --git a/tests/template/m19277_2.nim b/tests/template/m19277_2.nim
new file mode 100644
index 000000000..de72dad45
--- /dev/null
+++ b/tests/template/m19277_2.nim
@@ -0,0 +1,2 @@
+proc foo*(a: string) =
+  echo "got string: ", a
diff --git a/tests/template/mdotcall.nim b/tests/template/mdotcall.nim
new file mode 100644
index 000000000..fecd8ee26
--- /dev/null
+++ b/tests/template/mdotcall.nim
@@ -0,0 +1,82 @@
+# issue #20073
+
+type Foo = object
+proc foo(f: Foo) = discard
+
+template works*() =
+  var f: Foo
+  foo(f)
+
+template boom*() =
+  var f: Foo
+  f.foo() # Error: attempting to call undeclared routine: 'foo'
+  f.foo # Error: undeclared field: 'foo' for type a.Foo
+
+# issue #7085
+
+proc bar(a: string): string =
+  return a & "bar"
+
+template baz*(a: string): string =
+  var b = a.bar()
+  b
+
+# issue #7223
+
+import mdotcall2
+
+type
+  Bytes* = seq[byte]
+  
+  BytesRange* = object
+    bytes*: Bytes
+    ibegin*, iend*: int
+
+proc privateProc(r: BytesRange): int = r.ibegin
+
+template rangeBeginAddr*(r: BytesRange): pointer =
+  r.bytes.baseAddr.shift(r.privateProc)
+
+# issue #11733
+
+type ObjA* = object
+
+proc foo2(o: var ObjA) = discard
+proc bar2(o: var ObjA, arg: Natural) = discard
+
+template publicTemplateObjSyntax*(o: var ObjA, arg: Natural, doStuff: untyped) =
+  o.foo2()
+  doStuff
+  o.bar2(arg)
+
+# issue #15246
+import os
+
+template sourceBaseName*(): string =
+  bind splitFile
+  instantiationInfo().filename.splitFile().name
+
+# issue #12683
+
+import unicode
+template toRune(s: string): Rune = s.runeAt(0)
+proc heh*[T](x: Slice[T], chars: string) = discard chars.toRune
+
+# issue #7889
+
+from streams import newStringStream, readData, writeData
+
+template bindmeTemplate*(): untyped =
+  var tst = "sometext"
+  var ss = newStringStream("anothertext")
+  ss.writeData(tst[0].addr, 2)
+  discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful
+
+from macros import quote, newIdentNode
+
+macro bindmeQuote*(): untyped =
+  quote do:
+    var tst = "sometext"
+    var ss = newStringStream("anothertext")
+    ss.writeData(tst[0].addr, 2)
+    discard ss.readData(tst[0].addr, 2) # <= comment this out to make compilation successful
diff --git a/tests/template/mdotcall2.nim b/tests/template/mdotcall2.nim
new file mode 100644
index 000000000..e906ac9d6
--- /dev/null
+++ b/tests/template/mdotcall2.nim
@@ -0,0 +1,7 @@
+# imported by mdotcall
+
+proc baseAddr*[T](x: openarray[T]): pointer =
+  cast[pointer](x)
+
+proc shift*(p: pointer, delta: int): pointer =
+  cast[pointer](cast[int](p) + delta)
diff --git a/tests/template/mqualifiedtype1.nim b/tests/template/mqualifiedtype1.nim
new file mode 100644
index 000000000..46569107f
--- /dev/null
+++ b/tests/template/mqualifiedtype1.nim
@@ -0,0 +1,2 @@
+type A* = object
+  x*: int
diff --git a/tests/template/mqualifiedtype2.nim b/tests/template/mqualifiedtype2.nim
new file mode 100644
index 000000000..6a61c14bd
--- /dev/null
+++ b/tests/template/mqualifiedtype2.nim
@@ -0,0 +1,2 @@
+type A* = object
+  x*: array[1000, byte]
diff --git a/tests/template/t1027.nim b/tests/template/t1027.nim
new file mode 100644
index 000000000..fe03c4ea9
--- /dev/null
+++ b/tests/template/t1027.nim
@@ -0,0 +1,22 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t1027.nim(20, 19) Error: ambiguous identifier: 'version_str' -- use one of the following:
+  m1027a.version_str: string
+  m1027b.version_str: string
+'''
+"""
+
+
+
+
+
+
+import m1027a, m1027b
+
+# bug #1027
+template wrap_me(stuff): untyped =
+  echo "Using " & version_str
+
+wrap_me("hey")
diff --git a/tests/template/t11705.nim b/tests/template/t11705.nim
new file mode 100644
index 000000000..65ddc7e6c
--- /dev/null
+++ b/tests/template/t11705.nim
@@ -0,0 +1,17 @@
+type RefObj = ref object
+
+proc `[]`(val: static[int]) = # works with different name/overload or without static arg
+  discard
+
+template noRef*(T: typedesc): typedesc = # works without template indirection
+  typeof(default(T)[])
+
+proc `=destroy`(x: var noRef(RefObj)) =
+  discard
+
+proc foo =
+  var x = new RefObj
+  doAssert $(x[]) == "()"
+
+# bug #11705
+foo()
diff --git a/tests/template/t13426.nim b/tests/template/t13426.nim
new file mode 100644
index 000000000..f7f44749c
--- /dev/null
+++ b/tests/template/t13426.nim
@@ -0,0 +1,87 @@
+discard """
+  cmd: "nim check --hints:off $file"
+  errormsg: ""
+  nimout: '''
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 24) Error: type mismatch: got <int> but expected 'string'
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 17) Error: type mismatch: got <uint, string>
+but expected one of:
+proc `and`(x, y: uint): uint
+  first type mismatch at position: 2
+  required type for y: uint
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint64): uint64
+  first type mismatch at position: 2
+  required type for y: uint64
+  but expression 'high(@[1])' is of type: string
+10 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: 1'u and high(@[1])
+t13426.nim(81, 6) template/generic instantiation of `fun` from here
+t13426.nim(80, 17) Error: expression '' has no type (or is ambiguous)
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 22) Error: type mismatch: got <int> but expected 'string'
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 15) Error: type mismatch: got <int literal(1), string>
+but expected one of:
+proc `and`(x, y: int): int
+  first type mismatch at position: 2
+  required type for y: int
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int16): int16
+  first type mismatch at position: 2
+  required type for y: int16
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int32): int32
+  first type mismatch at position: 2
+  required type for y: int32
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int64): int64
+  first type mismatch at position: 2
+  required type for y: int64
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: int8): int8
+  first type mismatch at position: 2
+  required type for y: int8
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint): uint
+  first type mismatch at position: 2
+  required type for y: uint
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint16): uint16
+  first type mismatch at position: 2
+  required type for y: uint16
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint32): uint32
+  first type mismatch at position: 2
+  required type for y: uint32
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint64): uint64
+  first type mismatch at position: 2
+  required type for y: uint64
+  but expression 'high(@[1])' is of type: string
+proc `and`(x, y: uint8): uint8
+  first type mismatch at position: 2
+  required type for y: uint8
+  but expression 'high(@[1])' is of type: string
+2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
+
+expression: 1 and high(@[1])
+t13426.nim(87, 6) template/generic instantiation of `fun` from here
+t13426.nim(86, 15) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+# bug # #13426
+block:
+  template bar(t): string = high(t)
+  proc fun[A](key: A) =
+    var h = 1'u and bar(@[1])
+  fun(0)
+
+block:
+  template bar(t): string = high(t)
+  proc fun[A](key: A) =
+    var h = 1 and bar(@[1])
+  fun(0)
diff --git a/tests/template/t13515.nim b/tests/template/t13515.nim
deleted file mode 100644
index ffebedcbe..000000000
--- a/tests/template/t13515.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  action: compile
-"""
-
-template test: bool = true
-
-# compiles:
-if not test:
-  echo "wtf"
-
-# does not compile:
-template x =
-  if not test:
-    echo "wtf"
-
-x
diff --git a/tests/template/t19149.nim b/tests/template/t19149.nim
new file mode 100644
index 000000000..631e8fc30
--- /dev/null
+++ b/tests/template/t19149.nim
@@ -0,0 +1,19 @@
+type Foo = tuple[active: bool, index: int]
+
+
+var f: Foo
+
+# template result type during match stage
+# f:var Foo
+# a:Foo
+# tyVar
+# tyTuple
+# after change to proc
+# f:Foo
+# a:Foo
+# tyTuple
+# tyTuple
+
+template cursor(): var Foo = f
+discard cursor()
+
diff --git a/tests/template/t19277.nim b/tests/template/t19277.nim
new file mode 100644
index 000000000..16435a09c
--- /dev/null
+++ b/tests/template/t19277.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''
+got: 0
+'''
+"""
+
+# issue #19277
+
+import m19277_1, m19277_2
+
+template injector(val: untyped): untyped =
+  template subtemplate: untyped = val
+  subtemplate()
+
+template methodCall(val: untyped): untyped = val
+
+{.push raises: [Defect].}
+
+foo(injector(0).methodCall())
diff --git a/tests/template/t19700.nim b/tests/template/t19700.nim
new file mode 100644
index 000000000..cc2944944
--- /dev/null
+++ b/tests/template/t19700.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "type mismatch: got <Obj, Obj, template (x: untyped, y: untyped): untyped>"
+"""
+
+type Obj = object
+
+proc apply[T, R](a, b: T; f: proc(x, y: T): R): R = f(a, b)
+
+let a, b = Obj()
+discard apply(a, b, `!=`)
diff --git a/tests/template/t21231.nim b/tests/template/t21231.nim
new file mode 100644
index 000000000..51e96cdd6
--- /dev/null
+++ b/tests/template/t21231.nim
@@ -0,0 +1,10 @@
+discard """
+  errormsg: "undeclared identifier: 'x'"
+"""
+
+var x: int
+# bug #21231
+template f(y: untyped) = echo y.x
+for i in 1 .. 10:
+  x = i
+  f(system)
diff --git a/tests/template/t21532.nim b/tests/template/t21532.nim
new file mode 100644
index 000000000..3193b0dc3
--- /dev/null
+++ b/tests/template/t21532.nim
@@ -0,0 +1,8 @@
+
+template elementType(a: untyped): typedesc =
+  typeof(block: (for ai in a: ai))
+
+func fn[T](a: T) =
+  doAssert elementType(a) is int
+
+@[1,2,3].fn
\ No newline at end of file
diff --git a/tests/template/t24112.nim b/tests/template/t24112.nim
new file mode 100644
index 000000000..175fc7d5e
--- /dev/null
+++ b/tests/template/t24112.nim
@@ -0,0 +1,19 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj --hints:off"
+  action: reject
+"""
+
+# issue #24112, needs --experimental:openSym disabled
+
+block: # simplified
+  type
+    SomeObj = ref object # Doesn't error if you make SomeObj be non-ref
+  template foo = yield SomeObj()
+  when compiles(foo): discard
+
+import std/asyncdispatch
+block:
+  proc someProc(): Future[void] {.async.} = discard
+  proc foo() =
+    await someProc() #[tt.Error
+                  ^ Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead]#
diff --git a/tests/template/taliassyntax.nim b/tests/template/taliassyntax.nim
new file mode 100644
index 000000000..2d8237d93
--- /dev/null
+++ b/tests/template/taliassyntax.nim
@@ -0,0 +1,74 @@
+type Foo = object
+  bar: int
+
+var foo = Foo(bar: 10)
+template bar: int = foo.bar
+doAssert bar == 10
+bar = 15
+doAssert bar == 15
+var foo2 = Foo(bar: -10)
+doAssert bar == 15
+# works in generics
+proc genericProc[T](x: T): string =
+  $(x, bar)
+doAssert genericProc(true) == "(true, 15)"
+# redefine
+template bar: int {.redefine.} = foo2.bar
+doAssert bar == -10
+
+block: # subscript
+  var bazVal = @[1, 2, 3]
+  template baz: seq[int] = bazVal
+  doAssert baz[1] == 2
+  proc genericProc2[T](x: T): string =
+    result = $(x, baz[1])
+    baz[1] = 7
+  doAssert genericProc2(true) == "(true, 2)"
+  doAssert baz[1] == 7
+  baz[1] = 14
+  doAssert baz[1] == 14
+
+block: # type alias
+  template Int2: untyped = int
+  let x: Int2 = 123
+  proc generic[T](): string =
+    template U: untyped = T
+    var x: U
+    result = $typeof(x)
+    doAssert result == $U
+    doAssert result == $T
+  doAssert generic[int]() == "int"
+  doAssert generic[Int2]() == "int"
+  doAssert generic[string]() == "string"
+  doAssert generic[seq[int]]() == "seq[int]"
+  doAssert generic[seq[Int2]]() == "seq[int]"
+  discard generic[123]()
+  proc genericStatic[X; T: static[X]](): string =
+    template U: untyped = T
+    result = $U
+    doAssert result == $T
+  doAssert genericStatic[int, 123]() == "123"
+  doAssert genericStatic[Int2, 123]() == "123"
+  doAssert genericStatic[(string, bool), ("a", true)]() == "(\"a\", true)"
+
+block: # issue #13515
+  template test: bool = true
+  # compiles:
+  if not test:
+    doAssert false
+  # does not compile:
+  template x =
+    if not test:
+      doAssert false
+  x
+
+import macros
+
+block: # issue #21727
+  template debugAnnotation(s: typed): string =
+    astToStr s
+
+  macro cpsJump(x: int): untyped =
+    result = newLit(debugAnnotation(cpsJump))
+  
+  doAssert cpsJump(13) == "cpsJump"
diff --git a/tests/template/taliassyntaxerrors.nim b/tests/template/taliassyntaxerrors.nim
new file mode 100644
index 000000000..f16acaf91
--- /dev/null
+++ b/tests/template/taliassyntaxerrors.nim
@@ -0,0 +1,28 @@
+discard """
+  cmd: "nim check --hints:off $file"
+"""
+
+block: # with params
+  type Foo = object
+    bar: int
+
+  var foo = Foo(bar: 10)
+  template bar(x: int): int = x + foo.bar
+  let a = bar #[tt.Error
+      ^ invalid type: 'template (x: int): int' for let. Did you mean to call the template with '()'?]#
+  bar = 15 #[tt.Error
+  ^ 'bar' cannot be assigned to]#
+
+block: # generic template
+  type Foo = object
+    bar: int
+
+  var foo = Foo(bar: 10)
+  template bar[T]: T = T(foo.bar)
+  let a = bar #[tt.Error
+      ^ invalid type: 'template (): T' for let. Did you mean to call the template with '()'?; tt.Error
+          ^ 'bar' has unspecified generic parameters]#
+  let b = bar[float]()
+  doAssert b == 10.0
+  bar = 15 #[tt.Error
+  ^ 'bar' cannot be assigned to]#
diff --git a/tests/template/tcallsitelineinfo.nim b/tests/template/tcallsitelineinfo.nim
new file mode 100644
index 000000000..5fed93363
--- /dev/null
+++ b/tests/template/tcallsitelineinfo.nim
@@ -0,0 +1,17 @@
+discard """
+  nimout: '''
+tcallsitelineinfo.nim(17, 4) Warning: abc [User]
+'''
+  exitcode: 1
+  outputsub: '''
+tcallsitelineinfo.nim(17) tcallsitelineinfo
+Error: unhandled exception: def [ValueError]
+'''
+"""
+
+template foo(): untyped {.callsite.} =
+  {.warning: "abc".}
+  raise newException(ValueError, "def")
+  echo "hello"
+
+foo()
diff --git a/tests/template/tcallsitelineinfo2.nim b/tests/template/tcallsitelineinfo2.nim
new file mode 100644
index 000000000..d5f257474
--- /dev/null
+++ b/tests/template/tcallsitelineinfo2.nim
@@ -0,0 +1,20 @@
+discard """
+  nimout: '''
+tcallsitelineinfo2.nim(18, 1) Warning: abc [User]
+tcallsitelineinfo2.nim(19, 12) Warning: def [User]
+'''
+  exitcode: 1
+  outputsub: '''
+tcallsitelineinfo2.nim(20) tcallsitelineinfo2
+Error: unhandled exception: ghi [ValueError]
+'''
+"""
+
+template foo(a: untyped): untyped {.callsite.} =
+  {.warning: "abc".}
+  a
+  echo "hello"
+
+foo: # with `{.line.}:`, the following do not keep their line information:
+  {.warning: "def".}
+  raise newException(ValueError, "ghi")
diff --git a/tests/template/tdefaultparam.nim b/tests/template/tdefaultparam.nim
new file mode 100644
index 000000000..7ea0b2b25
--- /dev/null
+++ b/tests/template/tdefaultparam.nim
@@ -0,0 +1,56 @@
+block:
+  template foo(a: untyped, b: untyped = a(0)): untyped =
+    let x = a(0)
+    let y = b
+    (x, y)
+  proc bar(x: int): int = x + 1
+  doAssert foo(bar, b = bar(0)) == (1, 1)
+  doAssert foo(bar) == (1, 1)
+
+block: # issue #23506
+  var a: string
+  template foo(x: int; y = x) =
+    a = $($x, $y)
+  foo(1)
+  doAssert a == "(\"1\", \"1\")"
+
+block: # untyped params with default value
+  macro foo(x: typed): untyped =
+    result = x
+  template test(body: untyped, alt: untyped = (;), maxTries = 3): untyped {.foo.} =
+    body
+    alt
+  var s = "a"
+  test:
+    s.add "b"
+  do:
+    s.add "c"
+  doAssert s == "abc"
+  template test2(body: untyped, alt: untyped = s.add("e"), maxTries = 3): untyped =
+    body
+    alt
+  test2:
+    s.add "d"
+  doAssert s == "abcde"
+  template test3(body: untyped = willNotCompile) =
+    discard
+  test3()
+
+block: # typed params with `void` default value
+  macro foo(x: typed): untyped =
+    result = x
+  template test(body: untyped, alt: typed = (;), maxTries = 3): untyped {.foo.} =
+    body
+    alt
+  var s = "a"
+  test:
+    s.add "b"
+  do:
+    s.add "c"
+  doAssert s == "abc"
+  template test2(body: untyped, alt: typed = s.add("e"), maxTries = 3): untyped =
+    body
+    alt
+  test2:
+    s.add "d"
+  doAssert s == "abcde"
diff --git a/tests/template/tdotcall.nim b/tests/template/tdotcall.nim
new file mode 100644
index 000000000..dc97fd52e
--- /dev/null
+++ b/tests/template/tdotcall.nim
@@ -0,0 +1,32 @@
+import mdotcall
+
+block: # issue #20073
+  works()
+  boom()
+
+block: # issue #7085
+  doAssert baz("hello") == "hellobar"
+  doAssert baz"hello" == "hellobar"
+  doAssert "hello".baz == "hellobar"
+
+block: # issue #7223
+  var r = BytesRange(bytes: @[1.byte, 2, 3], ibegin: 0, iend: 2)
+  var a = r.rangeBeginAddr
+
+block: # issue #11733
+  var a: ObjA
+  var evaluated = false
+  a.publicTemplateObjSyntax(42): evaluated = true
+  doAssert evaluated
+
+block: # issue #15246
+  doAssert sourceBaseName() == "tdotcall"
+
+block: # issue #12683
+  heh(0..40, "|")
+
+block: # issue #7889
+  if false:
+    bindmeQuote()
+  if false:
+    bindmeTemplate()
diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim
index 1fed694ef..58c40941d 100644
--- a/tests/template/template_issues.nim
+++ b/tests/template/template_issues.nim
@@ -296,3 +296,9 @@ block: # bug #12595
     discard {i: ""}
 
   test()
+
+block: # bug #21920
+  template t[T](): T =
+    discard
+
+  t[void]() # Error: expression has no type: discard
diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim
index 6b1290fb9..2088b1739 100644
--- a/tests/template/template_various.nim
+++ b/tests/template/template_various.nim
@@ -353,3 +353,54 @@ block gensym3:
   echo a ! b ! c ! d
   echo a ! b ! c ! d ! e
   echo x,y,z
+
+block: # issue #2465
+  template t() =
+    template declX(str: string) {.gensym.} =
+      var x {.inject.} : string = str
+
+  t()
+  doAssert not declared(declX)
+  doAssert not compiles(declX("a string"))
+
+  template t2() =
+    template fooGensym() {.gensym.} =
+      echo 42
+
+  t2()
+  doAssert not declared(fooGensym)
+  doAssert not compiles(fooGensym())
+
+
+block identifier_construction_with_overridden_symbol:
+  # could use add, but wanna make sure it's an override no matter what
+  func examplefn = discard
+  func examplefn(x: int) = discard
+
+  # the function our template wants to use
+  func examplefn1 = discard
+
+  template exampletempl(n) =
+    # attempt to build a name using the overridden symbol "examplefn"
+    `examplefn n`()
+
+  exampletempl(1)
+
+import typetraits
+
+block: # issue #4596
+  type
+    T0 = object
+    T1 = object
+
+  template printFuncsT() =
+    proc getV[A](a: typedesc[A]): string =
+      var s {. global .} = name(A)
+      return s
+
+  printFuncsT()
+
+  doAssert getV(T1) == "T1"
+  doAssert getV(T0) == "T0"
+  doAssert getV(T0) == "T0"
+  doAssert getV(T1) == "T1"
diff --git a/tests/template/tgenericparam.nim b/tests/template/tgenericparam.nim
new file mode 100644
index 000000000..becf75d36
--- /dev/null
+++ b/tests/template/tgenericparam.nim
@@ -0,0 +1,93 @@
+block: # basic template generic parameter substitution
+  block: # issue #13527
+    template typeNameTempl[T](a: T): string = $T
+    proc typeNameProc[T](a: T): string = $T
+    doAssert typeNameTempl(1) == typeNameProc(1)
+    doAssert typeNameTempl(true) == typeNameProc(true)
+    doAssert typeNameTempl(1.0) == typeNameProc(1.0)
+    doAssert typeNameTempl(1u8) == typeNameProc(1u8)
+
+    template isDefault[T](a: T): bool = a == default(T)
+    doAssert isDefault(0.0)
+
+  block: # issue #17240
+    func to(c: int, t: typedesc[float]): t = discard
+    template converted[I, T](i: seq[I], t: typedesc[T]): seq[T] =
+      var result = newSeq[T](2)
+      result[0] = i[0].to(T)
+      result
+    doAssert newSeq[int](3).converted(float) == @[0.0, 0.0]
+
+  block: # issue #6340
+    type A[T] = object
+      v: T
+    proc foo(x: int): string = "int"
+    proc foo(x: typedesc[int]): string = "typedesc[int]"
+    template fooT(x: int): string = "int"
+    template fooT(x: typedesc[int]): string = "typedesc[int]"
+    proc foo[T](x: A[T]): (string, string) =
+      (foo(T), fooT(T))
+    template fooT[T](x: A[T]): (string, string) =
+      (foo(T), fooT(T))
+    var x: A[int]
+    doAssert foo(x) == fooT(x)
+
+  block: # issue #20033
+    template run[T](): T = default(T)
+    doAssert run[int]() == 0
+
+import options, tables, typetraits
+
+block: # complex cases of above with imports
+  block: # issue #19576, complex case
+    type RegistryKey = object
+      key, val: string
+    var regKey = @[RegistryKey(key: "abc", val: "def")]
+    template findFirst[T](s: seq[T], pred: proc(x: T): bool): Option[T] =
+      var res = none(T) # important line
+      for x in s:
+        if pred(x):
+          res = some(x)
+          break
+      res
+    proc getval(searchKey: string): Option[string] =
+      let found = regKey.findFirst(proc (rk: RegistryKey): bool = rk.key == searchKey)
+      if found.isNone: none(string)
+      else: some(found.get().val)
+    doAssert getval("strange") == none(string)
+    doAssert getval("abc") == some("def")
+  block: # issue #19076
+    block: # case 1
+      var tested: Table[string,int]
+      template `[]`[V](t:Table[string,V],key:string):untyped =
+        $V
+      doAssert tested["abc"] == "int"
+      template `{}`[V](t:Table[string,V],key:string):untyped =
+        ($V, tables.`[]`(t, key))
+      doAssert (try: tested{"abc"} except KeyError: ("not there", 123)) == ("not there", 123)
+      tables.`[]=`(tested, "abc", 456)
+      doAssert tested["abc"] == "int"
+      doAssert tested{"abc"} == ("int", 456)
+    block: # case 2
+      type Foo[A,T] = object
+        t:T
+      proc init[A,T](f:type Foo,a:typedesc[A],t:T):Foo[A,T] = Foo[A,T](t:t)
+      template fromOption[A](o:Option[A]):auto =
+        when o.isSome:
+          Foo.init(A,35)
+        else:
+          Foo.init(A,"hi")
+      let op = fromOption(some(5))
+  block: # issue #7461
+    template p[T](): untyped = none(T)
+    doAssert p[int]() == none(int)
+  block: # issue #7995
+    var res: string
+    template copyRange[T](dest: seq[T], destOffset: int) =
+      when supportsCopyMem(T):
+        res = "A"
+      else:    
+        res = "B"
+    var a = @[1, 2, 3]
+    copyRange(a, 0)
+    doAssert res == "A"
diff --git a/tests/template/tgensymhijack.nim b/tests/template/tgensymhijack.nim
new file mode 100644
index 000000000..72ff3d495
--- /dev/null
+++ b/tests/template/tgensymhijack.nim
@@ -0,0 +1,37 @@
+# issue #23326
+
+type Result*[E] = object
+  e*: E
+
+proc error*[E](v: Result[E]): E = discard
+
+template valueOr*[E](self: Result[E], def: untyped): int =
+  when E isnot void:
+    when false:
+      # Comment line below to make it work
+      template error(): E {.used, gensym.} = s.e
+      discard
+    else:
+      template error(): E {.used, inject.} =
+        self.e
+
+      def
+  else:
+    def
+
+
+block:
+  let rErr = Result[string](e: "a")
+  let rErrV = rErr.valueOr:
+    ord(error[0])
+
+block:
+  template foo(x: static bool): untyped =
+    when x:
+      let a = 123
+    else:
+      template a: untyped {.gensym.} = 456
+    a
+  
+  doAssert foo(false) == 456
+  doAssert foo(true) == 123
diff --git a/tests/misc/tidentconcatenations.nim b/tests/template/tidentconcatenations.nim
index ddd2e49cc..ddd2e49cc 100644
--- a/tests/misc/tidentconcatenations.nim
+++ b/tests/template/tidentconcatenations.nim
diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim
index 1f15fb13e..56e0d02df 100644
--- a/tests/template/tinnerouterproc.nim
+++ b/tests/template/tinnerouterproc.nim
@@ -6,3 +6,15 @@ block: # #20002
     discard 3.bar # evaluates to 10 but only check if it compiles for now
   block:
     foo()
+
+block: # issue #23813
+  template r(body: untyped) =
+    proc x() {.gensym.} =
+      body
+  template g() =
+    r:
+      let y = 0
+    r:
+      proc y() = discard
+      y()
+  g()
diff --git a/tests/template/tnested.nim b/tests/template/tnested.nim
new file mode 100644
index 000000000..81e416a76
--- /dev/null
+++ b/tests/template/tnested.nim
@@ -0,0 +1,38 @@
+block: # issue #22775
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = v.h()
+    p()
+  let a = @[0]
+  k(0 and not a[0])
+
+block: # issue #22775 case 2
+  proc h(c: int, q: int) = discard
+  template k(v: int) =
+    template p() = h(v, v)
+    p()
+  let a = [0]
+  k(0 and not a[0])
+
+block: # issue #22775 minimal cases
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = h(v)
+    p()
+  let a = [0]
+  k(not a[0])
+  block:
+    k(-a[0])
+  block:
+    proc f(x: int): int = x
+    k(f a[0])
+
+block: # bracket assignment case of above tests
+  proc h(c: int) = discard
+  template k(v: int) =
+    template p() = h(v)
+    p()
+  var a = [0]
+  k(not (block:
+    a[0] = 1
+    1))
diff --git a/tests/template/tobjectdeclfield.nim b/tests/template/tobjectdeclfield.nim
new file mode 100644
index 000000000..afce2cae8
--- /dev/null
+++ b/tests/template/tobjectdeclfield.nim
@@ -0,0 +1,21 @@
+block: # issue #16005
+  var x = 0
+
+  block:
+    type Foo = object
+      x: float # ok
+
+  template main() =
+    block:
+      type Foo = object
+        x: float # Error: cannot use symbol of kind 'var' as a 'field'
+
+  main()
+
+block: # issue #19552
+  template test =
+    type
+      test2 = ref object
+        reset: int
+
+  test()
diff --git a/tests/template/topensym.nim b/tests/template/topensym.nim
new file mode 100644
index 000000000..2f930407b
--- /dev/null
+++ b/tests/template/topensym.nim
@@ -0,0 +1,209 @@
+{.experimental: "openSym".}
+
+block: # issue #24002
+  type Result[T, E] = object
+  func value[T, E](self: Result[T, E]): T {.inline.} =
+    discard
+  func value[T: not void, E](self: var Result[T, E]): var T {.inline.} =
+    discard
+  template unrecognizedFieldWarning =
+    doAssert value == 123
+    let x = value
+    doAssert value == x
+  proc readValue(value: var int) =
+    unrecognizedFieldWarning()
+  var foo: int = 123
+  readValue(foo)
+
+block: # issue #22605 for templates, normal call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = valueOr 123:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g(int) == "good"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = valueOr 123:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605 for templates, method call syntax
+  const error = "bad"
+
+  template valueOr(self: int, def: untyped): untyped =
+    case false
+    of true: ""
+    of false:
+      template error: untyped {.used, inject.} = "good"
+      def
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = 123.valueOr:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g(int) == "good"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = 123.valueOr:
+      res = $error
+      "dummy"
+    res
+
+  doAssert g2(int) == "bad"
+
+block: # issue #22605 for templates, original complex example
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate*: bool
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate*: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate*: T
+        else:
+          case oResultPrivate*: bool
+          of false:
+            eResultPrivate*: E
+          of true:
+            vResultPrivate*: T
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+  template g(T: type): string =
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+
+  doAssert g(int) == "f"
+
+  template g2(T: type): string =
+    bind error # use the bad version on purpose
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+
+  doAssert g2(int) == "error"
+
+block: # issue #23865 for templates
+  type Xxx = enum
+    error
+    value
+
+  type
+    Result[T, E] = object
+      when T is void:
+        when E is void:
+          oResultPrivate: bool
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            discard
+      else:
+        when E is void:
+          case oResultPrivate: bool
+          of false:
+            discard
+          of true:
+            vResultPrivate: T
+        else:
+          case oResultPrivate: bool
+          of false:
+            eResultPrivate: E
+          of true:
+            vResultPrivate: T
+
+  func error[T, E](self: Result[T, E]): E =
+    ## Fetch error of result if set, or raise Defect
+    case self.oResultPrivate
+    of true:
+      when T isnot void:
+        raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
+      else:
+        raiseResultDefect("Trying to access error when value is set")
+    of false:
+      when E isnot void:
+        self.eResultPrivate
+
+  template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+    let s = (self) # TODO avoid copy
+    case s.oResultPrivate
+    of true:
+      s.vResultPrivate
+    of false:
+      when E isnot void:
+        template error: untyped {.used, inject.} = s.eResultPrivate
+      def
+  proc f(): Result[int, cstring] =
+    Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+  template g(T: type): string =
+    var res = "ok"
+    let x = f().valueOr:
+      res = $error
+      123
+    res
+  doAssert g(int) == "f"
+
+import std/sequtils
+
+block: # issue #15314
+  var it: string
+  var nums = @[1,2,3]
+
+  template doubleNums() =
+    nums.applyIt(it * 2)
+
+  doubleNums()
+  doAssert nums == @[2, 4, 6]
diff --git a/tests/template/topensymoverride.nim b/tests/template/topensymoverride.nim
new file mode 100644
index 000000000..3d4bb59f1
--- /dev/null
+++ b/tests/template/topensymoverride.nim
@@ -0,0 +1,39 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+const value = "captured"
+template fooOld(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  body
+template foo(x: int, body: untyped): untyped =
+  let value {.inject.} = "injected"
+  {.push experimental: "genericsOpenSym".}
+  body
+  {.pop.}
+
+proc old[T](): string =
+  fooOld(123):
+    return value
+doAssert old[int]() == "captured"
+
+template oldTempl(): string =
+  block:
+    var res: string
+    fooOld(123):
+      res = value
+    res
+doAssert oldTempl() == "captured"
+
+proc bar[T](): string =
+  foo(123):
+    return value
+doAssert bar[int]() == "injected"
+
+template barTempl(): string =
+  block:
+    var res: string
+    foo(123):
+      res = value
+    res
+doAssert barTempl() == "injected"
diff --git a/tests/template/topensymwarning.nim b/tests/template/topensymwarning.nim
new file mode 100644
index 000000000..0bbe0a9fb
--- /dev/null
+++ b/tests/template/topensymwarning.nim
@@ -0,0 +1,60 @@
+discard """
+  matrix: "--skipParentCfg --filenames:legacyRelProj"
+"""
+
+type Xxx = enum
+  error
+  value
+
+type
+  Result[T, E] = object
+    when T is void:
+      when E is void:
+        oResultPrivate*: bool
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          discard
+    else:
+      when E is void:
+        case oResultPrivate*: bool
+        of false:
+          discard
+        of true:
+          vResultPrivate*: T
+      else:
+        case oResultPrivate*: bool
+        of false:
+          eResultPrivate*: E
+        of true:
+          vResultPrivate*: T
+
+template valueOr[T: not void, E](self: Result[T, E], def: untyped): untyped =
+  let s = (self) # TODO avoid copy
+  case s.oResultPrivate
+  of true:
+    s.vResultPrivate
+  of false:
+    when E isnot void:
+      template error: untyped {.used, inject.} = s.eResultPrivate
+    def
+
+proc f(): Result[int, cstring] =
+  Result[int, cstring](oResultPrivate: false, eResultPrivate: "f")
+
+template g(T: type): string =
+  var res = "ok"
+  let x = f().valueOr:
+    {.push warningAsError[IgnoredSymbolInjection]: on.}
+    # test spurious error
+    discard true
+    let _ = f
+    {.pop.}
+    res = $error #[tt.Warning
+           ^ a new symbol 'error' has been injected during template or generic instantiation, however 'error' [enumField declared in topensymwarning.nim(6, 3)] captured at the proc declaration will be used instead; either enable --experimental:openSym to use the injected symbol, or `bind` this captured symbol explicitly [IgnoredSymbolInjection]]#
+    123
+  res
+
+discard g(int)
diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim
index b68d7e253..b559c2d9e 100644
--- a/tests/template/tparams_gensymed.nim
+++ b/tests/template/tparams_gensymed.nim
@@ -16,6 +16,7 @@ wth
 (total: 6)
 S1
 5
+abc
 '''
 """
 # bug #1915
@@ -394,3 +395,11 @@ proc chunkedReadLoop2 =
   test2
 
 test1(); test2()
+
+block: # bug #22846
+  template foo2(x: proc (y: string)) =
+    let f = x
+    f("abc")
+
+  foo2(proc (y: string) = echo y)
+
diff --git a/tests/template/tqualifiedident.nim b/tests/template/tqualifiedident.nim
new file mode 100644
index 000000000..463b14ee7
--- /dev/null
+++ b/tests/template/tqualifiedident.nim
@@ -0,0 +1,8 @@
+block: # issue #19865
+  template f() = discard default(system.int)
+  f()
+
+# issue #21221, same as above
+type M = object
+template r() = discard default(tqualifiedident.M)
+r()
diff --git a/tests/template/tqualifiedtype.nim b/tests/template/tqualifiedtype.nim
new file mode 100644
index 000000000..6497af6ee
--- /dev/null
+++ b/tests/template/tqualifiedtype.nim
@@ -0,0 +1,25 @@
+# issue #19866
+
+# Switch module import order to switch which of last two
+# doAsserts fails
+import mqualifiedtype1
+import mqualifiedtype2
+
+# this isn't officially supported but needed to point out the issue:
+template f(moduleName: untyped): int = sizeof(`moduleName`.A)
+template g(someType:   untyped): int = sizeof(someType)
+
+# These are legitimately true.
+doAssert sizeof(mqualifiedtype1.A) != sizeof(mqualifiedtype2.A)
+doAssert g(mqualifiedtype1.A) != g(mqualifiedtype2.A)
+
+# Which means that this should not be true, but is in Nim 1.6
+doAssert f(`mqualifiedtype1`) != f(`mqualifiedtype2`)
+doAssert f(mqualifiedtype1) != f(mqualifiedtype2)
+
+# These should be true, but depending on import order, exactly one
+# fails in Nim 1.2, 1.6 and devel.
+doAssert f(`mqualifiedtype1`) == g(mqualifiedtype1.A)
+doAssert f(`mqualifiedtype2`) == g(mqualifiedtype2.A)
+doAssert f(mqualifiedtype1) == g(mqualifiedtype1.A)
+doAssert f(mqualifiedtype2) == g(mqualifiedtype2.A)
diff --git a/tests/template/tredefinition_override.nim b/tests/template/tredefinition_override.nim
index 0bda4025b..7ae232bba 100644
--- a/tests/template/tredefinition_override.nim
+++ b/tests/template/tredefinition_override.nim
@@ -1,4 +1,4 @@
-{.push warningAsError[TemplateRedefinition]: on.}
+{.push warningAsError[ImplicitTemplateRedefinition]: on.}
 
 doAssert not (compiles do:
   template foo(): int = 1
diff --git a/tests/template/tunderscore1.nim b/tests/template/tunderscore1.nim
new file mode 100644
index 000000000..d74e5ba63
--- /dev/null
+++ b/tests/template/tunderscore1.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "the special identifier '_' is ignored in declarations and cannot be used"
+"""
+
+# issue #12094, #13804
+
+template foo =
+  let _ = 1
+  echo _
+
+foo()
diff --git a/tests/template/twrongmapit.nim b/tests/template/twrongmapit.nim
index 13f53d119..2d53d03f5 100644
--- a/tests/template/twrongmapit.nim
+++ b/tests/template/twrongmapit.nim
@@ -1,9 +1,6 @@
 discard """
-  disabled: true
-  output: "####"
+  joinable: false
 """
-# unfortunately our tester doesn't support multiple lines of compiler
-# error messages yet...
 
 # bug #1562
 type Foo* {.pure, final.} = object
@@ -30,4 +27,4 @@ import sequtils
 
 (var i = @[""];i).applyIt(it)
 # now works:
-echo "##", i[0], "##"
+doAssert i[0] == ""
diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims
index 5fc77f6e4..32b7d1416 100644
--- a/tests/test_nimscript.nims
+++ b/tests/test_nimscript.nims
@@ -6,7 +6,9 @@
 from stdtest/specialpaths import buildDir
 
 when defined(nimPreviewSlimSystem):
-  import std/syncio
+  import std/[
+    syncio, assertions, formatfloat, objectdollar, widestrs
+  ]
 
 import std/[
   # Core:
@@ -24,7 +26,7 @@ import std/[
 
   # Strings:
   editdistance, wordwrap, parseutils, ropes,
-  pegs, punycode, strformat, strmisc, strscans, strtabs,
+  pegs, strformat, strmisc, strscans, strtabs,
   strutils, unicode, unidecode, cstrutils,
   # works but uses FFI: encodings
 
@@ -62,7 +64,7 @@ import std/[
   htmlgen,
 
   # Hashing:
-  base64, hashes, md5,
+  base64, hashes,
   # fails due to cstring cast/times import/endians import: oids
   # fails due to copyMem/endians import: sha1
 
diff --git a/tests/threads/t7172.nim b/tests/threads/t7172.nim
index 983765dba..87e89417b 100644
--- a/tests/threads/t7172.nim
+++ b/tests/threads/t7172.nim
@@ -1,15 +1,17 @@
 discard """
+  disabled: i386
   output: '''
 In doStuff()
 In initProcess()
-initProcess() done
 TEST
+initProcess() done
 Crashes before getting here!
 '''
   joinable: false
 """
 
 import std/os
+import std/typedthreads
 
 proc whatever() {.thread, nimcall.} =
   echo("TEST")
@@ -18,8 +20,8 @@ proc initProcess(): void =
   echo("In initProcess()")
   var thread: Thread[void]
   createThread(thread, whatever)
-  echo("initProcess() done")
   joinThread(thread)
+  echo("initProcess() done")
 
 proc doStuff(): void =
   echo("In doStuff()")
diff --git a/tests/threads/t8535.nim b/tests/threads/t8535.nim
index 1af11f9ad..a4296df11 100644
--- a/tests/threads/t8535.nim
+++ b/tests/threads/t8535.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''0
 hello'''
 """
diff --git a/tests/threads/threadex.nim b/tests/threads/threadex.nim
index 50a1a4d34..90119aee7 100644
--- a/tests/threads/threadex.nim
+++ b/tests/threads/threadex.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   outputsub: "Just a simple text for test"
 """
 
diff --git a/tests/threads/tjsthreads.nim b/tests/threads/tjsthreads.nim
index 2a8ff60fb..5122c9cd6 100644
--- a/tests/threads/tjsthreads.nim
+++ b/tests/threads/tjsthreads.nim
@@ -1,5 +1,5 @@
 discard """
-  targets: "c cpp js"
+  targets: "js"
   matrix: "--threads:on"
 """
 
diff --git a/tests/threads/tmanyjoin.nim b/tests/threads/tmanyjoin.nim
index aca284965..af5bc460e 100644
--- a/tests/threads/tmanyjoin.nim
+++ b/tests/threads/tmanyjoin.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   outputsub: "129"
 """
 
diff --git a/tests/threads/tmembug.nim b/tests/threads/tmembug.nim
new file mode 100644
index 000000000..3618f0ecc
--- /dev/null
+++ b/tests/threads/tmembug.nim
@@ -0,0 +1,51 @@
+
+import std / [atomics, strutils, sequtils]
+
+type
+  BackendMessage* = object
+    field*: seq[int]
+
+var
+  chan1: Channel[BackendMessage]
+  chan2: Channel[BackendMessage]
+
+chan1.open()
+chan2.open()
+
+proc routeMessage*(msg: BackendMessage) =
+  discard chan2.trySend(msg)
+
+var
+  recv: Thread[void]
+  stopToken: Atomic[bool]
+
+proc recvMsg() =
+  while not stopToken.load(moRelaxed):
+    let resp = chan1.tryRecv()
+    if resp.dataAvailable:
+      routeMessage(resp.msg)
+      echo "child consumes ", formatSize getOccupiedMem()
+
+createThread[void](recv, recvMsg)
+
+const MESSAGE_COUNT = 100
+
+proc main() =
+  let msg: BackendMessage = BackendMessage(field: (0..500).toSeq())
+  for j in 0..0: #100:
+    echo "New iteration"
+
+    for _ in 1..MESSAGE_COUNT:
+      chan1.send(msg)
+    echo "After sending"
+
+    var counter = 0
+    while counter < MESSAGE_COUNT:
+      let resp = recv(chan2)
+      counter.inc
+    echo "After receiving ", formatSize getOccupiedMem()
+
+  stopToken.store true, moRelaxed
+  joinThreads(recv)
+
+main()
diff --git a/tests/threads/tonthreadcreation.nim b/tests/threads/tonthreadcreation.nim
index 0652d79f8..61529477d 100644
--- a/tests/threads/tonthreadcreation.nim
+++ b/tests/threads/tonthreadcreation.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   matrix: "--mm:refc; --mm:orc --deepcopy:on"
   output: '''some string here
 dying some string here'''
diff --git a/tests/threads/tracy_allocator.nim b/tests/threads/tracy_allocator.nim
index e8f0ec927..f3b39f4dc 100644
--- a/tests/threads/tracy_allocator.nim
+++ b/tests/threads/tracy_allocator.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   output: '''true'''
 """
 
diff --git a/tests/threads/treusetvar.nim b/tests/threads/treusetvar.nim
index 2b0017595..f0337801a 100644
--- a/tests/threads/treusetvar.nim
+++ b/tests/threads/treusetvar.nim
@@ -1,4 +1,5 @@
 discard """
+  disabled: i386
   outputsub: "65"
 """
 
diff --git a/tests/threads/tthreadvars.nim b/tests/threads/tthreadvars.nim
index 81aa2e5ec..745e3562c 100644
--- a/tests/threads/tthreadvars.nim
+++ b/tests/threads/tthreadvars.nim
@@ -1,4 +1,5 @@
 discard """
+disabled: i386
 output: '''
 10
 1111
diff --git a/tests/threads/ttryrecv.nim b/tests/threads/ttryrecv.nim
index cadf6c550..fcff94e78 100644
--- a/tests/threads/ttryrecv.nim
+++ b/tests/threads/ttryrecv.nim
@@ -1,4 +1,5 @@
 discard """
+  matrix: "--mm:refc"
   outputsub: "channel is empty"
 """
 
diff --git a/tests/tools/compile/tkoch.nim b/tests/tools/compile/tkoch.nim
new file mode 100644
index 000000000..008721dc0
--- /dev/null
+++ b/tests/tools/compile/tkoch.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include koch
\ No newline at end of file
diff --git a/tests/tools/compile/tniminst.nim b/tests/tools/compile/tniminst.nim
new file mode 100644
index 000000000..78c736af0
--- /dev/null
+++ b/tests/tools/compile/tniminst.nim
@@ -0,0 +1,5 @@
+discard """
+  action: compile
+"""
+
+include tools/niminst/niminst
\ No newline at end of file
diff --git a/tests/tools/config.nims b/tests/tools/config.nims
new file mode 100644
index 000000000..0f0cba8b4
--- /dev/null
+++ b/tests/tools/config.nims
@@ -0,0 +1,3 @@
+--d:nimPreviewSlimSystem
+--d:nimPreviewCstringConversion
+--d:nimPreviewProcConversion
diff --git a/tests/tools/tlinter.nim b/tests/tools/tlinter.nim
index a6d45ab3b..16f67905e 100644
--- a/tests/tools/tlinter.nim
+++ b/tests/tools/tlinter.nim
@@ -1,10 +1,10 @@
 discard """
   cmd: '''nim c --styleCheck:hint $file'''
   nimout: '''
+tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name]
 tlinter.nim(21, 14) Hint: 'nosideeffect' should be: 'noSideEffect' [Name]
 tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [template declared in tlinter.nim(19, 9)] [Name]
 tlinter.nim(21, 35) Hint: 'inLine' should be: 'inline' [Name]
-tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name]
 tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [proc declared in tlinter.nim(21, 6)] [Name]
 tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [type declared in tlinter.nim(25, 6)] [Name]
 tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [var declared in tlinter.nim(27, 5)] [Name]
diff --git a/tests/tools/tnimgrep.nim b/tests/tools/tnimgrep.nim
index e97b979f1..890a36e79 100644
--- a/tests/tools/tnimgrep.nim
+++ b/tests/tools/tnimgrep.nim
@@ -8,7 +8,9 @@ discard """
 """
 ## Authors: quantimnot, a-mr
 
-import osproc, os, streams, unittest, strutils
+import std/[osproc, os, streams, unittest, strutils]
+
+import std/syncio
 
 #=======
 # setup
diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim
index 31d6cf7d7..539608ad6 100644
--- a/tests/tools/tunused_imports.nim
+++ b/tests/tools/tunused_imports.nim
@@ -1,9 +1,12 @@
 discard """
   cmd: '''nim c --hint:Processing:off $file'''
   nimout: '''
-tunused_imports.nim(11, 10) Warning: BEGIN [User]
-tunused_imports.nim(36, 10) Warning: END [User]
-tunused_imports.nim(34, 8) Warning: imported and not used: 'strutils' [UnusedImport]
+tunused_imports.nim(14, 10) Warning: BEGIN [User]
+tunused_imports.nim(41, 10) Warning: END [User]
+tunused_imports.nim(37, 8) Warning: imported and not used: 'strutils' [UnusedImport]
+tunused_imports.nim(38, 13) Warning: imported and not used: 'strtabs' [UnusedImport]
+tunused_imports.nim(38, 22) Warning: imported and not used: 'cstrutils' [UnusedImport]
+tunused_imports.nim(39, 12) Warning: imported and not used: 'macrocache' [UnusedImport]
 '''
   action: "compile"
 """
@@ -32,5 +35,7 @@ macro bar(): untyped =
 bar()
 
 import strutils
+import std/[strtabs, cstrutils]
+import std/macrocache
 
 {.warning: "END".}
diff --git a/tests/trmacros/tnorewrite.nim b/tests/trmacros/tnorewrite.nim
new file mode 100644
index 000000000..e6769246f
--- /dev/null
+++ b/tests/trmacros/tnorewrite.nim
@@ -0,0 +1,20 @@
+block:
+  proc get(x: int): int = x
+
+  template t{get(a)}(a: int): int =
+    {.noRewrite.}:
+      get(a) + 1
+
+  doAssert get(0) == 1
+
+block:
+  var x: int
+
+  template asgn{a = b}(a: int{lvalue}, b: int) =
+    let newVal = b + 1
+    # ^ this is needed but should it be?
+    {.noRewrite.}:
+      a = newVal
+
+  x = 10
+  doAssert x == 11, $x
diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim
index 087dc0d68..9defc4d1b 100644
--- a/tests/trmacros/tor.nim
+++ b/tests/trmacros/tor.nim
@@ -1,11 +1,23 @@
 discard """
-  output: '''0
+  output: '''
+3
+30
 true
-3'''
+'''
 """
 
+
+# bug #798
+template t012{(0|1|2){x}}(x: untyped): untyped = x+1
+let z = 1
+# outputs 3 thanks to fixpoint iteration:
+echo z
+
+
 template arithOps: untyped = (`+` | `-` | `*`)
-template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a mod 10, b)
+template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped =
+  {.noRewrite.}:
+    f(a mod 10, b)
 
 let xx = 10
 echo 10*xx
@@ -20,9 +32,3 @@ var
   c = false
 a = b and a
 echo a
-
-# bug #798
-template t012{(0|1|2){x}}(x: untyped): untyped = x+1
-let z = 1
-# outputs 3 thanks to fixpoint iteration:
-echo z
diff --git a/tests/trmacros/trmacros_various.nim b/tests/trmacros/trmacros_various.nim
index 74b248739..8fe51e548 100644
--- a/tests/trmacros/trmacros_various.nim
+++ b/tests/trmacros/trmacros_various.nim
@@ -33,7 +33,8 @@ block tcse:
 
 block hoist:
   template optPeg{peg(pattern)}(pattern: string{lit}): Peg =
-    var gl {.global, gensym.} = peg(pattern)
+    {.noRewrite.}:
+      var gl {.global, gensym.} = peg(pattern)
     gl
   doAssert match("(a b c)", peg"'(' @ ')'")
   doAssert match("W_HI_Le", peg"\y 'while'")
diff --git a/tests/trmacros/trmacros_various2.nim b/tests/trmacros/trmacros_various2.nim
index 257ee8ba6..981df4ca6 100644
--- a/tests/trmacros/trmacros_various2.nim
+++ b/tests/trmacros/trmacros_various2.nim
@@ -70,7 +70,9 @@ block tstar:
     for i in 1..len(s)-1: result.add s[i]
     inc calls
 
-  template optConc{ `&&` * a }(a: string): string = &&a
+  template optConc{ `&&` * a }(a: string): string =
+    {.noRewrite.}:
+      &&a
 
   let space = " "
   echo "my" && (space & "awe" && "some " ) && "concat"
diff --git a/tests/tuples/mnimsconstunpack.nim b/tests/tuples/mnimsconstunpack.nim
new file mode 100644
index 000000000..65fafc12f
--- /dev/null
+++ b/tests/tuples/mnimsconstunpack.nim
@@ -0,0 +1,4 @@
+proc foo(): tuple[a, b: string] =
+  result = ("a", "b")
+
+const (a, b*) = foo()
diff --git a/tests/tuples/t18125_1.nim b/tests/tuples/t18125_1.nim
new file mode 100644
index 000000000..74fdfe8f5
--- /dev/null
+++ b/tests/tuples/t18125_1.nim
@@ -0,0 +1,14 @@
+# issue #18125 solved with type inference
+
+type
+  Parent = ref object of RootObj
+
+  Child = ref object of Parent
+    c: char
+
+func foo(c: char): (Parent, int) =
+  # Works if you use (Parent(Child(c: c)), 0)
+  (Child(c: c), 0)
+
+let x = foo('x')[0]
+doAssert Child(x).c == 'x'
diff --git a/tests/tuples/t18125_2.nim b/tests/tuples/t18125_2.nim
new file mode 100644
index 000000000..fe0a4a8bb
--- /dev/null
+++ b/tests/tuples/t18125_2.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "type mismatch: got <(Child, int)> but expected '(Parent, int)'"
+  line: 17
+"""
+
+# issue #18125 solved with correct type relation
+
+type
+  Parent = ref object of RootObj
+
+  Child = ref object of Parent
+    c: char
+
+func foo(c: char): (Parent, int) =
+  # Works if you use (Parent(Child(c: c)), 0)
+  let x = (Child(c: c), 0)
+  x
+
+let x = foo('x')[0]
+doAssert Child(x).c == 'x'
diff --git a/tests/tuples/tnimsconstunpack.nim b/tests/tuples/tnimsconstunpack.nim
new file mode 100644
index 000000000..7860fc0a4
--- /dev/null
+++ b/tests/tuples/tnimsconstunpack.nim
@@ -0,0 +1,8 @@
+discard """
+  action: compile
+  cmd: "nim e $file"
+"""
+
+import mnimsconstunpack
+
+doAssert b == "b"
diff --git a/tests/tuples/ttuples_issues.nim b/tests/tuples/ttuples_issues.nim
index 0cc505d28..70defdfce 100644
--- a/tests/tuples/ttuples_issues.nim
+++ b/tests/tuples/ttuples_issues.nim
@@ -107,6 +107,18 @@ template main() =
         return (x,y)
     doAssert bar() == (10, (11,))
 
+  block: # bug #16331
+    type T1 = tuple[a, b: int]
+
+    proc p(b: bool): T1 =
+      var x: T1 = (10, 20)
+      x = if b: (x.b, x.a) else: (-x.b, -x.a)
+      x
+
+    doAssert p(false) == (-20, -10)
+    doAssert p(true) == (20, 10)
+
+
 proc mainProc() =
   # other tests should be in `main`
   block:
diff --git a/tests/tuples/ttuples_various.nim b/tests/tuples/ttuples_various.nim
index dc060da1e..e392731d2 100644
--- a/tests/tuples/ttuples_various.nim
+++ b/tests/tuples/ttuples_various.nim
@@ -171,3 +171,41 @@ block tuple_with_seq:
     echo s
     (s, 7)
   t = test(t.a)
+
+block: # bug #22049
+  type A = object
+    field: tuple[a, b, c: seq[int]]
+
+  func value(v: var A): var tuple[a, b, c: seq[int]] =
+    v.field
+  template get(v: A): tuple[a, b, c: seq[int]] = v.value
+
+  var v = A(field: (@[1], @[2], @[3]))
+  var (a, b, c) = v.get()
+
+  doAssert a == @[1]
+  doAssert b == @[2]
+  doAssert c == @[3]
+
+block: # bug #22054
+  type A = object
+    field: tuple[a: int]
+
+  func value(v: var A): var tuple[a: int] =
+    v.field
+  template get(v: A): tuple[a: int] = v.value
+
+  var v = A(field: (a: 1314))
+  doAssert get(v)[0] == 1314
+
+block: # tuple unpacking assignment with underscore
+  var
+    a = 1
+    b = 2
+  doAssert (a, b) == (1, 2)
+  (a, _) = (3, 4)
+  doAssert (a, b) == (3, 2)
+  (_, a) = (5, 6)
+  doAssert (a, b) == (6, 2)
+  (b, _) = (7, 8)
+  doAssert (a, b) == (6, 7)
diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim
index e9d01bd0d..83f93ae7f 100644
--- a/tests/typerel/t7600_1.nim
+++ b/tests/typerel/t7600_1.nim
@@ -1,6 +1,6 @@
 discard """
 errormsg: "type mismatch: got <Thin[system.int]>"
-nimout: '''t7600_1.nim(21, 6) Error: type mismatch: got <Thin[system.int]>
+nimout: '''t7600_1.nim(21, 1) Error: type mismatch: got <Thin[system.int]>
 but expected one of:
 proc test[T](x: Paper[T])
   first type mismatch at position: 1
diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim
index 371707f4c..9488a44bc 100644
--- a/tests/typerel/t7600_2.nim
+++ b/tests/typerel/t7600_2.nim
@@ -1,6 +1,6 @@
 discard """
 errormsg: "type mismatch: got <Thin>"
-nimout: '''t7600_2.nim(20, 6) Error: type mismatch: got <Thin>
+nimout: '''t7600_2.nim(20, 1) Error: type mismatch: got <Thin>
 but expected one of:
 proc test(x: Paper)
   first type mismatch at position: 1
diff --git a/tests/typerel/tnoargopenarray.nim b/tests/typerel/tnoargopenarray.nim
deleted file mode 100644
index 9e2b2fb86..000000000
--- a/tests/typerel/tnoargopenarray.nim
+++ /dev/null
@@ -1,8 +0,0 @@
-discard """
-action: compile
-"""
-
-import db_sqlite
-
-var db: DbConn
-exec(db, sql"create table blabla()")
diff --git a/tests/typerel/tproctypeclass.nim b/tests/typerel/tproctypeclass.nim
new file mode 100644
index 000000000..e8fab9780
--- /dev/null
+++ b/tests/typerel/tproctypeclass.nim
@@ -0,0 +1,89 @@
+import std/assertions
+
+proc main =
+  iterator closureIter(): int {.closure.} =
+    yield 1
+    yield 2
+  iterator inlineIter(): int {.inline.} =
+    yield 1
+    yield 2
+  proc procNotIter(): int = 1
+
+  doAssert closureIter is iterator
+  doAssert inlineIter is iterator
+  doAssert procNotIter isnot iterator
+
+  doAssert closureIter isnot proc
+  doAssert inlineIter isnot proc
+  doAssert procNotIter is proc
+
+  doAssert typeof(closureIter) is iterator
+  doAssert typeof(inlineIter) is iterator
+  doAssert typeof(procNotIter) isnot iterator
+
+  doAssert typeof(closureIter) isnot proc
+  doAssert typeof(inlineIter) isnot proc
+  doAssert typeof(procNotIter) is proc
+
+  block:
+    proc fn1(iter: iterator {.closure.}) = discard
+    proc fn2[T: iterator {.closure.}](iter: T) = discard
+
+    fn1(closureIter)
+    fn2(closureIter)
+
+    doAssert not compiles(fn1(procNotIter))
+    doAssert not compiles(fn2(procNotIter))
+
+    doAssert not compiles(fn1(inlineIter))
+    doAssert not compiles(fn2(inlineIter))
+
+  block: # concrete iterator type
+    proc fn1(iter: iterator(): int) = discard
+    proc fn2[T: iterator(): int](iter: T) = discard
+
+    fn1(closureIter)
+    fn2(closureIter)
+
+    doAssert not compiles(fn1(procNotIter))
+    doAssert not compiles(fn2(procNotIter))
+
+    doAssert not compiles(fn1(inlineIter))
+    doAssert not compiles(fn2(inlineIter))
+
+  proc takesNimcall[T: proc {.nimcall.}](p: T) = discard
+  proc takesClosure[T: proc {.closure.}](p: T) = discard
+  proc takesAnyProc[T: proc](p: T) = discard
+
+  proc nimcallProc(): int {.nimcall.} = 1
+  proc closureProc(): int {.closure.} = 2
+
+  doAssert nimcallProc is proc {.nimcall.}
+  takesNimcall(nimcallProc)
+  doAssert closureProc isnot proc {.nimcall.}
+  doAssert not compiles(takesNimcall(closureProc))
+
+  doAssert nimcallProc isnot proc {.closure.}
+  doAssert not compiles(takesClosure(nimcallProc))
+  doAssert closureProc is proc {.closure.}
+  takesClosure(closureProc)
+  
+  doAssert nimcallProc is proc
+  takesAnyProc(nimcallProc)
+  doAssert closureProc is proc
+  takesAnyProc(closureProc)
+
+  block: # supposed to test that sameType works 
+    template ensureNotRedefine(Ty): untyped =
+      proc foo[T: Ty](x: T) = discard
+      doAssert not (compiles do:
+        proc bar[T: Ty](x: T) = discard
+        proc bar[T: Ty](x: T) = discard)
+    ensureNotRedefine proc
+    ensureNotRedefine iterator
+    ensureNotRedefine proc {.nimcall.}
+    ensureNotRedefine iterator {.nimcall.}
+    ensureNotRedefine proc {.closure.}
+    ensureNotRedefine iterator {.closure.}
+
+main()
diff --git a/tests/typerel/tregionptrs.nim b/tests/typerel/tregionptrs.nim
deleted file mode 100644
index 504ec1011..000000000
--- a/tests/typerel/tregionptrs.nim
+++ /dev/null
@@ -1,16 +0,0 @@
-discard """
-  errormsg: "type mismatch: got <BPtr> but expected 'APtr = ptr[RegionA, int]'"
-  line: 16
-"""
-
-type
-  RegionA = object
-  APtr = RegionA ptr int
-  RegionB = object
-  BPtr = RegionB ptr int
-
-var x,xx: APtr
-var y: BPtr
-x = nil
-x = xx
-x = y
diff --git a/tests/typerel/tsigmatch.nim b/tests/typerel/tsigmatch.nim
new file mode 100644
index 000000000..7541f2028
--- /dev/null
+++ b/tests/typerel/tsigmatch.nim
@@ -0,0 +1,6 @@
+block: # bug #13618
+  proc test(x: Natural or BackwardsIndex): int =
+    int(x)
+
+  doAssert test(^1) == 1
+  doAssert test(1) == 1
diff --git a/tests/typerel/ttynilinstantiation.nim b/tests/typerel/ttynilinstantiation.nim
new file mode 100644
index 000000000..303506689
--- /dev/null
+++ b/tests/typerel/ttynilinstantiation.nim
@@ -0,0 +1,7 @@
+proc foo[T: proc](x: T) =
+  # old error here:
+  let y = x
+  # invalid type: 'typeof(nil)' for let
+
+foo(nil) #[tt.Error
+   ^ type mismatch: got <typeof(nil)>]#
diff --git a/tests/typerel/ttypedesc_as_genericparam1_orc.nim b/tests/typerel/ttypedesc_as_genericparam1_orc.nim
index 0ee4d8f92..d528a7421 100644
--- a/tests/typerel/ttypedesc_as_genericparam1_orc.nim
+++ b/tests/typerel/ttypedesc_as_genericparam1_orc.nim
@@ -1 +1,5 @@
-doAssert repr(int) == "int"
\ No newline at end of file
+discard """
+  matrix: "--mm:orc"
+"""
+
+doAssert repr(int) == "int"
diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim
index 5f49c33fd..a4b6f003a 100644
--- a/tests/typerel/ttypelessemptyset.nim
+++ b/tests/typerel/ttypelessemptyset.nim
@@ -1,5 +1,5 @@
 discard """
-  errormsg: "internal error: invalid kind for lastOrd(tyEmpty)"
+  errormsg: "statement returns no value that can be discarded"
 """
 var q = false
 discard (if q: {} else: {})
diff --git a/tests/typerel/tuncheckedarray_eq.nim b/tests/typerel/tuncheckedarray_eq.nim
new file mode 100644
index 000000000..c07696b5f
--- /dev/null
+++ b/tests/typerel/tuncheckedarray_eq.nim
@@ -0,0 +1,2 @@
+type p = ptr UncheckedArray[char]
+doAssert p is ptr UncheckedArray
\ No newline at end of file
diff --git a/tests/typerel/typedescs.nim b/tests/typerel/typedescs.nim
index 23b9ce64f..faf919e13 100644
--- a/tests/typerel/typedescs.nim
+++ b/tests/typerel/typedescs.nim
@@ -5,3 +5,18 @@ p(type((5, 6)))       # Compiles
 (type((5, 6))).p      # Doesn't compile (SIGSEGV: Illegal storage access.)
 type T = type((5, 6)) # Doesn't compile (SIGSEGV: Illegal storage access.)
 
+block: # issue #21677
+  type
+    Uints = uint16|uint32
+
+  template constructor(name: untyped, typ: typedesc[Uints], typ2: typedesc[Uints]) =
+    type
+      name = object
+        data: typ
+        data2: typ2
+
+    proc `init name`(data: typ, data2: typ2): name =
+      result.data = data
+      result.data2 = data2
+
+  constructor(Test, uint32, uint16)
diff --git a/tests/types/t15836.nim b/tests/types/t15836.nim
new file mode 100644
index 000000000..27d3ad0d0
--- /dev/null
+++ b/tests/types/t15836.nim
@@ -0,0 +1,11 @@
+discard """
+  errormsg: "type mismatch: got <int literal(1), proc (a: GenericParam): auto>"
+  line: 11
+""" 
+
+proc takesProc[T](x: T, f: proc(x: T): int) =
+    echo f(x) + 2
+
+takesProc(1, proc (a: int): int = 2) # ok, prints 4
+takesProc(1, proc (a: auto): auto = 2) # ok, prints 4
+takesProc(1, proc (a: auto): auto = "uh uh") # prints garbage
diff --git a/tests/types/t15836_2.nim b/tests/types/t15836_2.nim
new file mode 100644
index 000000000..6a16e2d22
--- /dev/null
+++ b/tests/types/t15836_2.nim
@@ -0,0 +1,21 @@
+import std/sugar
+
+type Tensor[T] = object
+  x: T
+
+proc numerical_gradient*[T](input: T, f: (proc(x: T): T), h = T(1e-5)): T {.inline.} =
+  result = default(T)
+
+proc numerical_gradient*[T](input: Tensor[T], f: (proc(x: Tensor[T]): T), h = T(1e-5)): Tensor[T] {.noinit.} =
+  result = default(Tensor[T])
+
+proc conv2d*[T](input: Tensor[T]): Tensor[T] {.inline.} =
+  result = default(Tensor[T])
+
+proc sum*[T](arg: Tensor[T]): T = default(T)
+
+proc sum*[T](arg: Tensor[T], axis: int): Tensor[T] {.noinit.} = default(Tensor[T])
+
+let dinput = Tensor[int](x: 1)
+let target_grad_input = dinput.numerical_gradient(
+    x => conv2d(x).sum())
diff --git a/tests/types/t21027.nim b/tests/types/t21027.nim
new file mode 100644
index 000000000..3a992177a
--- /dev/null
+++ b/tests/types/t21027.nim
@@ -0,0 +1,5 @@
+discard """
+  errormsg: "Invalid usage of cast, cast requires a type to convert to, e.g., cast[int](0d)."
+"""
+# bug #21027
+let x: uint64 = cast(5)
diff --git a/tests/types/t21260.nim b/tests/types/t21260.nim
new file mode 100644
index 000000000..90d6613c1
--- /dev/null
+++ b/tests/types/t21260.nim
@@ -0,0 +1,13 @@
+discard """
+  errormsg: "illegal recursion in type 'Foo'"
+  line: 8
+"""
+
+type
+  Kind = enum kA, kB
+  Foo = object
+    case k: Kind:
+    of kA:
+      foo: Foo
+    of kB:
+      discard
diff --git a/tests/types/t5648.nim b/tests/types/t5648.nim
new file mode 100644
index 000000000..b3bd406b3
--- /dev/null
+++ b/tests/types/t5648.nim
@@ -0,0 +1,32 @@
+discard """
+  output: '''
+ptr Foo
+'''
+joinable: false
+"""
+# not joinable because it causes out of memory with --gc:boehm
+
+# issue #5648
+
+import typetraits
+
+type Foo = object
+  bar: int
+
+proc main() =
+  var f = create(Foo)
+  f.bar = 3
+  echo f.type.name
+
+  discard realloc(f, 0)
+
+  var g = Foo()
+  g.bar = 3
+
+var
+  mainPtr = cast[pointer](main)
+  mainFromPtr = cast[typeof(main)](mainPtr)
+
+doAssert main == mainFromPtr
+
+main()
diff --git a/tests/types/tcyclic.nim b/tests/types/tcyclic.nim
new file mode 100644
index 000000000..651394c8b
--- /dev/null
+++ b/tests/types/tcyclic.nim
@@ -0,0 +1,135 @@
+## todo publish the `isCyclic` when it's mature.
+proc isCyclic(t: typedesc): bool {.magic: "TypeTrait".} =
+  ## Returns true if the type can potentially form a cyclic type
+
+template cyclicYes(x: typed) =
+  doAssert isCyclic(x)
+
+template cyclicNo(x: typed) =
+  doAssert not isCyclic(x)
+
+# atomic types are not cyclic
+cyclicNo(int)
+cyclicNo(float)
+cyclicNo(string)
+cyclicNo(char)
+cyclicNo(void)
+
+type
+  Object = object
+  Ref = ref object
+
+cyclicNo(Object)
+cyclicNo(Ref)
+
+type
+  Data1 = ref object
+  Data2 = ref object
+    id: Data1
+
+cyclicNo(Data2)
+
+type
+  Cyclone = ref object
+    data: Cyclone
+
+  Alias = Cyclone
+
+  Acyclic {.acyclic.} = ref object
+    data: Acyclic
+
+  LinkedNode = object
+    next: ref LinkedNode
+
+  LinkedNodeWithCursor = object
+    next {.cursor.} : ref LinkedNodeWithCursor
+
+cyclicYes(Cyclone)
+cyclicYes(Alias)
+cyclicNo(seq[Cyclone])
+cyclicNo((Cyclone, ))
+cyclicNo(Acyclic)
+
+cyclicYes(LinkedNode)
+cyclicNo(LinkedNodeWithCursor) # cursor doesn't increase rc, cannot form a cycle
+
+type
+  ObjectWithoutCycles = object
+    data: seq[ObjectWithoutCycles]
+
+cyclicNo(ObjectWithoutCycles)
+
+
+block:
+  type
+    Try = object
+      id: Best
+    Best = object
+      name: ref Try
+    Best2 = ref Best
+
+  cyclicYes(Best)
+  cyclicYes(Try)
+  cyclicNo(Best2)
+
+  type
+    Base = object
+      data: ref seq[Base]
+    Base2 = ref Base
+
+  cyclicYes(Base)
+  cyclicNo(Base2)
+
+
+  type
+    Base3 = ref object
+      id: Base3
+
+    Base4 = object
+      id: ref Base4
+
+  cyclicYes(Base3)
+  cyclicYes(Base4)
+  cyclicYes(ref Base4)
+
+block:
+  type Cyclic2 = object
+    x: ref (Cyclic2, int)
+
+  cyclicYes (Cyclic2, int)
+  cyclicYes (ref (Cyclic2, int))
+
+block:
+  type
+    myseq[T] = object
+      data: ptr UncheckedArray[T]
+    Node = ref object
+      kids: myseq[Node]
+
+  cyclicNo(Node)
+
+block:
+  type
+    myseq[T] = object
+      data: ptr UncheckedArray[T]
+    Node = ref object
+      kids: myseq[Node]
+
+  proc `=trace`(x: var myseq[Node]; env: pointer) = discard
+
+  cyclicYes(Node)
+
+block:
+  type
+    Foo = object
+      id: ptr ref Foo
+
+  cyclicNo(Foo)
+
+block:
+  type
+    InheritableObj = object of RootObj
+    InheritableRef = ref object of RootObj
+
+  cyclicYes(InheritableObj)
+  cyclicYes(InheritableRef)
diff --git a/tests/types/texportgeneric.nim b/tests/types/texportgeneric.nim
new file mode 100644
index 000000000..9e6be2bb6
--- /dev/null
+++ b/tests/types/texportgeneric.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "{.exportc.} not allowed for generic types"
+  line: 6
+"""
+
+type Struct[T] {.exportc.} = object
+  a:int
+  b: T
\ No newline at end of file
diff --git a/tests/types/tinheritgenericparameter.nim b/tests/types/tinheritgenericparameter.nim
new file mode 100644
index 000000000..d0d313635
--- /dev/null
+++ b/tests/types/tinheritgenericparameter.nim
@@ -0,0 +1,39 @@
+discard """
+  cmd: "nim check --hints:off --warnings:off $file"
+  action: "reject"
+  nimout:'''
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 15) Error: Cannot inherit from: 'MyObject'
+tinheritgenericparameter.nim(36, 23) Error: object constructor needs an object type [error]
+tinheritgenericparameter.nim(36, 23) Error: expression '' has no type (or is ambiguous)
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 15) Error: Cannot inherit from: 'int'
+tinheritgenericparameter.nim(37, 23) Error: object constructor needs an object type [error]
+tinheritgenericparameter.nim(37, 23) Error: expression '' has no type (or is ambiguous)
+'''
+"""
+
+type
+  MyObject = object
+  HorzLayout[Base, T] = ref object of Base
+    data: seq[T]
+  VertLayout[T, Base] = ref object of Base
+    data: seq[T]
+  UiElement = ref object of RootObj
+    a: int
+  MyType[T] = ref object of RootObj
+    data: seq[T]
+  OtherElement[T] = ref object of T
+  Child[T] = ref object of HorzLayout[UiElement, T]
+  Child2[T] = ref object of VertLayout[T, UiElement]
+  Child3[T] = ref object of HorzLayout[MyObject, T]
+  Child4[T] = ref object of HorzLayout[int, T]
+static:
+  var a = Child[int](a: 300, data: @[100, 200, 300])
+  assert a.a == 300
+  assert a.data == @[100, 200, 300]
+discard Child2[string]()
+discard Child3[string]()
+discard Child4[string]()
+discard OtherElement[MyType[int]]()
+
diff --git a/tests/types/tinheritref.nim b/tests/types/tinheritref.nim
index b79926356..c03e3bcab 100644
--- a/tests/types/tinheritref.nim
+++ b/tests/types/tinheritref.nim
@@ -11,10 +11,10 @@ ob = T[int](elem: 23)
 doAssert ob.elem == 23
 
 type
-  TTreeIteratorA* {.inheritable.} = ref object
-
   TKeysIteratorA* = ref object of TTreeIteratorA  #compiles
 
+  TTreeIteratorA* {.inheritable.} = ref object
+
   TTreeIterator* [T,D] {.inheritable.} = ref object
 
   TKeysIterator* [T,D] = ref object of TTreeIterator[T,D]  #this not
diff --git a/tests/types/tisop.nim b/tests/types/tisop.nim
deleted file mode 100644
index 5f9cba0d8..000000000
--- a/tests/types/tisop.nim
+++ /dev/null
@@ -1,48 +0,0 @@
-discard """
-  disabled: true
-"""
-
-import typetraits
-
-type
-  TRecord = (tuple) or (object)
-
-  TFoo[T, U] = object
-    x: int
-
-    when T is string:
-      y: float
-    else:
-      y: string
-
-    when U is TRecord:
-      z: float
-
-  E = enum A, B, C
-
-macro m(t: typedesc): typedesc =
-  if t is enum:
-    result = string
-  else:
-    result = int
-
-var f: TFoo[int, int]
-static: doAssert(f.y.type.name == "string")
-
-when compiles(f.z):
-  {.error: "Foo should not have a `z` field".}
-
-proc p(a, b: auto) =
-  when a.type is int:
-    static: doAssert false
-
-  var f: TFoo[m(a.type), b.type]
-  static:
-    doAssert f.x.type.name == "int"
-    echo  f.y.type.name
-    doAssert f.y.type.name == "float"
-    echo  f.z.type.name
-    doAssert f.z.type.name == "float"
-
-p(A, f)
-
diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim
index 67f1ce0c1..c95b63c90 100644
--- a/tests/types/tisopr.nim
+++ b/tests/types/tisopr.nim
@@ -135,3 +135,37 @@ block:
   let Q = MyObj[P](y: 2)
   doAssert($Q  == "(y: 2)")
 
+block: # previously tisop.nim
+  type
+    TRecord = (tuple) or (object)
+    TFoo[T, U] = object
+      x: int
+      when T is string:
+        y: float
+      else:
+        y: string
+      when U is TRecord:
+        z: float
+    E = enum A, B, C
+  template m(t: typedesc): typedesc =
+    when t is enum:
+      string
+    else:
+      int
+  var f: TFoo[int, int]
+  static: doAssert(typeof(f.y) is string)
+  when compiles(f.z):
+    {.error: "Foo should not have a `z` field".}
+  proc p(a, b: auto) =
+    when typeof(a) is int:
+      static: doAssert false
+    var f: TFoo[m(a.typeof), b.typeof]
+    static:
+      doAssert f.x.typeof is int
+      doAssert f.y.typeof is float
+      doAssert f.z.typeof is float
+  p(A, f)
+
+block: # issue #22850
+  doAssert not (type is int)
+  doAssert not (typedesc is int)
diff --git a/tests/types/tissues_types.nim b/tests/types/tissues_types.nim
index 7ed0547bf..6bb1258f4 100644
--- a/tests/types/tissues_types.nim
+++ b/tests/types/tissues_types.nim
@@ -3,7 +3,6 @@ discard """
 true
 true
 true
-ptr Foo
 (member: "hello world")
 (member: 123.456)
 (member: "hello world", x: ...)
@@ -11,10 +10,7 @@ ptr Foo
 0
 false
 '''
-joinable: false
 """
-# not joinable because it causes out of memory with --gc:boehm
-import typetraits
 
 block t1252:
   echo float32 isnot float64
@@ -29,29 +25,6 @@ block t5640:
 
   var v = vec2([0.0'f32, 0.0'f32])
 
-block t5648:
-  type Foo = object
-    bar: int
-
-  proc main() =
-    var f = create(Foo)
-    f.bar = 3
-    echo f.type.name
-
-    discard realloc(f, 0)
-
-    var g = Foo()
-    g.bar = 3
-
-  var
-    mainPtr1: pointer = main
-    mainPtr2 = pointer(main)
-    mainPtr3 = cast[pointer](main)
-
-  doAssert mainPtr1 == mainPtr2 and mainPtr2 == mainPtr3
-
-  main()
-
 block t7581:
   discard int -1
 
@@ -108,3 +81,38 @@ block:
 
 var f1: Foo
 echo f1.bar
+
+import macros
+
+block: # issue #12582
+  macro foo(T: type): type =
+    nnkBracketExpr.newTree(bindSym "array", newLit 1, T)
+  var
+    _: foo(int) # fine
+  type
+    Foo = object
+      x: foo(int) # fine
+    Bar = ref object
+      x: foo(int) # error
+  let b = Bar()
+  let b2 = Bar(x: [123])
+
+block:
+  when true: # bug #14710
+    type Foo[T] = object
+      x1: int
+      when T.sizeof == 4: discard # SIGSEGV
+      when sizeof(T) == 4: discard # ok
+    let t = Foo[float](x1: 1)
+    doAssert t.x1 == 1
+
+block:
+  template s(d: varargs[typed])=discard
+
+  proc something(x:float)=discard
+  proc something(x:int)=discard
+  proc otherthing()=discard
+
+  s(something)
+  s(otherthing, something)
+  s(something, otherthing)
diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim
index 296729cd8..765761e99 100644
--- a/tests/types/ttopdowninference.nim
+++ b/tests/types/ttopdowninference.nim
@@ -193,3 +193,141 @@ block: # templates
   doAssert a == float(1)
   doAssert b == byte(2)
   doAssert c == cstring("abc")
+
+
+proc foo(): set[char] = # bug #11259
+  discard "a"
+  {}
+
+discard foo()
+
+block: # bug #11085
+  const ok1: set[char] = {}
+  var ok1b: set[char] = {}
+
+  const ok2: set[char] = block:
+    {}
+
+  const ok3: set[char] = block:
+    var x: set[char] = {}
+    x
+  var ok3b: set[char] = block:
+    var x: set[char] = {}
+    x
+
+  var bad: set[char] = block:
+    {}
+
+# bug #6213
+block:
+  block:
+    type MyEnum = enum a, b
+    type MyTuple = tuple[x: set[MyEnum]]
+
+    var myVar: seq[MyTuple] = @[ (x: {}) ]
+    doAssert myVar.len == 1
+
+  block:
+    type
+      Foo = tuple
+        f: seq[string]
+        s: string
+
+    proc e(): seq[Foo] =
+      return @[
+        (@[], "asd")
+      ]
+
+    doAssert e()[0].f == @[]
+
+block: # bug #11777
+  type S = set[0..5]
+  var s: S = {1, 2}
+  doAssert 1 in s
+
+block: # bug #20807
+  var s: seq[string]
+  template fail =
+    s = @[]
+  template test(body: untyped) =
+    body
+  proc test(a: string) = discard
+  test: fail()
+  doAssert not (compiles do:
+    let x: seq[int] = `@`[string]([]))
+
+block: # bug #21377
+  proc b[T](v: T): seq[int] =
+    let x = 0
+    @[]
+
+  doAssert b(0) == @[]
+
+block: # bug #21377
+  proc b[T](v: T): seq[T] =
+    let x = 0
+    @[]
+
+  doAssert b(0) == @[]
+
+block: # bug #21377
+  proc b[T](v: T): set[bool] =
+    let x = 0
+    {}
+
+  doAssert b(0) == {}
+
+block: # bug #21377
+  proc b[T](v: T): array[0, int] =
+    let x = 0
+    []
+
+  doAssert b(0) == []
+
+block: # bug #21377
+  proc b[T](v: T): array[0, (string, string)] =
+    let x = 0
+    {:}
+
+  doAssert b(0) == {:}
+
+block: # bug #22180
+  type A = object
+  proc j() = discard
+
+  let x =
+    if false:
+      (ref A)(nil)
+    else:
+      if false:
+        quit 1
+      else:
+        if true:
+          j()
+          nil  # compiles with (ref A)(nil) here
+        else:
+          (ref A)(nil)
+  doAssert x.isNil
+  
+  let y =
+    case true
+    of false:
+      (ref A)(nil)
+    else:
+      case true
+      of false:
+        quit 1
+      else:
+        case true
+        of true:
+          j()
+          nil  # compiles with (ref A)(nil) here
+        else:
+          (ref A)(nil)
+  doAssert y.isNil
+
+block: # issue #24164, related regression
+  proc foo(x: proc ()) = discard
+  template bar(x: untyped = nil) =
+    foo(x)
+  bar()
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py
index 8f0f88e85..aed0cfeb0 100644
--- a/tests/untestable/gdb/gdb_pretty_printer_test.py
+++ b/tests/untestable/gdb/gdb_pretty_printer_test.py
@@ -1,11 +1,14 @@
 import gdb
+import re
+import sys
 # this test should test the gdb pretty printers of the nim
 # library. But be aware this test is not complete. It only tests the
 # command line version of gdb. It does not test anything for the
 # machine interface of gdb. This means if if this test passes gdb
 # frontends might still be broken.
 
-gdb.execute("source ../../../tools/nim-gdb.py")
+gdb.execute("set python print-stack full")
+gdb.execute("source ../../../tools/debug/nim-gdb.py")
 # debug all instances of the generic function `myDebug`, should be 14
 gdb.execute("rbreak myDebug")
 gdb.execute("run")
@@ -16,7 +19,7 @@ outputs = [
   '"meTwo"',
   '{meOne, meThree}',
   'MyOtherEnum(1)',
-  '5',
+  '{MyOtherEnum(0), MyOtherEnum(2)}',
   'array = {1, 2, 3, 4, 5}',
   'seq(0, 0)',
   'seq(0, 10)',
@@ -25,16 +28,21 @@ outputs = [
   'seq(3, 3) = {"one", "two", "three"}',
   'Table(3, 64) = {[4] = "four", [5] = "five", [6] = "six"}',
   'Table(3, 8) = {["two"] = 2, ["three"] = 3, ["one"] = 1}',
-  '{a = 1, b = "some string"}'
+  '{a = 1, b = "some string"}',
+  '("hello", 42)'
 ]
 
+argRegex = re.compile("^.* = (?:No suitable Nim \$ operator found for type: \w+\s*)*(.*)$")
+# Remove this error message which can pop up
+noSuitableRegex = re.compile("(No suitable Nim \$ operator found for type: \w+\s*)")
+
 for i, expected in enumerate(outputs):
-  gdb.write(f"{i+1}) expecting: {expected}: ", gdb.STDLOG)
+  gdb.write(f"\x1b[38;5;105m{i+1}) expecting: {expected}: \x1b[0m", gdb.STDLOG)
   gdb.flush()
-
-  functionSymbol = gdb.selected_frame().block().function
-  assert functionSymbol.line == 41, str(functionSymbol.line)
-
+  currFrame = gdb.selected_frame()
+  functionSymbol = currFrame.block().function
+  assert functionSymbol.line == 24, str(functionSymbol.line)
+  raw = ""
   if i == 6:
     # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack
     gdb.execute("up")
@@ -44,10 +52,13 @@ for i, expected in enumerate(outputs):
     gdb.execute("up")
     raw = gdb.parse_and_eval("myOtherArray")
   else:
-    raw = gdb.parse_and_eval("arg")
-
+    rawArg = re.sub(noSuitableRegex, "", gdb.execute("info args", to_string = True))
+    raw = rawArg.split("=", 1)[-1].strip()
   output = str(raw)
 
-  assert output == expected, "{0} : output: ({1}) != expected: ({2})".format(i, output, expected)
-  gdb.write(f"passed\n", gdb.STDLOG)
+  if output != expected:
+    gdb.write(f"\x1b[38;5;196m ({output}) != expected: ({expected})\x1b[0m\n", gdb.STDERR)
+    gdb.execute("quit 1")
+  else:
+    gdb.write("\x1b[38;5;34mpassed\x1b[0m\n", gdb.STDLOG)
   gdb.execute("continue")
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_output.txt b/tests/untestable/gdb/gdb_pretty_printer_test_output.txt
deleted file mode 100644
index 73d26016f..000000000
--- a/tests/untestable/gdb/gdb_pretty_printer_test_output.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-Loading Nim Runtime support.
-NimEnumPrinter: lookup global symbol 'NTI__z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum__z9cu80OJCfNgw9bUdzn5ZEzw.
-8
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim
index d2acdd282..163c99860 100644
--- a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim
+++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim
@@ -18,23 +18,6 @@ type
   MyObj = object
     a*: int
     b*: string
-  
-  # MyVariant = ref object
-  #   id*: int
-  #   case kind*: MyEnum
-  #   of meOne: mInt*: int
-  #   of meTwo, meThree: discard
-  #   of meFour:
-  #     moInt*: int
-  #     babies*: seq[MyVariant]
-  #   after: float
-
-  # MyIntVariant = ref object
-  #   stuff*: int
-  #   case myKind*: range[0..32766]
-  #   of 0: mFloat*: float
-  #   of 2: mString*: string
-  #   else: mBabies*: seq[MyIntVariant]
 
 var counter = 0
 
@@ -97,16 +80,10 @@ proc testProc(): void =
   var obj = MyObj(a: 1, b: "some string")
   myDebug(obj) #15
 
-  # var varObj = MyVariant(id: 13, kind: meFour, moInt: 94,
-  #                        babies: @[MyVariant(id: 18, kind: meOne, mInt: 7, after: 1.0),
-  #                                  MyVariant(id: 21, kind: meThree, after: 2.0)],
-  #                        after: 3.0)
-  # myDebug(varObj) #16
-
-  # var varObjInt = MyIntVariant(stuff: 5, myKind: 2, mString: "this is my sweet string")
-  # myDebug(varObjInt) #17
+  var tup = ("hello", 42)
+  myDebug(tup) # 16
 
-  echo(counter)
+  assert counter == 16
 
 
 testProc()
diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_run.sh b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh
index 525f54705..411c68435 100644..100755
--- a/tests/untestable/gdb/gdb_pretty_printer_test_run.sh
+++ b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh
@@ -1,15 +1,13 @@
 #!/usr/bin/env bash
-# Exit if anything fails
 set -e
-#!/usr/bin/env bash
 # Compile the test project with fresh debug information.
-nim c --debugger:native gdb_pretty_printer_test_program.nim &> /dev/null
+nim c --debugger:native --mm:orc --out:gdbNew gdb_pretty_printer_test_program.nim
+echo "Running new runtime tests..."
 # 2>&1 redirects stderr to stdout (all output in stdout)
-# <(...) is a bash feature that makes the output of a command into a
-# file handle.
-# diff compares the two files, the expected output, and the file
-# handle that is created by the execution of gdb.
-diff ./gdb_pretty_printer_test_output.txt <(gdb -x gdb_pretty_printer_test.py --batch-silent --args gdb_pretty_printer_test_program 2>&1)
-# The exit code of diff is forwarded as the exit code of this
-# script. So when the comparison fails, the exit code of this script
-# won't be 0. So this script should be embeddable in a test suite.
+gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbNew 2>&1
+
+
+# Do it all again, but with old runtime
+nim c --debugger:native --mm:refc --out:gdbOld gdb_pretty_printer_test_program.nim &> /dev/null
+echo "Running old runtime tests"
+gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbOld 2>&1
diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim
index d2366d9a9..3cb759516 100644
--- a/tests/untestable/thttpclient_ssl_remotenetwork.nim
+++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim
@@ -32,65 +32,71 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win
       good, bad, dubious, good_broken, bad_broken, dubious_broken
     CertTest = tuple[url:string, category:Category, desc: string]
 
-  const certificate_tests: array[0..54, CertTest] = [
-    ("https://wrong.host.badssl.com/", bad, "wrong.host"),
-    ("https://captive-portal.badssl.com/", bad, "captive-portal"),
-    ("https://expired.badssl.com/", bad, "expired"),
-    ("https://google.com/", good, "good"),
-    ("https://self-signed.badssl.com/", bad, "self-signed"),
-    ("https://untrusted-root.badssl.com/", bad, "untrusted-root"),
-    ("https://revoked.badssl.com/", bad_broken, "revoked"),
-    ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"),
-    ("https://no-common-name.badssl.com/", bad, "no-common-name"),
-    ("https://no-subject.badssl.com/", bad, "no-subject"),
-    ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"),
-    ("https://sha256.badssl.com/", good, "sha256"),
-    ("https://sha384.badssl.com/", bad, "sha384"),
-    ("https://sha512.badssl.com/", bad, "sha512"),
-    ("https://1000-sans.badssl.com/", bad, "1000-sans"),
-    ("https://10000-sans.badssl.com/", good_broken, "10000-sans"),
-    ("https://ecc256.badssl.com/", good_broken, "ecc256"),
-    ("https://ecc384.badssl.com/", good_broken, "ecc384"),
-    ("https://rsa2048.badssl.com/", good, "rsa2048"),
-    ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"),
-    ("http://http.badssl.com/", good, "regular http"),
-    ("https://http.badssl.com/", bad_broken, "http on https URL"),  # FIXME
-    ("https://cbc.badssl.com/", dubious, "cbc"),
-    ("https://rc4-md5.badssl.com/", bad, "rc4-md5"),
-    ("https://rc4.badssl.com/", bad, "rc4"),
-    ("https://3des.badssl.com/", bad, "3des"),
-    ("https://null.badssl.com/", bad, "null"),
-    ("https://mozilla-old.badssl.com/", bad_broken, "mozilla-old"),
-    ("https://mozilla-intermediate.badssl.com/", dubious_broken, "mozilla-intermediate"),
-    ("https://mozilla-modern.badssl.com/", good, "mozilla-modern"),
-    ("https://dh480.badssl.com/", bad, "dh480"),
-    ("https://dh512.badssl.com/", bad, "dh512"),
-    ("https://dh1024.badssl.com/", dubious_broken, "dh1024"),
-    ("https://dh2048.badssl.com/", good, "dh2048"),
-    ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"),
-    ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"),
-    ("https://static-rsa.badssl.com/", dubious, "static-rsa"),
-    ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"),
-    ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"),
-    ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"),
-    ("https://hsts.badssl.com/", good, "hsts"),
-    ("https://upgrade.badssl.com/", good, "upgrade"),
-    ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"),
-    ("https://subdomain.preloaded-hsts.badssl.com/", bad, "subdomain.preloaded-hsts"),
-    ("https://https-everywhere.badssl.com/", good, "https-everywhere"),
-    ("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", good,
-      "long-extended-subdomain-name-containing-many-letters-and-dashes"),
-    ("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", good,
-      "longextendedsubdomainnamewithoutdashesinordertotestwordwrapping"),
-    ("https://superfish.badssl.com/", bad, "(Lenovo) Superfish"),
-    ("https://edellroot.badssl.com/", bad, "(Dell) eDellRoot"),
-    ("https://dsdtestprovider.badssl.com/", bad, "(Dell) DSD Test Provider"),
-    ("https://preact-cli.badssl.com/", bad, "preact-cli"),
-    ("https://webpack-dev-server.badssl.com/", bad, "webpack-dev-server"),
-    ("https://mitm-software.badssl.com/", bad, "mitm-software"),
-    ("https://sha1-2016.badssl.com/", dubious, "sha1-2016"),
-    ("https://sha1-2017.badssl.com/", bad, "sha1-2017"),
-  ]
+  # badssl certs sometimes expire, set to false when that happens
+  when true:
+    const certificate_tests: array[0..54, CertTest] = [
+      ("https://wrong.host.badssl.com/", bad, "wrong.host"),
+      ("https://captive-portal.badssl.com/", bad, "captive-portal"),
+      ("https://expired.badssl.com/", bad, "expired"),
+      ("https://google.com/", good, "good"),
+      ("https://self-signed.badssl.com/", bad, "self-signed"),
+      ("https://untrusted-root.badssl.com/", bad, "untrusted-root"),
+      ("https://revoked.badssl.com/", bad_broken, "revoked"),
+      ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"),
+      ("https://no-common-name.badssl.com/", bad, "no-common-name"),
+      ("https://no-subject.badssl.com/", bad, "no-subject"),
+      ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"),
+      ("https://sha256.badssl.com/", good, "sha256"),
+      ("https://sha384.badssl.com/", bad, "sha384"),
+      ("https://sha512.badssl.com/", bad, "sha512"),
+      ("https://1000-sans.badssl.com/", bad, "1000-sans"),
+      ("https://10000-sans.badssl.com/", good_broken, "10000-sans"),
+      ("https://ecc256.badssl.com/", good_broken, "ecc256"),
+      ("https://ecc384.badssl.com/", good_broken, "ecc384"),
+      ("https://rsa2048.badssl.com/", good, "rsa2048"),
+      ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"),
+      ("http://http.badssl.com/", good, "regular http"),
+      ("https://http.badssl.com/", bad_broken, "http on https URL"),  # FIXME
+      ("https://cbc.badssl.com/", dubious, "cbc"),
+      ("https://rc4-md5.badssl.com/", bad, "rc4-md5"),
+      ("https://rc4.badssl.com/", bad, "rc4"),
+      ("https://3des.badssl.com/", bad, "3des"),
+      ("https://null.badssl.com/", bad, "null"),
+      ("https://mozilla-old.badssl.com/", bad_broken, "mozilla-old"),
+      ("https://mozilla-intermediate.badssl.com/", dubious_broken, "mozilla-intermediate"),
+      ("https://mozilla-modern.badssl.com/", good, "mozilla-modern"),
+      ("https://dh480.badssl.com/", bad, "dh480"),
+      ("https://dh512.badssl.com/", bad, "dh512"),
+      ("https://dh1024.badssl.com/", dubious_broken, "dh1024"),
+      ("https://dh2048.badssl.com/", good, "dh2048"),
+      ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"),
+      ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"),
+      ("https://static-rsa.badssl.com/", dubious, "static-rsa"),
+      ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"),
+      ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"),
+      ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"),
+      ("https://hsts.badssl.com/", good, "hsts"),
+      ("https://upgrade.badssl.com/", good, "upgrade"),
+      ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"),
+      ("https://subdomain.preloaded-hsts.badssl.com/", bad, "subdomain.preloaded-hsts"),
+      ("https://https-everywhere.badssl.com/", good, "https-everywhere"),
+      ("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", good,
+        "long-extended-subdomain-name-containing-many-letters-and-dashes"),
+      ("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", good,
+        "longextendedsubdomainnamewithoutdashesinordertotestwordwrapping"),
+      ("https://superfish.badssl.com/", bad, "(Lenovo) Superfish"),
+      ("https://edellroot.badssl.com/", bad, "(Dell) eDellRoot"),
+      ("https://dsdtestprovider.badssl.com/", bad, "(Dell) DSD Test Provider"),
+      ("https://preact-cli.badssl.com/", bad, "preact-cli"),
+      ("https://webpack-dev-server.badssl.com/", bad, "webpack-dev-server"),
+      ("https://mitm-software.badssl.com/", bad, "mitm-software"),
+      ("https://sha1-2016.badssl.com/", dubious, "sha1-2016"),
+      ("https://sha1-2017.badssl.com/", bad, "sha1-2017"),
+    ]
+  else:
+    const certificate_tests: array[0..0, CertTest] = [
+      ("https://google.com/", good, "good")
+    ]
 
 
   template evaluate(exception_msg: string, category: Category, desc: string) =
@@ -190,12 +196,18 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win
 
 
   type NetSocketTest = tuple[hostname: string, port: Port, category:Category, desc: string]
-  const net_tests:array[0..3, NetSocketTest] = [
-    ("imap.gmail.com", 993.Port, good, "IMAP"),
-    ("wrong.host.badssl.com", 443.Port, bad, "wrong.host"),
-    ("captive-portal.badssl.com", 443.Port, bad, "captive-portal"),
-    ("expired.badssl.com", 443.Port, bad, "expired"),
-  ]
+  # badssl certs sometimes expire, set to false when that happens
+  when true:
+    const net_tests:array[0..3, NetSocketTest] = [
+      ("imap.gmail.com", 993.Port, good, "IMAP"),
+      ("wrong.host.badssl.com", 443.Port, bad, "wrong.host"),
+      ("captive-portal.badssl.com", 443.Port, bad, "captive-portal"),
+      ("expired.badssl.com", 443.Port, bad, "expired"),
+    ]
+  else:
+    const net_tests: array[0..0, NetSocketTest] = [
+      ("imap.gmail.com", 993.Port, good, "IMAP")
+    ]
   # TODO: ("null.badssl.com", 443.Port, bad_broken, "null"),
 
 
diff --git a/tests/untestable/tpostgres.nim b/tests/untestable/tpostgres.nim
index d3397e53a..8b1378917 100644
--- a/tests/untestable/tpostgres.nim
+++ b/tests/untestable/tpostgres.nim
@@ -1,328 +1 @@
-import db_postgres, strutils
-
-
-let db = open("localhost", "dom", "", "test")
-db.exec(sql"DROP TABLE IF EXISTS myTable")
-db.exec(sql("""CREATE TABLE myTable (
-                  id integer PRIMARY KEY,
-                  name varchar(50) not null)"""))
-let name = "Dom"
-db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
-        name)
-doAssert db.getValue(sql"SELECT name FROM myTable") == name
-# Check issue #3513
-doAssert db.getValue(sql"SELECT name FROM myTable") == name
-
-
-# issue #3560
-proc addToDb(conn: DbConn, fileId: int, fileName: string): int64 =
-  result = conn.insertId(sql("INSERT into files (id, filename) VALUES (?, ?)"), fileId, fileName)
-
-db.exec(sql"DROP TABLE IF EXISTS files")
-db.exec(sql"DROP TABLE IF EXISTS fileobjects")
-db.exec(sql("""CREATE TABLE FILEOBJECTS(
-               ID             SERIAL PRIMARY KEY,
-               FILE_SIZE      INT,
-               MD5            CHAR(32)  NOT NULL UNIQUE
-            );"""))
-
-db.exec(sql("""CREATE TABLE FILES(
-               ID                  SERIAL PRIMARY KEY,
-               OBJECT_ID           INT,
-               FILENAME            TEXT NOT NULL,
-               URI                 TEXT,
-               SCHEME              CHAR(10),
-               PUBLIC              BOOLEAN DEFAULT FALSE,
-               CONSTRAINT fk1_fileobjs FOREIGN KEY (object_id)
-               REFERENCES fileobjects (id) MATCH SIMPLE
-               ON DELETE CASCADE
-            );"""))
-
-let f1 = db.addToDb(1, "hello.tmp")
-doAssert f1 == 1
-let f2 = db.addToDb(2, "hello2.tmp")
-doAssert f2 == 2
-
-# PreparedStmt vs. normal query
-try:
-  echo db.getValue(sql("select * from files where id = $1"), 1)
-  doAssert false, "Exception expected"
-except DbError:
-  let msg = getCurrentExceptionMsg().normalize
-  doAssert "expects" in msg
-  doAssert "?" in msg
-  doAssert "parameter substitution" in msg
-
-doAssert db.getValue(sql("select filename from files where id = ?"), 1) == "hello.tmp"
-
-var first = prepare(db, "one", sql"select filename from files where id = $1", 1)
-doAssert db.getValue(first, 1) == "hello.tmp"
-
-try:
-  var second = prepare(db, "two", sql"select filename from files where id = ?", 1)
-  doAssert false, "Exception expected"
-except:
-  let msg = getCurrentExceptionMsg().normalize
-  doAssert "expects" in msg
-  doAssert "$1" in msg
-  doAssert "parameter substitution" in msg
-
-# issue #3569
-db.exec(SqlQuery("DROP TABLE IF EXISTS tags"))
-db.exec(SqlQuery("CREATE TABLE tags(id serial UNIQUE, name varchar(255))"))
-
-for i in 1..10:
-  var name = "t" & $i
-  echo(name)
-  discard db.getRow(
-    SqlQuery("INSERT INTO tags(name) VALUES(\'$1\') RETURNING id" % [name]))
-    
-# get column details
-db.exec(SqlQuery("DROP TABLE IF EXISTS dbtypes;"))
-db.exec(SqlQuery("DROP TYPE IF EXISTS custom_enum;"))
-db.exec(SqlQuery("CREATE TYPE custom_enum AS ENUM ('1', '2', '3');"))
-db.exec(SqlQuery("DROP TYPE IF EXISTS custom_composite;"))
-db.exec(SqlQuery("CREATE TYPE custom_composite AS (r double precision, i double precision);"))
-db.exec(SqlQuery("""CREATE TABLE dbtypes(
-                    id serial UNIQUE,
-                    bytea_col bytea,
-                    smallint_col smallint,
-                    integer_col integer,
-                    bigint_col bigint,
-                    decimal_col decimal,
-                    numeric_col numeric,
-                    real_col real,
-                    double_precision_col double precision,
-                    smallserial_col smallserial,
-                    serial_col serial,
-                    bigserial_col bigserial,
-                    money_col money,
-                    varchar_col varchar(10),
-                    character_col character(1),
-                    text_col text,
-                    timestamp_col timestamp,
-                    date_col date,
-                    time_col time,
-                    interval_col interval,
-                    bool_col boolean,
-                    custom_enum_col custom_enum,
-                    point_col point,
-                    line_col line,
-                    lseg_col lseg,
-                    box_col box,
-                    path_col path,
-                    polygon_col polygon,
-                    circle_col circle,
-                    cidr_col cidr,
-                    inet_col inet,
-                    macaddr_col macaddr,
-                    bit_col bit,
-                    varbit_col bit(3),
-                    tsvector_col tsvector,
-                    tsquery_col tsquery,
-                    uuid_col uuid,
-                    xml_col xml,
-                    json_col json,
-                    array_col integer[],
-                    custom_composite_col custom_composite,
-                    range_col int4range
-                    );"""))
-db.exec(SqlQuery("INSERT INTO dbtypes (id) VALUES(0);"))
-
-var dbCols : DbColumns = @[]
-for row in db.instantRows(dbCols, sql"SELECT * FROM dbtypes"):
-  doAssert len(dbCols) == 42
-
-doAssert dbCols[0].name == "id"
-doAssert dbCols[0].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[0].typ.name == "int4"
-doAssert dbCols[0].typ.size == 4
-
-doAssert dbCols[1].name == "bytea_col"
-doAssert dbCols[1].typ.kind == DbTypeKind.dbBlob
-doAssert dbCols[1].typ.name == "bytea"
-
-doAssert dbCols[2].name == "smallint_col"
-doAssert dbCols[2].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[2].typ.name == "int2"
-doAssert dbCols[2].typ.size == 2
-
-doAssert dbCols[3].name == "integer_col"
-doAssert dbCols[3].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[3].typ.name == "int4"
-doAssert dbCols[3].typ.size == 4
-
-doAssert dbCols[4].name == "bigint_col"
-doAssert dbCols[4].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[4].typ.name == "int8"
-doAssert dbCols[4].typ.size == 8
-
-doAssert dbCols[5].name == "decimal_col"
-doAssert dbCols[5].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[5].typ.name == "numeric"
-
-doAssert dbCols[6].name == "numeric_col"
-doAssert dbCols[6].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[6].typ.name == "numeric"
-
-doAssert dbCols[7].name == "real_col"
-doAssert dbCols[7].typ.kind == DbTypeKind.dbFloat
-doAssert dbCols[7].typ.name == "float4"
-
-doAssert dbCols[8].name == "double_precision_col"
-doAssert dbCols[8].typ.kind == DbTypeKind.dbFloat
-doAssert dbCols[8].typ.name == "float8"
-
-doAssert dbCols[9].name == "smallserial_col"
-doAssert dbCols[9].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[9].typ.name == "int2"
-
-doAssert dbCols[10].name == "serial_col"
-doAssert dbCols[10].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[10].typ.name == "int4"
-
-doAssert dbCols[11].name == "bigserial_col"
-doAssert dbCols[11].typ.kind == DbTypeKind.dbInt
-doAssert dbCols[11].typ.name == "int8"
-
-doAssert dbCols[12].name == "money_col"
-doAssert dbCols[12].typ.kind == DbTypeKind.dbDecimal
-doAssert dbCols[12].typ.name == "money"    
-    
-doAssert dbCols[13].name == "varchar_col"
-doAssert dbCols[13].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[13].typ.name == "varchar"
-
-doAssert dbCols[14].name == "character_col"
-doAssert dbCols[14].typ.kind == DbTypeKind.dbFixedChar
-doAssert dbCols[14].typ.name == "bpchar"
-
-doAssert dbCols[15].name == "text_col"
-doAssert dbCols[15].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[15].typ.name == "text"
-
-doAssert dbCols[16].name == "timestamp_col"
-doAssert dbCols[16].typ.kind == DbTypeKind.dbTimestamp
-doAssert dbCols[16].typ.name == "timestamp"
-
-doAssert dbCols[17].name == "date_col"
-doAssert dbCols[17].typ.kind == DbTypeKind.dbDate
-doAssert dbCols[17].typ.name == "date"
-
-doAssert dbCols[18].name == "time_col"
-doAssert dbCols[18].typ.kind == DbTypeKind.dbTime
-doAssert dbCols[18].typ.name == "time"
-
-doAssert dbCols[19].name == "interval_col"
-doAssert dbCols[19].typ.kind == DbTypeKind.dbTimeInterval
-doAssert dbCols[19].typ.name == "interval"
-
-doAssert dbCols[20].name == "bool_col"
-doAssert dbCols[20].typ.kind == DbTypeKind.dbBool
-doAssert dbCols[20].typ.name == "bool"
-
-doAssert dbCols[21].name == "custom_enum_col"
-doAssert dbCols[21].typ.kind == DbTypeKind.dbUnknown
-doAssert parseInt(dbCols[21].typ.name) > 0
-    
-doAssert dbCols[22].name == "point_col"
-doAssert dbCols[22].typ.kind == DbTypeKind.dbPoint
-doAssert dbCols[22].typ.name == "point"    
-
-doAssert dbCols[23].name == "line_col"
-doAssert dbCols[23].typ.kind == DbTypeKind.dbLine
-doAssert dbCols[23].typ.name == "line"    
-
-doAssert dbCols[24].name == "lseg_col"
-doAssert dbCols[24].typ.kind == DbTypeKind.dbLseg
-doAssert dbCols[24].typ.name == "lseg"    
-
-doAssert dbCols[25].name == "box_col"
-doAssert dbCols[25].typ.kind == DbTypeKind.dbBox
-doAssert dbCols[25].typ.name == "box"    
-
-doAssert dbCols[26].name == "path_col"
-doAssert dbCols[26].typ.kind == DbTypeKind.dbPath
-doAssert dbCols[26].typ.name == "path"    
-
-doAssert dbCols[27].name == "polygon_col"
-doAssert dbCols[27].typ.kind == DbTypeKind.dbPolygon
-doAssert dbCols[27].typ.name == "polygon"
-
-doAssert dbCols[28].name == "circle_col"
-doAssert dbCols[28].typ.kind == DbTypeKind.dbCircle
-doAssert dbCols[28].typ.name == "circle"
-
-doAssert dbCols[29].name == "cidr_col"
-doAssert dbCols[29].typ.kind == DbTypeKind.dbInet
-doAssert dbCols[29].typ.name == "cidr"
-
-doAssert dbCols[30].name == "inet_col"
-doAssert dbCols[30].typ.kind == DbTypeKind.dbInet
-doAssert dbCols[30].typ.name == "inet"
-
-doAssert dbCols[31].name == "macaddr_col"
-doAssert dbCols[31].typ.kind == DbTypeKind.dbMacAddress
-doAssert dbCols[31].typ.name == "macaddr"
-
-doAssert dbCols[32].name == "bit_col"
-doAssert dbCols[32].typ.kind == DbTypeKind.dbBit
-doAssert dbCols[32].typ.name == "bit"
-
-doAssert dbCols[33].name == "varbit_col"
-doAssert dbCols[33].typ.kind == DbTypeKind.dbBit
-doAssert dbCols[33].typ.name == "bit"
-
-doAssert dbCols[34].name == "tsvector_col"
-doAssert dbCols[34].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[34].typ.name == "tsvector"
-
-doAssert dbCols[35].name == "tsquery_col"
-doAssert dbCols[35].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[35].typ.name == "tsquery"
-
-doAssert dbCols[36].name == "uuid_col"
-doAssert dbCols[36].typ.kind == DbTypeKind.dbVarchar
-doAssert dbCols[36].typ.name == "uuid"
-
-doAssert dbCols[37].name == "xml_col"
-doAssert dbCols[37].typ.kind == DbTypeKind.dbXml
-doAssert dbCols[37].typ.name == "xml"
-
-doAssert dbCols[38].name == "json_col"
-doAssert dbCols[38].typ.kind == DbTypeKind.dbJson
-doAssert dbCols[38].typ.name == "json"
-
-doAssert dbCols[39].name == "array_col"
-doAssert dbCols[39].typ.kind == DbTypeKind.dbArray
-doAssert dbCols[39].typ.name == "int4[]"
-
-doAssert dbCols[40].name == "custom_composite_col"
-doAssert dbCols[40].typ.kind == DbTypeKind.dbUnknown
-doAssert parseInt(dbCols[40].typ.name) > 0
-
-doAssert dbCols[41].name == "range_col"
-doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite
-doAssert dbCols[41].typ.name == "int4range"
-
-# issue 6571
-db.exec(sql"DROP TABLE IF EXISTS DICTIONARY")
-db.exec(sql("""CREATE TABLE DICTIONARY(
-               id             SERIAL PRIMARY KEY,
-               entry      VARCHAR(1000) NOT NULL,
-               definition VARCHAR(4000) NOT NULL
-            );"""))
-var entry = "あっそ"
-var definition = "(int) (See ああそうそう) oh, really (uninterested)/oh yeah?/hmmmmm"
-discard db.getRow(
-  SqlQuery("INSERT INTO DICTIONARY(entry, definition) VALUES(\'$1\', \'$2\') RETURNING id" % [entry, definition]))
-doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
-entry = "Format string entry"
-definition = "Format string definition"
-db.exec(sql"INSERT INTO DICTIONARY(entry, definition) VALUES (?, ?)", entry, definition)
-doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
-
-echo("All tests succeeded!")
-
-db.close()
 
diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim
index 96bb208c2..c9aeb94d8 100644
--- a/tests/varres/tprevent_forloopvar_mutations.nim
+++ b/tests/varres/tprevent_forloopvar_mutations.nim
@@ -1,8 +1,8 @@
 discard """
   errormsg: "type mismatch: got <int>"
-  nimout: '''tprevent_forloopvar_mutations.nim(16, 7) Error: type mismatch: got <int>
+  nimout: '''tprevent_forloopvar_mutations.nim(16, 3) Error: type mismatch: got <int>
 but expected one of:
-proc inc[T: Ordinal](x: var T; y = 1)
+proc inc[T, V: Ordinal](x: var T; y: V = 1)
   first type mismatch at position: 1
   required type for x: var T: Ordinal
   but expression 'i' is immutable, not 'var'
diff --git a/tests/varstmt/tvardecl.nim b/tests/varstmt/tvardecl.nim
index 37bc4bad7..d5ccfafb7 100644
--- a/tests/varstmt/tvardecl.nim
+++ b/tests/varstmt/tvardecl.nim
@@ -2,6 +2,7 @@ discard """
   output: "44"
 """
 # Test the new variable declaration syntax
+import std/sequtils
 
 var
   x = 0
@@ -10,3 +11,9 @@ var
 
 write(stdout, a)
 writeLine(stdout, b) #OUT 44
+
+proc p() = # bug #18104
+  var x, y = newSeqWith(10, newString(3))
+  discard (x, y)
+
+p()
diff --git a/tests/views/tconst_views.nim b/tests/views/tconst_views.nim
index d7f1fc481..a85b03864 100644
--- a/tests/views/tconst_views.nim
+++ b/tests/views/tconst_views.nim
@@ -24,3 +24,14 @@ proc `$`(x: openArray[int]): string =
 echo c
 echo c2.data
 
+
+type MyObj = object
+  data: openarray[char]
+
+const
+  val1 = Foo(data: toOpenArray([1, 2, 3], 1, 1))
+  val2 = Foo(data: toOpenArray([1, 2, 3], 0, 2))
+  val3 = MyObj(data: "Hello".toOpenArray(0, 2))
+assert val1.data == [2]
+assert val2.data == [1, 2, 3]
+assert val3.data == "Hel"
diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim
index b81b17f30..9785d25e5 100644
--- a/tests/views/tviews1.nim
+++ b/tests/views/tviews1.nim
@@ -77,3 +77,60 @@ type Inner = object
 var o = Outer(value: 1234)
 var v = Inner(owner: o).owner.value
 doAssert v == 1234
+
+block: # bug #21674
+  type
+    Lent = object
+      data: lent int
+
+  proc foo(s: Lent) =
+    var m = 12
+    discard cast[lent int](m)
+
+  proc main =
+    var m1 = 123
+    var x = Lent(data: m1)
+    foo(x)
+
+  main()
+
+block: # bug #22117
+  proc main: int =
+    var a = 10
+    defer: discard a # extend a's lifetime
+
+    var aref: var int = a
+      #└──── 'aref' borrows from location 'a' which does not live long enough
+
+    result = aref
+
+  doAssert main() == 10
+
+type
+  Slice*[T] = object
+    first, last: int
+    p: ptr UncheckedArray[T]
+
+var i = 0
+
+converter autoToOpenArray*[T](s: Slice[T]): openArray[T] =
+  inc i
+  result = toOpenArray(s.p, s.first, s.last)
+
+proc acceptOpenArray(s: openArray[byte]) = discard
+
+proc bug22597 = # bug #22597
+  acceptOpenArray(Slice[byte]())
+  doAssert i == 1
+
+bug22597()
+
+block: # bug #20048
+  type
+    Test = object
+      tokens: openArray[string]
+
+  func init(Self: typedesc[Test], tokens: openArray[string]): Self = Self(tokens: tokens)
+
+  let data = Test.init(["123"])
+  doAssert @(data.tokens) == @["123"] 
diff --git a/tests/views/tviews2.nim b/tests/views/tviews2.nim
new file mode 100644
index 000000000..29aff76df
--- /dev/null
+++ b/tests/views/tviews2.nim
@@ -0,0 +1,90 @@
+discard """
+  targets: "c js"
+"""
+
+{.experimental: "views".}
+
+block:
+  type
+    Foo = object
+      id: openArray[char]
+
+  proc foo(): Foo =
+    var source = "1245"
+    result = Foo(id: source.toOpenArray(0, 1))
+
+  doAssert foo().id == @['1', '2']
+
+block: # bug #15778
+  type
+    Reader = object
+      data: openArray[char]
+      current: int
+
+  var count = 0
+
+  proc read(data: var Reader, length: int): openArray[char] =
+    inc count
+    let start = data.current
+    data.current.inc length
+    return data.data.toOpenArray(start, data.current-1)
+
+  var data = "hello there"
+  var reader = Reader(data: data.toOpenArray(0, data.len-1), current: 0)
+  doAssert @(reader.read(2)) == @['h', 'e']
+  doAssert @(reader.read(3)) == @['l', 'l', 'o']
+  doAssert count == 2
+
+block: # bug #16671
+  block:
+    type X = ref object of RootObj
+    type Y = ref object of X
+      field: openArray[int]
+
+    var s: seq[X]
+    proc f() =
+      s.add(Y(field: [1]))
+
+    f()
+
+  block:
+    type X = ref object of RootObj
+    type Y = ref object of X
+      field: openArray[int]
+
+    var s: seq[X]
+    proc f() =
+      s.add(Y(field: toOpenArray([1, 2, 3], 0, 1)))
+
+    f()
+
+block: # bug #15746
+  type
+    Reader = object
+      data: openArray[char]
+      current: int
+
+  proc initReader(data: openArray[char], offset = 0): Reader =
+    result = Reader(data: data, current: offset)
+
+  let s = "\x01\x00\x00\x00"
+  doAssert initReader(s).data[0].int == 1
+
+block:
+  proc foo(x: openArray[char]) =
+    discard x
+
+  foo("12254")
+  foo(@['a', 'b'])
+
+  var a1 = "12254"
+  foo(a1)
+
+  var a2 = @['a', 'b']
+  foo(a2)
+
+  var s = "138443"
+  var ooo: openArray[char] = s
+  var xxx: openArray[char] = ooo
+  foo(ooo)
+  foo(xxx)
diff --git a/tests/vm/mscriptcompiletime.nim b/tests/vm/mscriptcompiletime.nim
new file mode 100644
index 000000000..ed7e7c029
--- /dev/null
+++ b/tests/vm/mscriptcompiletime.nim
@@ -0,0 +1,7 @@
+# bug.nim
+var bar* {.compileTime.} = 1
+
+proc dummy = discard
+
+static:
+  inc bar
\ No newline at end of file
diff --git a/tests/vm/t17121.nim b/tests/vm/t17121.nim
new file mode 100644
index 000000000..bf2d6423f
--- /dev/null
+++ b/tests/vm/t17121.nim
@@ -0,0 +1,9 @@
+discard """
+  errormsg: "cannot 'importc' variable at compile time; c_printf"
+"""
+
+proc c_printf*(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>", varargs, discardable.} =
+  ## foo bar
+  runnableExamples: discard
+static:
+  let a = c_printf("abc\n")
diff --git a/tests/vm/t18103.nim b/tests/vm/t18103.nim
new file mode 100644
index 000000000..8622ab290
--- /dev/null
+++ b/tests/vm/t18103.nim
@@ -0,0 +1,35 @@
+discard """
+  targets: "c cpp"
+  matrix: "--mm:refc; --mm:arc"
+"""
+
+import base64, complex, sequtils, math, sugar
+
+type
+
+  FP = float
+  T = object
+    index: int
+    arg: FP
+    val: Complex[FP]
+  M = object
+    alpha, beta: FP
+
+func a(s: openArray[T], model: M): seq[T] =
+  let f = (tn: int) => model.alpha + FP(tn) * model.beta;
+  return mapIt s:
+    block:
+      let s = it.val * rect(1.0, - f(it.index))
+      T(index: it.index, arg: phase(s), val: s)
+
+proc b(): float64 =
+  var s = toSeq(0..10).mapIt(T(index: it, arg: 1.0, val: complex.complex(1.0)))
+  discard a(s, M(alpha: 1, beta: 1))
+  return 1.0
+
+func cc(str: cstring, offset: ptr[cdouble]): cint {.exportc.} =
+  offset[] = b()
+  return 0
+
+static:
+  echo b()
diff --git a/tests/vm/t19075.nim b/tests/vm/t19075.nim
new file mode 100644
index 000000000..89ca9cb19
--- /dev/null
+++ b/tests/vm/t19075.nim
@@ -0,0 +1,19 @@
+discard """
+  timeout: 10
+  joinable: false
+"""
+
+# bug #19075
+const size = 50_000
+
+const stuff = block:
+    var a: array[size, int]
+    a
+
+const zeugs = block:
+    var zeugs: array[size, int]
+    for i in 0..<size:
+        zeugs[i] = stuff[i]
+    zeugs
+
+doAssert zeugs[0] == 0
\ No newline at end of file
diff --git a/tests/vm/t20746.nim b/tests/vm/t20746.nim
new file mode 100644
index 000000000..bfad269ef
--- /dev/null
+++ b/tests/vm/t20746.nim
@@ -0,0 +1,13 @@
+discard """
+  timeout: 10
+  joinable: false
+  output: "fine"
+"""
+
+func addString(): string =
+  let x = newString(1000000)
+  for i in 0..<1000000:
+    discard x[i]
+
+const translationTable = addString()
+echo "fine"
diff --git a/tests/vm/t21704.nim b/tests/vm/t21704.nim
new file mode 100644
index 000000000..27f4f5b06
--- /dev/null
+++ b/tests/vm/t21704.nim
@@ -0,0 +1,69 @@
+discard """
+matrix: "--hints:off"
+nimout: '''
+Found 2 tests to run.
+Found 3 benches to compile.
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow 
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow 
+ 
+  --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow
+'''
+"""
+# bug #21704
+import std/strformat
+
+const testDesc: seq[string] = @[
+  "tests/t_hash_sha256_vs_openssl.nim",
+  "tests/t_cipher_chacha20.nim"
+]
+const benchDesc = [
+  "bench_sha256",
+  "bench_hash_to_curve",
+  "bench_ethereum_bls_signatures"
+]
+
+proc setupTestCommand(flags, path: string): string =
+  return "nim c -r " &
+    flags &
+    &" --nimcache:nimcache/{path} " & # Commenting this out also solves the issue
+    path
+
+proc testBatch(commands: var string, flags, path: string) =
+  commands &= setupTestCommand(flags, path) & '\n'
+
+proc setupBench(benchName: string): string =
+  var runFlags = if false: " -r "
+                 else: " " # taking this branch is needed to trigger the bug
+
+  echo runFlags # Somehow runflags isn't reset in corner cases
+  runFlags &= " --passC:-Wno-stringop-overflow --passL:-Wno-stringop-overflow "
+  echo runFlags
+
+  return "nim c " &
+       runFlags &
+       &" benchmarks/{benchName}.nim"
+
+proc buildBenchBatch(commands: var string, benchName: string) =
+  let command = setupBench(benchName)
+  commands &= command & '\n'
+
+proc addTestSet(cmdFile: var string) =
+  echo "Found " & $testDesc.len & " tests to run."
+
+  for path in testDesc:
+    var flags = "" # This is important
+    cmdFile.testBatch(flags, path)
+
+proc addBenchSet(cmdFile: var string) =
+  echo "Found " & $benchDesc.len & " benches to compile."
+  for bd in benchDesc:
+    cmdFile.buildBenchBatch(bd)
+
+proc task_bug() =
+  var cmdFile: string
+  cmdFile.addTestSet() # Comment this out and there is no bug
+  cmdFile.addBenchSet()
+
+static: task_bug()
diff --git a/tests/vm/t9622.nim b/tests/vm/t9622.nim
index 214ab0f19..fada8fe59 100644
--- a/tests/vm/t9622.nim
+++ b/tests/vm/t9622.nim
@@ -1,6 +1,6 @@
 discard """
   targets: "c cpp"
-  matrix: "--gc:refc; --gc:arc"
+  matrix: "--mm:refc; --mm:arc"
 """
 
 type
diff --git a/tests/cnstseq/tcnstseq.nim b/tests/vm/tcnstseq.nim
index 5679a6e37..5679a6e37 100644
--- a/tests/cnstseq/tcnstseq.nim
+++ b/tests/vm/tcnstseq.nim
diff --git a/tests/vm/tconst.nim b/tests/vm/tconst.nim
index c2bcf78b5..5cfe7533e 100644
--- a/tests/vm/tconst.nim
+++ b/tests/vm/tconst.nim
@@ -42,6 +42,16 @@ template main() =
       discard (x, increment, a)
     mytemp()
 
+  block: # bug #12334
+    block:
+      const b: cstring = "foo"
+      var c = b
+      doAssert c == "foo"
+    block:
+      const a = "foo"
+      const b: cstring = a
+      var c = b
+      doAssert c == "foo"
 
 
 static: main()
diff --git a/tests/vm/tconstarrayresem.nim b/tests/vm/tconstarrayresem.nim
new file mode 100644
index 000000000..6701cfe4d
--- /dev/null
+++ b/tests/vm/tconstarrayresem.nim
@@ -0,0 +1,29 @@
+# issue #23010
+
+type
+  Result[T, E] = object
+    case oResult: bool
+    of false:
+      discard
+    of true:
+      vResult: T
+
+  Opt[T] = Result[T, void]
+
+template ok[T, E](R: type Result[T, E], x: untyped): R =
+  R(oResult: true, vResult: x)
+
+template c[T](v: T): Opt[T] = Opt[T].ok(v)
+
+type
+  FixedBytes[N: static[int]] = distinct array[N, byte]
+
+  H = object
+    d: FixedBytes[2]
+
+const b = default(H)
+template g(): untyped =
+  const t = default(H)
+  b
+
+discard c(g())
diff --git a/tests/vm/tconstresem.nim b/tests/vm/tconstresem.nim
new file mode 100644
index 000000000..4526cb891
--- /dev/null
+++ b/tests/vm/tconstresem.nim
@@ -0,0 +1,10 @@
+block: # issue #19849
+  type
+    Vec2[T] = object
+      x, y: T
+    Vec2i = Vec2[int]
+  template getX(p: Vec2i): int = p.x
+  let x = getX:
+    const t = Vec2i(x: 1, y: 2)
+    t
+  doAssert x == 1
diff --git a/tests/vm/tconstscope1.nim b/tests/vm/tconstscope1.nim
new file mode 100644
index 000000000..41c45a28f
--- /dev/null
+++ b/tests/vm/tconstscope1.nim
@@ -0,0 +1,5 @@
+# issue #5395
+
+const a = (var b = 3; b)
+echo b #[tt.Error
+     ^ undeclared identifier: 'b']#
diff --git a/tests/vm/tconstscope2.nim b/tests/vm/tconstscope2.nim
new file mode 100644
index 000000000..d858e96c2
--- /dev/null
+++ b/tests/vm/tconstscope2.nim
@@ -0,0 +1,5 @@
+const
+  a = (var x = 3; x)
+  # should we allow this?
+  b = x #[tt.Error
+      ^ undeclared identifier: 'x']#
diff --git a/tests/vm/tconsttable.nim b/tests/vm/tconsttable.nim
index 64a74a59d..152a33cba 100644
--- a/tests/vm/tconsttable.nim
+++ b/tests/vm/tconsttable.nim
@@ -17,3 +17,18 @@ x = "ah"
 echo foo[x]
 x = "possible."
 echo foo[x]
+
+block: # bug #19840
+  const testBytes = [byte 0xD8, 0x08, 0xDF, 0x45, 0x00, 0x3D, 0x00, 0x52, 0x00, 0x61]
+  var tempStr = "__________________"
+
+  tempStr.prepareMutation
+  copyMem(addr tempStr[0], addr testBytes[0], testBytes.len)
+
+block: # bug #22389
+  func foo(): ptr UncheckedArray[byte] =
+    const bar = [77.byte]
+    cast[ptr UncheckedArray[byte]](addr bar[0])
+
+  doAssert foo()[0] == 77
+
diff --git a/tests/vm/tconvaddr.nim b/tests/vm/tconvaddr.nim
new file mode 100644
index 000000000..9762a9e59
--- /dev/null
+++ b/tests/vm/tconvaddr.nim
@@ -0,0 +1,49 @@
+block: # issue #24097
+  type Foo = distinct int
+  proc foo(x: var Foo) =
+    int(x) += 1
+  proc bar(x: var int) =
+    x += 1
+  static:
+    var x = Foo(1)
+    int(x) = int(x) + 1
+    doAssert x.int == 2
+    int(x) += 1
+    doAssert x.int == 3
+    foo(x)
+    doAssert x.int == 4
+    bar(int(x)) # need vmgen flags propagated for this
+    doAssert x.int == 5
+  type Bar = object
+    x: Foo
+  static:
+    var obj = Bar(x: Foo(1))
+    int(obj.x) = int(obj.x) + 1
+    doAssert obj.x.int == 2
+    int(obj.x) += 1
+    doAssert obj.x.int == 3
+    foo(obj.x)
+    doAssert obj.x.int == 4
+    bar(int(obj.x)) # need vmgen flags propagated for this
+    doAssert obj.x.int == 5
+  static:
+    var arr = @[Foo(1)]
+    int(arr[0]) = int(arr[0]) + 1
+    doAssert arr[0].int == 2
+    int(arr[0]) += 1
+    doAssert arr[0].int == 3
+    foo(arr[0])
+    doAssert arr[0].int == 4
+    bar(int(arr[0])) # need vmgen flags propagated for this
+    doAssert arr[0].int == 5
+  proc testResult(): Foo =
+    result = Foo(1)
+    int(result) = int(result) + 1
+    doAssert result.int == 2
+    int(result) += 1
+    doAssert result.int == 3
+    foo(result)
+    doAssert result.int == 4
+    bar(int(result)) # need vmgen flags propagated for this
+    doAssert result.int == 5
+  doAssert testResult().int == 5
diff --git a/tests/constr/tconexpr.nim b/tests/vm/tfibconst.nim
index cca6dd84f..cca6dd84f 100644
--- a/tests/constr/tconexpr.nim
+++ b/tests/vm/tfibconst.nim
diff --git a/tests/vm/tgenericcompiletimeproc.nim b/tests/vm/tgenericcompiletimeproc.nim
new file mode 100644
index 000000000..08099ebbe
--- /dev/null
+++ b/tests/vm/tgenericcompiletimeproc.nim
@@ -0,0 +1,36 @@
+block: # issue #10753
+  proc foo(x: int): int {.compileTime.} = x
+  const a = foo(123)
+  doAssert foo(123) == a
+
+  proc bar[T](x: T): T {.compileTime.} = x
+  const b = bar(123)
+  doAssert bar(123) == b
+  const c = bar("abc")
+  doAssert bar("abc") == c
+
+block: # issue #22021
+  proc foo(x: static int): int {.compileTime.} = x + 1
+  doAssert foo(123) == 124
+
+block: # issue #19365
+  proc f[T](x: static T): T {.compileTime.} = x + x
+  doAssert f(123) == 246
+  doAssert f(1.0) == 2.0
+
+block:
+  # don't fold compile time procs in typeof
+  proc fail[T](x: T): T {.compileTime.} =
+    doAssert false
+    x
+  doAssert typeof(fail(123)) is typeof(123)
+  proc p(x: int): int = x
+
+  type Foo = typeof(p(fail(123)))
+
+block: # issue #24150, related regression
+  proc w(T: type): T {.compileTime.} = default(ptr T)[]
+  template y(v: auto): auto = typeof(v) is int
+  discard compiles(y(w int))
+  proc s(): int {.compileTime.} = discard
+  discard s()
diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim
index af09e8871..1ad830b5f 100644
--- a/tests/vm/tmisc_vm.nim
+++ b/tests/vm/tmisc_vm.nim
@@ -445,3 +445,15 @@ static:
         needSecondIdentifier.mgetOrPut(firstPart, @[]).add((name, pattern))
 
     doAssert needSecondIdentifier[0] == @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")]
+
+# bug #17864
+macro transform*(fn: typed) =
+  quote do:
+    `fn`
+
+var map: Table[string, HashSet[string]]
+proc publish*(): void {.transform.} =
+  map["k"] = init_hash_set[string]()
+  map["k"].incl "d"
+
+publish()
diff --git a/tests/vm/tnilclosurecall.nim b/tests/vm/tnilclosurecall.nim
new file mode 100644
index 000000000..449865b9c
--- /dev/null
+++ b/tests/vm/tnilclosurecall.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "attempt to call nil closure"
+  line: 8
+"""
+
+static:
+  let x: proc () = nil
+  x()
diff --git a/tests/vm/tnilclosurecallstacktrace.nim b/tests/vm/tnilclosurecallstacktrace.nim
new file mode 100644
index 000000000..879060e8e
--- /dev/null
+++ b/tests/vm/tnilclosurecallstacktrace.nim
@@ -0,0 +1,23 @@
+discard """
+  action: reject
+  nimout: '''
+stack trace: (most recent call last)
+tnilclosurecallstacktrace.nim(23, 6) tnilclosurecallstacktrace
+tnilclosurecallstacktrace.nim(20, 6) baz
+tnilclosurecallstacktrace.nim(17, 6) bar
+tnilclosurecallstacktrace.nim(14, 4) foo
+tnilclosurecallstacktrace.nim(14, 4) Error: attempt to call nil closure
+'''
+"""
+
+proc foo(x: proc ()) =
+  x()
+
+proc bar(x: proc ()) =
+  foo(x)
+
+proc baz(x: proc ()) =
+  bar(x)
+
+static:
+  baz(nil)
diff --git a/tests/constr/tnocompiletimefunc.nim b/tests/vm/tnocompiletimefunc.nim
index a95648c0f..a95648c0f 100644
--- a/tests/constr/tnocompiletimefunc.nim
+++ b/tests/vm/tnocompiletimefunc.nim
diff --git a/tests/constr/tnocompiletimefunclambda.nim b/tests/vm/tnocompiletimefunclambda.nim
index d134eea40..d134eea40 100644
--- a/tests/constr/tnocompiletimefunclambda.nim
+++ b/tests/vm/tnocompiletimefunclambda.nim
diff --git a/tests/vm/tnoreturn.nim b/tests/vm/tnoreturn.nim
new file mode 100644
index 000000000..d4f8601a9
--- /dev/null
+++ b/tests/vm/tnoreturn.nim
@@ -0,0 +1,32 @@
+block: # issue #22216
+  type
+    Result[T, E] = object
+      case oVal: bool
+      of false:
+        eVal: E
+      of true:
+        vVal: T
+
+  func raiseResultDefect(m: string) {.noreturn, noinline.} =
+    raise (ref Defect)(msg: m)
+
+  template withAssertOk(self: Result, body: untyped): untyped =
+    case self.oVal
+    of false:
+      raiseResultDefect("Trying to access value with err Result")    
+    else:
+      body
+
+  func value[T, E](self: Result[T, E]): T {.inline.} =    
+    withAssertOk(self):  
+      self.vVal
+      
+  const
+    x = Result[int, string](oVal: true, vVal: 123)
+    z = x.value()
+    
+  let
+    xx = Result[int, string](oVal: true, vVal: 123)
+    zz = x.value()
+  
+  doAssert z == zz
diff --git a/tests/vm/topenarrays.nim b/tests/vm/topenarrays.nim
new file mode 100644
index 000000000..375d2523d
--- /dev/null
+++ b/tests/vm/topenarrays.nim
@@ -0,0 +1,89 @@
+proc mutate(a: var openarray[int]) =
+  var i = 0
+  for x in a.mitems:
+    x = i
+    inc i
+
+proc mutate(a: var openarray[char]) =
+  var i = 1
+  for ch in a.mitems:
+    ch = 'a'
+
+
+static:
+  var a = [10, 20, 30]
+  assert a.toOpenArray(1, 2).len == 2
+
+  mutate(a)
+  assert a.toOpenArray(0, 2) == [0, 1, 2]
+  assert a.toOpenArray(0, 0) == [0]
+  assert a.toOpenArray(1, 2) == [1, 2]
+  assert "Hello".toOpenArray(1, 4) == "ello"
+  var str = "Hello"
+  str.toOpenArray(2, 4).mutate()
+  assert str.toOpenArray(0, 4).len == 5
+  assert str.toOpenArray(0, 0).len == 1
+  assert str.toOpenArray(0, 0).high == 0
+  assert str == "Heaaa"
+  assert str.toOpenArray(0, 4) == "Heaaa"
+
+  var arr: array[3..4, int] = [1, 2]
+  assert arr.toOpenArray(3, 4) == [1, 2]
+  assert arr.toOpenArray(3, 4).len == 2
+  assert arr.toOpenArray(3, 3).high == 0
+
+  assert arr.toOpenArray(3, 4).toOpenArray(0, 0) == [1]
+
+
+proc doThing(s: static openArray[int]) = discard
+
+doThing([10, 20, 30].toOpenArray(0, 0))
+
+# bug #19969
+proc f(): array[1, byte] =
+  var a: array[1, byte]
+  result[0..0] = a.toOpenArray(0, 0)
+
+doAssert static(f()) == [byte(0)]
+
+
+# bug #15952
+proc main1[T](a: openArray[T]) = discard
+proc main2[T](a: var openArray[T]) = discard
+
+proc main =
+  var a = [1,2,3,4,5]
+  main1(a.toOpenArray(1,3))
+  main2(a.toOpenArray(1,3))
+static: main()
+main()
+
+# bug #16306
+{.experimental: "views".}
+proc test(x: openArray[int]): tuple[id: int] =
+  let y: openArray[int] = toOpenArray(x, 0, 2)
+  result = (y[0],)
+template fn=
+  doAssert test([0,1,2,3,4,5]).id == 0
+fn() # ok
+static: fn()
+
+
+block: # bug #22095
+  type
+    StUint = object
+      limbs: array[4, uint64]
+
+  func shlAddMod(a: var openArray[uint64]) =  
+    a[0] = 10
+
+  func divRem(r: var openArray[uint64]) =
+    shlAddMod(r.toOpenArray(0, 3))
+
+  func fn(): StUint =
+    divRem(result.limbs)
+
+  const
+    z = fn()
+
+  doAssert z.limbs[0] == 10
diff --git a/tests/vm/tscriptcompiletime.nims b/tests/vm/tscriptcompiletime.nims
new file mode 100644
index 000000000..daec54bf7
--- /dev/null
+++ b/tests/vm/tscriptcompiletime.nims
@@ -0,0 +1,9 @@
+discard """
+  cmd: "nim e $file"
+"""
+
+import mscriptcompiletime
+
+macro foo =
+  doAssert bar == 2
+foo()
diff --git a/tests/vm/tsignaturehash.nim b/tests/vm/tsignaturehash.nim
index 42e0a1571..972ec6fb0 100644
--- a/tests/vm/tsignaturehash.nim
+++ b/tests/vm/tsignaturehash.nim
@@ -1,7 +1,7 @@
 # test sym digest is computable at compile time
 
 import macros, algorithm
-import md5
+import ../../dist/checksums/src/checksums/md5
 
 macro testmacro(s: typed{nkSym}): string =
   let s = getMD5(signaturehash(s) & " - " & symBodyHash(s))
diff --git a/tests/vm/ttouintconv.nim b/tests/vm/ttouintconv.nim
index ff2187a36..8c43a3adb 100644
--- a/tests/vm/ttouintconv.nim
+++ b/tests/vm/ttouintconv.nim
@@ -75,3 +75,10 @@ macro foo2() =
 
 foo()
 foo2()
+
+block:
+  const neg5VM = block:
+    let x = -5'i8
+    uint64(x)
+  let y = -5'i8
+  doAssert uint64(y) == neg5VM
diff --git a/tests/vm/ttypedesc.nim b/tests/vm/ttypedesc.nim
new file mode 100644
index 000000000..d799e5adb
--- /dev/null
+++ b/tests/vm/ttypedesc.nim
@@ -0,0 +1,31 @@
+block: # issue #15760
+  type
+    Banana = object
+    SpecialBanana = object
+    
+  proc getName(_: type Banana): string = "Banana"
+  proc getName(_: type SpecialBanana): string = "SpecialBanana"
+
+  proc x[T](): string = 
+    const n = getName(T) # this one works
+    result = n
+    
+  proc y(T: type): string =
+    const n = getName(T) # this one failed to compile
+    result = n
+
+  doAssert x[SpecialBanana]() == "SpecialBanana"
+  doAssert y(SpecialBanana) == "SpecialBanana"
+
+import macros
+
+block: # issue #23112
+  type Container = object
+    foo: string
+
+  proc canBeImplicit(t: typedesc) {.compileTime.} =
+    let tDesc = getType(t)
+    doAssert tDesc.kind == nnkObjectTy
+
+  static:
+    canBeImplicit(Container)
diff --git a/tests/vm/tunsupportedintfloatcast.nim b/tests/vm/tunsupportedintfloatcast.nim
new file mode 100644
index 000000000..d65f10d86
--- /dev/null
+++ b/tests/vm/tunsupportedintfloatcast.nim
@@ -0,0 +1,3 @@
+static:
+  echo cast[int32](12.0) #[tt.Error
+       ^ VM does not support 'cast' from tyFloat with size 8 to tyInt32 with size 4 due to different sizes]#
diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim
index dfe086d10..6aeac5529 100644
--- a/tests/vm/tvmmisc.nim
+++ b/tests/vm/tvmmisc.nim
@@ -1,7 +1,7 @@
-# bug #4462
 import macros
 import os
 
+# bug #4462
 block:
   proc foo(t: typedesc) {.compileTime.} =
     assert sameType(getType(t), getType(int))
@@ -9,14 +9,14 @@ block:
   static:
     foo(int)
 
-# #4412
+# bug #4412
 block:
   proc default[T](t: typedesc[T]): T {.inline.} = discard
 
   static:
     var x = default(type(0))
 
-# #6379
+# bug #6379
 import algorithm
 
 static:
@@ -28,7 +28,7 @@ static:
   str.sort(cmp)
   doAssert str == "abc"
 
-# #6086
+# bug #6086
 import math, sequtils, sugar
 
 block:
@@ -82,8 +82,10 @@ block:
 
     doAssert fileExists("MISSINGFILE") == false
     doAssert dirExists("MISSINGDIR") == false
+    doAssert fileExists(currentSourcePath())
+    doAssert dirExists(currentSourcePath().parentDir)
 
-# #7210
+# bug #7210
 block:
   static:
     proc f(size: int): int =
@@ -91,7 +93,7 @@ block:
       result = size
     doAssert f(4) == 4
 
-# #6689
+# bug #6689
 block:
   static:
     proc foo(): int = 0
@@ -106,7 +108,7 @@ block:
     new(x)
     doAssert(not x.isNil)
 
-# #7871
+# bug #7871
 static:
   type Obj = object
     field: int
@@ -116,11 +118,11 @@ static:
   o.field = 2
   doAssert s[0].field == 0
 
-# #8125
+# bug #8125
 static:
    let def_iter_var = ident("it")
 
-# #8142
+# bug #8142
 static:
   type Obj = object
     names: string
@@ -136,7 +138,7 @@ static:
   o.pushName()
   doAssert o.names == "FOOBARFOOBAR"
 
-# #8154
+# bug #8154
 import parseutils
 
 static:
@@ -149,7 +151,7 @@ static:
   static:
     doAssert foo().i == 1
 
-# #10333
+# bug #10333
 block:
   const
     encoding: auto = [
@@ -160,7 +162,7 @@ block:
     ]
   doAssert encoding.len == 4
 
-# #10886
+# bug #10886
 
 proc tor(): bool =
   result = true
@@ -229,6 +231,14 @@ block: # bug #15595
   static: main()
   main()
 
+block: # issue #20543
+  type F = proc()
+  const myArray = block:
+    var r: array[1, F]
+    r[0] = nil
+    r
+  doAssert isNil(myArray[0])
+
 # bug #15363
 import sequtils
 
@@ -291,14 +301,6 @@ block: # bug #10815
   const a = P()
   doAssert $a == ""
 
-when defined osx: # xxx bug https://github.com/nim-lang/Nim/issues/10815#issuecomment-476380734
-  block:
-    type CharSet {.union.} = object
-      cs: set[char]
-      vs: array[4, uint64]
-    const a = Charset(cs: {'a'..'z'})
-    doAssert a.repr.len > 0
-
 import tables
 
 block: # bug #8007
@@ -353,7 +355,7 @@ block: # bug #8007
 block: # bug #14340
   block:
     proc opl3EnvelopeCalcSin0() = discard
-    type EnvelopeSinfunc = proc()
+    type EnvelopeSinfunc = proc() {.nimcall.} # todo: fixme 
     # const EnvelopeCalcSin0 = opl3EnvelopeCalcSin0 # ok
     const EnvelopeCalcSin0: EnvelopeSinfunc = opl3EnvelopeCalcSin0 # was bug
     const envelopeSin = [EnvelopeCalcSin0]
@@ -361,7 +363,7 @@ block: # bug #14340
     envelopeSin[a]()
 
   block:
-    type Mutator = proc() {.noSideEffect, gcsafe, locks: 0.}
+    type Mutator = proc() {.noSideEffect, gcsafe.}
     proc mutator0() = discard
     const mTable = [Mutator(mutator0)]
     var i=0
@@ -564,11 +566,231 @@ block:
 block:
   static:
     let x = int 1
-    echo x.type   # Foo
+    doAssert $(x.type) == "int"  # Foo
 
 block:
   static:
     let x = int 1
     let y = x + 1
     # Error: unhandled exception: value out of range: -8 notin 0 .. 65535 [RangeDefect]
-    echo y
+    doAssert y == 2
+
+
+type Atom* = object
+  bar: int
+
+proc main() = # bug #12994
+  var s: seq[Atom]
+  var atom: Atom
+  var checked = 0
+  for i in 0..<2:
+    atom.bar = 5
+    s.add atom
+    atom.reset
+    if i == 0:
+      checked += 1
+      doAssert $s == "@[(bar: 5)]"
+    else:
+      checked += 1
+      doAssert $s == "@[(bar: 5), (bar: 5)]"
+  doAssert checked == 2
+
+static: main()
+main()
+
+# bug #19201
+proc foo(s: sink string) = doAssert s.len == 3
+
+static:
+  foo("abc")
+
+
+static:
+  for i in '1' .. '2': # bug #10938
+    var s: set[char]
+    doAssert s == {}
+    incl(s, i)
+
+  for _ in 0 ..< 3: # bug #13312
+    var s: string
+    s.add("foo")
+    doAssert s == "foo"
+
+  for i in 1 .. 5: # bug #13918
+    var arr: array[3, int]
+    var val: int
+    doAssert arr == [0, 0, 0] and val == 0
+    for j in 0 ..< len(arr):
+      arr[j] = i
+      val = i
+
+# bug #20985
+let a = block:
+  var groups: seq[seq[int]]
+  for i in 0 ..< 3:
+    var group: seq[int]
+    for j in 0 ..< 3:
+      group.add j
+    groups.add group
+  groups
+
+const b = block:
+  var groups: seq[seq[int]]
+  for i in 0 ..< 3:
+    var group: seq[int]
+    for j in 0 ..< 3:
+      group.add j
+    groups.add group
+  groups
+
+doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
+doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]]
+
+macro m1(s: string): int =
+  var ProcID {.global, compileTime.}: int
+  inc(ProcID)
+  result = newLit(ProcID)
+
+proc macroGlobal =
+  doAssert m1("Macro argument") == 1
+  doAssert m1("Macro argument") == 2
+  doAssert m1("Macro argument") == 3
+
+static: macroGlobal()
+macroGlobal()
+
+block: # bug #10108
+  template reject(x) =
+    static: doAssert(not compiles(x))
+
+  static:
+    let x: int = 2
+    proc deliver_x(): int = x
+    var y2 = deliver_x()
+    discard y2
+    reject:
+      const c5 = deliver_x()
+
+block: # bug #7590
+  proc doInit[T]():auto=
+    var a: T
+    return a
+
+  proc fun2[T](tup1:T)=
+    const tup0=doInit[T]()
+
+    # var tup=tup0 #ok
+    const tup=tup0 #causes bug
+
+    doAssert tup is tuple
+    doAssert tup[0] is tuple
+    for ai in tup.fields:
+      doAssert ai is tuple, "BUG2"
+
+  # const c=(foo:(bar1: 0.0))
+  const c=(foo:(bar1:"foo1"))
+  fun2(c)
+
+block: # bug #21708
+  type
+    Tup = tuple[name: string]
+
+  const X: array[2, Tup] = [(name: "foo",), (name: "bar",)]
+
+  static:
+    let s = X[0]
+    doAssert s[0] == "foo"
+
+block:
+  proc swap[T](x: var T): T =
+    result = x
+    x = default(T)
+
+  proc merge[T](a, b: var openArray[T]) =
+    a[0] = swap b[0]
+
+  static:
+    var x = "abc"
+    var y = "356"
+    merge(x, y)
+    doAssert x == "3bc"
+
+block: # bug #22190
+  type
+    EVMFork = enum
+      Berlin
+      Istanbul
+      Shanghai
+
+  const
+    Vm2OpAllForks =
+      {EVMFork.low .. EVMFork.high}
+
+    vm2OpExecBlockData = [(forks: Vm2OpAllForks)]
+
+  proc mkOpTable(selected: EVMFork): bool =
+    selected notin vm2OpExecBlockData[0].forks
+
+  const
+    tab = mkOpTable(Berlin)
+
+  doAssert not tab
+
+block: # issue #22524
+  const cnst = cstring(nil)
+  doAssert cnst.isNil
+  doAssert cnst == nil
+  let b = cnst
+  doAssert b.isNil
+  doAssert b == nil
+
+  let a = static: cstring(nil)
+  doAssert a.isNil
+
+  static:
+    var x: cstring
+    doAssert x.isNil
+    doAssert x == nil
+    doAssert x != ""
+
+block: # issue #15730
+  const s: cstring = ""
+  doAssert s != nil
+
+  static:
+    let s: cstring = ""
+    doAssert not s.isNil
+    doAssert s != nil
+    doAssert s == ""
+
+static: # more nil cstring issues
+  let x = cstring(nil)
+  doAssert x.len == 0
+
+block: # bug #23925
+  type Foo = enum A = -1
+  proc foo =
+    doAssert cast[Foo](-1) == A
+    doAssert ord(A) == -1
+
+  static: foo()
+  foo()
+
+  type E = enum
+    e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 e10 e11 e12 e13 e14 e15 e16 e17 e18 e19 e20
+    e21 e22 e23 e24 e25 e26 e27 e28 e29 e30 e31 e32 e33 e34 e35 e36 e37 e38
+    e39 e40 e41 e42 e43 e44 e45 e46 e47 e48 e49 e50 e51 e52 e53 e54 e55 e56
+    e57 e58 e59 e60 e61 e62 e63 e64 e65 e66 e67 e68 e69 e70 e71 e72 e73 e74
+    e75 e76 e77 e78 e79 e80 e81 e82 e83 e84 e85 e86 e87 e88 e89 e90 e91 e92
+    e93 e94 e95 e96 e97 e98 e99 e100 e101 e102 e103 e104 e105 e106 e107 e108
+    e109 e110 e111 e112 e113 e114 e115 e116 e117 e118 e119 e120 e121 e122
+    e123 e124 e125 e126 e127 e128
+  proc bar =
+    doAssert cast[E](int(e128)) == e128
+
+  static: bar()
+  bar()
+
+static: # bug #21353
+  var s: proc () = default(proc ())
+  doAssert s == nil
diff --git a/tests/vm/tvmopsDanger.nim b/tests/vm/tvmopsDanger.nim
new file mode 100644
index 000000000..966feffe6
--- /dev/null
+++ b/tests/vm/tvmopsDanger.nim
@@ -0,0 +1,13 @@
+discard """
+  cmd: "nim c --experimental:vmopsDanger -r $file"
+"""
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+import std/[times, os]
+
+const foo = getTime()
+let bar = foo
+doAssert bar > low(Time)
+
+static: # bug #23932
+  doAssert getCurrentDir().len > 0
diff --git a/tests/vm/tzero_extend.nim b/tests/vm/tzero_extend.nim
index 1fed5d419..418dbc486 100644
--- a/tests/vm/tzero_extend.nim
+++ b/tests/vm/tzero_extend.nim
@@ -5,10 +5,10 @@ proc get_values(): (seq[int8], seq[int16], seq[int32]) =
   let i8 = -3'i8
   let i16 = -3'i16
   let i32 = -3'i32
-  doAssert i8.ze == 0xFD
-  doAssert i8.ze64 == 0xFD
-  doAssert i16.ze == 0xFFFD
-  doAssert i16.ze64 == 0xFFFD
+  doAssert int(cast[uint8](i8)) == 0xFD
+  doAssert int64(cast[uint8](i8)) == 0xFD
+  doAssert int(cast[uint16](i16)) == 0xFFFD
+  doAssert int64(cast[uint16](i16)) == 0xFFFD
 
   result[0] = @[]; result[1] = @[]; result[2] = @[]
 
diff --git a/tests/whenstmt/t12517.nim b/tests/whenstmt/t12517.nim
new file mode 100644
index 000000000..8be0171e1
--- /dev/null
+++ b/tests/whenstmt/t12517.nim
@@ -0,0 +1,21 @@
+# Test based on issue #12517
+
+discard """
+  nimout: '''
+nimvm
+both
+'''
+  output: '''
+both
+'''
+"""
+
+proc test() =
+  when nimvm:
+    echo "nimvm"
+  echo "both"
+
+static:
+  test()
+test()
+
diff --git a/tests/whenstmt/t19426.nim b/tests/whenstmt/t19426.nim
new file mode 100644
index 000000000..95fb54a9e
--- /dev/null
+++ b/tests/whenstmt/t19426.nim
@@ -0,0 +1,16 @@
+type
+  MyInt = object
+    bitWidth: int
+
+template toRealType*(t: MyInt): typedesc =
+  when t.bitWidth == 32: int32
+  elif t.bitWidth == 64: int64
+  else: {.error.}
+
+proc doFail(T: typedesc): T = default(T)
+
+proc test =
+  const myInt = MyInt(bitWidth:32)
+  discard doFail(toRealType(myInt))
+
+test()
\ No newline at end of file
diff --git a/tests/xml/ttree_add.nim b/tests/xml/ttree_add.nim
new file mode 100644
index 000000000..4c6ef6cf9
--- /dev/null
+++ b/tests/xml/ttree_add.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_add() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  for item in baseDocBodyTree.items():
+    newBody.add(item)
+  
+  echo $newBody
+  
+  testDoc.add(newBody)
+  echo $testDoc
+
+test_add()
diff --git a/tests/xml/ttree_add1.nim b/tests/xml/ttree_add1.nim
new file mode 100644
index 000000000..30ec83c02
--- /dev/null
+++ b/tests/xml/ttree_add1.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_add() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  var bodyItems: seq[XmlNode] = @[]
+  for item in baseDocBodyTree.items():
+    bodyItems.add(item)
+  newBody.add(bodyItems)
+  
+  echo $newBody
+  
+  testDoc.add(newBody)
+  echo $testDoc
+
+test_add()
diff --git a/tests/xml/ttree_delete.nim b/tests/xml/ttree_delete.nim
new file mode 100644
index 000000000..32b477839
--- /dev/null
+++ b/tests/xml/ttree_delete.nim
@@ -0,0 +1,47 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_delete() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.delete(1..2)
+  echo $testDoc
+
+test_delete()
diff --git a/tests/xml/ttree_delete1.nim b/tests/xml/ttree_delete1.nim
new file mode 100644
index 000000000..a8442a093
--- /dev/null
+++ b/tests/xml/ttree_delete1.nim
@@ -0,0 +1,48 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <tag>
+    <div>MORE TEXT </div>
+    <div>MORE TEXT Some more text</div>
+  </tag>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_delete() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.delete(1)
+  testDoc.delete(1)
+  echo $testDoc
+
+test_delete()
diff --git a/tests/xml/ttree_insert.nim b/tests/xml/ttree_insert.nim
new file mode 100644
index 000000000..b2941395b
--- /dev/null
+++ b/tests/xml/ttree_insert.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_insert() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  var bodyItems: seq[XmlNode] = @[]
+  for item in baseDocBodyTree.items():
+    bodyItems.insert(item, len(bodyItems))
+  newBody.insert(bodyItems, 1)
+  
+  echo $newBody
+  
+  testDoc.insert(newBody, 1)
+  echo $testDoc
+
+test_insert()
diff --git a/tests/xml/ttree_insert1.nim b/tests/xml/ttree_insert1.nim
new file mode 100644
index 000000000..9aa3faf69
--- /dev/null
+++ b/tests/xml/ttree_insert1.nim
@@ -0,0 +1,51 @@
+discard """
+  output: '''
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+</xml>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+
+proc test_insert() =
+  var testDoc = baseDocHeadTree
+  var newBody = newElement("body")
+  for item in baseDocBodyTree.items():
+    newBody.insert(item, len(newBody))
+  
+  echo $newBody
+  
+  testDoc.insert(newBody, 1)
+  echo $testDoc
+
+test_insert()
diff --git a/tests/xml/ttree_replace.nim b/tests/xml/ttree_replace.nim
new file mode 100644
index 000000000..97d2db638
--- /dev/null
+++ b/tests/xml/ttree_replace.nim
@@ -0,0 +1,46 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body before replace </div>
+    <div>Some more text in body before replace </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_replace() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.replace(1, @[baseDocBodyTree])
+  echo $testDoc
+
+test_replace()
diff --git a/tests/xml/ttree_replace1.nim b/tests/xml/ttree_replace1.nim
new file mode 100644
index 000000000..059ce2085
--- /dev/null
+++ b/tests/xml/ttree_replace1.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+<xml>
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+  <body>
+    <div>Some text in body</div>
+    <div>Some more text in body </div>
+  </body>
+</xml>
+'''
+"""
+
+# Test xmltree add/insert/delete/replace operations
+import xmlparser
+import xmltree
+var baseDocHead = """
+  <head>
+    <div>Some text</div>
+    <div>Some more text </div>
+  </head>
+"""
+var baseDocHeadTree = parseXml(baseDocHead)
+var baseDocBody = """
+<body>
+  <div>Some text in body</div>
+  <div>Some more text in body </div>
+</body>
+"""
+var baseDocBodyTree = parseXml(baseDocBody)
+let initialDocBase = """
+<xml>
+  <head>
+    <div>Some text before replace </div>
+    <div>Some more text before replace </div>
+  </head>
+  <body>
+    <div>Some text in body before replace </div>
+    <div>Some more text in body before replace </div>
+  </body>
+</xml>
+"""
+var initialDocBaseTree = parseXml(initialDocBase)
+
+proc test_replace() =
+  var testDoc = initialDocBaseTree
+  
+  testDoc.replace(0..1, @[baseDocHeadTree, baseDocBodyTree])
+  echo $testDoc
+
+test_replace()
diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md
deleted file mode 100644
index a36817dc5..000000000
--- a/tools/atlas/atlas.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Atlas Package Cloner
-
-Atlas is a simple package cloner tool that automates some of the
-workflows and needs for Nim's stdlib evolution.
-
-Atlas is compatible with Nimble in the sense that it supports the Nimble
-file format.
-
-
-## How it works
-
-Atlas uses git commits internally; version requirements are translated
-to git commits via `git show-ref --tags`.
-
-Atlas uses URLs internally; Nimble package names are translated to URLs
-via Nimble's  `packages.json` file.
-
-Atlas does not call the Nim compiler for a build, instead it creates/patches
-a `nim.cfg` file for the compiler. For example:
-
-```
-############# begin Atlas config section ##########
---noNimblePath
---path:"../nimx"
---path:"../sdl2/src"
---path:"../opengl/src"
-############# end Atlas config section   ##########
-```
-
-The version selection is deterministic, it picks up the *minimum* required
-version. Thanks to this design, lock files are not required.
-
-
-## Dependencies
-
-Dependencies are neither installed globally, nor locally into the current
-project. Instead a "workspace" is used. The workspace is the nearest parent
-directory of the current directory that does not contain a `.git` subdirectory.
-Dependencies are managed as **siblings**, not as children. Dependencies are
-kept as git repositories.
-
-Thanks to this setup, it's easy to develop multiple projects at the same time.
-
-A project plus its dependencies are stored in a workspace:
-
-  $workspace / main project
-  $workspace / dependency A
-  $workspace / dependency B
-
-
-No attempts are being made at keeping directory hygiene inside the
-workspace, you're supposed to create appropriate `$workspace` directories
-at your own leisure.
-
-
-## Commands
-
-Atlas supports the following commands:
-
-
-### Clone <url>
-
-Clones a URL and all of its dependencies (recursively) into the workspace.
-Creates or patches a `nim.cfg` file with the required `--path` entries.
-
-
-### Clone <package name>
-
-The `<package name>` is translated into an URL via `packages.json` and
-then `clone <url>` is performed.
-
-
-### Search <term term2 term3 ...>
-
-Search the package index `packages.json` for a package that the given terms
-in its description (or name or list of tags).
-
diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim
deleted file mode 100644
index e604c3167..000000000
--- a/tools/atlas/atlas.nim
+++ /dev/null
@@ -1,509 +0,0 @@
-#
-#           Atlas Package Cloner
-#        (c) Copyright 2021 Andreas Rumpf
-#
-#    See the file "copying.txt", included in this
-#    distribution, for details about the copyright.
-#
-
-## Simple tool to automate frequent workflows: Can "clone"
-## a Nimble dependency and its dependencies recursively.
-
-import std/[parseopt, strutils, os, osproc, unicode, tables, sets, json, jsonutils]
-import parse_requires, osutils, packagesjson
-
-const
-  Version = "0.1"
-  Usage = "atlas - Nim Package Cloner Version " & Version & """
-
-  (c) 2021 Andreas Rumpf
-Usage:
-  atlas [options] [command] [arguments]
-Command:
-  clone url|pkgname     clone a package and all of its dependencies
-  search keyw keywB...  search for package that contains the given keywords
-  extract file.nimble   extract the requirements and custom commands from
-                        the given Nimble file
-
-Options:
-  --keepCommits         do not perform any `git checkouts`
-  --cfgHere             also create/maintain a nim.cfg in the current
-                        working directory
-  --version             show the version
-  --help                show this help
-"""
-
-proc writeHelp() =
-  stdout.write(Usage)
-  stdout.flushFile()
-  quit(0)
-
-proc writeVersion() =
-  stdout.write(Version & "\n")
-  stdout.flushFile()
-  quit(0)
-
-const
-  MockupRun = defined(atlasTests)
-  TestsDir = "tools/atlas/tests"
-
-type
-  PackageName = distinct string
-  DepRelation = enum
-    normal, strictlyLess, strictlyGreater
-
-  Dependency = object
-    name: PackageName
-    url, commit: string
-    rel: DepRelation # "requires x < 1.0" is silly, but Nimble allows it so we have too.
-  AtlasContext = object
-    projectDir, workspace: string
-    hasPackageList: bool
-    keepCommits: bool
-    cfgHere: bool
-    p: Table[string, string] # name -> url mapping
-    processed: HashSet[string] # the key is (url / commit)
-    errors: int
-    when MockupRun:
-      currentDir: string
-      step: int
-      mockupSuccess: bool
-
-const
-  InvalidCommit = "<invalid commit>"
-  ProduceTest = false
-
-type
-  Command = enum
-    GitDiff = "git diff",
-    GitTags = "git show-ref --tags",
-    GitRevParse = "git rev-parse",
-    GitCheckout = "git checkout",
-    GitPull = "git pull",
-    GitCurrentCommit = "git log -n 1 --format=%H"
-    GitMergeBase = "git merge-base"
-
-include testdata
-
-proc exec(c: var AtlasContext; cmd: Command; args: openArray[string]): (string, int) =
-  when MockupRun:
-    assert TestLog[c.step].cmd == cmd
-    case cmd
-    of GitDiff, GitTags, GitRevParse, GitPull, GitCurrentCommit:
-      result = (TestLog[c.step].output, TestLog[c.step].exitCode)
-    of GitCheckout:
-      assert args[0] == TestLog[c.step].output
-    of GitMergeBase:
-      let tmp = TestLog[c.step].output.splitLines()
-      assert tmp.len == 4, $tmp.len
-      assert tmp[0] == args[0]
-      assert tmp[1] == args[1]
-      assert tmp[3] == ""
-      result[0] = tmp[2]
-      result[1] = TestLog[c.step].exitCode
-    inc c.step
-  else:
-    var cmdLine = $cmd
-    for i in 0..<args.len:
-      cmdLine.add ' '
-      cmdLine.add quoteShell(args[i])
-    result = osproc.execCmdEx(cmdLine)
-    when ProduceTest:
-      echo "cmd ", cmd, " args ", args, " --> ", result
-
-proc cloneUrl(c: var AtlasContext; url, dest: string; cloneUsingHttps: bool): string =
-  when MockupRun:
-    result = ""
-  else:
-    result = osutils.cloneUrl(url, dest, cloneUsingHttps)
-    when ProduceTest:
-      echo "cloned ", url, " into ", dest
-
-template withDir*(c: var AtlasContext; dir: string; body: untyped) =
-  when MockupRun:
-    c.currentDir = dir
-    body
-  else:
-    let oldDir = getCurrentDir()
-    try:
-      when ProduceTest:
-        echo "Current directory is now ", dir
-      setCurrentDir(dir)
-      body
-    finally:
-      setCurrentDir(oldDir)
-
-proc extractRequiresInfo(c: var AtlasContext; nimbleFile: string): NimbleFileInfo =
-  result = extractRequiresInfo(nimbleFile)
-  when ProduceTest:
-    echo "nimble ", nimbleFile, " info ", result
-
-proc toDepRelation(s: string): DepRelation =
-  case s
-  of "<": strictlyLess
-  of ">": strictlyGreater
-  else: normal
-
-proc isCleanGit(c: var AtlasContext; dir: string): string =
-  result = ""
-  let (outp, status) = exec(c, GitDiff, [])
-  if outp.len != 0:
-    result = "'git diff' not empty"
-  elif status != 0:
-    result = "'git diff' returned non-zero"
-
-proc message(c: var AtlasContext; category: string; p: PackageName; args: varargs[string]) =
-  var msg = category & "(" & p.string & ")"
-  for a in args:
-    msg.add ' '
-    msg.add a
-  stdout.writeLine msg
-  inc c.errors
-
-proc warn(c: var AtlasContext; p: PackageName; args: varargs[string]) =
-  message(c, "[Warning] ", p, args)
-
-proc error(c: var AtlasContext; p: PackageName; args: varargs[string]) =
-  message(c, "[Error] ", p, args)
-
-proc sameVersionAs(tag, ver: string): bool =
-  const VersionChars = {'0'..'9', '.'}
-
-  proc safeCharAt(s: string; i: int): char {.inline.} =
-    if i >= 0 and i < s.len: s[i] else: '\0'
-
-  let idx = find(tag, ver)
-  if idx >= 0:
-    # we found the version as a substring inside the `tag`. But we
-    # need to watch out the the boundaries are not part of a
-    # larger/different version number:
-    result = safeCharAt(tag, idx-1) notin VersionChars and
-      safeCharAt(tag, idx+ver.len) notin VersionChars
-
-proc versionToCommit(c: var AtlasContext; d: Dependency): string =
-  let (outp, status) = exec(c, GitTags, [])
-  if status == 0:
-    var useNextOne = false
-    for line in splitLines(outp):
-      let commitsAndTags = strutils.splitWhitespace(line)
-      if commitsAndTags.len == 2:
-        case d.rel
-        of normal:
-          if commitsAndTags[1].sameVersionAs(d.commit):
-            return commitsAndTags[0]
-        of strictlyLess:
-          if d.commit == InvalidCommit or not commitsAndTags[1].sameVersionAs(d.commit):
-            return commitsAndTags[0]
-        of strictlyGreater:
-          if commitsAndTags[1].sameVersionAs(d.commit):
-            useNextOne = true
-          elif useNextOne:
-            return commitsAndTags[0]
-
-  return ""
-
-proc shortToCommit(c: var AtlasContext; short: string): string =
-  let (cc, status) = exec(c, GitRevParse, [short])
-  result = if status == 0: strutils.strip(cc) else: ""
-
-proc checkoutGitCommit(c: var AtlasContext; p: PackageName; commit: string) =
-  let (_, status) = exec(c, GitCheckout, [commit])
-  if status != 0:
-    error(c, p, "could not checkout commit", commit)
-
-proc gitPull(c: var AtlasContext; p: PackageName) =
-  let (_, status) = exec(c, GitPull, [])
-  if status != 0:
-    error(c, p, "could not 'git pull'")
-
-proc updatePackages(c: var AtlasContext) =
-  if dirExists(c.workspace / PackagesDir):
-    withDir(c, c.workspace / PackagesDir):
-      gitPull(c, PackageName PackagesDir)
-  else:
-    withDir c, c.workspace:
-      let err = cloneUrl(c, "https://github.com/nim-lang/packages", PackagesDir, false)
-      if err != "":
-        error c, PackageName(PackagesDir), err
-
-proc fillPackageLookupTable(c: var AtlasContext) =
-  if not c.hasPackageList:
-    c.hasPackageList = true
-    when not MockupRun:
-      updatePackages(c)
-    let plist = getPackages(when MockupRun: TestsDir else: c.workspace)
-    for entry in plist:
-      c.p[unicode.toLower entry.name] = entry.url
-
-proc toUrl(c: var AtlasContext; p: string): string =
-  if p.isUrl:
-    result = p
-  else:
-    fillPackageLookupTable(c)
-    result = c.p.getOrDefault(unicode.toLower p)
-  if result.len == 0:
-    inc c.errors
-
-proc toName(p: string): PackageName =
-  if p.isUrl:
-    result = PackageName splitFile(p).name
-  else:
-    result = PackageName p
-
-proc needsCommitLookup(commit: string): bool {.inline.} =
-  '.' in commit or commit == InvalidCommit
-
-proc isShortCommitHash(commit: string): bool {.inline.} =
-  commit.len >= 4 and commit.len < 40
-
-proc checkoutCommit(c: var AtlasContext; w: Dependency) =
-  let dir = c.workspace / w.name.string
-  withDir c, dir:
-    if w.commit.len == 0 or cmpIgnoreCase(w.commit, "head") == 0:
-      gitPull(c, w.name)
-    else:
-      let err = isCleanGit(c, dir)
-      if err != "":
-        warn c, w.name, err
-      else:
-        let requiredCommit =
-          if needsCommitLookup(w.commit): versionToCommit(c, w)
-          elif isShortCommitHash(w.commit): shortToCommit(c, w.commit)
-          else: w.commit
-        let (cc, status) = exec(c, GitCurrentCommit, [])
-        let currentCommit = strutils.strip(cc)
-        if requiredCommit == "" or status != 0:
-          if requiredCommit == "" and w.commit == InvalidCommit:
-            warn c, w.name, "package has no tagged releases"
-          else:
-            warn c, w.name, "cannot find specified version/commit", w.commit
-        else:
-          if currentCommit != requiredCommit:
-            # checkout the later commit:
-            # git merge-base --is-ancestor <commit> <commit>
-            let (cc, status) = exec(c, GitMergeBase, [currentCommit, requiredCommit])
-            let mergeBase = strutils.strip(cc)
-            if status == 0 and (mergeBase == currentCommit or mergeBase == requiredCommit):
-              # conflict resolution: pick the later commit:
-              if mergeBase == currentCommit:
-                checkoutGitCommit(c, w.name, requiredCommit)
-            else:
-              checkoutGitCommit(c, w.name, requiredCommit)
-              when false:
-                warn c, w.name, "do not know which commit is more recent:",
-                  currentCommit, "(current) or", w.commit, " =", requiredCommit, "(required)"
-
-proc findNimbleFile(c: AtlasContext; dep: Dependency): string =
-  when MockupRun:
-    result = TestsDir / dep.name.string & ".nimble"
-    doAssert fileExists(result), "file does not exist " & result
-  else:
-    result = c.workspace / dep.name.string / (dep.name.string & ".nimble")
-    if not fileExists(result):
-      result = ""
-      for x in walkFiles(c.workspace / dep.name.string / "*.nimble"):
-        if result.len == 0:
-          result = x
-        else:
-          # ambiguous .nimble file
-          return ""
-
-proc addUniqueDep(c: var AtlasContext; work: var seq[Dependency];
-                  tokens: seq[string]) =
-  let oldErrors = c.errors
-  let url = toUrl(c, tokens[0])
-  if oldErrors != c.errors:
-    warn c, toName(tokens[0]), "cannot resolve package name"
-  elif not c.processed.containsOrIncl(url / tokens[2]):
-    work.add Dependency(name: toName(tokens[0]), url: url, commit: tokens[2],
-                        rel: toDepRelation(tokens[1]))
-
-proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency];
-                    dep: Dependency; result: var seq[string];
-                    isMainProject: bool) =
-  let nimbleFile = findNimbleFile(c, dep)
-  if nimbleFile != "":
-    let nimbleInfo = extractRequiresInfo(c, nimbleFile)
-    for r in nimbleInfo.requires:
-      var tokens: seq[string] = @[]
-      for token in tokenizeRequires(r):
-        tokens.add token
-      if tokens.len == 1:
-        # nimx uses dependencies like 'requires "sdl2"'.
-        # Via this hack we map them to the first tagged release.
-        # (See the `isStrictlySmallerThan` logic.)
-        tokens.add "<"
-        tokens.add InvalidCommit
-      elif tokens.len == 2 and tokens[1].startsWith("#"):
-        # Dependencies can also look like 'requires "sdl2#head"
-        var commit = tokens[1][1 .. ^1]
-        tokens[1] = "=="
-        tokens.add commit
-
-      if tokens.len >= 3 and cmpIgnoreCase(tokens[0], "nim") != 0:
-        c.addUniqueDep work, tokens
-
-    result.add dep.name.string / nimbleInfo.srcDir
-  else:
-    result.add dep.name.string
-
-proc clone(c: var AtlasContext; start: string): seq[string] =
-  # non-recursive clone.
-  let oldErrors = c.errors
-  var work = @[Dependency(name: toName(start), url: toUrl(c, start), commit: "")]
-
-  if oldErrors != c.errors:
-    error c, toName(start), "cannot resolve package name"
-    return
-
-  c.projectDir = c.workspace / work[0].name.string
-  result = @[]
-  var i = 0
-  while i < work.len:
-    let w = work[i]
-    let oldErrors = c.errors
-    if not dirExists(c.workspace / w.name.string):
-      withDir c, c.workspace:
-        let err = cloneUrl(c, w.url, w.name.string, false)
-        if err != "":
-          error c, w.name, err
-    if oldErrors == c.errors:
-      if not c.keepCommits: checkoutCommit(c, w)
-      # even if the checkout fails, we can make use of the somewhat
-      # outdated .nimble file to clone more of the most likely still relevant
-      # dependencies:
-      collectNewDeps(c, work, w, result, i == 0)
-    inc i
-
-const
-  configPatternBegin = "############# begin Atlas config section ##########\n"
-  configPatternEnd =   "############# end Atlas config section   ##########\n"
-
-proc patchNimCfg(c: var AtlasContext; deps: seq[string]; cfgHere: bool) =
-  var paths = "--noNimblePath\n"
-  if cfgHere:
-    let cwd = getCurrentDir()
-    for d in deps:
-      let x = relativePath(c.workspace / d, cwd, '/')
-      paths.add "--path:\"" & x & "\"\n"
-  else:
-    for d in deps:
-      paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n"
-
-  var cfgContent = configPatternBegin & paths & configPatternEnd
-
-  when MockupRun:
-    assert readFile(TestsDir / "nim.cfg") == cfgContent
-    c.mockupSuccess = true
-  else:
-    let cfg = if cfgHere: getCurrentDir() / "nim.cfg"
-              else: c.projectDir / "nim.cfg"
-    if not fileExists(cfg):
-      writeFile(cfg, cfgContent)
-    else:
-      let content = readFile(cfg)
-      let start = content.find(configPatternBegin)
-      if start >= 0:
-        cfgContent = content.substr(0, start-1) & cfgContent
-        let theEnd = content.find(configPatternEnd, start)
-        if theEnd >= 0:
-          cfgContent.add content.substr(theEnd+len(configPatternEnd))
-      else:
-        cfgContent = content & "\n" & cfgContent
-      if cfgContent != content:
-        # do not touch the file if nothing changed
-        # (preserves the file date information):
-        writeFile(cfg, cfgContent)
-
-proc error*(msg: string) =
-  when defined(debug):
-    writeStackTrace()
-  quit "[Error] " & msg
-
-proc main =
-  var action = ""
-  var args: seq[string] = @[]
-  template singleArg() =
-    if args.len != 1:
-      error action & " command takes a single package name"
-
-  template noArgs() =
-    if args.len != 0:
-      error action & " command takes no arguments"
-
-  var c = AtlasContext(
-    projectDir: getCurrentDir(),
-    workspace: getCurrentDir())
-
-  for kind, key, val in getopt():
-    case kind
-    of cmdArgument:
-      if action.len == 0:
-        action = key.normalize
-      else:
-        args.add key
-    of cmdLongOption, cmdShortOption:
-      case normalize(key)
-      of "help", "h": writeHelp()
-      of "version", "v": writeVersion()
-      of "keepcommits": c.keepCommits = true
-      of "cfghere": c.cfgHere = true
-      else: writeHelp()
-    of cmdEnd: assert false, "cannot happen"
-
-  while c.workspace.len > 0 and dirExists(c.workspace / ".git"):
-    c.workspace = c.workspace.parentDir()
-
-  case action
-  of "":
-    error "No action."
-  of "clone":
-    singleArg()
-    let deps = clone(c, args[0])
-    patchNimCfg c, deps, false
-    if c.cfgHere:
-      patchNimCfg c, deps, true
-    when MockupRun:
-      if not c.mockupSuccess:
-        error "There were problems."
-    else:
-      if c.errors > 0:
-        error "There were problems."
-  of "refresh":
-    noArgs()
-    updatePackages(c)
-  of "search", "list":
-    updatePackages(c)
-    search getPackages(c.workspace), args
-  of "extract":
-    singleArg()
-    if fileExists(args[0]):
-      echo toJson(extractRequiresInfo(args[0]))
-    else:
-      error "File does not exist: " & args[0]
-  else:
-    error "Invalid action: " & action
-
-when isMainModule:
-  main()
-
-when false:
-  # some testing code for the `patchNimCfg` logic:
-  var c = AtlasContext(
-    projectDir: getCurrentDir(),
-    workspace: getCurrentDir().parentDir)
-
-  patchNimCfg(c, @[PackageName"abc", PackageName"xyz"])
-
-when false:
-  assert sameVersionAs("v0.2.0", "0.2.0")
-  assert sameVersionAs("v1", "1")
-
-  assert sameVersionAs("1.90", "1.90")
-
-  assert sameVersionAs("v1.2.3-zuzu", "1.2.3")
-  assert sameVersionAs("foo-1.2.3.4", "1.2.3.4")
-
-  assert not sameVersionAs("foo-1.2.3.4", "1.2.3")
-  assert not sameVersionAs("foo", "1.2.3")
-  assert not sameVersionAs("", "1.2.3")
diff --git a/tools/atlas/osutils.nim b/tools/atlas/osutils.nim
deleted file mode 100644
index 6134830b5..000000000
--- a/tools/atlas/osutils.nim
+++ /dev/null
@@ -1,51 +0,0 @@
-## OS utilities like 'withDir'.
-## (c) 2021 Andreas Rumpf
-
-import os, strutils, osproc
-
-proc isUrl*(x: string): bool =
-  x.startsWith("git://") or x.startsWith("https://") or x.startsWith("http://")
-
-proc cloneUrl*(url, dest: string; cloneUsingHttps: bool): string =
-  ## Returns an error message on error or else "".
-  result = ""
-  var modUrl =
-    if url.startsWith("git://") and cloneUsingHttps:
-      "https://" & url[6 .. ^1]
-    else: url
-
-  # github + https + trailing url slash causes a
-  # checkout/ls-remote to fail with Repository not found
-  var isGithub = false
-  if modUrl.contains("github.com") and modUrl.endsWith("/"):
-    modUrl = modUrl[0 .. ^2]
-    isGithub = true
-
-  let (_, exitCode) = execCmdEx("git ls-remote --quiet --tags " & modUrl)
-  var xcode = exitCode
-  if isGithub and exitCode != QuitSuccess:
-    # retry multiple times to avoid annoying github timeouts:
-    for i in 0..4:
-      os.sleep(4000)
-      xcode = execCmdEx("git ls-remote --quiet --tags " & modUrl)[1]
-      if xcode == QuitSuccess: break
-
-  if xcode == QuitSuccess:
-    # retry multiple times to avoid annoying github timeouts:
-    let cmd = "git clone " & modUrl & " " & dest
-    for i in 0..4:
-      if execShellCmd(cmd) == 0: return ""
-      os.sleep(4000)
-    result = "exernal program failed: " & cmd
-  elif not isGithub:
-    let (_, exitCode) = execCmdEx("hg identify " & modUrl)
-    if exitCode == QuitSuccess:
-      let cmd = "hg clone " & modUrl & " " & dest
-      for i in 0..4:
-        if execShellCmd(cmd) == 0: return ""
-        os.sleep(4000)
-      result = "exernal program failed: " & cmd
-    else:
-      result = "Unable to identify url: " & modUrl
-  else:
-    result = "Unable to identify url: " & modUrl
diff --git a/tools/atlas/packagesjson.nim b/tools/atlas/packagesjson.nim
deleted file mode 100644
index 0b8599769..000000000
--- a/tools/atlas/packagesjson.nim
+++ /dev/null
@@ -1,114 +0,0 @@
-
-import std / [json, os, sets, strutils]
-
-type
-  Package* = ref object
-    # Required fields in a package.
-    name*: string
-    url*: string # Download location.
-    license*: string
-    downloadMethod*: string
-    description*: string
-    tags*: seq[string] # \
-    # From here on, optional fields set to the empty string if not available.
-    version*: string
-    dvcsTag*: string
-    web*: string # Info url for humans.
-
-proc optionalField(obj: JsonNode, name: string, default = ""): string =
-  if hasKey(obj, name) and obj[name].kind == JString:
-    result = obj[name].str
-  else:
-    result = default
-
-proc requiredField(obj: JsonNode, name: string): string =
-  result = optionalField(obj, name, "")
-
-proc fromJson*(obj: JSonNode): Package =
-  result = Package()
-  result.name = obj.requiredField("name")
-  if result.name.len == 0: return nil
-  result.version = obj.optionalField("version")
-  result.url = obj.requiredField("url")
-  if result.url.len == 0: return nil
-  result.downloadMethod = obj.requiredField("method")
-  if result.downloadMethod.len == 0: return nil
-  result.dvcsTag = obj.optionalField("dvcs-tag")
-  result.license = obj.optionalField("license")
-  result.tags = @[]
-  for t in obj["tags"]:
-    result.tags.add(t.str)
-  result.description = obj.requiredField("description")
-  result.web = obj.optionalField("web")
-
-const PackagesDir* = "packages"
-
-proc getPackages*(workspaceDir: string): seq[Package] =
-  result = @[]
-  var uniqueNames = initHashSet[string]()
-  var jsonFiles = 0
-  for kind, path in walkDir(workspaceDir / PackagesDir):
-    if kind == pcFile and path.endsWith(".json"):
-      inc jsonFiles
-      let packages = json.parseFile(path)
-      for p in packages:
-        let pkg = p.fromJson()
-        if pkg != nil and not uniqueNames.containsOrIncl(pkg.name):
-          result.add(pkg)
-
-proc `$`*(pkg: Package): string =
-  result = pkg.name & ":\n"
-  result &= "  url:         " & pkg.url & " (" & pkg.downloadMethod & ")\n"
-  result &= "  tags:        " & pkg.tags.join(", ") & "\n"
-  result &= "  description: " & pkg.description & "\n"
-  result &= "  license:     " & pkg.license & "\n"
-  if pkg.web.len > 0:
-    result &= "  website:     " & pkg.web & "\n"
-
-proc search*(pkgList: seq[Package]; terms: seq[string]) =
-  var found = false
-  template onFound =
-    echo pkg
-    found = true
-    break forPackage
-
-  for pkg in pkgList:
-    if terms.len > 0:
-      block forPackage:
-        for term in terms:
-          let word = term.toLower
-          # Search by name.
-          if word in pkg.name.toLower:
-            onFound()
-          # Search by tag.
-          for tag in pkg.tags:
-            if word in tag.toLower:
-              onFound()
-    else:
-      echo(pkg)
-
-  if not found and terms.len > 0:
-    echo("No package found.")
-
-type PkgCandidates* = array[3, seq[Package]]
-
-proc determineCandidates*(pkgList: seq[Package];
-                         terms: seq[string]): PkgCandidates =
-  result[0] = @[]
-  result[1] = @[]
-  result[2] = @[]
-  for pkg in pkgList:
-    block termLoop:
-      for term in terms:
-        let word = term.toLower
-        if word == pkg.name.toLower:
-          result[0].add pkg
-          break termLoop
-        elif word in pkg.name.toLower:
-          result[1].add pkg
-          break termLoop
-        else:
-          for tag in pkg.tags:
-            if word in tag.toLower:
-              result[2].add pkg
-              break termLoop
diff --git a/tools/atlas/parse_requires.nim b/tools/atlas/parse_requires.nim
index 7e26a1656..66879d04f 100644
--- a/tools/atlas/parse_requires.nim
+++ b/tools/atlas/parse_requires.nim
@@ -2,7 +2,7 @@
 ## (c) 2021 Andreas Rumpf
 
 import std / strutils
-import ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
+import ".." / ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils]
 
 type
   NimbleFileInfo* = object
diff --git a/tools/atlas/testdata.nim b/tools/atlas/testdata.nim
deleted file mode 100644
index aefaeacd2..000000000
--- a/tools/atlas/testdata.nim
+++ /dev/null
@@ -1,63 +0,0 @@
-
-type
-  PerDirData = object
-    dirname: string
-    cmd: Command
-    exitCode: int
-    output: string
-
-template toData(a, b, c, d): untyped =
-  PerDirData(dirname: a, cmd: b, exitCode: c, output: d)
-
-const
-  TestLog = [
-    toData("balls", GitPull, 0, "Already up to date.\n"),
-    toData("grok", GitDiff, 0, ""),
-    toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"),
-    toData("grok", GitCurrentCommit, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
-    toData("grok", GitMergeBase, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n1b5643d04fba6d996a16d1ffc13d034a40003f8f\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
-    toData("grok", GitCheckout, 0, "1b5643d04fba6d996a16d1ffc13d034a40003f8f"), # watch out!
-
-    toData("ups", GitDiff, 0, ""),
-    toData("ups", GitTags, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1 refs/tags/0.0.2\n19bc490c22b4f5b0628c31cdedead1375b279356 refs/tags/0.0.3\nff34602aaea824cb46d6588cd5fe1178132e9702 refs/tags/0.0.4\n09de599138f20b745133b6e4fe563e204415a7e8 refs/tags/0.0.5\n85fee3b74798311108a105635df31f892150f5d0 refs/tags/0.0.6\nfd303913b22b121dc42f332109e9c44950b9acd4 refs/tags/0.0.7\n"),
-    toData("ups", GitCurrentCommit, 0, "74c31af8030112dac758440aa51ef175992f71f3\n"),
-    toData("ups", GitMergeBase, 0, "74c31af8030112dac758440aa51ef175992f71f3\n4008f9339cd22b30e180bc87a6cca7270fd28ac1\n74c31af8030112dac758440aa51ef175992f71f3\n"),
-    toData("ups", GitCheckout, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1"),
-
-    toData("sync", GitDiff, 0, ""),
-    toData("sync", GitRevParse, 0, "810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"),
-    toData("sync", GitCurrentCommit, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n"),
-    toData("sync", GitMergeBase, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"),
-
-    toData("npeg", GitDiff, 0, ""),
-    toData("npeg", GitTags, 0, "8df2f0c9391995fd086b8aab00e8ab7aded1e8f0 refs/tags/0.1.0\n4c959a72db5283b55eeef491076eefb5e02316f1 refs/tags/0.10.0\n802f47c0f7f4318a4f0858ba5a6a6ed2333bde71 refs/tags/0.11.0\n82c8d92837108dce225358ace2c416bf9a3f30ce refs/tags/0.12.0\n87d2f2c4f6ef7da350d45beb5a336611bde7f518 refs/tags/0.13.0\n39964f0d220bfaade47a568bf03c1cf28aa2bc37 refs/tags/0.14.0\nbe9f03f92304cbeab70572944a8563db9b23b2fb refs/tags/0.14.1\na933fb9832566fc95273e417597bfb4faf564ca6 refs/tags/0.15.0\n6aad2e438c52ff0636c7bfb64338e444ac3e83ba refs/tags/0.16.0\nf4ddffb5848c42c6151743dd9c7eddcaaabc56cc refs/tags/0.17.0\n30b446b39442cdbc53a97018ab8a54149aa7c3b7 refs/tags/0.17.1\n1a9d36aa3b34a6169d4530463f1c17a3fe1e075e refs/tags/0.18.0\ndd34f903a9a63b876cb2db19b7a4ce0bcc252134 refs/tags/0.19.0\nd93d49c81fc8722d7929ac463b435c0f2e10c53b refs/tags/0.2.0\neeae7746c9b1118bcf27744ab2aee26969051256 refs/tags/0.20.0\n8c3471a548129f3bf62df15cd0fd8cca1787d852 refs/tags/0.21.0\nc0e873a17bc713c80e74fec3c30cb62dcd5d194a refs/tags/0.21.1\nbae84c47a1bb259b209b6f6be1582327b784539d refs/tags/0.21.2\nbfcb4bcae76a917c3c88736ca773e4cb67dbb2d8 refs/tags/0.21.3\n0eabb7c462d30932049f0b7e6a030c1562cf9fee refs/tags/0.22.0\n2e75367095f54d4351005078bad98041a55b14c1 refs/tags/0.22.1\n814ea235dd398108d7b18f966694c3d951575701 refs/tags/0.22.2\na812064587d983c129737f8500bf74990e6b8dab refs/tags/0.23.0\nbd969ad3745db0d66022564cac76cf9424651104 refs/tags/0.23.1\na037c646a47623b92718efadc2bb74d03664b360 refs/tags/0.23.2\n078475ccceeaca0fac947492acdd24514da8d863 refs/tags/0.24.0\ne7bd87dc992512fd5825a557a56907647e03c979 refs/tags/0.24.1\n45ea601e1c7f64fb857bc99df984b86673621d2c refs/tags/0.3.0\n1ea9868a3fee3aa487ab7ec9129208a4dd483d0d refs/tags/0.4.0\n39afdb5733d3245386d29d08c5ff61c89268f499 refs/tags/0.5.0\n458c7b5910fcb157af3fc51bc3b3e663fdb3ed4a refs/tags/0.6.0\n06c38bd8563d822455bc237c2a98c153d938ed1b refs/tags/0.7.0\nf446b6056eef6d8dc9d8b47a79aca93d17dc8230 refs/tags/0.8.0\nbb25a195133f9f7af06386d0809793923cc5e8ab refs/tags/0.9.0\n"),
-    toData("npeg", GitCurrentCommit, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\n"),
-    toData("npeg", GitMergeBase, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\na037c646a47623b92718efadc2bb74d03664b360\na037c646a47623b92718efadc2bb74d03664b360\n"),
-
-    toData("testes", GitDiff, 0, ""),
-    toData("testes", GitTags, 0, "3ce9b2968b5f644755a0ced1baa3eece88c2f12e refs/tags/0.1.0\nf73af8318b54737678fab8b54bdcd8a451015e0d refs/tags/0.1.1\nd21d84d37b161a123a43318bae353108755916de refs/tags/0.1.2\n5c36b6095353ed03b08ac939d00aff2d73f79a35 refs/tags/0.1.3\na1220d11237ee8f135f772ff9731c11b2d91ba31 refs/tags/0.1.4\n574f741b90d04a7ce8c9b990e6077708d7ad076e refs/tags/0.1.5\nced0a9e58234b680def6931578e09165a32e6291 refs/tags/0.1.6\nbb248952e8742a6011eb1a45a9d2059aeb0341d7 refs/tags/0.1.7\nabb7d7c552da0a8e0ddc586c15ccf7e74b0d068b refs/tags/0.10.0\n6e42a768a90d6442196b344bcdcb6f834b76e7b7 refs/tags/0.2.0\n9d136c3a0851ca2c021f5fb4f7b63f0a0ef77232 refs/tags/0.2.1\ndcb282b2da863fd2939e1969cec7a99788feb456 refs/tags/0.2.2\nf708a632afaa40a322a1a61c1c13722edac8e8c5 refs/tags/0.3.0\n3213f59e3f9ba052452c59f01d1418360d856af6 refs/tags/0.3.1\nf7bb1743dffd327958dfcebae4cfb6f61cc1cb8c refs/tags/0.3.2\n6b64569ebecad6bc60cc8697713701e7659204f4 refs/tags/0.3.3\nb51c25a4367bd17f419f78cb5a27f319e9d820f5 refs/tags/0.3.4\nb265612710cbd5ddb1b173c94ece8ec5c7ceccac refs/tags/0.3.5\ne404bcfe42e92d7509717a2dfa115cacb4964c5d refs/tags/0.3.6\n5e4d0d5b7e7f314dde701c546c4365c59782d3dc refs/tags/0.3.7\ne13f91c9c913d2b81c59adeaad687efa2b35293a refs/tags/0.3.8\n17599625f09af0ae4b525e63ab726a3002540702 refs/tags/0.3.9\n13e907f70571dd146d8dc29ddec4599b40ba4e85 refs/tags/0.4.0\n155a74cf676495df1e0674dd07b5e4a0291a9a4a refs/tags/0.4.1\nf37abccdc148cb02ca637a6f0bc8821491cce358 refs/tags/0.4.2\n0250d29ebdd02f28f9020445adb5a4e51fd1902c refs/tags/0.5.0\n2fb87db6d9f34109a70205876030c53f815739b7 refs/tags/0.5.1\n629d17ba8d6a1a4eca8145eb089ed5bca4473dfc refs/tags/0.6.0\ne926130f5f1b7903f68be49cc1563225bd9d948d refs/tags/0.7.0\n7365303897e6185796c274425c079916047e3f14 refs/tags/0.7.1\na735c4adabeba637409f41c4325dd8fc5fb91e2d refs/tags/0.7.10\nfe023fd27404889c5122f902456cbba14b767405 refs/tags/0.7.11\n4430e72972c77a5e9c1555d59bba11d840682691 refs/tags/0.7.12\nf0e53eb490a9558c7f594d2e095b70665e36ca88 refs/tags/0.7.13\nf6520e25e7c329c2957cda447f149fc6a930db0d refs/tags/0.7.2\nd509762f7191757c240d3c79c9ecda53f8c0cfe3 refs/tags/0.7.3\nc02e7a783d1c42fd1f91bca7142f7c3733950c05 refs/tags/0.7.4\n8c8a9e496e9b86ba7602709438980ca31e6989d9 refs/tags/0.7.5\n29839c18b4ac83c0111a178322b57ebb8a8d402c refs/tags/0.7.6\n3b62973cf74fafd8ea906644d89ac34d29a8a6cf refs/tags/0.7.7\ne67ff99dc43c391e89a37f97a9d298c3428bbde2 refs/tags/0.7.8\n4b72ecda0d40ed8e5ab8ad4095a0691d30ec6cd0 refs/tags/0.7.9\n2512b8cc3d7f001d277e89978da2049a5feee5c4 refs/tags/0.8.0\n86c47029690bd2731d204245f3f54462227bba0d refs/tags/0.9.0\n9a7f94f78588e9b5ba7ca077e1f7eae0607c6cf6 refs/tags/0.9.1\n08c915dc016d16c1dfa9a77d0b045ec29c9f2074 refs/tags/0.9.2\n3fb658b1ce1e1efa37d6f9f14322bdac8def02a5 refs/tags/0.9.3\n738fda0add962379ffe6aa6ca5f01a6943a98a2e refs/tags/0.9.4\n48d821add361f7ad768ecb35a0b19c38f90c919e refs/tags/0.9.5\nff9ae890f597dac301b2ac6e6805eb9ac5afd49a refs/tags/0.9.6\n483c78f06e60b0ec5e79fc3476df075ee7286890 refs/tags/0.9.7\n416eec87a5ae39a1a6035552e9e9a47d76b13026 refs/tags/1.0.0\na935cfe9445cc5218fbdd7e0afb35aa1587fff61 refs/tags/1.0.1\n4b83863a9181f054bb695b11b5d663406dfd85d2 refs/tags/1.0.2\n295145fddaa4fe29c1e71a5044d968a84f9dbf69 refs/tags/1.1.0\n8f74ea4e5718436c47305b4488842e6458a13dac refs/tags/1.1.1\n4135bb291e53d615a976e997c44fb2bd9e1ad343 refs/tags/1.1.10\n8c09dbcd16612f5989065db02ea2e7a752dd2656 refs/tags/1.1.11\naedfebdb6c016431d84b0c07cf181b957a900640 refs/tags/1.1.12\n2c2e958366ef6998115740bdf110588d730e5738 refs/tags/1.1.2\nbecc77258321e6ec40d89efdddf37bafd0d07fc3 refs/tags/1.1.3\ne070d7c9853bf94c35b81cf0c0a8980c2449bb22 refs/tags/1.1.4\n12c986cbbf65e8571a486e9230808bf887e5f04f refs/tags/1.1.5\n63df8986f5b56913b02d26954fa033eeaf43714c refs/tags/1.1.6\n38e02c9c6bd728b043036fe0d1894d774cab3108 refs/tags/1.1.7\n3c3879fff16450d28ade79a6b08982bf5cefc061 refs/tags/1.1.8\ne32b811b3b2e70a1d189d7a663bc2583e9c18f96 refs/tags/1.1.9\n0c1b4277c08197ce7e7e0aa2bad91d909fcd96ac refs/tags/2.0.0\n"),
-    toData("testes", GitCurrentCommit, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n"),
-    toData("testes", GitMergeBase, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n416eec87a5ae39a1a6035552e9e9a47d76b13026\nd9db2ad09aa38fc26625341e1b666602959e144f\n"),
-    toData("testes", GitCheckout, 0, "416eec87a5ae39a1a6035552e9e9a47d76b13026"),
-
-    toData("grok", GitDiff, 0, ""),
-    toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"),
-    toData("grok", GitCurrentCommit, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n"),
-    toData("grok", GitMergeBase, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n62707b8ac684efac35d301dbde57dc750880268e\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"),
-    toData("grok", GitCheckout, 0, "62707b8ac684efac35d301dbde57dc750880268e"),
-
-    toData("nim-bytes2human", GitDiff, 0, ""),
-    toData("nim-bytes2human", GitTags, 0, ""),
-    toData("nim-bytes2human", GitCurrentcommit, 0, "ec2c1a758cabdd4751a06c8ebf2b923f19e32731\n")
-  ]
-
-#[
-Current directory is now E:\atlastest\nim-bytes2human
-cmd git diff args [] --> ("", 0)
-cmd git show-ref --tags args [] --> ("", 1)
-cmd git log -n 1 --format=%H args [] --> (, 0)
-[Warning] (nim-bytes2human) package has no tagged releases
-nimble E:\atlastest\nim-bytes2human\bytes2human.nimble info (requires: @["nim >= 1.0.0"], srcDir: "src", tasks: @[])
-[Error] There were problems.
-Error: execution of an external program failed: 'E:\nim\tools\atlas\atlas.exe clone https://github.com/disruptek/balls'
-]#
diff --git a/tools/atlas/tests/balls.nimble b/tools/atlas/tests/balls.nimble
deleted file mode 100644
index 143e757e9..000000000
--- a/tools/atlas/tests/balls.nimble
+++ /dev/null
@@ -1,32 +0,0 @@
-version = "3.4.1"
-author = "disruptek"
-description = "a unittest framework with balls 🔴🟡🟢"
-license = "MIT"
-
-# requires newTreeFrom
-requires "https://github.com/disruptek/grok >= 0.5.0 & < 1.0.0"
-requires "https://github.com/disruptek/ups < 1.0.0"
-requires "https://github.com/planetis-m/sync#810bd2d"
-#requires "https://github.com/c-blake/cligen < 2.0.0"
-
-bin = @["balls"]            # build the binary for basic test running
-installExt = @["nim"]       # we need to install balls.nim also
-skipDirs = @["tests"]       # so stupid...  who doesn't want tests?
-#installFiles = @["balls.nim"] # https://github.com/nim-lang/Nim/issues/16661
-
-task test, "run tests for ci":
-  when defined(windows):
-    exec "balls.cmd"
-  else:
-    exec "balls"
-
-task demo, "produce a demo":
-  exec "nim c --define:release balls.nim"
-  when (NimMajor, NimMinor) != (1, 0):
-    echo "due to nim bug #16307, use nim-1.0"
-    quit 1
-  exec """demo docs/demo.svg "nim c --out=\$1 examples/fails.nim""""
-  exec """demo docs/clean.svg "nim c --define:danger -f --out=\$1 tests/test.nim""""
-  exec "nim c --define:release --define:ballsDry balls.nim"
-  exec """demo docs/runner.svg "balls""""
-
diff --git a/tools/atlas/tests/grok.nimble b/tools/atlas/tests/grok.nimble
deleted file mode 100644
index 1b6d77c08..000000000
--- a/tools/atlas/tests/grok.nimble
+++ /dev/null
@@ -1,5 +0,0 @@
-version = "0.0.4"
-author = "disruptek"
-description = "don't read too much into it"
-license = "MIT"
-requires "nim >= 1.0.0"
diff --git a/tools/atlas/tests/nim-bytes2human.nimble b/tools/atlas/tests/nim-bytes2human.nimble
deleted file mode 100644
index 9f3ae2479..000000000
--- a/tools/atlas/tests/nim-bytes2human.nimble
+++ /dev/null
@@ -1,7 +0,0 @@
-version     = "0.2.2"
-author      = "Juan Carlos"
-description = "Convert bytes to kilobytes, megabytes, gigabytes, etc."
-license     = "MIT"
-srcDir      = "src"
-
-requires "nim >= 1.0.0"  # https://github.com/juancarlospaco/nim-bytes2human/issues/2#issue-714338524
diff --git a/tools/atlas/tests/nim.cfg b/tools/atlas/tests/nim.cfg
deleted file mode 100644
index 5f568569b..000000000
--- a/tools/atlas/tests/nim.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-############# begin Atlas config section ##########
---noNimblePath
---path:"../balls"
---path:"../grok"
---path:"../ups"
---path:"../sync"
---path:"../npeg/src"
---path:"../testes"
---path:"../grok"
---path:"../nim-bytes2human/src"
-############# end Atlas config section   ##########
diff --git a/tools/atlas/tests/npeg.nimble b/tools/atlas/tests/npeg.nimble
deleted file mode 100644
index e71fc5aa5..000000000
--- a/tools/atlas/tests/npeg.nimble
+++ /dev/null
@@ -1,48 +0,0 @@
-# Package
-
-version       = "0.24.1"
-author        = "Ico Doornekamp"
-description   = "a PEG library"
-license       = "MIT"
-srcDir        = "src"
-installExt    = @["nim"]
-
-# Dependencies
-
-requires "nim >= 0.19.0"
-
-# Test
-
-task test, "Runs the test suite":
-  exec "nimble testc && nimble testcpp && nimble testarc && nimble testjs"
-
-task testc, "C tests":
-  exec "nim c -r tests/tests.nim"
-
-task testcpp, "CPP tests":
-  exec "nim cpp -r tests/tests.nim"
-
-task testjs, "JS tests":
-  exec "nim js -r tests/tests.nim"
-
-task testdanger, "Runs the test suite in danger mode":
-  exec "nim c -d:danger -r tests/tests.nim"
-
-task testwin, "Mingw tests":
-  exec "nim c -d:mingw tests/tests.nim && wine tests/tests.exe"
-
-task test32, "32 bit tests":
-  exec "nim c --cpu:i386 --passC:-m32 --passL:-m32 tests/tests.nim && tests/tests"
-
-task testall, "Test all":
-  exec "nimble test && nimble testcpp && nimble testdanger && nimble testjs && nimble testwin"
-
-when (NimMajor, NimMinor) >= (1, 1):
-  task testarc, "--gc:arc tests":
-    exec "nim c --gc:arc -r tests/tests.nim"
-else:
-  task testarc, "--gc:arc tests":
-    exec "true"
-
-task perf, "Test performance":
-  exec "nim cpp -r -d:danger tests/performance.nim"
diff --git a/tools/atlas/tests/packages/packages.json b/tools/atlas/tests/packages/packages.json
deleted file mode 100644
index d054a201b..000000000
--- a/tools/atlas/tests/packages/packages.json
+++ /dev/null
@@ -1,36 +0,0 @@
-[
-  {
-    "name": "bytes2human",
-    "url": "https://github.com/juancarlospaco/nim-bytes2human",
-    "method": "git",
-    "tags": [
-      "bytes",
-      "human",
-      "minimalism",
-      "size"
-    ],
-    "description": "Convert bytes to kilobytes, megabytes, gigabytes, etc.",
-    "license": "LGPLv3",
-    "web": "https://github.com/juancarlospaco/nim-bytes2human"
-  },
-  {
-    "name": "npeg",
-    "url": "https://github.com/zevv/npeg",
-    "method": "git",
-    "tags": [
-      "PEG",
-      "parser",
-      "parsing",
-      "regexp",
-      "regular",
-      "grammar",
-      "lexer",
-      "lexing",
-      "pattern",
-      "matching"
-    ],
-    "description": "PEG (Parsing Expression Grammars) string matching library for Nim",
-    "license": "MIT",
-    "web": "https://github.com/zevv/npeg"
-  }
-]
diff --git a/tools/atlas/tests/sync.nimble b/tools/atlas/tests/sync.nimble
deleted file mode 100644
index a07ae8925..000000000
--- a/tools/atlas/tests/sync.nimble
+++ /dev/null
@@ -1,10 +0,0 @@
-# Package
-
-version     = "1.4.0"
-author      = "Antonis Geralis"
-description = "Useful synchronization primitives."
-license     = "MIT"
-
-# Deps
-
-requires "nim >= 1.0.0"
diff --git a/tools/atlas/tests/testes.nimble b/tools/atlas/tests/testes.nimble
deleted file mode 100644
index 60fe1d508..000000000
--- a/tools/atlas/tests/testes.nimble
+++ /dev/null
@@ -1,23 +0,0 @@
-version = "1.0.0"
-author = "disruptek"
-description = "a cure for salty testes"
-license = "MIT"
-
-#requires "cligen >= 0.9.41 & <= 0.9.45"
-#requires "bump >= 1.8.18 & < 2.0.0"
-requires "https://github.com/disruptek/grok >= 0.0.4 & < 1.0.0"
-requires "https://github.com/juancarlospaco/nim-bytes2human"
-
-bin = @["testes"]           # build the binary for basic test running
-installExt = @["nim"]       # we need to install testes.nim also
-skipDirs = @["tests"]       # so stupid...  who doesn't want tests?
-
-task test, "run tests for ci":
-  exec "nim c --run testes.nim"
-
-task demo, "produce a demo":
-  when (NimMajor, NimMinor) != (1, 0):
-    echo "due to nim bug #16307, use nim-1.0"
-    quit 1
-  exec """demo docs/demo.svg "nim c --out=\$1 examples/balls.nim""""
-  exec """demo docs/clean.svg "nim c --define:danger --out=\$1 tests/testicles.nim""""
diff --git a/tools/atlas/tests/ups.nimble b/tools/atlas/tests/ups.nimble
deleted file mode 100644
index d91abbe60..000000000
--- a/tools/atlas/tests/ups.nimble
+++ /dev/null
@@ -1,13 +0,0 @@
-version = "0.0.2"
-author = "disruptek"
-description = "a package handler"
-license = "MIT"
-
-requires "npeg >= 0.23.2 & < 1.0.0"
-requires "https://github.com/disruptek/testes >= 1.0.0 & < 2.0.0"
-
-task test, "run tests":
-  when defined(windows):
-    exec "testes.cmd"
-  else:
-    exec findExe"testes"
diff --git a/tools/debug/customdebugtype.nim b/tools/debug/customdebugtype.nim
new file mode 100644
index 000000000..f48979661
--- /dev/null
+++ b/tools/debug/customdebugtype.nim
@@ -0,0 +1,72 @@
+## This is a demo file containing an example of how to
+## create custom LLDB summaries and objects with synthetic
+## children. These are implemented in Nim and called from the Python
+## nimlldb.py module.
+##
+## For summaries, prefix your proc names with "lldbDebugSummary", use
+## the `{.exportc.}` pragma, and return a string. Also, any `$` proc
+## that is available will be used for a given type.
+##
+## For creating a synthetic object (LLDB will display the children), use
+## the prefix "lldbDebugSynthetic", use the `{.exportc.}` pragma, and
+## return any Nim object, array, or sequence. Returning a Nim object
+## will display the fields and values of the object as children.
+## Returning an array or sequence will display children with the index
+## surrounded by square brackets as the key name
+##
+## You may also return a Nim table that contains the string
+## "LLDBDynamicObject" (case insensitive). This allows for dynamic
+## fields to be created at runtime instead of at compile time if you
+## return a Nim object as mentioned above. See the proc
+## `lldbDebugSyntheticDynamicFields` below for an example
+
+import intsets
+import tables
+
+type
+  CustomType* = object of RootObj # RootObj is not necessary, but can be used
+    myField*: int
+
+  DynamicFields* = object
+    customField*: string
+
+  CustomSyntheticReturn* = object
+    differentField*: float
+
+  LLDBDynamicObject = object
+    fields: TableRef[string, int]
+
+  LLDBDynamicObjectDynamicFields = object
+    fields: TableRef[string, string]
+
+proc lldbDebugSummaryCustomType*(ty: CustomType): string {.exportc.} =
+  ## Will display "CustomType(myField: <int_val>)" as a summary
+  result = "CustomType" & $ty
+
+proc lldbDebugSyntheticCustomType*(ty: CustomType): CustomSyntheticReturn {.exportc.} =
+  ## Will display differentField: <float_val> as a child of CustomType instead of
+  ## myField: <int_val>
+  result = CustomSyntheticReturn(differentField: ty.myField.float)
+
+proc lldbDebugSyntheticDynamicFields*(ty: DynamicFields): LLDBDynamicObjectDynamicFields {.exportc.} =
+  ## Returning an object that contains "LLDBDynamicObject" in the type name will expect an
+  ## object with one property that is a Nim Table/TableRef. If the key is a string,
+  ## it will appear in the debugger like an object field name. The value will be whatever you
+  ## set it to here as well.
+  let fields = {"customFieldName": ty.customField & " MORE TEXT"}.newTable()
+  return LLDBDynamicObjectDynamicFields(fields: fields)
+
+proc lldbDebugSummaryIntSet*(intset: IntSet): string {.exportc.} =
+  ## This will print the object in the LLDB summary just as Nim prints it
+  result = $intset
+
+proc lldbDebugSyntheticIntSet*(intset: IntSet): seq[int] {.exportc.} =
+  ## This will create a synthetic object to make it so that IntSet
+  ## will appear as a Nim object in the LLDB debugger window
+  ##
+  ## returning a seq here will display children like:
+  ## [0]: <child_value>
+  ##
+  result = newSeqOfCap[int](intset.len)
+  for val in intset:
+    result.add(val)
diff --git a/tools/nim-gdb.py b/tools/debug/nim-gdb.py
index f35b9a033..8c9854bda 100644
--- a/tools/nim-gdb.py
+++ b/tools/debug/nim-gdb.py
@@ -16,6 +16,10 @@ def printErrorOnce(id, message):
     errorSet.add(id)
     gdb.write("printErrorOnce: " + message, gdb.STDERR)
 
+def debugPrint(x):
+  gdb.write(str(x) + "\n", gdb.STDERR)
+
+NIM_STRING_TYPES = ["NimStringDesc", "NimStringV2"]
 
 ################################################################################
 #####  Type pretty printers
@@ -23,23 +27,28 @@ def printErrorOnce(id, message):
 
 type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$")
 
+def getNimName(typ):
+  if m := type_hash_regex.match(typ):
+    return m.group(2)
+  return f"unknown <{typ}>"
+
 def getNimRti(type_name):
   """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """
 
   # Get static const TNimType variable. This should be available for
   # every non trivial Nim type.
   m = type_hash_regex.match(type_name)
-  lookups = [
-    "NTI" + m.group(2).lower() + "__" + m.group(3) + "_",
-    "NTI" + "__" + m.group(3) + "_",
-    "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_"
-    ]
   if m:
-      for l in lookups:
-        try:
-          return gdb.parse_and_eval(l)
-        except:
-          pass
+    lookups = [
+      "NTI" + m.group(2).lower() + "__" + m.group(3) + "_",
+      "NTI" + "__" + m.group(3) + "_",
+      "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_"
+      ]
+    for l in lookups:
+      try:
+        return gdb.parse_and_eval(l)
+      except:
+        pass
   None
 
 def getNameFromNimRti(rti):
@@ -68,7 +77,7 @@ class NimTypeRecognizer:
     
     'NIM_BOOL': 'bool',
 
-    'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string'
+    'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string', 'NimStringV2': 'string'
   }
 
   # object_type_pattern = re.compile("^(\w*):ObjectType$")
@@ -103,6 +112,12 @@ class NimTypeRecognizer:
       result = self.type_map_static.get(tname, None)
       if result:
         return result
+      elif tname.startswith("tyEnum_"):
+        return getNimName(tname)
+      elif tname.startswith("tyTuple__"):
+        # We make the name be the field types (Just like in Nim)
+        fields = ", ".join([self.recognize(field.type) for field in type_obj.fields()])
+        return f"({fields})"
 
       rti = getNimRti(tname)
       if rti:
@@ -136,7 +151,7 @@ class DollarPrintFunction (gdb.Function):
   "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)"
 
   dollar_functions = re.findall(
-    'NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);',
+    r'(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);',
     gdb.execute("info functions dollar__", True, True)
   )
 
@@ -145,25 +160,23 @@ class DollarPrintFunction (gdb.Function):
 
 
   @staticmethod
-  def invoke_static(arg):
-
-    if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name == "NimStringDesc":
+  def invoke_static(arg, ignore_errors = False):
+    if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES:
       return arg
-
     argTypeName = str(arg.type)
-
     for func, arg_typ in DollarPrintFunction.dollar_functions:
       # this way of overload resolution cannot deal with type aliases,
       # therefore it won't find all overloads.
       if arg_typ == argTypeName:
-        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
+        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
         return func_value(arg)
 
       elif arg_typ == argTypeName + " *":
-        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
+        func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
         return func_value(arg.address)
 
-    printErrorOnce(argTypeName, "No suitable Nim $ operator found for type: " + argTypeName + "\n")
+    if not ignore_errors:
+      debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n")
     return None
 
   def invoke(self, arg):
@@ -184,11 +197,11 @@ class NimStringEqFunction (gdb.Function):
 
   @staticmethod
   def invoke_static(arg1,arg2):
-    if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name == "NimStringDesc":
+    if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name in NIM_STRING_TYPES:
       str1 = NimStringPrinter(arg1).to_string()
     else:
       str1 = arg1.string()
-    if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name == "NimStringDesc":
+    if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name in NIM_STRING_TYPES:
       str2 = NimStringPrinter(arg1).to_string()
     else:
       str2 = arg2.string()
@@ -216,7 +229,7 @@ class DollarPrintCmd (gdb.Command):
     strValue = DollarPrintFunction.invoke_static(param)
     if strValue:
       gdb.write(
-        NimStringPrinter(strValue).to_string() + "\n",
+        str(NimStringPrinter(strValue)) + "\n",
         gdb.STDOUT
       )
 
@@ -254,7 +267,6 @@ class KochCmd (gdb.Command):
       os.path.dirname(os.path.dirname(__file__)), "koch")
 
   def invoke(self, argument, from_tty):
-    import os
     subprocess.run([self.binary] + gdb.string_to_argv(argument))
 
 KochCmd()
@@ -308,8 +320,14 @@ class NimBoolPrinter:
 
 ################################################################################
 
+def strFromLazy(strVal):
+  if isinstance(strVal, str):
+    return strVal
+  else:
+    return strVal.value().string("utf-8")
+
 class NimStringPrinter:
-  pattern = re.compile(r'^NimStringDesc \*$')
+  pattern = re.compile(r'^(NimStringDesc \*|NimStringV2)$')
 
   def __init__(self, val):
     self.val = val
@@ -319,11 +337,19 @@ class NimStringPrinter:
 
   def to_string(self):
     if self.val:
-      l = int(self.val['Sup']['len'])
-      return self.val['data'].lazy_string(encoding="utf-8", length=l)
+      if self.val.type.name == "NimStringV2":
+        l = int(self.val["len"])
+        data = self.val["p"]["data"]
+      else:
+        l = int(self.val['Sup']['len'])
+        data = self.val["data"]
+      return data.lazy_string(encoding="utf-8", length=l)
     else:
       return ""
 
+  def __str__(self):
+    return strFromLazy(self.to_string())
+
 class NimRopePrinter:
   pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$')
 
@@ -345,39 +371,11 @@ class NimRopePrinter:
 
 ################################################################################
 
-# proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
-#   ## Return string representation for enumeration values
-#   var n = typ.node
-#   if ntfEnumHole notin typ.flags:
-#     let o = e - n.sons[0].offset
-#     if o >= 0 and o <% typ.node.len:
-#       return $n.sons[o].name
-#   else:
-#     # ugh we need a slow linear search:
-#     var s = n.sons
-#     for i in 0 .. n.len-1:
-#       if s[i].offset == e:
-#         return $s[i].name
-#   result = $e & " (invalid data!)"
-
 def reprEnum(e, typ):
-  """ this is a port of the nim runtime function `reprEnum` to python """
+  # Casts the value to the enum type and then calls the enum printer
   e = int(e)
-  n = typ["node"]
-  flags = int(typ["flags"])
-  # 1 << 6 is {ntfEnumHole}
-  if ((1 << 6) & flags) == 0:
-    o = e - int(n["sons"][0]["offset"])
-    if o >= 0 and 0 < int(n["len"]):
-      return n["sons"][o]["name"].string("utf-8", "ignore")
-  else:
-    # ugh we need a slow linear search:
-    s = n["sons"]
-    for i in range(0, int(n["len"])):
-      if int(s[i]["offset"]) == e:
-        return s[i]["name"].string("utf-8", "ignore")
-
-  return str(e) + " (invalid data!)"
+  val = gdb.Value(e).cast(typ)
+  return strFromLazy(NimEnumPrinter(val).to_string())
 
 def enumNti(typeNimName, idString):
   typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_"
@@ -389,6 +387,7 @@ def enumNti(typeNimName, idString):
 
 class NimEnumPrinter:
   pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
+  enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTION_DOMAIN)
 
   def __init__(self, val):
     self.val = val
@@ -397,14 +396,18 @@ class NimEnumPrinter:
     self.typeNimName  = match.group(1)
     typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
 
-    if self.nti is None:
-      printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n")
-
   def to_string(self):
-    if self.nti:
-      arg0     = self.val
-      arg1     = self.nti.value(gdb.newest_frame())
-      return reprEnum(arg0, arg1)
+    if NimEnumPrinter.enumReprProc and self.nti:
+      # Use the old runtimes enumRepr function.
+      # We call the Nim proc itself so that the implementation is correct
+      f = gdb.newest_frame()
+      # We need to strip the quotes so it looks like an enum instead of a string
+      reprProc = NimEnumPrinter.enumReprProc.value()
+      return str(reprProc(self.val, self.nti.value(f).address)).strip('"')
+    elif dollarResult := DollarPrintFunction.invoke_static(self.val):
+      # New runtime doesn't use enumRepr so we instead try and call the
+      # dollar function for it
+      return str(NimStringPrinter(dollarResult))
     else:
       return self.typeNimName + "(" + str(int(self.val)) + ")"
 
@@ -421,26 +424,20 @@ class NimSetPrinter:
     typeName = self.val.type.name
     match = self.pattern.match(typeName)
     self.typeNimName = match.group(1)
-    typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
-
-    if self.nti is None:
-      printErrorOnce(typeInfoName, f"NimSetPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n")
 
   def to_string(self):
-    if self.nti:
-      nti = self.nti.value(gdb.newest_frame())
-      enumStrings = []
-      val = int(self.val)
-      i   = 0
-      while val > 0:
-        if (val & 1) == 1:
-          enumStrings.append(reprEnum(i, nti))
-        val = val >> 1
-        i += 1
-
-      return '{' + ', '.join(enumStrings) + '}'
-    else:
-      return str(int(self.val))
+    # Remove the tySet from the type name
+    typ = gdb.lookup_type(self.val.type.name[6:])
+    enumStrings = []
+    val = int(self.val)
+    i   = 0
+    while val > 0:
+      if (val & 1) == 1:
+        enumStrings.append(reprEnum(i, typ))
+      val = val >> 1
+      i += 1
+
+    return '{' + ', '.join(enumStrings) + '}'
 
 ################################################################################
 
@@ -472,41 +469,81 @@ class NimHashSetPrinter:
 
 ################################################################################
 
-class NimSeqPrinter:
-  # the pointer is explicity part of the type. So it is part of
-  # ``pattern``.
-  pattern = re.compile(r'^tySequence_\w* \*$')
+class NimSeq:
+  # Wrapper around sequences.
+  # This handles the differences between old and new runtime
 
   def __init__(self, val):
     self.val = val
+    # new runtime has sequences on stack, old has them on heap
+    self.new = val.type.code != gdb.TYPE_CODE_PTR
+    if self.new:
+      # Some seqs are just the content and to save repeating ourselves we do
+      # handle them here. Only thing that needs to check this is the len/data getters
+      self.isContent = val.type.name.endswith("Content")
+
+  def __bool__(self):
+    if self.new:
+      return self.val is not None
+    else:
+      return bool(self.val)
+
+  def __len__(self):
+    if not self:
+      return 0
+    if self.new:
+      if self.isContent:
+        return int(self.val["cap"])
+      else:
+        return int(self.val["len"])
+    else:
+      return self.val["Sup"]["len"]
+
+  @property
+  def data(self):
+    if self.new:
+      if self.isContent:
+        return self.val["data"]
+      elif self.val["p"]:
+        return self.val["p"]["data"]
+    else:
+      return self.val["data"]
+
+  @property
+  def cap(self):
+    if not self:
+      return 0
+    if self.new:
+      if self.isContent:
+        return int(self.val["cap"])
+      elif self.val["p"]:
+        return int(self.val["p"]["cap"])
+      else:
+        return 0
+    return int(self.val['Sup']['reserved'])
+
+class NimSeqPrinter:
+  pattern = re.compile(r'^tySequence_\w*\s?\*?$')
+
+  def __init__(self, val):
+    self.val = NimSeq(val)
+
 
   def display_hint(self):
     return 'array'
 
   def to_string(self):
-    len = 0
-    cap = 0
-    if self.val:
-      len = int(self.val['Sup']['len'])
-      cap = int(self.val['Sup']['reserved'])
-
-    return 'seq({0}, {1})'.format(len, cap)
+    return f'seq({len(self.val)}, {self.val.cap})'
 
   def children(self):
     if self.val:
       val = self.val
-      valType = val.type
-      length = int(val['Sup']['len'])
+      length = len(val)
 
       if length <= 0:
         return
 
-      dataType = valType['data'].type
-      data = val['data']
-
-      if self.val.type.name is None:
-        dataType = valType['data'].type.target().pointer()
-        data = val['data'].cast(dataType)
+      data = val.data
 
       inaccessible = False
       for i in range(length):
@@ -585,7 +622,7 @@ class NimTablePrinter:
     if self.val:
       counter  = int(self.val['counter'])
       if self.val['data']:
-        capacity = int(self.val['data']['Sup']['len'])
+        capacity = NimSeq(self.val["data"]).cap
 
     return 'Table({0}, {1})'.format(counter, capacity)
 
@@ -597,162 +634,18 @@ class NimTablePrinter:
           yield (idxStr + '.Field1', entry['Field1'])
           yield (idxStr + '.Field2', entry['Field2'])
 
-################################################################
+################################################################################
 
-# this is untested, therefore disabled
-
-# class NimObjectPrinter:
-#   pattern = re.compile(r'^tyObject_([A-Za-z0-9]+)__(_?[A-Za-z0-9]*)(:? \*)?$')
-
-#   def __init__(self, val):
-#     self.val = val
-#     self.valType = None
-#     self.valTypeNimName = None
-
-#   def display_hint(self):
-#     return 'object'
-
-#   def _determineValType(self):
-#     if self.valType is None:
-#       vt = self.val.type
-#       if vt.name is None:
-#         target = vt.target()
-#         self.valType = target.pointer()
-#         self.fields = target.fields()
-#         self.valTypeName = target.name
-#         self.isPointer = True
-#       else:
-#         self.valType = vt
-#         self.fields = vt.fields()
-#         self.valTypeName = vt.name
-#         self.isPointer = False
-
-#   def to_string(self):
-#     if self.valTypeNimName is None:
-#       self._determineValType()
-#       match = self.pattern.match(self.valTypeName)
-#       self.valTypeNimName = match.group(1)
-
-#     return self.valTypeNimName
-
-#   def children(self):
-#     self._determineValType()
-#     if self.isPointer and int(self.val) == 0:
-#       return
-#     self.baseVal = self.val.referenced_value() if self.isPointer else self.val
-
-#     for c in self.handleFields(self.baseVal, getNimRti(self.valTypeName)):
-#       yield c
-  
-#   def handleFields(self, currVal, rti, fields = None):
-#     rtiSons = None
-#     discField = (0, None)
-#     seenSup = False
-#     if fields is None:
-#       fields = self.fields
-#     try: # XXX: remove try after finished debugging this method
-#       for (i, field) in enumerate(fields):
-#         if field.name == "Sup": # inherited data
-#           seenSup = True
-#           baseRef = rti['base']
-#           if baseRef:
-#             baseRti = baseRef.referenced_value()
-#             baseVal = currVal['Sup']
-#             baseValType = baseVal.type
-#             if baseValType.name is None:
-#               baseValType = baseValType.target().pointer()
-#               baseValFields = baseValType.target().fields()
-#             else:
-#               baseValFields = baseValType.fields()
-            
-#             for c in self.handleFields(baseVal, baseRti, baseValFields):
-#               yield c
-#         else:
-#           if field.type.code == gdb.TYPE_CODE_UNION:
-#             # if not rtiSons:
-#             rtiNode = rti['node'].referenced_value()
-#             rtiSons = rtiNode['sons']
-
-#             if not rtiSons and int(rtiNode['len']) == 0 and str(rtiNode['name']) != "0x0":
-#               rtiSons = [rti['node']] # sons are dereferenced by the consumer
-            
-#             if not rtiSons:
-#               printErrorOnce(self.valTypeName, f"NimObjectPrinter: UNION field can't be displayed without RTI {self.valTypeName}, using fallback.\n")
-#               # yield (field.name, self.baseVal[field]) # XXX: this fallback seems wrong
-#               return # XXX: this should probably continue instead?
-
-#             if int(rtiNode['len']) != 0 and str(rtiNode['name']) != "0x0":
-#               gdb.write(f"wtf IT HAPPENED {self.valTypeName}\n", gdb.STDERR)
-
-#             discNode = rtiSons[discField[0]].referenced_value()
-#             if not discNode:
-#               raise ValueError("Can't find union discriminant field in object RTI")
-            
-#             discNodeLen = int(discNode['len'])
-#             discFieldVal = int(currVal[discField[1].name])
-
-#             unionNodeRef = None
-#             if discFieldVal < discNodeLen:
-#               unionNodeRef = discNode['sons'][discFieldVal]
-#             if not unionNodeRef:
-#               unionNodeRef = discNode['sons'][discNodeLen]
-
-#             if not unionNodeRef:
-#               printErrorOnce(self.valTypeName + "no union node", f"wtf is up with sons {self.valTypeName} {unionNodeRef} {rtiNode['offset']} {discNode} {discFieldVal} {discNodeLen} {discField[1].name} {field.name} {field.type}\n")
-#               continue
-
-#             unionNode = unionNodeRef.referenced_value()
-            
-#             fieldName = "" if field.name == None else field.name.lower()
-#             unionNodeName = "" if not unionNode['name'] else unionNode['name'].string("utf-8", "ignore")
-#             if not unionNodeName or unionNodeName.lower() != fieldName:
-#               unionFieldName = f"_{discField[1].name.lower()}_{int(rti['node'].referenced_value()['len'])}"
-#               gdb.write(f"wtf i: {i} union: {unionFieldName} field: {fieldName} type: {field.type.name} tag: {field.type.tag}\n", gdb.STDERR)
-#             else:
-#               unionFieldName = unionNodeName
-
-#             if discNodeLen == 0:
-#               yield (unionFieldName, currVal[unionFieldName])
-#             else:
-#               unionNodeLen = int(unionNode['len'])
-#               if unionNodeLen > 0:
-#                 for u in range(unionNodeLen):
-#                   un = unionNode['sons'][u].referenced_value()['name'].string("utf-8", "ignore")
-#                   yield (un, currVal[unionFieldName][un])
-#               else:
-#                 yield(unionNodeName, currVal[unionFieldName])
-#           else:
-#             discIndex = i - 1 if seenSup else i
-#             discField = (discIndex, field) # discriminant field is the last normal field
-#             yield (field.name, currVal[field.name])
-#     except GeneratorExit:
-#       raise
-#     except:
-#       gdb.write(f"wtf {self.valTypeName} {i} fn: {field.name} df: {discField} rti: {rti} rtiNode: {rti['node'].referenced_value()} rtiSons: {rtiSons} {sys.exc_info()} {traceback.format_tb(sys.exc_info()[2], limit = 10)}\n", gdb.STDERR)
-#       gdb.write(f"wtf {self.valTypeName} {i} {field.name}\n", gdb.STDERR)
-      
-#       # seenSup = False
-#       # for (i, field) in enumerate(fields):
-#       #   # if field.name:
-#       #   #   val = currVal[field.name]
-#       #   # else:
-#       #   #   val = None
-#       #   rtiNode = rti['node'].referenced_value()
-#       #   rtiLen = int(rtiNode['len'])
-#       #   if int(rtiNode['len']) > 0:
-#       #     sons = rtiNode['sons']
-#       #   elif int(rti['len']) == 0 and str(rti['name']) != "0x0":
-#       #     sons = [rti['node']] # sons are dereferenced by the consumer
-#       #   sonsIdx = i - 1 if seenSup else i
-#       #   s = sons[sonsIdx].referenced_value()
-#       #   addr = int(currVal.address)
-#       #   off = addr + int(rtiNode['offset'])
-#       #   seenSup = seenSup or field.name == "Sup"
-
-#       #   gdb.write(f"wtf: i: {i} sonsIdx: {sonsIdx} field: {field.name} rtiLen: {rtiLen} rti: {rti} rtiNode: {rtiNode} isUnion: {field.type.code == gdb.TYPE_CODE_UNION} s: {s}\n", gdb.STDERR)
-
-#       raise
+class NimTuplePrinter:
+  pattern = re.compile(r"^tyTuple__([A-Za-z0-9]*)")
 
+  def __init__(self, val):
+    self.val = val
+
+  def to_string(self):
+    # We don't have field names so just print out the tuple as if it was anonymous
+    tupleValues = [str(self.val[field.name]) for field in self.val.type.fields()]
+    return f"({', '.join(tupleValues)})"
 
 ################################################################################
 
@@ -784,7 +677,7 @@ def makematcher(klass):
   return matcher
 
 def register_nim_pretty_printers_for_object(objfile):
-  nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTIONS_DOMAIN)
+  nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTION_DOMAIN)
   if nimMainSym and nimMainSym.symtab.objfile == objfile:
     print("set Nim pretty printers for ", objfile.filename)
 
diff --git a/tools/debug/nimlldb.py b/tools/debug/nimlldb.py
new file mode 100644
index 000000000..4bc4e771f
--- /dev/null
+++ b/tools/debug/nimlldb.py
@@ -0,0 +1,1380 @@
+import lldb
+from collections import OrderedDict
+from typing import Union
+
+
+def sbvaluegetitem(self: lldb.SBValue, name: Union[int, str]) -> lldb.SBValue:
+    if isinstance(name, str):
+        return self.GetChildMemberWithName(name)
+    else:
+        return self.GetChildAtIndex(name)
+
+
+# Make this easier to work with
+lldb.SBValue.__getitem__ = sbvaluegetitem
+
+NIM_IS_V2 = True
+
+
+def get_nti(value: lldb.SBValue, nim_name=None):
+    name_split = value.type.name.split("_")
+    type_nim_name = nim_name or name_split[1]
+    id_string = name_split[-1].split(" ")[0]
+
+    type_info_name = "NTI" + type_nim_name.lower() + "__" + id_string + "_"
+    nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        type_info_name = "NTI" + "__" + id_string + "_"
+        nti = value.target.FindFirstGlobalVariable(type_info_name)
+    if not nti.IsValid():
+        print(f"NimEnumPrinter: lookup global symbol: '{type_info_name}' failed for {value.type.name}.\n")
+    return type_nim_name, nti
+
+
+def enum_to_string(value: lldb.SBValue, int_val=None, nim_name=None):
+    tname = nim_name or value.type.name.split("_")[1]
+
+    enum_val = value.signed
+    if int_val is not None:
+        enum_val = int_val
+
+    default_val = f"{tname}.{str(enum_val)}"
+
+    fn_syms = value.target.FindFunctions("reprEnum")
+    if not fn_syms.GetSize() > 0:
+        return default_val
+
+    fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+    fn: lldb.SBFunction = fn_sym.function
+
+    fn_type: lldb.SBType = fn.type
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    if arg_types.GetSize() < 2:
+        return default_val
+
+    arg1_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+    arg2_type: lldb.SBType = arg_types.GetTypeAtIndex(1)
+
+    ty_info_name, nti = get_nti(value, nim_name=tname)
+
+    if not nti.IsValid():
+        return default_val
+
+    call = f"{fn.name}(({arg1_type.name}){enum_val}, ({arg2_type.name})" + str(nti.GetLoadAddress()) + ");"
+
+    res = executeCommand(call)
+
+    if res.error.fail:
+        return default_val
+
+    return f"{tname}.{res.summary[1:-1]}"
+
+
+def to_string(value: lldb.SBValue):
+    # For getting NimStringDesc * value
+    value = value.GetNonSyntheticValue()
+
+    # Check if data pointer is Null
+    if value.type.is_pointer and value.unsigned == 0:
+        return None
+
+    size = int(value["Sup"]["len"].unsigned)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    data = value["data"]
+
+    # Check if first element is NULL
+    base_data_type = value.target.FindFirstType("char")
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(value.target.FindFirstType("char").GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def to_stringV2(value: lldb.SBValue):
+    # For getting NimStringV2 value
+    value = value.GetNonSyntheticValue()
+
+    data = value["p"]["data"]
+
+    # Check if data pointer is Null
+    if value["p"].unsigned == 0:
+        return None
+
+    size = int(value["len"].signed)
+
+    if size == 0:
+        return ""
+
+    if size > 2**14:
+        return "... (too long) ..."
+
+    # Check if first element is NULL
+    base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+    cast = data.Cast(base_data_type)
+
+    if cast.unsigned == 0:
+        return None
+
+    cast = data.Cast(base_data_type.GetArrayType(size))
+    return bytearray(cast.data.uint8s).decode("utf-8")
+
+
+def NimString(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    if NIM_IS_V2:
+        res = to_stringV2(value)
+    else:
+        res = to_string(value)
+
+    if res is not None:
+        return f'"{res}"'
+    else:
+        return "nil"
+
+
+def rope_helper(value: lldb.SBValue) -> str:
+    value = value.GetNonSyntheticValue()
+    if value.type.is_pointer and value.unsigned == 0:
+        return ""
+
+    if value["length"].unsigned == 0:
+        return ""
+
+    if NIM_IS_V2:
+        str_val = to_stringV2(value["data"])
+    else:
+        str_val = to_string(value["data"])
+
+    if str_val is None:
+        str_val = ""
+
+    return rope_helper(value["left"]) + str_val + rope_helper(value["right"])
+
+
+def Rope(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    rope_str = rope_helper(value)
+
+    if len(rope_str) == 0:
+        rope_str = "nil"
+    else:
+        rope_str = f'"{rope_str}"'
+
+    return f"Rope({rope_str})"
+
+
+def NCSTRING(value: lldb.SBValue, internal_dict=None):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    ty = value.Dereference().type
+    val = value.target.CreateValueFromAddress(
+        value.name or "temp", lldb.SBAddress(value.unsigned, value.target), ty
+    ).AddressOf()
+    return val.summary
+
+
+def ObjectV2(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    orig_value = value.GetNonSyntheticValue()
+    if orig_value.type.is_pointer and orig_value.unsigned == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    while orig_value.type.is_pointer:
+        orig_value = orig_value.Dereference()
+
+    if "_" in orig_value.type.name:
+        obj_name = orig_value.type.name.split("_")[1].replace("colonObjectType", "")
+    else:
+        obj_name = orig_value.type.name
+
+    num_children = value.num_children
+    fields = []
+
+    for i in range(num_children):
+        fields.append(f"{value[i].name}: {value[i].summary}")
+
+    res = f"{obj_name}(" + ", ".join(fields) + ")"
+    return res
+
+
+def Number(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    if value.type.is_pointer and value.signed == 0:
+        return "nil"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.signed)
+
+
+def Float(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def UnsignedNumber(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.unsigned)
+
+
+def Bool(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str(value.value)
+
+
+def CharArray(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return str([f"'{char}'" for char in value.uint8s])
+
+
+def Array(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    value = value.GetNonSyntheticValue()
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    value = value.GetNonSyntheticValue()
+    return "[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+def Tuple(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    num_children = value.num_children
+
+    fields = []
+
+    for i in range(num_children):
+        key = value[i].name
+        val = value[i].summary
+        if key.startswith("Field"):
+            fields.append(f"{val}")
+        else:
+            fields.append(f"{key}: {val}")
+
+    return "(" + ", ".join(fields) + f")"
+
+
+def is_local(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if line.file == decl.file and decl.line != 0:
+        return True
+
+    return False
+
+
+def is_in_scope(value: lldb.SBValue) -> bool:
+    line: lldb.SBLineEntry = value.frame.GetLineEntry()
+    decl: lldb.SBDeclaration = value.GetDeclaration()
+
+    if is_local(value) and decl.line < line.line:
+        return True
+
+    return False
+
+
+def Enum(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_value_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    return enum_to_string(value)
+
+
+def EnumSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.summary)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Set(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    vals = []
+    max_vals = 7
+    for child in value.children:
+        vals.append(child.value)
+        if len(vals) > max_vals:
+            vals.append("...")
+            break
+
+    return "{" + ", ".join(vals) + "}"
+
+
+def Table(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    return "Table({" + ", ".join(fields) + "})"
+
+
+def HashSet(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if custom_summary is not None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children):
+        fields.append(f"{value[i].summary}")
+
+    return "HashSet({" + ", ".join(fields) + "})"
+
+
+def StringTable(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    fields = []
+
+    for i in range(value.num_children - 1):
+        key = value[i].name
+        val = value[i].summary
+        fields.append(f"{key}: {val}")
+
+    mode = value[value.num_children - 1].summary
+
+    return "StringTable({" + ", ".join(fields) + f"}}, mode={mode})"
+
+
+def Sequence(value: lldb.SBValue, internal_dict):
+    if is_local(value):
+        if not is_in_scope(value):
+            return "undefined"
+
+    custom_summary = get_custom_summary(value)
+    if not custom_summary is None:
+        return custom_summary
+
+    return "@[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]"
+
+
+class StringChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        if not NIM_IS_V2:
+            self.data_type = self.value.target.FindFirstType("char")
+
+        self.first_element: lldb.SBValue
+        self.update()
+        self.count = 0
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data_size
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> int:
+        if NIM_IS_V2:
+            if self.value["p"].unsigned == 0:
+                return 0
+
+            size = int(self.value["len"].signed)
+
+            if size == 0:
+                return 0
+
+            data = self.value["p"]["data"]
+
+            # Check if first element is NULL
+            base_data_type = data.type.GetArrayElementType().GetTypedefedType()
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+        else:
+            if self.value.type.is_pointer and self.value.unsigned == 0:
+                return 0
+
+            size = int(self.value["Sup"]["len"].unsigned)
+
+            if size == 0:
+                return 0
+
+            data = self.value["data"]
+
+            # Check if first element is NULL
+            base_data_type = self.value.target.FindFirstType("char")
+            cast = data.Cast(base_data_type)
+
+            if cast.unsigned == 0:
+                return 0
+
+        return size
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        data = self.get_data()
+        size = self.get_len()
+
+        self.count = size
+        self.first_element = data
+
+        if NIM_IS_V2:
+            self.data_type = data.type.GetArrayElementType().GetTypedefedType()
+
+        self.data_size = self.data_type.GetByteSize()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ArrayChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.update()
+
+    def num_children(self):
+        return self.has_children() and self.value.num_children
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.value[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def update(self):
+        if not self.has_children():
+            return
+
+        self.first_element = self.value[0]
+        self.data_type = self.value.type.GetArrayElementType()
+
+    def has_children(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return False
+        return bool(self.value.num_children)
+
+
+class SeqChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.count = 0
+        self.update()
+
+    def num_children(self):
+        return self.count
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        offset = index * self.data[index].GetByteSize()
+        return self.first_element.CreateChildAtOffset("[" + str(index) + "]", offset, self.data_type)
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["p"]["data"] if NIM_IS_V2 else self.value["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["len"] if NIM_IS_V2 else self.value["Sup"]["len"]
+
+    def update(self):
+        self.count = 0
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        self.count = self.get_len().unsigned
+
+        if not self.has_children():
+            return
+
+        data = self.get_data()
+        self.data_type = data.type.GetArrayElementType()
+
+        self.data = data.Cast(self.data_type.GetArrayType(self.num_children()))
+        self.first_element = self.data
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class ObjectChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.data_type: lldb.SBType
+        self.first_element: lldb.SBValue
+        self.data: lldb.SBValue
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.children)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def populate_children(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        stack = [self.value.GetNonSyntheticValue()]
+
+        index = 0
+
+        while stack:
+            cur_val = stack.pop()
+            if cur_val.type.is_pointer and cur_val.unsigned == 0:
+                continue
+
+            while cur_val.type.is_pointer:
+                cur_val = cur_val.Dereference()
+
+            # Add super objects if they exist
+            if cur_val.num_children > 0 and cur_val[0].name == "Sup" and cur_val[0].type.name.startswith("tyObject"):
+                stack.append(cur_val[0])
+
+            for child in cur_val.children:
+                child = child.GetNonSyntheticValue()
+                if child.name == "Sup":
+                    continue
+                self.children[child.name] = index
+                self.child_list.append(child)
+                index += 1
+
+    def update(self):
+        self.populate_children()
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class HashSetChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            child = key.CreateValueFromAddress(f"[{str(index)}]", key.GetLoadAddress(), key.GetType())
+            index += 1
+
+            self.child_list.append(child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetCharChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType("char")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        cur_pos = 0
+        for child in self.value.children:
+            child_val = child.signed
+            if child_val != 0:
+                temp = child_val
+                num_bits = 8
+                while temp != 0:
+                    is_set = temp & 1
+                    if is_set == 1:
+                        data = lldb.SBData.CreateDataFromInt(cur_pos)
+                        child = self.value.synthetic_child_from_data(f"[{len(self.child_list)}]", data, self.ty)
+                        self.child_list.append(child)
+                    temp = temp >> 1
+                    cur_pos += 1
+                    num_bits -= 1
+                cur_pos += num_bits
+            else:
+                cur_pos += 8
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+def create_set_children(value: lldb.SBValue, child_type: lldb.SBType, starting_pos: int) -> list[lldb.SBValue]:
+    child_list: list[lldb.SBValue] = []
+    cur_pos = starting_pos
+
+    if value.num_children > 0:
+        children = value.children
+    else:
+        children = [value]
+
+    for child in children:
+        child_val = child.signed
+        if child_val != 0:
+            temp = child_val
+            num_bits = 8
+            while temp != 0:
+                is_set = temp & 1
+                if is_set == 1:
+                    data = lldb.SBData.CreateDataFromInt(cur_pos)
+                    child = value.synthetic_child_from_data(f"[{len(child_list)}]", data, child_type)
+                    child_list.append(child)
+                temp = temp >> 1
+                cur_pos += 1
+                num_bits -= 1
+            cur_pos += num_bits
+        else:
+            cur_pos += 8
+
+    return child_list
+
+
+class SetIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NI64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        bits = self.value.GetByteSize() * 8
+        starting_pos = -(bits // 2)
+        self.child_list = create_set_children(self.value, self.ty, starting_pos)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetUIntChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(f"NU64")
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class SetEnumChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.ty = self.value.target.FindFirstType(self.value.type.name.replace("tySet_", ""))
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return int(name.lstrip("[").rstrip("]"))
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+        self.child_list = create_set_children(self.value, self.ty, starting_pos=0)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class TableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.child_list = []
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[0].unsigned)
+            if field0 == 0:
+                continue
+            key = el[1]
+            val = el[2]
+            key_summary = key.summary
+            child = self.value.CreateValueFromAddress(key_summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key_summary] = index
+            index += 1
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class StringTableChildrenProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value = value
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def get_data(self) -> lldb.SBValue:
+        return self.value["data"]["p"]["data"] if NIM_IS_V2 else self.value["data"]["data"]
+
+    def get_len(self) -> lldb.SBValue:
+        return self.value["data"]["len"] if NIM_IS_V2 else self.value["data"]["Sup"]["len"]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        if is_local(self.value):
+            if not is_in_scope(self.value):
+                return
+
+        tuple_len = int(self.get_len().unsigned)
+        tuple = self.get_data()
+
+        base_data_type = tuple.type.GetArrayElementType()
+
+        cast = tuple.Cast(base_data_type.GetArrayType(tuple_len))
+
+        index = 0
+        for i in range(tuple_len):
+            el = cast[i]
+            field0 = int(el[2].unsigned)
+            if field0 == 0:
+                continue
+            key = el[0]
+            val = el[1]
+            child = val.CreateValueFromAddress(key.summary, val.GetLoadAddress(), val.GetType())
+            self.child_list.append(child)
+            self.children[key.summary] = index
+            index += 1
+
+        self.child_list.append(self.value["mode"])
+        self.children["mode"] = index
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBDynamicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        value = value.GetNonSyntheticValue()
+        self.value: lldb.SBValue = value[0]
+        self.children: OrderedDict[str, int] = OrderedDict()
+        self.child_list: list[lldb.SBValue] = []
+
+        while self.value.type.is_pointer:
+            self.value = self.value.Dereference()
+
+        self.update()
+
+    def num_children(self):
+        return len(self.child_list)
+
+    def get_child_index(self, name: str):
+        return self.children[name]
+
+    def get_child_at_index(self, index):
+        return self.child_list[index]
+
+    def update(self):
+        self.children.clear()
+        self.child_list = []
+
+        for i, child in enumerate(self.value.children):
+            name = child.name.strip('"')
+            new_child = child.CreateValueFromAddress(name, child.GetLoadAddress(), child.GetType())
+
+            self.children[name] = i
+            self.child_list.append(new_child)
+
+    def has_children(self):
+        return bool(self.num_children())
+
+
+class LLDBBasicObjectProvider:
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = value
+
+    def num_children(self):
+        if self.value is not None:
+            return self.value.num_children
+        return 0
+
+    def get_child_index(self, name: str):
+        return self.value.GetIndexOfChildWithName(name)
+
+    def get_child_at_index(self, index):
+        return self.value.GetChildAtIndex(index)
+
+    def update(self):
+        pass
+
+    def has_children(self):
+        return self.num_children() > 0
+
+
+class CustomObjectChildrenProvider:
+    """
+    This children provider handles values returned from lldbDebugSynthetic*
+    Nim procedures
+    """
+
+    def __init__(self, value: lldb.SBValue, internalDict):
+        self.value: lldb.SBValue = get_custom_synthetic(value) or value
+        if "lldbdynamicobject" in self.value.type.name.lower():
+            self.provider = LLDBDynamicObjectProvider(self.value, internalDict)
+        else:
+            self.provider = LLDBBasicObjectProvider(self.value, internalDict)
+
+    def num_children(self):
+        return self.provider.num_children()
+
+    def get_child_index(self, name: str):
+        return self.provider.get_child_index(name)
+
+    def get_child_at_index(self, index):
+        return self.provider.get_child_at_index(index)
+
+    def update(self):
+        self.provider.update()
+
+    def has_children(self):
+        return self.provider.has_children()
+
+
+def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict):
+    debugger.HandleCommand("po " + command)
+
+
+SUMMARY_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+SYNTHETIC_FUNCTIONS: dict[str, lldb.SBFunction] = {}
+
+
+def get_custom_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SUMMARY_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        command = f"{fn.name}(({first_type.name})" + str(value.GetLoadAddress()) + ");"
+    else:
+        command = f"{fn.name}(*({first_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_value_summary(value: lldb.SBValue) -> Union[str, None]:
+    """Get a custom summary if a function exists for it"""
+
+    fn: lldb.SBFunction = SUMMARY_FUNCTIONS.get(value.type.name)
+    if fn is None:
+        return None
+
+    command = f"{fn.name}(({value.type.name})" + str(value.signed) + ");"
+    res = executeCommand(command)
+
+    if res.error.fail:
+        return None
+
+    return res.summary.strip('"')
+
+
+def get_custom_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]:
+    """Get a custom synthetic object if a function exists for it"""
+    value = value.GetNonSyntheticValue()
+    if value.GetAddress().GetOffset() == 0:
+        return None
+
+    base_type = get_base_type(value.type)
+
+    fn = SYNTHETIC_FUNCTIONS.get(base_type.name)
+    if fn is None:
+        return None
+
+    fn_type: lldb.SBType = fn.type
+
+    arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+    first_type = arg_types.GetTypeAtIndex(0)
+
+    while value.type.is_pointer:
+        value = value.Dereference()
+
+    if first_type.is_pointer:
+        first_arg = f"({first_type.name}){value.GetLoadAddress()}"
+    else:
+        first_arg = f"*({first_type.GetPointerType().name}){value.GetLoadAddress()}"
+
+    if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+        ret_type = arg_types.GetTypeAtIndex(1)
+        ret_type = get_base_type(ret_type)
+
+        command = f"""
+            {ret_type.name} lldbT;
+            nimZeroMem((void*)(&lldbT), sizeof({ret_type.name}));
+            {fn.name}(({first_arg}), (&lldbT));
+            lldbT;
+        """
+    else:
+        command = f"{fn.name}({first_arg});"
+
+    res = executeCommand(command)
+
+    if res.error.fail:
+        print(res.error)
+        return None
+
+    return res
+
+
+def get_base_type(ty: lldb.SBType) -> lldb.SBType:
+    """Get the base type of the type"""
+    temp = ty
+    while temp.IsPointerType():
+        temp = temp.GetPointeeType()
+    return temp
+
+
+def use_base_type(ty: lldb.SBType) -> bool:
+    types_to_check = [
+        "NF",
+        "NF32",
+        "NF64",
+        "NI",
+        "NI8",
+        "NI16",
+        "NI32",
+        "NI64",
+        "bool",
+        "NIM_BOOL",
+        "NU",
+        "NU8",
+        "NU16",
+        "NU32",
+        "NU64",
+    ]
+
+    for type_to_check in types_to_check:
+        if ty.name.startswith(type_to_check):
+            return False
+
+    return True
+
+
+def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict):
+    """This allows function calls to Nim for custom object summaries and synthetic children"""
+    debugger = lldb.debugger
+
+    global SUMMARY_FUNCTIONS
+    global SYNTHETIC_FUNCTIONS
+
+    global NIM_IS_V2
+
+    for tname, fn in SYNTHETIC_FUNCTIONS.items():
+        debugger.HandleCommand(f"type synthetic delete -w nim {tname}")
+
+    SUMMARY_FUNCTIONS = {}
+    SYNTHETIC_FUNCTIONS = {}
+
+    target: lldb.SBTarget = debugger.GetSelectedTarget()
+
+    NIM_IS_V2 = target.FindFirstType("TNimTypeV2").IsValid()
+
+    module = frame.GetSymbolContext(lldb.eSymbolContextModule).module
+
+    for sym in module:
+        if (
+            not sym.name.startswith("lldbDebugSummary")
+            and not sym.name.startswith("lldbDebugSynthetic")
+            and not sym.name.startswith("dollar___")
+        ):
+            continue
+
+        fn_syms: lldb.SBSymbolContextList = target.FindFunctions(sym.name)
+        if not fn_syms.GetSize() > 0:
+            continue
+
+        fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0)
+
+        fn: lldb.SBFunction = fn_sym.function
+        fn_type: lldb.SBType = fn.type
+        arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes()
+
+        if arg_types.GetSize() > 1 and fn.GetArgumentName(1) == "Result":
+            pass # don't continue
+        elif arg_types.GetSize() != 1:
+            continue
+
+        arg_type: lldb.SBType = arg_types.GetTypeAtIndex(0)
+        if use_base_type(arg_type):
+            arg_type = get_base_type(arg_type)
+
+        if sym.name.startswith("lldbDebugSummary") or sym.name.startswith("dollar___"):
+            SUMMARY_FUNCTIONS[arg_type.name] = fn
+        elif sym.name.startswith("lldbDebugSynthetic"):
+            SYNTHETIC_FUNCTIONS[arg_type.name] = fn
+            debugger.HandleCommand(
+                f"type synthetic add -w nim -l {__name__}.CustomObjectChildrenProvider {arg_type.name}"
+            )
+
+
+def executeCommand(command, *args):
+    debugger = lldb.debugger
+    process = debugger.GetSelectedTarget().GetProcess()
+    frame: lldb.SBFrame = process.GetSelectedThread().GetSelectedFrame()
+
+    expr_options = lldb.SBExpressionOptions()
+    expr_options.SetIgnoreBreakpoints(False)
+    expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget)
+    expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000)  # 30 second timeout
+    expr_options.SetTryAllThreads(True)
+    expr_options.SetUnwindOnError(False)
+    expr_options.SetGenerateDebugInfo(True)
+    expr_options.SetLanguage(lldb.eLanguageTypeC)
+    expr_options.SetCoerceResultToId(True)
+    res = frame.EvaluateExpression(command, expr_options)
+
+    return res
+
+
+def __lldb_init_module(debugger, internal_dict):
+    # fmt: off
+    debugger.HandleCommand(f"breakpoint command add -F {__name__}.breakpoint_function_wrapper --script-type python 1")
+    debugger.HandleCommand(f"type summary add -w nim -n sequence -F  {__name__}.Sequence -x tySequence_+[[:alnum:]]+$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SeqChildrenProvider -x tySequence_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n chararray -F  {__name__}.CharArray -x char\s+[\d+]")
+    debugger.HandleCommand(f"type summary add -w nim -n array -F  {__name__}.Array -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ArrayChildrenProvider -x tyArray_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n string -F  {__name__}.NimString NimStringDesc")
+
+    debugger.HandleCommand(f"type summary add -w nim -n stringv2 -F {__name__}.NimString -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringV2$")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringDesc$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n cstring -F  {__name__}.NCSTRING NCSTRING")
+
+    debugger.HandleCommand(f"type summary add -w nim -n object -F  {__name__}.ObjectV2 -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ObjectChildrenProvider -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n tframe -F  {__name__}.ObjectV2 -x TFrame$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rootobj -F  {__name__}.ObjectV2 -x RootObj$")
+
+    debugger.HandleCommand(f"type summary add -w nim -n enum -F  {__name__}.Enum -x tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n hashset -F  {__name__}.HashSet -x tyObject_+HashSet_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.HashSetChildrenProvider -x tyObject_+HashSet_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n rope -F  {__name__}.Rope -x tyObject_+Rope[[:alnum:]]+_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n setuint -F  {__name__}.Set -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetIntChildrenProvider -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setint -F  {__name__}.Set -x tySet_+tyInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setuint2 -F  {__name__}.Set -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyInt_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setenum -F  {__name__}.EnumSet -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetEnumChildrenProvider -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n setchar -F  {__name__}.Set -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetCharChildrenProvider -x tySet_+tyChar_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n table -F  {__name__}.Table -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.TableChildrenProvider -x tyObject_+Table_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n stringtable -F  {__name__}.StringTable -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringTableChildrenProvider -x tyObject_+StringTableObj_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple2 -F  {__name__}.Tuple -x tyObject_+Tuple_+[[:alnum:]]+")
+    debugger.HandleCommand(f"type summary add -w nim -n tuple -F  {__name__}.Tuple -x tyTuple_+[[:alnum:]]+")
+
+    debugger.HandleCommand(f"type summary add -w nim -n float -F  {__name__}.Float NF")
+    debugger.HandleCommand(f"type summary add -w nim -n float32 -F  {__name__}.Float NF32")
+    debugger.HandleCommand(f"type summary add -w nim -n float64 -F  {__name__}.Float NF64")
+    debugger.HandleCommand(f"type summary add -w nim -n integer -F  {__name__}.Number -x NI")
+    debugger.HandleCommand(f"type summary add -w nim -n integer8 -F  {__name__}.Number -x NI8")
+    debugger.HandleCommand(f"type summary add -w nim -n integer16 -F  {__name__}.Number -x NI16")
+    debugger.HandleCommand(f"type summary add -w nim -n integer32 -F  {__name__}.Number -x NI32")
+    debugger.HandleCommand(f"type summary add -w nim -n integer64 -F  {__name__}.Number -x NI64")
+    debugger.HandleCommand(f"type summary add -w nim -n bool -F  {__name__}.Bool -x bool")
+    debugger.HandleCommand(f"type summary add -w nim -n bool2 -F  {__name__}.Bool -x NIM_BOOL")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger -F  {__name__}.UnsignedNumber -x NU")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger8 -F  {__name__}.UnsignedNumber -x NU8")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger16 -F  {__name__}.UnsignedNumber -x NU16")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger32 -F  {__name__}.UnsignedNumber -x NU32")
+    debugger.HandleCommand(f"type summary add -w nim -n uinteger64 -F  {__name__}.UnsignedNumber -x NU64")
+    debugger.HandleCommand("type category enable nim")
+    debugger.HandleCommand(f"command script add -f  {__name__}.echo echo")
+    # fmt: on
diff --git a/tools/deps.nim b/tools/deps.nim
index 59872070d..e43f7a2b4 100644
--- a/tools/deps.nim
+++ b/tools/deps.nim
@@ -1,6 +1,9 @@
-import os, uri, strformat, strutils
+import std/[os, uri, strformat, strutils]
 import std/private/gitutils
 
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 proc exec(cmd: string) =
   echo "deps.cmd: " & cmd
   let status = execShellCmd(cmd)
diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim
index 841b3a675..ed9438494 100644
--- a/tools/detect/detect.nim
+++ b/tools/detect/detect.nim
@@ -12,7 +12,10 @@
 # The second one is more portable, and less semantically correct. It only works
 # when there's a backing C compiler available as well, preventing standalone
 # compilation.
-import os, strutils
+import std/[os, strutils]
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
 
 when defined(openbsd) or defined(freebsd) or defined(netbsd):
   const
@@ -615,6 +618,7 @@ v("MAP_POPULATE", no_other = true)
 
 header("<sys/resource.h>")
 v("RLIMIT_NOFILE")
+v("RLIMIT_STACK")
 
 header("<sys/select.h>")
 v("FD_SETSIZE")
@@ -626,6 +630,7 @@ v("MSG_EOR")
 v("MSG_OOB")
 v("SCM_RIGHTS")
 v("SO_ACCEPTCONN")
+v("SO_BINDTODEVICE")
 v("SO_BROADCAST")
 v("SO_DEBUG")
 v("SO_DONTROUTE")
diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim
index 1a5c86253..946945346 100644
--- a/tools/dochack/dochack.nim
+++ b/tools/dochack/dochack.nim
@@ -1,5 +1,6 @@
 import dom
 import fuzzysearch
+import std/[jsfetch, asyncjs]
 
 
 proc setTheme(theme: cstring) {.exportc.} =
@@ -252,21 +253,7 @@ proc escapeCString(x: var cstring) =
 
 proc dosearch(value: cstring): Element =
   if db.len == 0:
-    var stuff: Element
-    {.emit: """
-    var request = new XMLHttpRequest();
-    request.open("GET", "theindex.html", false);
-    request.send(null);
-
-    var doc = document.implementation.createHTMLDocument("theindex");
-    doc.documentElement.innerHTML = request.responseText;
-
-    `stuff` = doc.documentElement;
-    """.}
-    db = stuff.getElementsByClass"reference"
-    contents = @[]
-    for ahref in db:
-      contents.add ahref.getAttribute("data-doc-search-tag")
+    return
   let ul = tree("UL")
   result = tree("DIV")
   result.setClass"search_results"
@@ -293,8 +280,28 @@ proc dosearch(value: cstring): Element =
     result.add tree("B", text"search results")
     result.add ul
 
-var oldtoc: Element
-var timer: Timeout
+proc loadIndex() {.async.} =
+  ## Loads theindex.html to enable searching
+  let
+    indexURL = document.getElementById("indexLink").getAttribute("href")
+    # Get root of project documentation by cutting off theindex.html from index href
+    rootURL = ($indexURL)[0 ..< ^"theindex.html".len]
+  var resp = fetch(indexURL).await().text().await()
+  # Convert into element so we can use DOM functions to parse the html
+  var indexElem = document.createElement("div")
+  indexElem.innerHtml = resp
+  # Add items into the DB/contents
+  for href in indexElem.getElementsByClass("reference"):
+    # Make links be relative to project root instead of current page
+    href.setAttr("href", cstring(rootURL & $href.getAttribute("href")))
+    db &= href
+    contents &= href.getAttribute("data-doc-search-tag")
+
+
+var
+  oldtoc: Element
+  timer: Timeout
+  loadIndexFut: Future[void] = nil
 
 proc search*() {.exportc.} =
   proc wrapper() =
@@ -307,7 +314,12 @@ proc search*() {.exportc.} =
       replaceById("tocRoot", results)
     elif not oldtoc.isNil:
       replaceById("tocRoot", oldtoc)
-
+  # Start loading index as soon as user starts typing.
+  # Will only be loaded the once anyways
+  if loadIndexFut == nil:
+    loadIndexFut = loadIndex()
+    # Run wrapper once loaded so we don't miss the users query
+    discard loadIndexFut.then(wrapper)
   if timer != nil: clearTimeout(timer)
   timer = setTimeout(wrapper, 400)
 
@@ -316,8 +328,8 @@ proc copyToClipboard*() {.exportc.} =
 
     function updatePreTags() {
 
-      const allPreTags = document.querySelectorAll("pre")
-    
+      const allPreTags = document.querySelectorAll("pre:not(.line-nums)")
+
       allPreTags.forEach((e) => {
       
           const div = document.createElement("div")
diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim
index bcb3a044f..cbdc51efc 100644
--- a/tools/grammar_nanny.nim
+++ b/tools/grammar_nanny.nim
@@ -22,7 +22,6 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) =
     var
       L: Lexer
       tok: Token
-    initToken(tok)
     openLexer(L, f, stream, cache, config)
     # load the first token:
     rawGetTok(L, tok)
diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim
index 9fe0a8246..477fb29fa 100644
--- a/tools/kochdocs.nim
+++ b/tools/kochdocs.nim
@@ -1,14 +1,19 @@
 ## Part of 'koch' responsible for the documentation generation.
 
-import os, strutils, osproc, sets, pathnorm, sequtils
-# XXX: Remove this feature check once the csources supports it.
-when defined(nimHasCastPragmaBlocks):
-  import std/pegs
+import std/[os, strutils, osproc, sets, pathnorm, sequtils, pegs]
+
+import officialpackages
+export exec
+
+when defined(nimPreviewSlimSystem):
+  import std/assertions
+
 from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry
 import "../compiler/nimpaths"
 
 const
   gaCode* = " --doc.googleAnalytics:UA-48159761-1"
+  paCode* = " --doc.plausibleAnalytics:nim-lang.org"
   # errormax: subsequent errors are probably consequences of 1st one; a simple
   # bug could cause unlimited number of errors otherwise, hard to debug in CI.
   docDefines = "-d:nimExperimentalLinenoiseExtra"
@@ -43,38 +48,19 @@ proc findNimImpl*(): tuple[path: string, ok: bool] =
 
 proc findNim*(): string = findNimImpl().path
 
-proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
-  let prevPath = getEnv("PATH")
-  if additionalPath.len > 0:
-    var absolute = additionalPath
-    if not absolute.isAbsolute:
-      absolute = getCurrentDir() / absolute
-    echo("Adding to $PATH: ", absolute)
-    putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & absolute)
-  echo(cmd)
-  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
-  putEnv("PATH", prevPath)
-
 template inFold*(desc, body) =
-  if existsEnv("TRAVIS"):
-    echo "travis_fold:start:" & desc.replace(" ", "_")
-  elif existsEnv("GITHUB_ACTIONS"):
+  if existsEnv("GITHUB_ACTIONS"):
     echo "::group::" & desc
   elif existsEnv("TF_BUILD"):
     echo "##[group]" & desc
-
   body
-
-  if existsEnv("TRAVIS"):
-    echo "travis_fold:end:" & desc.replace(" ", "_")
-  elif existsEnv("GITHUB_ACTIONS"):
+  if existsEnv("GITHUB_ACTIONS"):
     echo "::endgroup::"
   elif existsEnv("TF_BUILD"):
     echo "##[endgroup]"
 
 proc execFold*(desc, cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
   ## Execute shell command. Add log folding for various CI services.
-  # https://github.com/travis-ci/travis-ci/issues/2285#issuecomment-42724719
   let desc = if desc.len == 0: cmd else: desc
   inFold(desc):
     exec(cmd, errorcode, additionalPath)
@@ -107,17 +93,22 @@ proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options
   let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input
   execFold(desc, cmd)
 
+const officialPackagesMarkdown = """
+pkgs/atlas/doc/atlas.md
+""".splitWhitespace()
+
 proc getMd2html(): seq[string] =
   for a in walkDirRecFilter("doc"):
     let path = a.path
     if a.kind == pcFile and path.splitFile.ext == ".md" and path.lastPathPart notin
-        ["docs.md", "nimfix.md",
+        ["docs.md",
          "docstyle.md" # docstyle.md shouldn't be converted to html separately;
                        # it's included in contributing.md.
         ]:
-          # maybe we should still show nimfix, could help reviving it
           # `docs` is redundant with `overview`, might as well remove that file?
       result.add path
+  for md in officialPackagesMarkdown:
+    result.add md
   doAssert "doc/manual/var_t_return.md".unixToNativePath in result # sanity check
 
 const
@@ -132,17 +123,8 @@ niminst.md
 mm.md
 """.splitWhitespace().mapIt("doc" / it)
 
-  doc0 = """
-lib/system/threads.nim
-lib/system/channels_builtin.nim
-""".splitWhitespace() # ran by `nim doc0` instead of `nim doc`
-
   withoutIndex = """
-lib/wrappers/mysql.nim
-lib/wrappers/sqlite3.nim
-lib/wrappers/postgres.nim
 lib/wrappers/tinyc.nim
-lib/wrappers/odbcsql.nim
 lib/wrappers/pcre.nim
 lib/wrappers/openssl.nim
 lib/posix/posix.nim
@@ -170,6 +152,34 @@ lib/posix/posix_other_consts.nim
 lib/posix/posix_freertos_consts.nim
 lib/posix/posix_openbsd_amd64.nim
 lib/posix/posix_haiku.nim
+lib/pure/md5.nim
+lib/std/sha1.nim
+lib/pure/htmlparser.nim
+""".splitWhitespace()
+
+  officialPackagesList = """
+pkgs/asyncftpclient/src/asyncftpclient.nim
+pkgs/smtp/src/smtp.nim
+pkgs/punycode/src/punycode.nim
+pkgs/db_connector/src/db_connector/db_common.nim
+pkgs/db_connector/src/db_connector/db_mysql.nim
+pkgs/db_connector/src/db_connector/db_odbc.nim
+pkgs/db_connector/src/db_connector/db_postgres.nim
+pkgs/db_connector/src/db_connector/db_sqlite.nim
+pkgs/checksums/src/checksums/md5.nim
+pkgs/checksums/src/checksums/sha1.nim
+pkgs/checksums/src/checksums/sha2.nim
+pkgs/checksums/src/checksums/sha3.nim
+pkgs/checksums/src/checksums/bcrypt.nim
+pkgs/htmlparser/src/htmlparser.nim
+""".splitWhitespace()
+
+  officialPackagesListWithoutIndex = """
+pkgs/db_connector/src/db_connector/mysql.nim
+pkgs/db_connector/src/db_connector/sqlite3.nim
+pkgs/db_connector/src/db_connector/postgres.nim
+pkgs/db_connector/src/db_connector/odbcsql.nim
+pkgs/db_connector/src/db_connector/private/dbutils.nim
 """.splitWhitespace()
 
 when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
@@ -180,8 +190,8 @@ when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo):
     result = path.len > 0 and not ret.startsWith ".."
 
 proc getDocList(): seq[string] =
+  ##
   var docIgnore: HashSet[string]
-  for a in doc0: docIgnore.incl a
   for a in withoutIndex: docIgnore.incl a
   for a in ignoredModules: docIgnore.incl a
 
@@ -190,8 +200,9 @@ proc getDocList(): seq[string] =
 lib/system/nimscript.nim
 lib/system/assertions.nim
 lib/system/iterators.nim
+lib/system/exceptions.nim
 lib/system/dollars.nim
-lib/system/widestrs.nim
+lib/system/ctypes.nim
 """.splitWhitespace()
 
   proc follow(a: PathEntry): bool =
@@ -231,49 +242,48 @@ proc buildDocSamples(nimArgs, destPath: string) =
   exec(findNim().quoteShell() & " doc $# -o:$# $#" %
     [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"])
 
-proc buildDocPackages(nimArgs, destPath: string) =
+proc buildDocPackages(nimArgs, destPath: string, indexOnly: bool) =
   # compiler docs; later, other packages (perhaps tools, testament etc)
   let nim = findNim().quoteShell()
     # to avoid broken links to manual from compiler dir, but a multi-package
     # structure could be supported later
 
   proc docProject(outdir, options, mainproj: string) =
-    exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $options $mainproj" % [
+    exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $index $options $mainproj" % [
       "nim", nim,
       "outdir", outdir,
       "nimArgs", nimArgs,
       "gitUrl", gitUrl,
       "options", options,
       "mainproj", mainproj,
+      "index", if indexOnly: "--index:only" else: ""
       ])
   let extra = "-u:boot"
   # xxx keep in sync with what's in $nim_prs_D/config/nimdoc.cfg, or, rather,
   # start using nims instead of nimdoc.cfg
   docProject(destPath/"compiler", extra, "compiler/index.nim")
 
-proc buildDoc(nimArgs, destPath: string) =
+proc buildDoc(nimArgs, destPath: string, indexOnly: bool) =
   # call nim for the documentation:
   let rst2html = getMd2html()
   var
-    commands = newSeq[string](rst2html.len + len(doc0) + len(doc) + withoutIndex.len)
+    commands = newSeq[string](rst2html.len + len(doc) + withoutIndex.len +
+              officialPackagesList.len + officialPackagesListWithoutIndex.len)
     i = 0
   let nim = findNim().quoteShell()
+
+  let index = if indexOnly: "--index:only" else: ""
   for d in items(rst2html):
-    commands[i] = nim & " md2html $# --git.url:$# -o:$# --index:on $#" %
+    commands[i] = nim & " md2html $# --git.url:$# -o:$# $# $#" %
       [nimArgs, gitUrl,
-      destPath / changeFileExt(splitFile(d).name, "html"), d]
-    i.inc
-  for d in items(doc0):
-    commands[i] = nim & " doc0 $# --git.url:$# -o:$# --index:on $#" %
-      [nimArgs, gitUrl,
-      destPath / changeFileExt(splitFile(d).name, "html"), d]
+      destPath / changeFileExt(splitFile(d).name, "html"), index, d]
     i.inc
   for d in items(doc):
     let extra = if isJsOnly(d): "--backend:js" else: ""
     var nimArgs2 = nimArgs
     if d.isRelativeTo("compiler"): doAssert false
-    commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# --index:on $#" %
-      [extra, nimArgs2, gitUrl, destPath, d]
+    commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# $# $#" %
+      [extra, nimArgs2, gitUrl, destPath, index, d]
     i.inc
   for d in items(withoutIndex):
     commands[i] = nim & " doc $# --git.url:$# -o:$# $#" %
@@ -281,13 +291,20 @@ proc buildDoc(nimArgs, destPath: string) =
       destPath / changeFileExt(splitFile(d).name, "html"), d]
     i.inc
 
+
+  for d in items(officialPackagesList):
+    var nimArgs2 = nimArgs
+    if d.isRelativeTo("compiler"): doAssert false
+    commands[i] = nim & " doc $# --outdir:$# --index:on $#" %
+      [nimArgs2, destPath, d]
+    i.inc
+  for d in items(officialPackagesListWithoutIndex):
+    commands[i] = nim & " doc $# -o:$# $#" %
+      [nimArgs,
+      destPath / changeFileExt(splitFile(d).name, "html"), d]
+    i.inc
+
   mexec(commands)
-  exec(nim & " buildIndex -o:$1/theindex.html $1" % [destPath])
-    # caveat: this works so long it's called before `buildDocPackages` which
-    # populates `compiler/` with unrelated idx files that shouldn't be in index,
-    # so should work in CI but you may need to remove your generated html files
-    # locally after calling `./koch docs`. The clean fix would be for `idx` files
-    # to be transient with `--project` (eg all in memory).
 
 proc nim2pdf(src: string, dst: string, nimArgs: string) =
   # xxx expose as a `nim` command or in some other reusable way.
@@ -308,7 +325,8 @@ proc nim2pdf(src: string, dst: string, nimArgs: string) =
       exec(cmd)
   moveFile(texFile.changeFileExt("pdf"), dst)
 
-proc buildPdfDoc*(nimArgs, destPath: string) =
+proc buildPdfDoc*(args: string, destPath: string) =
+  let args = nimArgs & " " & args
   var pdfList: seq[string]
   createDir(destPath)
   if os.execShellCmd("xelatex -version") != 0:
@@ -317,7 +335,7 @@ proc buildPdfDoc*(nimArgs, destPath: string) =
     for src in items(mdPdfList):
       let dst = destPath / src.lastPathPart.changeFileExt("pdf")
       pdfList.add dst
-      nim2pdf(src, dst, nimArgs)
+      nim2pdf(src, dst, args)
   echo "\nOutput PDF files: \n  ", pdfList.join(" ") # because `nim2pdf` is a bit verbose
 
 proc buildJS(): string =
@@ -330,11 +348,26 @@ proc buildJS(): string =
 proc buildDocsDir*(args: string, dir: string) =
   let args = nimArgs & " " & args
   let docHackJsSource = buildJS()
+  gitClonePackages(@["asyncftpclient", "punycode", "smtp", "db_connector", "checksums", "atlas", "htmlparser"])
   createDir(dir)
   buildDocSamples(args, dir)
-  buildDoc(args, dir) # bottleneck
+
+  # generate `.idx` files and top-level `theindex.html`:
+  buildDoc(args, dir, indexOnly=true) # bottleneck
+  let nim = findNim().quoteShell()
+  exec(nim & " buildIndex -o:$1/theindex.html $1" % [dir])
+    # caveat: this works so long it's called before `buildDocPackages` which
+    # populates `compiler/` with unrelated idx files that shouldn't be in index,
+    # so should work in CI but you may need to remove your generated html files
+    # locally after calling `./koch docs`. The clean fix would be for `idx` files
+    # to be transient with `--project` (eg all in memory).
+  buildDocPackages(args, dir, indexOnly=true)
+
+  # generate HTML and package-level `theindex.html`:
+  buildDoc(args, dir, indexOnly=false) # bottleneck
+  buildDocPackages(args, dir, indexOnly=false)
+
   copyFile(dir / "overview.html", dir / "index.html")
-  buildDocPackages(args, dir)
   copyFile(docHackJsSource, dir / docHackJsSource.lastPathPart)
 
 proc buildDocs*(args: string, localOnly = false, localOutDir = "") =
@@ -349,9 +382,7 @@ proc buildDocs*(args: string, localOnly = false, localOutDir = "") =
   if not localOnly:
     buildDocsDir(args, webUploadOutput / NimVersion)
 
-    # XXX: Remove this feature check once the csources supports it.
-    when defined(nimHasCastPragmaBlocks):
-      let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )"
-      args = args.replace(gaFilter)
+    let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )"
+    args = args.replace(gaFilter)
 
   buildDocsDir(args, localOutDir)
diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim
index 7e4161faf..c86159739 100644
--- a/tools/nimgrab.nim
+++ b/tools/nimgrab.nim
@@ -1,13 +1,20 @@
 import std/[os, httpclient]
 
 proc syncDownload(url, file: string) =
-  var client = newHttpClient()
+  let client = newHttpClient()
   proc onProgressChanged(total, progress, speed: BiggestInt) =
-    echo "Downloading " & url & " " & $(speed div 1000) & "kb/s"
-    echo clamp(int(progress*100 div total), 0, 100), "%"
+    var message = "Downloading "
+    message.add url
+    message.add ' '
+    message.addInt speed div 1000
+    message.add "kb/s\n"
+    message.add $clamp(int(progress * 100 div total), 0, 100)
+    message.add '%'
+    echo message
 
   client.onProgressChanged = onProgressChanged
   client.downloadFile(url, file)
+  client.close()
   echo "100%"
 
 if os.paramCount() != 2:
diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim
index ef8aa5570..599c616ba 100644
--- a/tools/nimgrep.nim
+++ b/tools/nimgrep.nim
@@ -11,7 +11,7 @@ import
   os, strutils, parseopt, pegs, re, terminal, osproc, tables, algorithm, times
 
 const
-  Version = "1.6.0"
+  Version = "2.0.0"
   Usage = "nimgrep - Nim Grep Searching and Replacement Utility Version " &
   Version & """
 
@@ -989,7 +989,7 @@ iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string
     let rightDirForFiles = d.isRightDirectory(walkOptC)
     var files = newSeq[string]()
     var dirs = newSeq[string]()
-    for kind, path in walkDir(d):
+    for kind, path in walkDir(d, skipSpecial = true):
       case kind
       of pcFile:
         if path.hasRightPath(walkOptC) and rightDirForFiles:
@@ -1258,6 +1258,11 @@ for kind, key, val in getopt():
     else:
       paths.add(key)
   of cmdLongOption, cmdShortOption:
+    proc addNotEmpty(s: var seq[string], name: string) =
+      if name == "":
+        reportError("empty string given for option --" & key &
+                    " (did you forget `:`?)")
+      s.add name
     case normalize(key)
     of "find", "f": incl(options, optFind)
     of "replace", "!": incl(options, optReplace)
@@ -1288,30 +1293,31 @@ for kind, key, val in getopt():
        "noext", "no-ext":  # 2 deprecated options
       walkOpt.notExtensions.add val.split('|')
     of "dirname",  "di":
-      walkOpt.dirname.add val
+      walkOpt.dirname.addNotEmpty val
     of "ndirname", "notdirname", "ndi", "notdi",
-       "excludedir", "ed":  # 2 deprecated options
-      walkOpt.notDirname.add val
+       "excludedir", "exclude-dir", "ed":  # 3 deprecated options
+      walkOpt.notDirname.addNotEmpty val
     of "dirpath", "dirp",
-       "includedir", "id":  # 2 deprecated options
-      walkOpt.dirPath.add val
+       "includedir", "include-dir", "id":  # 3 deprecated options
+      walkOpt.dirPath.addNotEmpty val
     of "ndirpath", "notdirpath", "ndirp", "notdirp":
-      walkOpt.notDirPath.add val
+      walkOpt.notDirPath.addNotEmpty val
     of "filename", "fi",
        "includefile", "include-file", "if":  # 3 deprecated options
-      walkOpt.filename.add val
+      walkOpt.filename.addNotEmpty val
     of "nfilename", "nfi", "notfilename", "notfi",
        "excludefile", "exclude-file", "ef":  # 3 deprecated options
-      walkOpt.notFilename.add val
+      walkOpt.notFilename.addNotEmpty val
     of "infile", "inf",
        "matchfile", "match", "mf":  # 3 deprecated options
-      searchOpt.inFile.add val
+      searchOpt.inFile.addNotEmpty val
     of "ninfile", "notinfile", "ninf", "notinf",
        "nomatchfile", "nomatch", "nf":  # 3 options are deprecated
-      searchOpt.notInFile.add val
-    of "incontext", "inc": searchOpt.inContext.add val
+      searchOpt.notInFile.addNotEmpty val
+    of "incontext", "inc":
+      searchOpt.inContext.addNotEmpty val
     of "nincontext", "notincontext", "ninc", "notinc":
-      searchOpt.notInContext.add val
+      searchOpt.notInContext.addNotEmpty val
     of "bin":
       case val
       of "on": searchOpt.checkBin = biOn
diff --git a/tools/nimgrep.nim.cfg b/tools/nimgrep.nim.cfg
deleted file mode 100644
index 64d3edc7a..000000000
--- a/tools/nimgrep.nim.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-# don't use --gc:refc because of bug
-# https://github.com/nim-lang/Nim/issues/14138 .
-# --gc:orc and --gc:markandsweep work well.
---threads:on --gc:orc
diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf
index 6b99c49ee..063a02779 100644
--- a/tools/niminst/buildsh.nimf
+++ b/tools/niminst/buildsh.nimf
@@ -122,6 +122,7 @@ case $uos in
   *netbsd* )
     myos="netbsd"
     LINK_FLAGS="$LINK_FLAGS -lm"
+    ucpu=`uname -p`
     ;;
   *darwin* )
     myos="macosx"
diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim
index d0f46fa52..219cb44ce 100644
--- a/tools/niminst/debcreation.nim
+++ b/tools/niminst/debcreation.nim
@@ -9,6 +9,10 @@
 
 import osproc, times, os, strutils
 
+
+when defined(nimPreviewSlimSystem):
+  import std/[assertions, syncio]
+
 # http://www.debian.org/doc/manuals/maint-guide/
 
 # Required files for debhelper.
diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf
index a1f3fa977..002bc0592 100644
--- a/tools/niminst/makefile.nimf
+++ b/tools/niminst/makefile.nimf
@@ -16,6 +16,7 @@ endif
 
 target := ?{"$(binDir)/" & toLowerAscii(c.name)}
 
+
 ucpu := $(shell sh -c 'uname -m | tr "[:upper:]" "[:lower:]"')
 ifeq ($(OS),Windows_NT)
   uos := windows
@@ -44,6 +45,7 @@ endif
 ifeq ($(uos),netbsd)
   myos = netbsd
   LDFLAGS += -lm
+  ucpu = $(shell sh -c 'uname -p')
 endif
 ifeq ($(uos),darwin)
   myos = macosx
@@ -103,9 +105,18 @@ endif
 ifeq ($(ucpu),x86_64)
   mycpu = amd64
 endif
+ifeq ($(ucpu),parisc64)
+  mycpu = hppa
+endif
+ifeq ($(ucpu),s390x)
+  mycpu = s390x
+endif
 ifeq ($(ucpu),sparc)
   mycpu = sparc
 endif
+ifeq ($(ucpu),sparc64)
+  mycpu = sparc64
+endif
 ifeq ($(ucpu),sun)
   mycpu = sparc
 endif
@@ -131,7 +142,7 @@ ifeq ($(ucpu),powerpc)
   endif
 endif
 ifeq ($(ucpu),ppc)
-  mycpu = ppc
+   mycpu = powerpc
 endif
 ifneq (,$(filter $(ucpu), mips mips64))
   mycpu = $(shell /bin/sh -c '"$(CC)" -dumpmachine | sed "s/-.*//"')
@@ -163,6 +174,9 @@ endif
 ifeq ($(ucpu),aarch64)
   mycpu = arm64
 endif
+ifeq ($(ucpu),arm64)
+  mycpu = arm64
+endif
 ifeq ($(ucpu),riscv64)
   mycpu = riscv64
 endif
diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim
index d81b98be9..40ee79814 100644
--- a/tools/niminst/niminst.nim
+++ b/tools/niminst/niminst.nim
@@ -8,8 +8,15 @@
 #
 
 import
-  os, strutils, parseopt, parsecfg, strtabs, streams, debcreation,
-  std / sha1
+  os, strutils, parseopt, parsecfg, strtabs, streams, debcreation
+
+import ../../dist/checksums/src/checksums/sha1
+
+when defined(nimPreviewSlimSystem):
+  import std/syncio
+
+when not defined(nimHasEffectsOf):
+  {.pragma: effectsOf.}
 
 const
   maxOS = 20 # max number of OSes
@@ -161,11 +168,11 @@ proc parseCmdLine(c: var ConfigData) =
     next(p)
     var kind = p.kind
     var key = p.key
-    var val = p.val.string
+    var val = p.val
     case kind
     of cmdArgument:
       if c.actions == {}:
-        for a in split(normalize(key.string), {';', ','}):
+        for a in split(normalize(key), {';', ','}):
           case a
           of "csource": incl(c.actions, actionCSource)
           of "scripts": incl(c.actions, actionScripts)
@@ -176,11 +183,11 @@ proc parseCmdLine(c: var ConfigData) =
           of "deb": incl(c.actions, actionDeb)
           else: quit(Usage)
       else:
-        c.infile = addFileExt(key.string, "ini")
-        c.nimArgs = cmdLineRest(p).string
+        c.infile = addFileExt(key, "ini")
+        c.nimArgs = cmdLineRest(p)
         break
     of cmdLongOption, cmdShortOption:
-      case normalize(key.string)
+      case normalize(key)
       of "help", "h":
         stdout.write(Usage)
         quit(0)
@@ -198,7 +205,7 @@ proc parseCmdLine(c: var ConfigData) =
   if c.infile.len == 0: quit(Usage)
   if c.mainfile.len == 0: c.mainfile = changeFileExt(c.infile, "nim")
 
-proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool =
+proc eqT(a, b: string; t: proc (a: char): char {.nimcall.}): bool {.effectsOf: t.} =
   ## equality under a transformation ``t``. candidate for the stdlib?
   var i = 0
   var j = 0
@@ -508,6 +515,17 @@ template gatherFiles(fun, libpath, outDir) =
     # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413
     # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h")
 
+proc exe(f: string): string =
+  result = addFileExt(f, ExeExt)
+  when defined(windows):
+    result = result.replace('/','\\')
+
+proc findNim(): string =
+  let nim = "nim".exe
+  result = quoteShell("bin" / nim)
+  if not fileExists(result):
+    result = "nim"
+
 proc srcdist(c: var ConfigData) =
   let cCodeDir = getOutputDir(c) / "c_code"
   if not dirExists(cCodeDir): createDir(cCodeDir)
@@ -526,10 +544,10 @@ proc srcdist(c: var ConfigData) =
       var dir = getOutputDir(c) / buildDir(osA, cpuA)
       if dirExists(dir): removeDir(dir)
       createDir(dir)
-      var cmd = ("nim compile -f --symbolfiles:off --compileonly " &
+      var cmd = ("$# compile -f --incremental:off --compileonly " &
                  "--gen_mapping --cc:gcc --skipUserCfg" &
                  " --os:$# --cpu:$# $# $#") %
-                 [osname, cpuname, c.nimArgs, c.mainfile]
+                 [findNim(), osname, cpuname, c.nimArgs, c.mainfile]
       echo(cmd)
       if execShellCmd(cmd) != 0:
         quit("Error: call to nim compiler failed")
diff --git a/tools/officialpackages.nim b/tools/officialpackages.nim
new file mode 100644
index 000000000..633944a14
--- /dev/null
+++ b/tools/officialpackages.nim
@@ -0,0 +1,21 @@
+import std/[strformat, paths, dirs, envvars]
+from std/os import execShellCmd
+
+proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") =
+  let prevPath = getEnv("PATH")
+  if additionalPath.len > 0:
+    var absolute = Path(additionalPath)
+    if not absolute.isAbsolute:
+      absolute = getCurrentDir() / absolute
+    echo("Adding to $PATH: ", string(absolute))
+    putEnv("PATH", (if prevPath.len > 0: prevPath & PathSep else: "") & string(absolute))
+  echo(cmd)
+  if execShellCmd(cmd) != 0: quit("FAILURE", errorcode)
+  putEnv("PATH", prevPath)
+
+proc gitClonePackages*(names: seq[string]) =
+  if not dirExists(Path"pkgs"):
+    createDir(Path"pkgs")
+  for name in names:
+    if not dirExists(Path"pkgs" / Path(name)):
+      exec fmt"git clone https://github.com/nim-lang/{name} pkgs/{name}"
diff --git a/tools/unicode_parsedata.nim b/tools/unicode_parsedata.nim
index cca377f51..bd12998d1 100644
--- a/tools/unicode_parsedata.nim
+++ b/tools/unicode_parsedata.nim
@@ -26,34 +26,54 @@ var
 
 
 proc parseData(data: seq[string]) =
-  for line in data:
-    let
-      fields = line.split(';')
-      code = fields[0].parseHexInt()
-      category = fields[2]
-      uc = fields[12]
-      lc = fields[13]
-      tc = fields[14]
-
+  proc doAdd(firstCode, lastCode: int, category, uc, lc, tc: string) =
     if category notin spaces and category notin letters:
-      continue
+      return
 
+    if firstCode != lastCode:
+      doAssert uc == "" and lc == "" and tc == ""
     if uc.len > 0:
-      let diff = 500 + uc.parseHexInt() - code
-      toUpper.add (code, diff)
+      let diff = 500 + uc.parseHexInt() - firstCode
+      toUpper.add (firstCode, diff)
     if lc.len > 0:
-      let diff = 500 + lc.parseHexInt() - code
-      toLower.add (code, diff)
+      let diff = 500 + lc.parseHexInt() - firstCode
+      toLower.add (firstCode, diff)
     if tc.len > 0 and tc != uc:
       # if titlecase is different than uppercase
-      let diff = 500 + tc.parseHexInt() - code
+      let diff = 500 + tc.parseHexInt() - firstCode
       if diff != 500:
-        toTitle.add (code, diff)
+        toTitle.add (firstCode, diff)
 
-    if category in spaces:
-      unispaces.add code
+    for code in firstCode..lastCode:
+      if category in spaces:
+        unispaces.add code
+      else:
+        alphas.add code
+
+  var idx = 0
+  while idx < data.len:
+    let
+      line = data[idx]
+      fields = line.split(';')
+      code = fields[0].parseHexInt()
+      name = fields[1]
+      category = fields[2]
+      uc = fields[12]
+      lc = fields[13]
+      tc = fields[14]
+    inc(idx)
+    if name.endsWith(", First>"):
+      doAssert idx < data.len
+      let
+        nextLine = data[idx]
+        nextFields = nextLine.split(';')
+        nextCode = nextFields[0].parseHexInt()
+        nextName = nextFields[1]
+      inc(idx)
+      doAssert nextName.endsWith(", Last>")
+      doAdd(code, nextCode, category, uc, lc, tc)
     else:
-      alphas.add code
+      doAdd(code, code, category, uc, lc, tc)
 
 proc splitRanges(a: seq[Singlets], r: var seq[Ranges], s: var seq[Singlets]) =
   ## Splits `toLower`, `toUpper` and `toTitle` into separate sequences:
@@ -153,18 +173,18 @@ proc createHeader(output: var string) =
 
 proc `$`(r: Ranges): string =
   let
-    start = "0x" & toHex(r.start, 5)
-    stop = "0x" & toHex(r.stop, 5)
+    start = "0x" & toHex(r.start, 5) & "'i32"
+    stop = "0x" & toHex(r.stop, 5) & "'i32"
   result = "$#, $#, $#,\n" % [start, stop, $r.diff]
 
 proc `$`(r: Singlets): string =
-  let code = "0x" & toHex(r.code, 5)
+  let code = "0x" & toHex(r.code, 5) & "'i32"
   result = "$#, $#,\n" % [code, $r.diff]
 
 proc `$`(r: NonLetterRanges): string =
   let
-    start = "0x" & toHex(r.start, 5)
-    stop = "0x" & toHex(r.stop, 5)
+    start = "0x" & toHex(r.start, 5) & "'i32"
+    stop = "0x" & toHex(r.stop, 5) & "'i32"
   result = "$#, $#,\n" % [start, stop]
 
 
@@ -178,7 +198,7 @@ proc outputSeq(s: seq[Ranges|Singlets|NonLetterRanges], name: string,
 proc outputSeq(s: seq[int], name: string, output: var string) =
   output.add "  $# = [\n" % name
   for i in s:
-    output.add "    0x$#,\n" % toHex(i, 5)
+    output.add "    0x$#'i32,\n" % toHex(i, 5)
   output.add "  ]\n\n"
 
 proc outputSpaces(s: seq[int], name: string, output: var string) =
diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim
index abe68c0a0..2a43f7422 100644
--- a/tools/vccexe/vccexe.nim
+++ b/tools/vccexe/vccexe.nim
@@ -41,6 +41,7 @@ const
   platformPrefix = "--platform"
   sdktypePrefix = "--sdktype"
   sdkversionPrefix = "--sdkversion"
+  vctoolsetPrefix = "--vctoolset"
   verbosePrefix = "--verbose"
 
   vccversionSepIdx = vccversionPrefix.len
@@ -49,6 +50,7 @@ const
   platformSepIdx = platformPrefix.len
   sdktypeSepIdx = sdktypePrefix.len
   sdkversionSepIdx = sdkversionPrefix.len
+  vctoolsetSepIdx = vctoolsetPrefix.len
 
   vcvarsallDefaultPath = "vcvarsall.bat"
 
@@ -97,6 +99,8 @@ Options:
                       "8.1" to use the windows 8.1 SDK
   --verbose           Echoes the command line for loading the Developer Command Prompt
                       and the command line passed on to the secondary command.
+  --vctoolset         Optionally specifies the Visual Studio compiler toolset to use. 
+                      By default, the environment is set to use the current Visual Studio compiler toolset.
 
 Other command line arguments are passed on to the
 secondary command specified by --command or to the
@@ -108,7 +112,7 @@ proc parseVccexeCmdLine(argseq: seq[string],
     vccversionArg: var seq[string], printPathArg: var bool,
     vcvarsallArg: var string, commandArg: var string, noCommandArg: var bool,
     platformArg: var VccArch, sdkTypeArg: var VccPlatformType,
-    sdkVersionArg: var string, verboseArg: var bool,
+    sdkVersionArg: var string,  vctoolsetArg: var string, verboseArg: var bool,
     clArgs: var seq[string]) =
   ## Cannot use usual command-line argument parser here
   ## Since vccexe command-line arguments are intermingled
@@ -125,7 +129,7 @@ proc parseVccexeCmdLine(argseq: seq[string],
         responseargs = parseCmdLine(responsecontent)
       parseVccexeCmdLine(responseargs, vccversionArg, printPathArg,
         vcvarsallArg, commandArg, noCommandArg, platformArg, sdkTypeArg,
-        sdkVersionArg, verboseArg, clArgs)
+        sdkVersionArg, vctoolsetArg, verboseArg, clArgs)
     elif wargv.startsWith(vccversionPrefix): # Check for vccversion
       vccversionArg.add(wargv.substr(vccversionSepIdx + 1))
     elif wargv.cmpIgnoreCase(printPathPrefix) == 0: # Check for printPath
@@ -142,6 +146,8 @@ proc parseVccexeCmdLine(argseq: seq[string],
       sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1))
     elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion
       sdkVersionArg = wargv.substr(sdkversionSepIdx + 1)
+    elif wargv.startsWith(vctoolsetPrefix): # Check for vctoolset
+      vctoolsetArg = wargv.substr(vctoolsetSepIdx + 1)
     elif wargv.startsWith(verbosePrefix):
       verboseArg = true
     else: # Regular cl.exe argument -> store for final cl.exe invocation
@@ -158,13 +164,14 @@ when isMainModule:
   var platformArg: VccArch
   var sdkTypeArg: VccPlatformType
   var sdkVersionArg: string
+  var vctoolsetArg: string
   var verboseArg: bool = false
 
   var clArgs: seq[string] = @[]
 
   let wrapperArgs = commandLineParams()
   parseVccexeCmdLine(wrapperArgs, vccversionArg, printPathArg, vcvarsallArg,
-    commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg,
+    commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg,
     verboseArg,
     clArgs)
 
@@ -195,7 +202,7 @@ when isMainModule:
     echo "$1: $2" % [head, vcvarsallArg]
 
   # Call vcvarsall to get the appropriate VCC process environment
-  var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg)
+  var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, verboseArg)
   if vcvars != nil:
     for vccEnvKey, vccEnvVal in vcvars:
       putEnv(vccEnvKey, vccEnvVal)
@@ -204,6 +211,11 @@ when isMainModule:
   if verboseArg:
     vccOptions.incl poEchoCmd
 
+  let currentDir = getCurrentDir()
+  for arg in clArgs.mitems:
+    if fileExists(arg):
+      arg = relativePath(arg, currentDir)
+
   # Default to the cl.exe command if no secondary command was specified
   if commandArg.len < 1:
     commandArg = "cl.exe"
diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim
index 29d13cc7e..73b103e3c 100644
--- a/tools/vccexe/vcvarsall.nim
+++ b/tools/vccexe/vcvarsall.nim
@@ -33,7 +33,7 @@ type
     vccplatUWP = "uwp", ## Universal Windows Platform (UWP) Application
     vccplatOneCore = "onecore" # Undocumented platform type in the Windows SDK, probably XBox One SDK platform type.
 
-proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = "", verbose: bool = false): StringTableRef =
+proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version, vctoolset: string = "", verbose: bool = false): StringTableRef =
   ## Returns a string table containing the proper process environment to successfully execute VCC compile commands for the specified SDK version, CPU architecture and platform type.
   ##
   ## path
@@ -44,6 +44,8 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type
   ##   The compile target Platform Type. Defaults to the Windows Desktop platform, i.e. a regular Windows executable binary.
   ## sdk_version
   ##   The Windows SDK version to use.
+  ## vctoolset
+  ##  Visual Studio compiler toolset to use.
   ## verbose
   ##   Echo the command-line passed on to the system to load the VCC environment. Defaults to `false`.
 
@@ -63,6 +65,9 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type
 
   if sdk_version.len > 0:
     args.add(sdk_version)
+  
+  if vctoolset.len > 0:
+    args.add("-vcvars_ver="&vctoolset)
 
   let argStr = args.join " "