summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog_v020.md26
-rw-r--r--changelogs/changelog_0_20_2.md55
-rw-r--r--changelogs/changelog_X_XX_X.md26
-rw-r--r--compiler/ast.nim3
-rw-r--r--compiler/ccgmerge.nim4
-rw-r--r--compiler/ccgutils.nim2
-rw-r--r--compiler/cgen.nim4
-rw-r--r--compiler/cgendata.nim2
-rw-r--r--compiler/cgmeth.nim2
-rw-r--r--compiler/closureiters.nim4
-rw-r--r--compiler/commands.nim2
-rw-r--r--compiler/condsyms.nim2
-rw-r--r--compiler/depends.nim3
-rw-r--r--compiler/dfa.nim2
-rw-r--r--compiler/docgen.nim2
-rw-r--r--compiler/docgen2.nim3
-rw-r--r--compiler/evaltempl.nim3
-rw-r--r--compiler/extccomp.nim8
-rw-r--r--compiler/filter_tmpl.nim4
-rw-r--r--compiler/filters.nim2
-rw-r--r--compiler/gorgeimpl.nim2
-rw-r--r--compiler/idents.nim2
-rw-r--r--compiler/idgen.nim2
-rw-r--r--compiler/importer.nim4
-rw-r--r--compiler/incremental.nim4
-rw-r--r--compiler/injectdestructors.nim2
-rw-r--r--compiler/jsgen.nim8
-rw-r--r--compiler/lambdalifting.nim2
-rw-r--r--compiler/layouter.nim57
-rw-r--r--compiler/liftdestructors.nim2
-rw-r--r--compiler/liftlocals.nim2
-rw-r--r--compiler/linter.nim6
-rw-r--r--compiler/llstream.nim4
-rw-r--r--compiler/lookups.nim2
-rw-r--r--compiler/lowerings.nim1
-rw-r--r--compiler/macrocacheimpl.nim2
-rw-r--r--compiler/magicsys.nim2
-rw-r--r--compiler/main.nim10
-rw-r--r--compiler/modulepaths.nim2
-rw-r--r--compiler/modules.nim4
-rw-r--r--compiler/msgs.nim2
-rw-r--r--compiler/nim.nim6
-rw-r--r--compiler/nimconf.nim2
-rw-r--r--compiler/nimfix/prettybase.nim2
-rw-r--r--compiler/nimsets.nim3
-rw-r--r--compiler/options.nim2
-rw-r--r--compiler/parampatterns.nim2
-rw-r--r--compiler/parser.nim35
-rw-r--r--compiler/passaux.nim2
-rw-r--r--compiler/passes.nim6
-rw-r--r--compiler/pathutils.nim2
-rw-r--r--compiler/patterns.nim3
-rw-r--r--compiler/plugins/itersgen.nim4
-rw-r--r--compiler/plugins/locals.nim2
-rw-r--r--compiler/reorder.nim4
-rw-r--r--compiler/rod.nim2
-rw-r--r--compiler/scriptconfig.nim7
-rw-r--r--compiler/sem.nim8
-rw-r--r--compiler/semcall.nim62
-rw-r--r--compiler/semdata.nim7
-rw-r--r--compiler/semexprs.nim3
-rw-r--r--compiler/semfold.nim4
-rw-r--r--compiler/semmacrosanity.nim2
-rw-r--r--compiler/sempass2.nim2
-rw-r--r--compiler/sighashes.nim2
-rw-r--r--compiler/sigmatch.nim88
-rw-r--r--compiler/spawn.nim856
-rw-r--r--compiler/suggest.nim4
-rw-r--r--compiler/syntaxes.nim2
-rw-r--r--compiler/transf.nim4
-rw-r--r--compiler/trees.nim2
-rw-r--r--compiler/typesrenderer.nim2
-rw-r--r--compiler/vm.nim2
-rw-r--r--compiler/vmdef.nim3
-rw-r--r--compiler/vmdeps.nim2
-rw-r--r--compiler/vmgen.nim6
-rw-r--r--compiler/writetracking.nim3
-rw-r--r--lib/core/macros.nim2
-rw-r--r--lib/pure/asyncdispatch.nim16
-rw-r--r--lib/pure/collections/intsets.nim2
-rw-r--r--lib/pure/concurrency/cpuinfo.nim4
-rw-r--r--lib/pure/hashes.nim4
-rw-r--r--lib/pure/httpclient.nim10
-rw-r--r--lib/pure/json.nim4
-rw-r--r--lib/pure/net.nim19
-rw-r--r--lib/pure/os.nim2
-rw-r--r--lib/pure/terminal.nim2
-rw-r--r--lib/pure/unicode.nim6
-rw-r--r--lib/pure/unittest.nim16
-rw-r--r--nimpretty/nimpretty.nim4
-rw-r--r--nimpretty/tests/exhaustive.nim61
-rw-r--r--nimpretty/tests/expected/exhaustive.nim66
-rw-r--r--tests/concepts/t3330.nim42
-rw-r--r--tests/concepts/texplain.nim126
-rw-r--r--tests/destructor/tdont_return_unowned_from_owned.nim28
-rw-r--r--tests/errmsgs/t8434.nim2
-rw-r--r--tests/errmsgs/tdetailed_position.nim2
-rw-r--r--tests/errmsgs/tgcsafety.nim2
-rw-r--r--tests/errmsgs/tsigmatch.nim172
-rw-r--r--tests/errmsgs/tunknown_named_parameter.nim4
-rw-r--r--tests/errmsgs/twrong_at_operator.nim10
-rw-r--r--tests/metatype/ttypedesc3.nim14
-rw-r--r--tests/typerel/t7600_1.nim5
-rw-r--r--tests/typerel/t7600_2.nim5
-rw-r--r--tests/varres/tprevent_forloopvar_mutations.nim6
105 files changed, 1274 insertions, 791 deletions
diff --git a/changelog_v020.md b/changelog_v020.md
index 346ccda89..0f4582ae7 100644
--- a/changelog_v020.md
+++ b/changelog_v020.md
@@ -1,39 +1,20 @@
-# v0.20.2 - XXXX-XX-XX
+# v0.20.4 - xxxx-xx-xx
 
 
 ## Changes affecting backwards compatibility
 
-- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
-  with the same data slice/index meaning. This is backwards compatible for
-  calls *not* changing the `rfind` `start` parameter from its default. (#11487)
-
-  In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
-  then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
-  should minimize gotchas porting code from other languages like Python or C++.)
-
-- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
-  `-d:nimBinaryStdFiles` for a transition period.
 
 ### Breaking changes in the standard library
 
-- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
-  conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
-  need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
-
 
 ### Breaking changes in the compiler
 
 
 ## Library additions
 
-- `toOpenArray` is now available for the JS target.
 
 ## Library changes
 
-- Fix async IO operations stalling even after socket is closed. (#11232)
-
-- More informative error message for `streams.openFileStream`. (#11438)
-
 
 ## Language additions
 
@@ -46,10 +27,5 @@
 
 ### Compiler changes
 
-- Better error message for IndexError for empty containers. (#11476)
-
-- Fix regression in semfold for old right shift. (#11477)
-
-- Fix for passing tuples as static params to macros. (#11423)
 
 ## Bugfixes
diff --git a/changelogs/changelog_0_20_2.md b/changelogs/changelog_0_20_2.md
new file mode 100644
index 000000000..4f09ae3c5
--- /dev/null
+++ b/changelogs/changelog_0_20_2.md
@@ -0,0 +1,55 @@
+# v0.20.2 - 2019-07-17
+
+
+## Changes affecting backwards compatibility
+
+- All `strutils.rfind` procs now take `start` and `last` like `strutils.find`
+  with the same data slice/index meaning. This is backwards compatible for
+  calls *not* changing the `rfind` `start` parameter from its default. (#11487)
+
+  In the unlikely case that you were using `rfind X, start=N`, or `rfind X, N`,
+  then you need to change that to `rfind X, last=N` or `rfind X, 0, N`. (This
+  should minimize gotchas porting code from other languages like Python or C++.)
+
+- On Windows stderr/stdout/stdin are not opened as binary files anymore. Use the switch
+  `-d:nimBinaryStdFiles` for a transition period.
+
+### Breaking changes in the standard library
+
+- Mac OS X / BSD: TSa_Family is now the ``uint8`` type, so type
+  conversions like ``x.sin_family = uint16 toInt(nativesockets.AF_INET)``
+  need to be changed into ``x.sin_family = TSa_Family toInt(nativesockets.AF_INET)``.
+
+
+### Breaking changes in the compiler
+
+
+## Library additions
+
+- `toOpenArray` is now available for the JS target.
+
+## Library changes
+
+- Fix async IO operations stalling even after socket is closed. (#11232)
+
+- More informative error message for `streams.openFileStream`. (#11438)
+
+
+## Language additions
+
+
+## Language changes
+
+
+### Tool changes
+
+
+### Compiler changes
+
+- Better error message for IndexError for empty containers. (#11476)
+
+- Fix regression in semfold for old right shift. (#11477)
+
+- Fix for passing tuples as static params to macros. (#11423)
+
+## Bugfixes
diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md
index 7ca4a1b9a..8b8d5eb91 100644
--- a/changelogs/changelog_X_XX_X.md
+++ b/changelogs/changelog_X_XX_X.md
@@ -1,27 +1,33 @@
-## v0.XX.0 - XX/XX/2018
+# vx.xx.x - yyyy-mm-dd
 
-### Changes affecting backwards compatibility
 
-- Example item: ``Foo`` changed to ``Bar``.
+## Changes affecting backwards compatibility
 
-#### Breaking changes in the standard library
+- Example item: `Foo` changed to `Bar`.
 
 
-#### Breaking changes in the compiler
+### Breaking changes in the standard library
 
-### Library additions
 
-### Library changes
+### Breaking changes in the compiler
 
 
-### Language additions
+## Library additions
 
 
-### Language changes
+## Library changes
+
+
+## Language additions
+
+
+## Language changes
 
 
 ### Tool changes
 
+
 ### Compiler changes
 
-### Bugfixes
+
+## Bugfixes
diff --git a/compiler/ast.nim b/compiler/ast.nim
index c8ce0dccb..8d52f12ff 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -10,8 +10,7 @@
 # abstract syntax tree + symbol table
 
 import
-  lineinfos, hashes, nversion, options, strutils, std / sha1, ropes, idents,
-  intsets, idgen
+  lineinfos, hashes, options, ropes, idents, idgen
 
 type
   TCallingConvention* = enum
diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim
index a0aa1b05d..cdb43f20f 100644
--- a/compiler/ccgmerge.nim
+++ b/compiler/ccgmerge.nim
@@ -11,8 +11,8 @@
 ## is needed for incremental compilation.
 
 import
-  ast, astalgo, ropes, options, strutils, nimlexbase, msgs, cgendata, rodutils,
-  intsets, platform, llstream, tables, sighashes, modulegraphs, pathutils
+  ast, ropes, options, strutils, nimlexbase, cgendata, rodutils,
+  intsets, llstream, tables, modulegraphs, pathutils
 
 # Careful! Section marks need to contain a tabulator so that they cannot
 # be part of C string literals.
diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim
index 455012e60..3c7b0510e 100644
--- a/compiler/ccgutils.nim
+++ b/compiler/ccgutils.nim
@@ -10,7 +10,7 @@
 # This module declares some helpers for the C code generator.
 
 import
-  ast, astalgo, ropes, hashes, strutils, types, msgs, wordrecg,
+  ast, hashes, strutils, msgs, wordrecg,
   platform, trees, options
 
 proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode =
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index 4f136d9e3..d2ddd4a39 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -11,9 +11,9 @@
 
 import
   ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets,
-  nversion, nimsets, msgs, std / sha1, bitsets, idents, types,
+  nversion, nimsets, msgs, bitsets, idents, types,
   ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth,
-  condsyms, rodutils, renderer, idgen, cgendata, ccgmerge, semfold, aliases,
+  condsyms, rodutils, renderer, cgendata, ccgmerge, aliases,
   lowerings, tables, sets, ndi, lineinfos, pathutils, transf, enumtostr
 
 when not defined(leanCompiler):
diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim
index 76c64782c..77445639e 100644
--- a/compiler/cgendata.nim
+++ b/compiler/cgendata.nim
@@ -10,7 +10,7 @@
 ## This module contains the data structures for the C code generation phase.
 
 import
-  ast, astalgo, ropes, passes, options, intsets, platform, sighashes,
+  ast, ropes, passes, options, intsets,
   tables, ndi, lineinfos, pathutils, modulegraphs
 
 type
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index ab406bc4b..d83ca3c55 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -10,7 +10,7 @@
 ## This module implements code generation for multi methods.
 
 import
-  intsets, options, ast, astalgo, msgs, idents, renderer, types, magicsys,
+  intsets, options, ast, msgs, idents, renderer, types, magicsys,
   sempass2, strutils, modulegraphs, lineinfos
 
 proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode =
diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim
index d1f2a6cd6..22595b772 100644
--- a/compiler/closureiters.nim
+++ b/compiler/closureiters.nim
@@ -131,8 +131,8 @@
 #   break :stateLoop
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, idents,
-  renderer, types, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
+  ast, msgs, idents,
+  renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos
 
 type
   Ctx = object
diff --git a/compiler/commands.nim b/compiler/commands.nim
index ff76fff71..ed0320a7c 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -27,7 +27,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none")
 
 import
   os, msgs, options, nversion, condsyms, strutils, extccomp, platform,
-  wordrecg, parseutils, nimblecmd, idents, parseopt, sequtils, lineinfos,
+  wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos,
   pathutils, strtabs
 
 # but some have deps to imported modules. Yay.
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index ed95c8183..ce295c8b9 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -10,7 +10,7 @@
 # This module handles the conditional symbols.
 
 import
-  strtabs, platform, strutils, idents
+  strtabs
 
 from options import Feature
 from lineinfos import HintsToStr, WarningsToStr
diff --git a/compiler/depends.nim b/compiler/depends.nim
index 300ab3b54..603f67e77 100644
--- a/compiler/depends.nim
+++ b/compiler/depends.nim
@@ -10,8 +10,7 @@
 # This module implements a dependency file generator.
 
 import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, modulepaths,
-  pathutils
+  options, ast, ropes, idents, passes, modulepaths, pathutils
 
 from modulegraphs import ModuleGraph, PPassContext
 
diff --git a/compiler/dfa.nim b/compiler/dfa.nim
index f97d8f298..7315e74bc 100644
--- a/compiler/dfa.nim
+++ b/compiler/dfa.nim
@@ -29,7 +29,7 @@
 ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen.
 ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf
 
-import ast, astalgo, types, intsets, tables, msgs, options, lineinfos, renderer
+import ast, types, intsets, lineinfos, renderer
 
 from patterns import sameTrees
 
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index a98360c07..2e03c0d4d 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -16,7 +16,7 @@ import
   wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast,
   packages/docutils/rst, packages/docutils/rstgen,
   packages/docutils/highlite, json, xmltree, cgi, trees, types,
-  typesrenderer, astalgo, modulepaths, lineinfos, sequtils, intsets,
+  typesrenderer, astalgo, modulepaths, lineinfos, intsets,
   pathutils, trees
 
 const
diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim
index 048860423..e19b3a989 100644
--- a/compiler/docgen2.nim
+++ b/compiler/docgen2.nim
@@ -11,8 +11,7 @@
 # semantic checking.
 
 import
-  os, options, ast, astalgo, msgs, ropes, idents, passes, docgen, lineinfos,
-  pathutils
+  options, ast, msgs, idents, passes, docgen, lineinfos, pathutils
 
 from modulegraphs import ModuleGraph, PPassContext
 
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 8acb85907..d3d3e5f77 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -10,8 +10,7 @@
 ## Template evaluation engine. Now hygienic.
 
 import
-  strutils, options, ast, astalgo, msgs, os, idents, wordrecg, renderer,
-  lineinfos
+  strutils, options, ast, astalgo, msgs, renderer, lineinfos
 
 type
   TemplCtx = object
diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim
index 4ecad6d58..011252fe4 100644
--- a/compiler/extccomp.nim
+++ b/compiler/extccomp.nim
@@ -949,6 +949,8 @@ proc callCCompiler*(conf: ConfigRef) =
 #from json import escapeJson
 import json, std / sha1
 
+template hashNimExe(): string = $secureHashFile(os.getAppFilename())
+
 proc writeJsonBuildInstructions*(conf: ConfigRef) =
   template lit(x: untyped) = f.write x
   template str(x: untyped) =
@@ -1028,7 +1030,9 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) =
       str conf.commandLine
       lit ",\L\"nimfiles\":[\L"
       nimfiles(conf, f)
-      lit "]\L"
+      lit "],\L\"nimexe\": \L"
+      str hashNimExe()
+      lit "\L"
 
     lit "\L}\L"
     close(f)
@@ -1045,6 +1049,8 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol
     let oldCmdLine = data["cmdline"].getStr
     if conf.commandLine != oldCmdLine:
       return true
+    if hashNimExe() != data["nimexe"].getStr:
+      return true
     let nimfilesPairs = data["nimfiles"]
     doAssert nimfilesPairs.kind == JArray
     for p in nimfilesPairs:
diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim
index b884b1ec3..490c184cf 100644
--- a/compiler/filter_tmpl.nim
+++ b/compiler/filter_tmpl.nim
@@ -10,8 +10,8 @@
 # This module implements Nim's standard template filter.
 
 import
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
-  renderer, filters, lineinfos, pathutils
+  llstream, strutils, ast, msgs, options,
+  filters, lineinfos, pathutils
 
 type
   TParseState = enum
diff --git a/compiler/filters.nim b/compiler/filters.nim
index c04d61320..a2f7b6bbb 100644
--- a/compiler/filters.nim
+++ b/compiler/filters.nim
@@ -10,7 +10,7 @@
 # This module implements Nim's simple filters and helpers for filters.
 
 import
-  llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
+  llstream, idents, strutils, ast, msgs, options,
   renderer, pathutils
 
 proc invalidPragma(conf: ConfigRef; n: PNode) =
diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim
index d61dcdac3..c9f82b505 100644
--- a/compiler/gorgeimpl.nim
+++ b/compiler/gorgeimpl.nim
@@ -9,7 +9,7 @@
 
 ## Module that implements ``gorge`` for the compiler.
 
-import msgs, std / sha1, os, osproc, streams, strutils, options,
+import msgs, std / sha1, os, osproc, streams, options,
   lineinfos, pathutils
 
 proc readOutput(p: Process): (string, int) =
diff --git a/compiler/idents.nim b/compiler/idents.nim
index f82ff5db5..3153745d5 100644
--- a/compiler/idents.nim
+++ b/compiler/idents.nim
@@ -12,7 +12,7 @@
 # id. This module is essential for the compiler's performance.
 
 import
-  hashes, strutils, wordrecg
+  hashes, wordrecg
 
 type
   TIdObj* = object of RootObj
diff --git a/compiler/idgen.nim b/compiler/idgen.nim
index 239df0c57..7d49e33e3 100644
--- a/compiler/idgen.nim
+++ b/compiler/idgen.nim
@@ -9,7 +9,7 @@
 
 ## This module contains a simple persistent id generator.
 
-import idents, strutils, os, options, pathutils
+import idents, strutils, options, pathutils
 
 var gFrontEndId*: int
 
diff --git a/compiler/importer.nim b/compiler/importer.nim
index cab5485aa..426b79981 100644
--- a/compiler/importer.nim
+++ b/compiler/importer.nim
@@ -10,8 +10,8 @@
 ## This module implements the symbol importing mechanism.
 
 import
-  intsets, strutils, os, ast, astalgo, msgs, options, idents, lookups,
-  semdata, passes, renderer, modulepaths, sigmatch, lineinfos
+  intsets, ast, astalgo, msgs, options, idents, lookups,
+  semdata, modulepaths, sigmatch, lineinfos
 
 proc readExceptSet*(c: PContext, n: PNode): IntSet =
   assert n.kind in {nkImportExceptStmt, nkExportExceptStmt}
diff --git a/compiler/incremental.nim b/compiler/incremental.nim
index f66a75efd..29528bbd3 100644
--- a/compiler/incremental.nim
+++ b/compiler/incremental.nim
@@ -12,10 +12,10 @@
 
 const nimIncremental* = defined(nimIncremental)
 
-import options, lineinfos, pathutils
+import options, lineinfos
 
 when nimIncremental:
-  import ast, msgs, intsets, btrees, db_sqlite, std / sha1
+  import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils
   from strutils import parseInt
 
   type
diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim
index a8a2cf97f..6f1da37fb 100644
--- a/compiler/injectdestructors.nim
+++ b/compiler/injectdestructors.nim
@@ -134,7 +134,7 @@ to do it.
 ]#
 
 import
-  intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
+  intsets, ast, msgs, renderer, magicsys, types, idents,
   strutils, options, dfa, lowerings, tables, modulegraphs, msgs,
   lineinfos, parampatterns, sighashes
 
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index 0c04b339c..788e3e75b 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -29,11 +29,11 @@ implements the required case distinction.
 
 
 import
-  ast, astalgo, strutils, hashes, trees, platform, magicsys, extccomp, options,
-  nversion, nimsets, msgs, std / sha1, bitsets, idents, types, os, tables,
-  times, ropes, math, passes, ccgutils, wordrecg, renderer,
+  ast, strutils, trees, magicsys, options,
+  nversion, msgs, idents, types, tables,
+  ropes, math, passes, ccgutils, wordrecg, renderer,
   intsets, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils,
-  pathutils, transf
+  transf
 
 
 from modulegraphs import ModuleGraph, PPassContext
diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim
index 6d87444ec..679391224 100644
--- a/compiler/lambdalifting.nim
+++ b/compiler/lambdalifting.nim
@@ -10,7 +10,7 @@
 # This file implements lambda lifting for the transformator.
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs,
+  intsets, strutils, options, ast, astalgo, msgs,
   idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos,
   transf, liftdestructors
 
diff --git a/compiler/layouter.nim b/compiler/layouter.nim
index bff7d8a1b..367d2aead 100644
--- a/compiler/layouter.nim
+++ b/compiler/layouter.nim
@@ -32,7 +32,7 @@ type
     ltTab,
     ltOptionalNewline, ## optional newline introduced by nimpretty
     ltComment, ltLit, ltKeyword, ltExportMarker, ltIdent,
-    ltOther, ltOpr,
+    ltOther, ltOpr, ltSomeParLe, ltSomeParRi,
     ltBeginSection, ltEndSection
 
   Emitter* = object
@@ -142,7 +142,7 @@ proc optionalIsGood(em: var Emitter; pos, currentLen: int): bool =
       result = true
     elif em.tokens[p+1].len < ourIndent:
       result = isLongEnough(lineLen, pos, p)
-  elif em.kinds[pos+1] == ltOther: # note: pos+1, not p+1
+  elif em.kinds[pos+1] in {ltOther, ltSomeParLe, ltSomeParRi}: # note: pos+1, not p+1
     result = false
   else:
     result = isLongEnough(lineLen, pos, p)
@@ -153,13 +153,28 @@ proc lenOfNextTokens(em: Emitter; pos: int): int =
     if em.kinds[pos+i] in {ltCrucialNewline, ltSplittingNewline, ltOptionalNewline}: break
     inc result, em.tokens[pos+i].len
 
+proc guidingInd(em: Emitter; pos: int): int =
+  var i = pos - 1
+  while i >= 0 and em.kinds[i] != ltSomeParLe:
+    dec i
+  while i+1 <= em.kinds.high and em.kinds[i] != ltSomeParRi:
+    if em.kinds[i] == ltSplittingNewline and em.kinds[i+1] == ltSpaces:
+      return em.tokens[i+1].len
+    inc i
+  result = -1
+
 proc closeEmitter*(em: var Emitter) =
+  template defaultCase() =
+    content.add em.tokens[i]
+    inc lineLen, em.tokens[i].len
+
   let outFile = em.config.absOutFile
 
   var content = newStringOfCap(16_000)
   var maxLhs = 0
   var lineLen = 0
   var lineBegin = 0
+  var openPars = 0
   var i = 0
   while i <= em.tokens.high:
     when defined(debug):
@@ -201,8 +216,13 @@ proc closeEmitter*(em: var Emitter) =
           let spaces = em.tokens[i-1].len
           content.setLen(content.len - spaces)
         content.add "\L"
-        content.add em.tokens[i]
-        lineLen = em.tokens[i].len
+        let guide = if openPars > 0: guidingInd(em, i) else: -1
+        if guide >= 0:
+          content.add repeat(' ', guide)
+          lineLen = guide
+        else:
+          content.add em.tokens[i]
+          lineLen = em.tokens[i].len
         lineBegin = i+1
         if i+1 < em.kinds.len and em.kinds[i+1] == ltSpaces:
           # inhibit extra spaces at the start of a new line
@@ -215,9 +235,15 @@ proc closeEmitter*(em: var Emitter) =
       else:
         inc lineLen, em.tokens[i].len
       content.add em.tokens[i]
+    of ltSomeParLe:
+      inc openPars
+      defaultCase()
+    of ltSomeParRi:
+      doAssert openPars > 0
+      dec openPars
+      defaultCase()
     else:
-      content.add em.tokens[i]
-      inc lineLen, em.tokens[i].len
+      defaultCase()
     inc i
 
   if fileExists(outFile) and readFile(outFile.string) == content:
@@ -292,7 +318,7 @@ const
               tkCurlyLe}
   closedPars = {tkParRi, tkParDotRi,
               tkBracketRi, tkCurlyDotRi,
-              tkCurlyRi}
+              tkCurlyRi, tkBracketDotRi}
 
   splitters = openPars + {tkComma, tkSemiColon} # do not add 'tkColon' here!
   oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs,
@@ -416,7 +442,8 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     var newlineKind = ltCrucialNewline
     if em.keepIndents > 0:
       em.indentLevel = tok.indent
-    elif (em.lastTok in (splitters + oprSet) and tok.tokType notin closedPars):
+    elif (em.lastTok in (splitters + oprSet) and
+        tok.tokType notin (closedPars - {tkBracketDotRi})):
       # aka: we are in an expression context:
       let alignment = max(tok.indent - em.indentStack[^1], 0)
       em.indentLevel = alignment + em.indentStack.high * em.indWidth
@@ -471,18 +498,14 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) =
     wr(em, TokTypeToStr[tok.tokType], ltOther)
     rememberSplit(splitComma)
     wrSpace em
-  of tkParDotLe, tkParLe, tkBracketDotLe, tkBracketLe,
-     tkCurlyLe, tkCurlyDotLe, tkBracketLeColon:
+  of openPars:
     if tok.strongSpaceA > 0 and not em.endsInWhite and not em.wasExportMarker:
       wrSpace em
-    wr(em, TokTypeToStr[tok.tokType], ltOther)
+    wr(em, TokTypeToStr[tok.tokType], ltSomeParLe)
     rememberSplit(splitParLe)
-  of tkParRi,
-     tkBracketRi, tkCurlyRi,
-     tkBracketDotRi,
-     tkCurlyDotRi,
-     tkParDotRi,
-     tkColonColon:
+  of closedPars:
+    wr(em, TokTypeToStr[tok.tokType], ltSomeParRi)
+  of tkColonColon:
     wr(em, TokTypeToStr[tok.tokType], ltOther)
   of tkDot:
     lastTokWasTerse = true
diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim
index 97c2636ac..5619350ef 100644
--- a/compiler/liftdestructors.nim
+++ b/compiler/liftdestructors.nim
@@ -13,7 +13,7 @@
 # Todo:
 # - use openArray instead of array to avoid over-specializations
 
-import modulegraphs, lineinfos, idents, ast, astalgo, renderer, semdata,
+import modulegraphs, lineinfos, idents, ast, renderer, semdata,
   sighashes, lowerings, options, types, msgs, magicsys, tables
 
 type
diff --git a/compiler/liftlocals.nim b/compiler/liftlocals.nim
index ae789cd88..eed0560ab 100644
--- a/compiler/liftlocals.nim
+++ b/compiler/liftlocals.nim
@@ -10,7 +10,7 @@
 ## This module implements the '.liftLocals' pragma.
 
 import
-  intsets, strutils, options, ast, astalgo, msgs,
+  strutils, options, ast, msgs,
   idents, renderer, types, lowerings, lineinfos
 
 from pragmas import getPragmaVal
diff --git a/compiler/linter.nim b/compiler/linter.nim
index abefe9bec..d3c461b66 100644
--- a/compiler/linter.nim
+++ b/compiler/linter.nim
@@ -9,11 +9,9 @@
 
 ## This module implements the style checker.
 
-import
-  strutils, os, intsets, strtabs
+import strutils
 
-import options, ast, astalgo, msgs, semdata, ropes, idents,
-  lineinfos, pathutils, wordrecg
+import options, ast, msgs, idents, lineinfos, wordrecg
 
 const
   Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'}
diff --git a/compiler/llstream.nim b/compiler/llstream.nim
index 69a6905af..ab4e1645c 100644
--- a/compiler/llstream.nim
+++ b/compiler/llstream.nim
@@ -10,7 +10,7 @@
 ## Low-level streams for high performance.
 
 import
-  strutils, pathutils
+  pathutils
 
 # support '-d:useGnuReadline' for backwards compatibility:
 when not defined(windows) and (defined(useGnuReadline) or defined(useLinenoise)):
@@ -97,7 +97,7 @@ proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
 
 proc countTriples(s: string): int =
   var i = 0
-  while i < s.len:
+  while i+2 < s.len:
     if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
       inc result
       inc i, 2
diff --git a/compiler/lookups.nim b/compiler/lookups.nim
index 7c37817fd..4de1fc371 100644
--- a/compiler/lookups.nim
+++ b/compiler/lookups.nim
@@ -11,7 +11,7 @@
 
 import
   intsets, ast, astalgo, idents, semdata, types, msgs, options,
-  renderer, wordrecg, idgen, nimfix/prettybase, lineinfos, strutils
+  renderer, nimfix/prettybase, lineinfos, strutils
 
 proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope)
 
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 0030f1a49..727b88760 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -14,7 +14,6 @@ const
 
 import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,
   lineinfos
-from trees import getMagic
 
 proc newDeref*(n: PNode): PNode {.inline.} =
   result = newNodeIT(nkHiddenDeref, n.info, n.typ.sons[0])
diff --git a/compiler/macrocacheimpl.nim b/compiler/macrocacheimpl.nim
index d23040763..365497e31 100644
--- a/compiler/macrocacheimpl.nim
+++ b/compiler/macrocacheimpl.nim
@@ -9,7 +9,7 @@
 
 ## This module implements helpers for the macro cache.
 
-import lineinfos, ast, modulegraphs, vmdef, magicsys
+import lineinfos, ast, modulegraphs, vmdef
 
 proc recordInc*(c: PCtx; info: TLineInfo; key: string; by: BiggestInt) =
   var recorded = newNodeI(nkCommentStmt, info)
diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim
index aeeb489c0..38240061f 100644
--- a/compiler/magicsys.nim
+++ b/compiler/magicsys.nim
@@ -10,7 +10,7 @@
 # Built-in types and compilerprocs are registered here.
 
 import
-  ast, astalgo, hashes, msgs, platform, nversion, times, idents,
+  ast, astalgo, msgs, platform, idents,
   modulegraphs, lineinfos
 
 export createMagic
diff --git a/compiler/main.nim b/compiler/main.nim
index 460fd90c7..ce80af36d 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
-  llstream, strutils, ast, astalgo, lexer, syntaxes, renderer, options, msgs,
-  os, condsyms, times,
-  wordrecg, sem, semdata, idents, passes, extccomp,
+  llstream, strutils, ast, lexer, syntaxes, options, msgs,
+  condsyms, times,
+  sem, idents, passes, extccomp,
   cgen, json, nversion,
-  platform, nimconf, importer, passaux, depends, vm, vmdef, types, idgen,
-  parser, modules, ccgutils, sigmatch, ropes,
+  platform, nimconf, passaux, depends, vm, idgen,
+  parser, modules,
   modulegraphs, tables, rod, lineinfos, pathutils
 
 when not defined(leanCompiler):
diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim
index 6130c3032..eedb22084 100644
--- a/compiler/modulepaths.nim
+++ b/compiler/modulepaths.nim
@@ -8,7 +8,7 @@
 #
 
 import ast, renderer, strutils, msgs, options, idents, os, lineinfos,
-  pathutils, nimblecmd
+  pathutils
 
 when false:
   const
diff --git a/compiler/modules.nim b/compiler/modules.nim
index 3dbc1b2c1..40d9a904c 100644
--- a/compiler/modules.nim
+++ b/compiler/modules.nim
@@ -10,8 +10,8 @@
 ## Implements the module handling, including the caching of modules.
 
 import
-  ast, astalgo, magicsys, std / sha1, msgs, cgendata, sigmatch, options,
-  idents, os, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
+  ast, astalgo, magicsys, msgs, options,
+  idents, lexer, idgen, passes, syntaxes, llstream, modulegraphs, rod,
   lineinfos, pathutils, tables
 
 proc resetSystemArtifacts*(g: ModuleGraph) =
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 03b6213fa..1264133a9 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  options, strutils, os, tables, ropes, platform, terminal, macros,
+  options, strutils, os, tables, ropes, terminal, macros,
   lineinfos, pathutils
 
 proc toCChar*(c: char; result: var string) =
diff --git a/compiler/nim.nim b/compiler/nim.nim
index 5fcf043b5..ec7e8b8dc 100644
--- a/compiler/nim.nim
+++ b/compiler/nim.nim
@@ -19,9 +19,9 @@ when defined(i386) and defined(windows) and defined(vcc):
   {.link: "../icons/nim-i386-windows-vcc.res".}
 
 import
-  commands, lexer, condsyms, options, msgs, nversion, nimconf, ropes,
-  extccomp, strutils, os, osproc, platform, main, parseopt,
-  scriptconfig, idents, modulegraphs, lineinfos, cmdlinehelper,
+  commands, options, msgs,
+  extccomp, strutils, os, main, parseopt,
+  idents, lineinfos, cmdlinehelper,
   pathutils
 
 include nodejs
diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim
index f518b3abf..a470179bd 100644
--- a/compiler/nimconf.nim
+++ b/compiler/nimconf.nim
@@ -10,7 +10,7 @@
 # This module handles the reading of the config file.
 
 import
-  llstream, nversion, commands, os, strutils, msgs, platform, condsyms, lexer,
+  llstream, commands, os, strutils, msgs, lexer,
   options, idents, wordrecg, strtabs, lineinfos, pathutils
 
 # ---------------- configuration file parser -----------------------------
diff --git a/compiler/nimfix/prettybase.nim b/compiler/nimfix/prettybase.nim
index c3e16e5ba..fbc2e3bd1 100644
--- a/compiler/nimfix/prettybase.nim
+++ b/compiler/nimfix/prettybase.nim
@@ -8,9 +8,7 @@
 #
 
 import strutils except Letters
-import lexbase, streams
 import ".." / [ast, msgs, lineinfos, idents, options, linter]
-from os import splitFile
 
 proc replaceDeprecated*(conf: ConfigRef; info: TLineInfo; oldSym, newSym: PIdent) =
   let line = sourceLine(conf, info)
diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim
index c2436dff0..f4ef0cc39 100644
--- a/compiler/nimsets.nim
+++ b/compiler/nimsets.nim
@@ -10,8 +10,7 @@
 # this unit handles Nim sets; it implements symbolic sets
 
 import
-  ast, astalgo, trees, nversion, lineinfos, platform, bitsets, types, renderer,
-  options
+  ast, astalgo, lineinfos, bitsets, types, options
 
 proc inSet*(s: PNode, elem: PNode): bool =
   assert s.kind == nkCurly
diff --git a/compiler/options.nim b/compiler/options.nim
index 985955d98..6303224a2 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -8,7 +8,7 @@
 #
 
 import
-  os, strutils, strtabs, osproc, sets, lineinfos, platform,
+  os, strutils, strtabs, sets, lineinfos, platform,
   prefixmatches, pathutils
 
 from terminal import isatty
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index b4409fcec..1bd703fe8 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -10,7 +10,7 @@
 ## This module implements the pattern matching features for term rewriting
 ## macro support.
 
-import strutils, ast, astalgo, types, msgs, idents, renderer, wordrecg, trees,
+import strutils, ast, types, msgs, idents, renderer, wordrecg, trees,
   options
 
 # we precompile the pattern here for efficiency into some internal
diff --git a/compiler/parser.nim b/compiler/parser.nim
index 12036f20b..101e66ba4 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -27,7 +27,7 @@ when isMainModule:
   outp.close
 
 import
-  llstream, lexer, idents, strutils, ast, astalgo, msgs, options, lineinfos,
+  llstream, lexer, idents, strutils, ast, msgs, options, lineinfos,
   pathutils
 
 when defined(nimpretty):
@@ -1203,6 +1203,13 @@ proc parseFor(p: var TParser): PNode =
   colcom(p, result)
   addSon(result, parseStmt(p))
 
+template nimprettyDontTouch(body) =
+  when defined(nimpretty):
+    inc p.em.keepIndents
+  body
+  when defined(nimpretty):
+    dec p.em.keepIndents
+
 proc parseExpr(p: var TParser): PNode =
   #| expr = (blockExpr
   #|       | ifExpr
@@ -1212,12 +1219,26 @@ proc parseExpr(p: var TParser): PNode =
   #|       | tryExpr)
   #|       / simpleExpr
   case p.tok.tokType:
-  of tkBlock: result = parseBlock(p)
-  of tkIf: result = parseIfExpr(p, nkIfExpr)
-  of tkFor: result = parseFor(p)
-  of tkWhen: result = parseIfExpr(p, nkWhenExpr)
-  of tkCase: result = parseCase(p)
-  of tkTry: result = parseTry(p, isExpr=true)
+  of tkBlock:
+    nimprettyDontTouch:
+      result = parseBlock(p)
+  of tkIf:
+    nimprettyDontTouch:
+      result = parseIfExpr(p, nkIfExpr)
+  of tkFor:
+    nimprettyDontTouch:
+      result = parseFor(p)
+  of tkWhen:
+    nimprettyDontTouch:
+      result = parseIfExpr(p, nkWhenExpr)
+  of tkCase:
+    # Currently we think nimpretty is good enough with case expressions,
+    # so it is allowed to touch them:
+    #nimprettyDontTouch:
+    result = parseCase(p)
+  of tkTry:
+    nimprettyDontTouch:
+      result = parseTry(p, isExpr=true)
   else: result = simpleExpr(p)
 
 proc parseEnum(p: var TParser): PNode
diff --git a/compiler/passaux.nim b/compiler/passaux.nim
index 17c4c506a..2ac89c24e 100644
--- a/compiler/passaux.nim
+++ b/compiler/passaux.nim
@@ -10,7 +10,7 @@
 ## implements some little helper passes
 
 import
-  strutils, ast, astalgo, passes, idents, msgs, options, idgen, lineinfos
+  ast, passes, idents, msgs, options, idgen, lineinfos
 
 from modulegraphs import ModuleGraph, PPassContext
 
diff --git a/compiler/passes.nim b/compiler/passes.nim
index 72140d9de..7effcfeb5 100644
--- a/compiler/passes.nim
+++ b/compiler/passes.nim
@@ -11,9 +11,9 @@
 ## `TPass` interface.
 
 import
-  strutils, options, ast, astalgo, llstream, msgs, platform, os,
-  condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
-  nimsets, syntaxes, times, idgen, modulegraphs, reorder, rod,
+  options, ast, llstream, msgs,
+  idents,
+  syntaxes, idgen, modulegraphs, reorder, rod,
   lineinfos, pathutils
 
 type
diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim
index cd6fb2a53..b73942a21 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, strutils, pathnorm
+import os, pathnorm
 
 type
   AbsoluteFile* = distinct string
diff --git a/compiler/patterns.nim b/compiler/patterns.nim
index 5ec18f67b..623a04406 100644
--- a/compiler/patterns.nim
+++ b/compiler/patterns.nim
@@ -11,8 +11,7 @@
 ## macro support.
 
 import
-  ast, astalgo, types, semdata, sigmatch, msgs, idents, aliases, parampatterns,
-  trees
+  ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees
 
 type
   TPatternContext = object
diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim
index 440d2e081..e4b3a5029 100644
--- a/compiler/plugins/itersgen.nim
+++ b/compiler/plugins/itersgen.nim
@@ -9,9 +9,7 @@
 
 ## Plugin to transform an inline iterator into a data structure.
 
-import ".." / [ast, astalgo,
-  magicsys, lookups, semdata,
-  lambdalifting, msgs]
+import ".." / [ast, lookups, semdata, lambdalifting, msgs]
 
 proc iterToProcImpl*(c: PContext, n: PNode): PNode =
   result = newNodeI(nkStmtList, n.info)
diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim
index eb3324b36..08d836f7d 100644
--- a/compiler/plugins/locals.nim
+++ b/compiler/plugins/locals.nim
@@ -9,7 +9,7 @@
 
 ## The builtin 'system.locals' implemented as a plugin.
 
-import ".." / [pluginsupport, ast, astalgo,
+import ".." / [ast, astalgo,
   magicsys, lookups, semdata, lowerings]
 
 proc semLocals*(c: PContext, n: PNode): PNode =
diff --git a/compiler/reorder.nim b/compiler/reorder.nim
index e341885ee..960929811 100644
--- a/compiler/reorder.nim
+++ b/compiler/reorder.nim
@@ -1,7 +1,7 @@
 
 import
-  intsets, ast, idents, algorithm, renderer, parser, os, strutils,
-  sequtils, msgs, modulegraphs, syntaxes, options, modulepaths, tables,
+  intsets, ast, idents, algorithm, renderer, os, strutils,
+  msgs, modulegraphs, syntaxes, options, modulepaths,
   lineinfos
 
 type
diff --git a/compiler/rod.nim b/compiler/rod.nim
index bbd2f0c6c..e3e568a0e 100644
--- a/compiler/rod.nim
+++ b/compiler/rod.nim
@@ -9,7 +9,7 @@
 
 ## This module implements the canonalization for the various caching mechanisms.
 
-import ast, idgen, lineinfos, msgs, incremental, modulegraphs, pathutils
+import ast, idgen, lineinfos, incremental, modulegraphs, pathutils
 
 when not nimIncremental:
   template setupModuleCache*(g: ModuleGraph) = discard
diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim
index 6c3d0a122..9878cf597 100644
--- a/compiler/scriptconfig.nim
+++ b/compiler/scriptconfig.nim
@@ -11,9 +11,10 @@
 ## language.
 
 import
-  ast, modules, idents, passes, passaux, condsyms,
-  options, nimconf, sem, semdata, llstream, vm, vmdef, commands, msgs,
-  os, times, osproc, wordrecg, strtabs, modulegraphs, lineinfos, pathutils
+  ast, modules, idents, passes, condsyms,
+  options, sem, llstream, vm, vmdef, commands, msgs,
+  os, times, osproc, wordrecg, strtabs, modulegraphs,
+  lineinfos, pathutils
 
 # we support 'cmpIgnoreStyle' natively for efficiency:
 from strutils import cmpIgnoreStyle, contains
diff --git a/compiler/sem.nim b/compiler/sem.nim
index 39ffeeb4d..e33170553 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -10,13 +10,13 @@
 # This module implements the semantic checking pass.
 
 import
-  ast, strutils, hashes, options, lexer, astalgo, trees, treetab,
-  wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
-  magicsys, parser, nversion, nimsets, semfold, modulepaths, importer,
+  ast, strutils, options, astalgo, trees,
+  wordrecg, ropes, msgs, idents, renderer, types, platform, math,
+  magicsys, nversion, nimsets, semfold, modulepaths, importer,
   procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch,
   intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting,
   evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity,
-  lowerings, pluginsupport, plugins/active, rod, lineinfos, strtabs, int128
+  lowerings, plugins/active, rod, lineinfos, strtabs, int128
 
 from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward
 
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 23ac4f65b..d2f878831 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -107,7 +107,6 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode,
       elif errorsEnabled or z.diagnosticsEnabled:
         errors.add(CandidateError(
           sym: sym,
-          unmatchedVarParam: int z.mutabilityProblem,
           firstMismatch: z.firstMismatch,
           diagnostics: z.diagnostics))
     else:
@@ -173,14 +172,14 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
   var filterOnlyFirst = false
   if optShowAllMismatches notin c.config.globalOptions:
     for err in errors:
-      if err.firstMismatch > 1:
+      if err.firstMismatch.arg > 1:
         filterOnlyFirst = true
         break
 
   var candidates = ""
   var skipped = 0
   for err in errors:
-    if filterOnlyFirst and err.firstMismatch == 1:
+    if filterOnlyFirst and err.firstMismatch.arg == 1:
       inc skipped
       continue
     if err.sym.kind in routineKinds and err.sym.ast != nil:
@@ -189,34 +188,35 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors):
     else:
       add(candidates, getProcHeader(c.config, err.sym, prefer))
     add(candidates, "\n")
-    if err.firstMismatch != 0 and n.len > 1:
-      let cond = n.len > 2
-      if cond:
-        candidates.add("  first type mismatch at position: " & $abs(err.firstMismatch))
-        if err.firstMismatch >= 0: candidates.add("\n  required type: ")
-        else: candidates.add("\n  unknown named parameter: " & $n[-err.firstMismatch][0])
-      var wanted, got: PType = nil
-      if err.firstMismatch < 0:
-        discard
-      elif err.firstMismatch < err.sym.typ.len:
-        wanted = err.sym.typ.sons[err.firstMismatch]
-        if cond: candidates.add typeToString(wanted)
-      else:
-        if cond: candidates.add "none"
-      if err.firstMismatch > 0 and err.firstMismatch < n.len:
-        if cond:
-          candidates.add "\n  but expression '"
-          candidates.add renderTree(n[err.firstMismatch])
+    let nArg = if err.firstMismatch.arg < n.len: n[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: candidates.add("\n  unknown named parameter: " & $nArg[0])
+      of kAlreadyGiven: candidates.add("\n  named param already provided: " & $nArg[0])
+      of kExtraArg: candidates.add("\n  extra argument given")
+      of kMissingParam: candidates.add("\n  missing parameter: " & nameParam)
+      of kTypeMismatch, kVarNeeded:
+        doAssert nArg != nil
+        var wanted = err.firstMismatch.formal.typ
+        doAssert err.firstMismatch.formal != nil
+        candidates.add("\n  required type for " & nameParam &  ": ")
+        candidates.add typeToString(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: "
-        got = n[err.firstMismatch].typ
-        if cond: candidates.add typeToString(got)
-      if wanted != nil and got != nil:
-        effectProblem(wanted, got, candidates)
-      if cond: candidates.add "\n"
-    if err.unmatchedVarParam != 0 and err.unmatchedVarParam < n.len:
-      candidates.add("  for a 'var' type a variable needs to be passed, but '" &
-                      renderNotLValue(n[err.unmatchedVarParam]) &
-                      "' is immutable\n")
+          var got = nArg.typ
+          candidates.add typeToString(got)
+          doAssert wanted != nil
+          if got != nil: effectProblem(wanted, got, candidates)
+      of kUnknown: internalAssert(c.config, false)
+      candidates.add "\n"
     for diag in err.diagnostics:
       candidates.add(diag & "\n")
   if skipped > 0:
@@ -260,7 +260,7 @@ proc bracketNotFoundError(c: PContext; n: PNode) =
   while symx != nil:
     if symx.kind in routineKinds:
       errors.add(CandidateError(sym: symx,
-                                unmatchedVarParam: 0, firstMismatch: 0,
+                                firstMismatch: MismatchInfo(),
                                 diagnostics: @[],
                                 enabled: false))
     symx = nextOverloadIter(o, c, headSymbol)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index a64f88962..76eb468ed 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -10,11 +10,8 @@
 ## This module contains the data structures for the semantic checking phase.
 
 import
-  strutils, intsets, options, lexer, ast, astalgo, trees, treetab,
-  wordrecg,
-  ropes, msgs, platform, os, condsyms, idents, renderer, types, extccomp, math,
-  magicsys, nversion, nimsets, parser, times, passes, vmdef,
-  modulegraphs, lineinfos
+  intsets, options, ast, astalgo, msgs, idents, renderer,
+  magicsys, passes, vmdef, modulegraphs, lineinfos
 
 type
   TOptionEntry* = object      # entries to put on a stack for pragma parsing
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 733df2c40..929283bef 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1414,6 +1414,9 @@ proc semDeref(c: PContext, n: PNode): PNode =
   var t = skipTypes(n.sons[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned})
   case t.kind
   of tyRef, tyPtr: n.typ = t.lastSon
+  of tyTypeDesc:
+    # typeof(x[]) is still a typedesc:
+    n.typ = makeTypeDesc(c, t.lastSon.lastSon)
   else: result = nil
   #GlobalError(n.sons[0].info, errCircumNeedsPointer)
 
diff --git a/compiler/semfold.nim b/compiler/semfold.nim
index 2f07940c8..c7efa1a87 100644
--- a/compiler/semfold.nim
+++ b/compiler/semfold.nim
@@ -11,8 +11,8 @@
 # and evaluation phase
 
 import
-  strutils, options, ast, astalgo, trees, treetab, nimsets,
-  nversion, platform, math, msgs, os, condsyms, idents, renderer, types,
+  strutils, options, ast, trees, nimsets,
+  platform, math, msgs, idents, renderer, types,
   commands, magicsys, modulegraphs, strtabs, lineinfos
 
 proc newIntNodeT*(intVal: BiggestInt, n: PNode; g: ModuleGraph): PNode =
diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim
index faf3e3868..af740e518 100644
--- a/compiler/semmacrosanity.nim
+++ b/compiler/semmacrosanity.nim
@@ -10,7 +10,7 @@
 ## Implements type sanity checking for ASTs resulting from macros. Lots of
 ## room for improvement here.
 
-import ast, astalgo, msgs, types, options
+import ast, msgs, types, options
 
 proc ithField(n: PNode, field: var int): PSym =
   result = nil
diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim
index 0e1c9cb07..8252a7857 100644
--- a/compiler/sempass2.nim
+++ b/compiler/sempass2.nim
@@ -10,7 +10,7 @@
 import
   intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees,
   wordrecg, strutils, options, guards, lineinfos, semfold, semdata,
-  modulegraphs, lowerings, sigmatch, tables
+  modulegraphs
 
 when not defined(leanCompiler):
   import writetracking
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index c69797391..a9d911e27 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -11,9 +11,7 @@
 
 import ast, tables, ropes, md5, modulegraphs
 from hashes import Hash
-from astalgo import debug
 import types
-from strutils import startsWith, contains
 
 proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len)
 proc `&=`(c: var MD5Context, ch: char) = md5Update(c, unsafeAddr ch, 1)
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 103608afc..828d25fe8 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -12,19 +12,29 @@
 
 import
   intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
-  magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
+  magicsys, idents, lexer, options, parampatterns, strutils, trees,
   linter, lineinfos, lowerings, modulegraphs
 
 when (defined(booting) or defined(nimsuggest)) and not defined(leanCompiler):
   import docgen
 
 type
+  MismatchKind* = enum
+    kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded,
+    kMissingParam, kExtraArg
+
+  MismatchInfo* = object
+    kind*: MismatchKind # reason for mismatch
+    arg*: int           # position of provided arguments that mismatches
+    formal*: PSym       # parameter that mismatches against provided argument
+                        # its position can differ from `arg` because of varargs
+
   TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
   CandidateError* = object
     sym*: PSym
-    unmatchedVarParam*, firstMismatch*: int
+    firstMismatch*: MismatchInfo
     diagnostics*: seq[string]
     enabled*: bool
 
@@ -56,7 +66,6 @@ type
                              # a distrinct type
     typedescMatched*: bool
     isNoCall*: bool          # misused for generic type instantiations C[T]
-    mutabilityProblem*: uint8 # tyVar mismatch
     inferredTypes: seq[PType] # inferred types during the current signature
                               # matching. they will be reset if the matching
                               # is not successful. may replace the bindings
@@ -70,8 +79,7 @@ type
                               # triggered with an idetools command in the
                               # future.
     inheritancePenalty: int   # to prefer closest father object type
-    firstMismatch*: int       # position of the first type mismatch for
-                              # better error messages
+    firstMismatch*: MismatchInfo # mismatch info for better error messages
     diagnosticsEnabled*: bool
 
   TTypeRelFlag* = enum
@@ -112,6 +120,7 @@ proc initCandidateAux(ctx: PContext,
   c.intConvMatches = 0
   c.genericMatches = 0
   c.state = csEmpty
+  c.firstMismatch = MismatchInfo()
   c.callee = callee
   c.call = nil
   c.baseTypeMatch = false
@@ -2280,42 +2289,47 @@ template isVarargsUntyped(x): untyped =
 
 proc matchesAux(c: PContext, n, nOrig: PNode,
                 m: var TCandidate, marker: var IntSet) =
+  var
+    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
+    formal: PSym # current routine parameter
+
+  template noMatch() =
+    m.state = csNoMatch
+    m.firstMismatch.arg = a
+    m.firstMismatch.formal = formal
+
   template checkConstraint(n: untyped) {.dirty.} =
     if not formal.constraint.isNil:
       if matchNodeKinds(formal.constraint, n):
         # better match over other routines with no such restriction:
         inc(m.genericMatches, 100)
       else:
-        m.state = csNoMatch
+        noMatch()
         return
 
     if formal.typ.kind == tyVar:
       let argConverter = if arg.kind == nkHiddenDeref: arg[0] else: arg
       if argConverter.kind == nkHiddenCallConv:
         if argConverter.typ.kind != tyVar:
-          m.state = csNoMatch
-          m.mutabilityProblem = uint8(f-1)
+          m.firstMismatch.kind = kVarNeeded
+          noMatch()
           return
       elif not n.isLValue:
-        m.state = csNoMatch
-        m.mutabilityProblem = uint8(f-1)
+        m.firstMismatch.kind = kVarNeeded
+        noMatch()
         return
 
-  var
-    # iterates over formal parameters
-    f = if m.callee.kind != tyGenericBody: 1
-        else: 0
-    # iterates over the actual given arguments
-    a = 1
-    arg: PNode # current prepared argument
-
   m.state = csMatch # until proven otherwise
+  m.firstMismatch = MismatchInfo()
   m.call = newNodeI(n.kind, n.info)
   m.call.typ = base(m.callee) # may be nil
   var formalLen = m.callee.n.len
   addSon(m.call, copyTree(n.sons[0]))
   var container: PNode = nil # constructed container
-  var formal: PSym = if formalLen > 1: m.callee.n.sons[1].sym else: nil
+  formal = if formalLen > 1: m.callee.n.sons[1].sym else: nil
 
   while a < n.len:
     if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped:
@@ -2336,26 +2350,26 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
         addSon(container, n.sons[a])
     elif n.sons[a].kind == nkExprEqExpr:
       # named param
+      m.firstMismatch.kind = kUnknownNamedParam
       # check if m.callee has such a param:
       prepareNamedParam(n.sons[a], c)
       if n.sons[a].sons[0].kind != nkIdent:
         localError(c.config, n.sons[a].info, "named parameter has to be an identifier")
-        m.state = csNoMatch
-        m.firstMismatch = -a
+        noMatch()
         return
       formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
       if formal == nil:
         # no error message!
-        m.state = csNoMatch
-        m.firstMismatch = -a
+        noMatch()
         return
       if containsOrIncl(marker, formal.position):
+        m.firstMismatch.kind = kAlreadyGiven
         # already in namedParams, so no match
         # we used to produce 'errCannotBindXTwice' here but see
         # bug #3836 of why that is not sound (other overload with
         # different parameter names could match later on):
         when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
-        m.state = csNoMatch
+        noMatch()
         return
       m.baseTypeMatch = false
       m.typedescMatched = false
@@ -2363,9 +2377,9 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
       n.sons[a].typ = n.sons[a].sons[1].typ
       arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
                                 n.sons[a].sons[1], n.sons[a].sons[1])
+      m.firstMismatch.kind = kTypeMismatch
       if arg == nil:
-        m.state = csNoMatch
-        m.firstMismatch = a
+        noMatch()
         return
       checkConstraint(n.sons[a].sons[1])
       if m.baseTypeMatch:
@@ -2392,6 +2406,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           else:
             addSon(m.call, copyTree(n.sons[a]))
         elif formal != nil and formal.typ.kind == tyVarargs:
+          m.firstMismatch.kind = kTypeMismatch
           # beware of the side-effects in 'prepareOperand'! So only do it for
           # varargs matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
@@ -2405,20 +2420,24 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
             incrIndexType(container.typ)
             checkConstraint(n.sons[a])
           else:
-            m.state = csNoMatch
+            noMatch()
             return
         else:
-          m.state = csNoMatch
+          m.firstMismatch.kind = kExtraArg
+          noMatch()
           return
       else:
         if m.callee.n.sons[f].kind != nkSym:
           internalError(c.config, n.sons[a].info, "matches")
+          noMatch()
           return
         formal = m.callee.n.sons[f].sym
+        m.firstMismatch.kind = kTypeMismatch
         if containsOrIncl(marker, formal.position) and container.isNil:
+          m.firstMismatch.kind = kAlreadyGiven
           # already in namedParams: (see above remark)
           when false: localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
-          m.state = csNoMatch
+          noMatch()
           return
 
         if formal.typ.isVarargsUntyped:
@@ -2435,8 +2454,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
                                     n.sons[a], nOrig.sons[a])
           if arg == nil:
-            m.state = csNoMatch
-            m.firstMismatch = f
+            noMatch()
             return
           if m.baseTypeMatch:
             assert formal.typ.kind == tyVarargs
@@ -2465,10 +2483,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
             # this assertion can be off
             localError(c.config, n.sons[a].info, "cannot convert $1 to $2" % [
               typeToString(n.sons[a].typ), typeToString(formal.typ) ])
-            m.state = csNoMatch
+            noMatch()
             return
         checkConstraint(n.sons[a])
     inc(a)
+  # for some edge cases (see tdont_return_unowned_from_owned test case)
+  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
@@ -2510,7 +2531,8 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) =
         else:
           # no default value
           m.state = csNoMatch
-          m.firstMismatch = f
+          m.firstMismatch.kind = kMissingParam
+          m.firstMismatch.formal = formal
           break
       else:
         if formal.ast.kind == nkEmpty:
diff --git a/compiler/spawn.nim b/compiler/spawn.nim
index b8477567b..733ce7732 100644
--- a/compiler/spawn.nim
+++ b/compiler/spawn.nim
@@ -1,428 +1,428 @@
-#

-#

-#           The Nim Compiler

-#        (c) Copyright 2015 Andreas Rumpf

-#

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

-#    distribution, for details about the copyright.

-#

-

-## This module implements threadpool's ``spawn``.

-

-import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs,

-  lineinfos, lowerings

-from trees import getMagic

-

-proc callProc(a: PNode): PNode =

-  result = newNodeI(nkCall, a.info)

-  result.add a

-  result.typ = a.typ.sons[0]

-

-# we have 4 cases to consider:

-# - a void proc --> nothing to do

-# - a proc returning GC'ed memory --> requires a flowVar

-# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter

-# - not in a parallel environment --> requires a flowVar for memory safety

-type

-  TSpawnResult* = enum

-    srVoid, srFlowVar, srByVar

-  TFlowVarKind = enum

-    fvInvalid # invalid type T for 'FlowVar[T]'

-    fvGC      # FlowVar of a GC'ed type

-    fvBlob    # FlowVar of a blob type

-

-proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =

-  if t.isEmptyType: srVoid

-  elif inParallel and not containsGarbageCollectedRef(t): srByVar

-  else: srFlowVar

-

-proc flowVarKind(t: PType): TFlowVarKind =

-  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC

-  elif containsGarbageCollectedRef(t): fvInvalid

-  else: fvBlob

-

-proc typeNeedsNoDeepCopy(t: PType): bool =

-  var t = t.skipTypes(abstractInst)

-  # for the tconvexhull example (and others) we're a bit lax here and pretend

-  # seqs and strings are *by value* only and 'shallow' doesn't exist!

-  if t.kind == tyString: return true

-  # 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

-  result = not containsGarbageCollectedRef(t)

-

-proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;

-                 v: PNode; useShallowCopy=false): PSym =

-  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,

-                  owner.options)

-  result.typ = typ

-  incl(result.flags, sfFromGeneric)

-

-  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)

-  vpart.sons[0] = newSymNode(result)

-  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)

-  vpart.sons[2] = if varInit.isNil: v else: vpart[1]

-  varSection.add vpart

-  if varInit != nil:

-    if useShallowCopy and typeNeedsNoDeepCopy(typ):

-      varInit.add newFastAsgnStmt(newSymNode(result), v)

-    else:

-      let deepCopyCall = newNodeI(nkCall, varInit.info, 3)

-      deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))

-      deepCopyCall.sons[1] = newSymNode(result)

-      deepCopyCall.sons[2] = v

-      varInit.add deepCopyCall

-

-discard """

-We generate roughly this:

-

-proc f_wrapper(thread, args) =

-  barrierEnter(args.barrier)  # for parallel statement

-  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy

-                 # depending on whether we're in a 'parallel' statement

-  var b = args.b

-  var fv = args.fv

-

-  fv.owner = thread # optional

-  nimArgsPassingDone() # signal parent that the work is done

-  #

-  args.fv.blob = f(a, b, ...)

-  nimFlowVarSignal(args.fv)

-

-  # - or -

-  f(a, b, ...)

-  barrierLeave(args.barrier)  # for parallel statement

-

-stmtList:

-  var scratchObj

-  scratchObj.a = a

-  scratchObj.b = b

-

-  nimSpawn(f_wrapper, addr scratchObj)

-  scratchObj.fv # optional

-

-"""

-

-proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;

-                       varSection, varInit, call, barrier, fv: PNode;

-                       spawnKind: TSpawnResult): PSym =

-  var body = newNodeI(nkStmtList, f.info)

-  body.flags.incl nfTransf # do not transform further

-

-  var threadLocalBarrier: PSym

-  if barrier != nil:

-    var varSection2 = newNodeI(nkVarSection, barrier.info)

-    threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,

-                                     barrier.typ, barrier)

-    body.add varSection2

-    body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,

-      threadLocalBarrier.newSymNode)

-  var threadLocalProm: PSym

-  if spawnKind == srByVar:

-    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)

-  elif fv != nil:

-    internalAssert g.config, fv.typ.kind == tyGenericInst

-    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)

-  body.add varSection

-  body.add varInit

-  if fv != nil and spawnKind != srByVar:

-    # generate:

-    #   fv.owner = threadParam

-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,

-      "owner", fv.info, g.cache), threadParam.newSymNode)

-

-  body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,

-    threadParam.newSymNode)

-  if spawnKind == srByVar:

-    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)

-  elif fv != nil:

-    let fk = fv.typ.sons[1].flowVarKind

-    if fk == fvInvalid:

-      localError(g.config, f.info, "cannot create a flowVar of type: " &

-        typeToString(fv.typ.sons[1]))

-    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,

-      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)

-    if fk == fvGC:

-      let incRefCall = newNodeI(nkCall, fv.info, 2)

-      incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))

-      incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,

-                                          "data", fv.info, g.cache)

-      body.add incRefCall

-    if barrier == nil:

-      # by now 'fv' is shared and thus might have beeen overwritten! we need

-      # to use the thread-local view instead:

-      body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,

-        threadLocalProm.newSymNode)

-  else:

-    body.add call

-  if barrier != nil:

-    body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,

-      threadLocalBarrier.newSymNode)

-

-  var params = newNodeI(nkFormalParams, f.info)

-  params.add newNodeI(nkEmpty, f.info)

-  params.add threadParam.newSymNode

-  params.add argsParam.newSymNode

-

-  var t = newType(tyProc, threadParam.owner)

-  t.rawAddSon nil

-  t.rawAddSon threadParam.typ

-  t.rawAddSon argsParam.typ

-  t.n = newNodeI(nkFormalParams, f.info)

-  t.n.add newNodeI(nkEffectList, f.info)

-  t.n.add threadParam.newSymNode

-  t.n.add argsParam.newSymNode

-

-  let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"

-  result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,

-                  argsParam.options)

-  let emptyNode = newNodeI(nkEmpty, f.info)

-  result.ast = newProcNode(nkProcDef, f.info, body = body,

-      params = params, name = newSymNode(result), pattern = emptyNode,

-      genericParams = emptyNode, pragmas = emptyNode,

-      exceptions = emptyNode)

-  result.typ = t

-

-proc createCastExpr(argsParam: PSym; objType: PType): PNode =

-  result = newNodeI(nkCast, argsParam.info)

-  result.add newNodeI(nkEmpty, argsParam.info)

-  result.add newSymNode(argsParam)

-  result.typ = newType(tyPtr, objType.owner)

-  result.typ.rawAddSon(objType)

-

-proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,

-                             castExpr, call,

-                             varSection, varInit, result: PNode) =

-  let formals = n[0].typ.n

-  let tmpName = getIdent(g.cache, genPrefix)

-  for i in 1 ..< n.len:

-    # we pick n's type here, which hopefully is 'tyArray' and not

-    # 'tyOpenArray':

-    var argType = n[i].typ.skipTypes(abstractInst)

-    if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:

-      localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")

-    #elif containsTyRef(argType):

-    #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")

-

-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName

-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)

-    field.typ = argType

-    objType.addField(field, g.cache)

-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])

-

-    let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,

-                           indirectAccess(castExpr, field, n.info))

-    call.add(newSymNode(temp))

-

-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

-  ## determined as the owner; ``obj`` in the example.

-  case n.kind

-  of nkSym:

-    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:

-      result = n.sym

-  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,

-      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:

-    result = getRoot(n.sons[0])

-  of nkHiddenStdConv, nkHiddenSubConv, nkConv:

-    result = getRoot(n.sons[1])

-  of nkCallKinds:

-    if getMagic(n) == mSlice: result = getRoot(n.sons[1])

-  else: discard

-

-proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;

-                             castExpr, call,

-                             varSection, varInit, result: PNode) =

-  let formals = n[0].typ.n

-  let tmpName = getIdent(g.cache, genPrefix)

-  # we need to copy the foreign scratch object fields into local variables

-  # for correctness: These are called 'threadLocal' here.

-  for i in 1 ..< n.len:

-    let n = n[i]

-    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,

-                            abstractInst)

-    #if containsTyRef(argType):

-    #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")

-

-    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName

-    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)

-

-    if argType.kind in {tyVarargs, tyOpenArray}:

-      # important special case: we always create a zero-copy slice:

-      let slice = newNodeI(nkCall, n.info, 4)

-      slice.typ = n.typ

-      slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))

-      slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type

-      var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)

-      fieldB.typ = getSysType(g, n.info, tyInt)

-      objType.addField(fieldB, g.cache)

-

-      if getMagic(n) == mSlice:

-        let a = genAddrOf(n[1])

-        field.typ = a.typ

-        objType.addField(field, g.cache)

-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)

-

-        var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)

-        fieldA.typ = getSysType(g, n.info, tyInt)

-        objType.addField(fieldA, g.cache)

-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])

-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])

-

-        let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,

-                                      indirectAccess(castExpr, fieldA, n.info),

-                                      useShallowCopy=true)

-        slice.sons[2] = threadLocal.newSymNode

-      else:

-        let a = genAddrOf(n)

-        field.typ = a.typ

-        objType.addField(field, g.cache)

-        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)

-        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))

-

-        slice.sons[2] = newIntLit(g, n.info, 0)

-      # the array itself does not need to go through a thread local variable:

-      slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))

-

-      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,

-                                    indirectAccess(castExpr, fieldB, n.info),

-                                    useShallowCopy=true)

-      slice.sons[3] = threadLocal.newSymNode

-      call.add slice

-    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and

-        n.getRoot != nil:

-      # it is more efficient to pass a pointer instead:

-      let a = genAddrOf(n)

-      field.typ = a.typ

-      objType.addField(field, g.cache)

-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)

-      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,

-                                    indirectAccess(castExpr, field, n.info),

-                                    useShallowCopy=true)

-      call.add(genDeref(threadLocal.newSymNode))

-    else:

-      # boring case

-      field.typ = argType

-      objType.addField(field, g.cache)

-      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)

-      let threadLocal = addLocalVar(g, varSection, varInit,

-                                    objType.owner, field.typ,

-                                    indirectAccess(castExpr, field, n.info),

-                                    useShallowCopy=true)

-      call.add(threadLocal.newSymNode)

-

-proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;

-                       barrier, dest: PNode = nil): PNode =

-  # if 'barrier' != nil, then it is in a 'parallel' section and we

-  # generate quite different code

-  let n = spawnExpr[^2]

-  let spawnKind = spawnResult(retType, barrier!=nil)

-  case spawnKind

-  of srVoid:

-    internalAssert g.config, dest == nil

-    result = newNodeI(nkStmtList, n.info)

-  of srFlowVar:

-    internalAssert g.config, dest == nil

-    result = newNodeIT(nkStmtListExpr, n.info, retType)

-  of srByVar:

-    if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")

-    result = newNodeI(nkStmtList, n.info)

-

-  if n.kind notin nkCallKinds:

-    localError(g.config, n.info, "'spawn' takes a call expression")

-    return

-  if optThreadAnalysis in g.config.globalOptions:

-    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:

-      localError(g.config, n.info, "'spawn' takes a GC safe call expression")

-  var

-    threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)

-    argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)

-  block:

-    let ptrType = getSysType(g, n.info, tyPointer)

-    threadParam.typ = ptrType

-    argsParam.typ = ptrType

-    argsParam.position = 1

-

-  var objType = createObj(g, owner, n.info)

-  incl(objType.flags, tfFinal)

-  let castExpr = createCastExpr(argsParam, objType)

-

-  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)

-  block:

-    scratchObj.typ = objType

-    incl(scratchObj.flags, sfFromGeneric)

-    var varSectionB = newNodeI(nkVarSection, n.info)

-    varSectionB.addVar(scratchObj.newSymNode)

-    result.add varSectionB

-

-  var call = newNodeIT(nkCall, n.info, n.typ)

-  var fn = n.sons[0]

-  # templates and macros are in fact valid here due to the nature of

-  # the transformation:

-  if fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure):

-    localError(g.config, n.info, "closure in spawn environment is not allowed")

-  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,

-                                               skFunc, skMethod, skConverter}):

-    # for indirect calls we pass the function pointer in the scratchObj

-    var argType = n[0].typ.skipTypes(abstractInst)

-    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)

-    field.typ = argType

-    objType.addField(field, g.cache)

-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])

-    fn = indirectAccess(castExpr, field, n.info)

-  elif fn.kind == nkSym and fn.sym.kind == skIterator:

-    localError(g.config, n.info, "iterator in spawn environment is not allowed")

-  elif fn.typ.callConv == ccClosure:

-    localError(g.config, n.info, "closure in spawn environment is not allowed")

-

-  call.add(fn)

-  var varSection = newNodeI(nkVarSection, n.info)

-  var varInit = newNodeI(nkStmtList, n.info)

-  if barrier.isNil:

-    setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,

-                            varSection, varInit, result)

-  else:

-    setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,

-                            varSection, varInit, result)

-

-  var barrierAsExpr: PNode = nil

-  if barrier != nil:

-    let typ = newType(tyPtr, owner)

-    typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)

-    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)

-    field.typ = typ

-    objType.addField(field, g.cache)

-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)

-    barrierAsExpr = indirectAccess(castExpr, field, n.info)

-

-  var fvField, fvAsExpr: PNode = nil

-  if spawnKind == srFlowVar:

-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)

-    field.typ = retType

-    objType.addField(field, g.cache)

-    fvField = newDotExpr(scratchObj, field)

-    fvAsExpr = indirectAccess(castExpr, field, n.info)

-    # create flowVar:

-    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))

-    if barrier == nil:

-      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,

-        fvField)

-

-  elif spawnKind == srByVar:

-    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)

-    field.typ = newType(tyPtr, objType.owner)

-    field.typ.rawAddSon(retType)

-    objType.addField(field, g.cache)

-    fvAsExpr = indirectAccess(castExpr, field, n.info)

-    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))

-

-  let wrapper = createWrapperProc(g, fn, threadParam, argsParam,

-                                  varSection, varInit, call,

-                                  barrierAsExpr, fvAsExpr, spawnKind)

-  result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,

-    wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)

-

-  if spawnKind == srFlowVar: result.add fvField

-

+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2015 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+## This module implements threadpool's ``spawn``.
+
+import ast, types, idents, magicsys, msgs, options, modulegraphs,
+  lowerings
+from trees import getMagic
+
+proc callProc(a: PNode): PNode =
+  result = newNodeI(nkCall, a.info)
+  result.add a
+  result.typ = a.typ.sons[0]
+
+# we have 4 cases to consider:
+# - a void proc --> nothing to do
+# - a proc returning GC'ed memory --> requires a flowVar
+# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
+# - not in a parallel environment --> requires a flowVar for memory safety
+type
+  TSpawnResult* = enum
+    srVoid, srFlowVar, srByVar
+  TFlowVarKind = enum
+    fvInvalid # invalid type T for 'FlowVar[T]'
+    fvGC      # FlowVar of a GC'ed type
+    fvBlob    # FlowVar of a blob type
+
+proc spawnResult*(t: PType; inParallel: bool): TSpawnResult =
+  if t.isEmptyType: srVoid
+  elif inParallel and not containsGarbageCollectedRef(t): srByVar
+  else: srFlowVar
+
+proc flowVarKind(t: PType): TFlowVarKind =
+  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: fvGC
+  elif containsGarbageCollectedRef(t): fvInvalid
+  else: fvBlob
+
+proc typeNeedsNoDeepCopy(t: PType): bool =
+  var t = t.skipTypes(abstractInst)
+  # for the tconvexhull example (and others) we're a bit lax here and pretend
+  # seqs and strings are *by value* only and 'shallow' doesn't exist!
+  if t.kind == tyString: return true
+  # 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
+  result = not containsGarbageCollectedRef(t)
+
+proc addLocalVar(g: ModuleGraph; varSection, varInit: PNode; owner: PSym; typ: PType;
+                 v: PNode; useShallowCopy=false): PSym =
+  result = newSym(skTemp, getIdent(g.cache, genPrefix), owner, varSection.info,
+                  owner.options)
+  result.typ = typ
+  incl(result.flags, sfFromGeneric)
+
+  var vpart = newNodeI(nkIdentDefs, varSection.info, 3)
+  vpart.sons[0] = newSymNode(result)
+  vpart.sons[1] = newNodeI(nkEmpty, varSection.info)
+  vpart.sons[2] = if varInit.isNil: v else: vpart[1]
+  varSection.add vpart
+  if varInit != nil:
+    if useShallowCopy and typeNeedsNoDeepCopy(typ):
+      varInit.add newFastAsgnStmt(newSymNode(result), v)
+    else:
+      let deepCopyCall = newNodeI(nkCall, varInit.info, 3)
+      deepCopyCall.sons[0] = newSymNode(getSysMagic(g, varSection.info, "deepCopy", mDeepCopy))
+      deepCopyCall.sons[1] = newSymNode(result)
+      deepCopyCall.sons[2] = v
+      varInit.add deepCopyCall
+
+discard """
+We generate roughly this:
+
+proc f_wrapper(thread, args) =
+  barrierEnter(args.barrier)  # for parallel statement
+  var a = args.a # thread transfer; deepCopy or shallowCopy or no copy
+                 # depending on whether we're in a 'parallel' statement
+  var b = args.b
+  var fv = args.fv
+
+  fv.owner = thread # optional
+  nimArgsPassingDone() # signal parent that the work is done
+  #
+  args.fv.blob = f(a, b, ...)
+  nimFlowVarSignal(args.fv)
+
+  # - or -
+  f(a, b, ...)
+  barrierLeave(args.barrier)  # for parallel statement
+
+stmtList:
+  var scratchObj
+  scratchObj.a = a
+  scratchObj.b = b
+
+  nimSpawn(f_wrapper, addr scratchObj)
+  scratchObj.fv # optional
+
+"""
+
+proc createWrapperProc(g: ModuleGraph; f: PNode; threadParam, argsParam: PSym;
+                       varSection, varInit, call, barrier, fv: PNode;
+                       spawnKind: TSpawnResult): PSym =
+  var body = newNodeI(nkStmtList, f.info)
+  body.flags.incl nfTransf # do not transform further
+
+  var threadLocalBarrier: PSym
+  if barrier != nil:
+    var varSection2 = newNodeI(nkVarSection, barrier.info)
+    threadLocalBarrier = addLocalVar(g, varSection2, nil, argsParam.owner,
+                                     barrier.typ, barrier)
+    body.add varSection2
+    body.add callCodegenProc(g, "barrierEnter", threadLocalBarrier.info,
+      threadLocalBarrier.newSymNode)
+  var threadLocalProm: PSym
+  if spawnKind == srByVar:
+    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
+  elif fv != nil:
+    internalAssert g.config, fv.typ.kind == tyGenericInst
+    threadLocalProm = addLocalVar(g, varSection, nil, argsParam.owner, fv.typ, fv)
+  body.add varSection
+  body.add varInit
+  if fv != nil and spawnKind != srByVar:
+    # generate:
+    #   fv.owner = threadParam
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      "owner", fv.info, g.cache), threadParam.newSymNode)
+
+  body.add callCodegenProc(g, "nimArgsPassingDone", threadParam.info,
+    threadParam.newSymNode)
+  if spawnKind == srByVar:
+    body.add newAsgnStmt(genDeref(threadLocalProm.newSymNode), call)
+  elif fv != nil:
+    let fk = fv.typ.sons[1].flowVarKind
+    if fk == fvInvalid:
+      localError(g.config, f.info, "cannot create a flowVar of type: " &
+        typeToString(fv.typ.sons[1]))
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
+      if fk == fvGC: "data" else: "blob", fv.info, g.cache), call)
+    if fk == fvGC:
+      let incRefCall = newNodeI(nkCall, fv.info, 2)
+      incRefCall.sons[0] = newSymNode(getSysMagic(g, fv.info, "GCref", mGCref))
+      incRefCall.sons[1] = indirectAccess(threadLocalProm.newSymNode,
+                                          "data", fv.info, g.cache)
+      body.add incRefCall
+    if barrier == nil:
+      # by now 'fv' is shared and thus might have beeen overwritten! we need
+      # to use the thread-local view instead:
+      body.add callCodegenProc(g, "nimFlowVarSignal", threadLocalProm.info,
+        threadLocalProm.newSymNode)
+  else:
+    body.add call
+  if barrier != nil:
+    body.add callCodegenProc(g, "barrierLeave", threadLocalBarrier.info,
+      threadLocalBarrier.newSymNode)
+
+  var params = newNodeI(nkFormalParams, f.info)
+  params.add newNodeI(nkEmpty, f.info)
+  params.add threadParam.newSymNode
+  params.add argsParam.newSymNode
+
+  var t = newType(tyProc, threadParam.owner)
+  t.rawAddSon nil
+  t.rawAddSon threadParam.typ
+  t.rawAddSon argsParam.typ
+  t.n = newNodeI(nkFormalParams, f.info)
+  t.n.add newNodeI(nkEffectList, f.info)
+  t.n.add threadParam.newSymNode
+  t.n.add argsParam.newSymNode
+
+  let name = (if f.kind == nkSym: f.sym.name.s else: genPrefix) & "Wrapper"
+  result = newSym(skProc, getIdent(g.cache, name), argsParam.owner, f.info,
+                  argsParam.options)
+  let emptyNode = newNodeI(nkEmpty, f.info)
+  result.ast = newProcNode(nkProcDef, f.info, body = body,
+      params = params, name = newSymNode(result), pattern = emptyNode,
+      genericParams = emptyNode, pragmas = emptyNode,
+      exceptions = emptyNode)
+  result.typ = t
+
+proc createCastExpr(argsParam: PSym; objType: PType): PNode =
+  result = newNodeI(nkCast, argsParam.info)
+  result.add newNodeI(nkEmpty, argsParam.info)
+  result.add newSymNode(argsParam)
+  result.typ = newType(tyPtr, objType.owner)
+  result.typ.rawAddSon(objType)
+
+proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym,
+                             castExpr, call,
+                             varSection, varInit, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(g.cache, genPrefix)
+  for i in 1 ..< n.len:
+    # we pick n's type here, which hopefully is 'tyArray' and not
+    # 'tyOpenArray':
+    var argType = n[i].typ.skipTypes(abstractInst)
+    if i < formals.len and formals[i].typ.kind in {tyVar, tyLent}:
+      localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter")
+    #elif containsTyRef(argType):
+    #  localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
+    field.typ = argType
+    objType.addField(field, g.cache)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
+
+    let temp = addLocalVar(g, varSection, varInit, objType.owner, argType,
+                           indirectAccess(castExpr, field, n.info))
+    call.add(newSymNode(temp))
+
+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
+  ## determined as the owner; ``obj`` in the example.
+  case n.kind
+  of nkSym:
+    if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
+      result = n.sym
+  of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
+      nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
+    result = getRoot(n.sons[0])
+  of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+    result = getRoot(n.sons[1])
+  of nkCallKinds:
+    if getMagic(n) == mSlice: result = getRoot(n.sons[1])
+  else: discard
+
+proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; scratchObj: PSym;
+                             castExpr, call,
+                             varSection, varInit, result: PNode) =
+  let formals = n[0].typ.n
+  let tmpName = getIdent(g.cache, genPrefix)
+  # we need to copy the foreign scratch object fields into local variables
+  # for correctness: These are called 'threadLocal' here.
+  for i in 1 ..< n.len:
+    let n = n[i]
+    let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
+                            abstractInst)
+    #if containsTyRef(argType):
+    #  localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
+
+    let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
+    var field = newSym(skField, fieldname, objType.owner, n.info, g.config.options)
+
+    if argType.kind in {tyVarargs, tyOpenArray}:
+      # important special case: we always create a zero-copy slice:
+      let slice = newNodeI(nkCall, n.info, 4)
+      slice.typ = n.typ
+      slice.sons[0] = newSymNode(createMagic(g, "slice", mSlice))
+      slice.sons[0].typ = getSysType(g, n.info, tyInt) # fake type
+      var fieldB = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
+      fieldB.typ = getSysType(g, n.info, tyInt)
+      objType.addField(fieldB, g.cache)
+
+      if getMagic(n) == mSlice:
+        let a = genAddrOf(n[1])
+        field.typ = a.typ
+        objType.addField(field, g.cache)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+
+        var fieldA = newSym(skField, tmpName, objType.owner, n.info, g.config.options)
+        fieldA.typ = getSysType(g, n.info, tyInt)
+        objType.addField(fieldA, g.cache)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
+
+        let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldA.typ,
+                                      indirectAccess(castExpr, fieldA, n.info),
+                                      useShallowCopy=true)
+        slice.sons[2] = threadLocal.newSymNode
+      else:
+        let a = genAddrOf(n)
+        field.typ = a.typ
+        objType.addField(field, g.cache)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+        result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n))
+
+        slice.sons[2] = newIntLit(g, n.info, 0)
+      # the array itself does not need to go through a thread local variable:
+      slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
+
+      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, fieldB.typ,
+                                    indirectAccess(castExpr, fieldB, n.info),
+                                    useShallowCopy=true)
+      slice.sons[3] = threadLocal.newSymNode
+      call.add slice
+    elif (let size = computeSize(g.config, argType); size < 0 or size > 16) and
+        n.getRoot != nil:
+      # it is more efficient to pass a pointer instead:
+      let a = genAddrOf(n)
+      field.typ = a.typ
+      objType.addField(field, g.cache)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
+      let threadLocal = addLocalVar(g, varSection,nil, objType.owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info),
+                                    useShallowCopy=true)
+      call.add(genDeref(threadLocal.newSymNode))
+    else:
+      # boring case
+      field.typ = argType
+      objType.addField(field, g.cache)
+      result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
+      let threadLocal = addLocalVar(g, varSection, varInit,
+                                    objType.owner, field.typ,
+                                    indirectAccess(castExpr, field, n.info),
+                                    useShallowCopy=true)
+      call.add(threadLocal.newSymNode)
+
+proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType;
+                       barrier, dest: PNode = nil): PNode =
+  # if 'barrier' != nil, then it is in a 'parallel' section and we
+  # generate quite different code
+  let n = spawnExpr[^2]
+  let spawnKind = spawnResult(retType, barrier!=nil)
+  case spawnKind
+  of srVoid:
+    internalAssert g.config, dest == nil
+    result = newNodeI(nkStmtList, n.info)
+  of srFlowVar:
+    internalAssert g.config, dest == nil
+    result = newNodeIT(nkStmtListExpr, n.info, retType)
+  of srByVar:
+    if dest == nil: localError(g.config, n.info, "'spawn' must not be discarded")
+    result = newNodeI(nkStmtList, n.info)
+
+  if n.kind notin nkCallKinds:
+    localError(g.config, n.info, "'spawn' takes a call expression")
+    return
+  if optThreadAnalysis in g.config.globalOptions:
+    if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
+      localError(g.config, n.info, "'spawn' takes a GC safe call expression")
+  var
+    threadParam = newSym(skParam, getIdent(g.cache, "thread"), owner, n.info, g.config.options)
+    argsParam = newSym(skParam, getIdent(g.cache, "args"), owner, n.info, g.config.options)
+  block:
+    let ptrType = getSysType(g, n.info, tyPointer)
+    threadParam.typ = ptrType
+    argsParam.typ = ptrType
+    argsParam.position = 1
+
+  var objType = createObj(g, owner, n.info)
+  incl(objType.flags, tfFinal)
+  let castExpr = createCastExpr(argsParam, objType)
+
+  var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options)
+  block:
+    scratchObj.typ = objType
+    incl(scratchObj.flags, sfFromGeneric)
+    var varSectionB = newNodeI(nkVarSection, n.info)
+    varSectionB.addVar(scratchObj.newSymNode)
+    result.add varSectionB
+
+  var call = newNodeIT(nkCall, n.info, n.typ)
+  var fn = n.sons[0]
+  # templates and macros are in fact valid here due to the nature of
+  # the transformation:
+  if fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure):
+    localError(g.config, n.info, "closure in spawn environment is not allowed")
+  if not (fn.kind == nkSym and fn.sym.kind in {skProc, skTemplate, skMacro,
+                                               skFunc, skMethod, skConverter}):
+    # for indirect calls we pass the function pointer in the scratchObj
+    var argType = n[0].typ.skipTypes(abstractInst)
+    var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options)
+    field.typ = argType
+    objType.addField(field, g.cache)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0])
+    fn = indirectAccess(castExpr, field, n.info)
+  elif fn.kind == nkSym and fn.sym.kind == skIterator:
+    localError(g.config, n.info, "iterator in spawn environment is not allowed")
+  elif fn.typ.callConv == ccClosure:
+    localError(g.config, n.info, "closure in spawn environment is not allowed")
+
+  call.add(fn)
+  var varSection = newNodeI(nkVarSection, n.info)
+  var varInit = newNodeI(nkStmtList, n.info)
+  if barrier.isNil:
+    setupArgsForConcurrency(g, n, objType, scratchObj, castExpr, call,
+                            varSection, varInit, result)
+  else:
+    setupArgsForParallelism(g, n, objType, scratchObj, castExpr, call,
+                            varSection, varInit, result)
+
+  var barrierAsExpr: PNode = nil
+  if barrier != nil:
+    let typ = newType(tyPtr, owner)
+    typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ)
+    var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options)
+    field.typ = typ
+    objType.addField(field, g.cache)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
+    barrierAsExpr = indirectAccess(castExpr, field, n.info)
+
+  var fvField, fvAsExpr: PNode = nil
+  if spawnKind == srFlowVar:
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
+    field.typ = retType
+    objType.addField(field, g.cache)
+    fvField = newDotExpr(scratchObj, field)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    # create flowVar:
+    result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1]))
+    if barrier == nil:
+      result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info,
+        fvField)
+
+  elif spawnKind == srByVar:
+    var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options)
+    field.typ = newType(tyPtr, objType.owner)
+    field.typ.rawAddSon(retType)
+    objType.addField(field, g.cache)
+    fvAsExpr = indirectAccess(castExpr, field, n.info)
+    result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest))
+
+  let wrapper = createWrapperProc(g, fn, threadParam, argsParam,
+                                  varSection, varInit, call,
+                                  barrierAsExpr, fvAsExpr, spawnKind)
+  result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapper.info,
+    wrapper.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr)
+
+  if spawnKind == srFlowVar: result.add fvField
+
diff --git a/compiler/suggest.nim b/compiler/suggest.nim
index 1fd12d750..59e3a7242 100644
--- a/compiler/suggest.nim
+++ b/compiler/suggest.nim
@@ -32,11 +32,11 @@
 
 # included from sigmatch.nim
 
-import algorithm, prefixmatches, lineinfos, pathutils, parseutils, linter
+import algorithm, prefixmatches, lineinfos, parseutils, linter
 from wordrecg import wDeprecated, wError, wAddr, wYield, specialWords
 
 when defined(nimsuggest):
-  import passes, tables # importer
+  import passes, tables, pathutils # importer
 
 const
   sep = '\t'
diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim
index 3e4f65c0b..3b67b3263 100644
--- a/compiler/syntaxes.nim
+++ b/compiler/syntaxes.nim
@@ -10,7 +10,7 @@
 ## Implements the dispatcher for the different parsers.
 
 import
-  strutils, llstream, ast, astalgo, idents, lexer, options, msgs, parser,
+  strutils, llstream, ast, idents, lexer, options, msgs, parser,
   filters, filter_tmpl, renderer, lineinfos, pathutils
 
 type
diff --git a/compiler/transf.nim b/compiler/transf.nim
index 4dd348742..60a7ce224 100644
--- a/compiler/transf.nim
+++ b/compiler/transf.nim
@@ -19,9 +19,9 @@
 # * transforms 'defer' into a 'try finally' statement
 
 import
-  intsets, strutils, options, ast, astalgo, trees, treetab, msgs, lookups,
+  options, ast, astalgo, trees, msgs,
   idents, renderer, types, passes, semfold, magicsys, cgmeth,
-  sempass2, lowerings, injectdestructors, liftlocals,
+  lowerings, injectdestructors, liftlocals,
   modulegraphs, lineinfos
 
 proc transformBody*(g: ModuleGraph, prc: PSym, cache = true;
diff --git a/compiler/trees.nim b/compiler/trees.nim
index c878eb1bf..87ab7c00e 100644
--- a/compiler/trees.nim
+++ b/compiler/trees.nim
@@ -10,7 +10,7 @@
 # tree helper routines
 
 import
-  ast, astalgo, lexer, msgs, strutils, wordrecg, idents
+  ast, wordrecg, idents
 
 proc cyclicTreeAux(n: PNode, visited: var seq[PNode]): bool =
   if n == nil: return
diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim
index 0c4fe01e1..7084349e5 100644
--- a/compiler/typesrenderer.nim
+++ b/compiler/typesrenderer.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import renderer, strutils, ast, msgs, types, astalgo
+import renderer, strutils, ast, types
 
 const defaultParamSeparator* = ","
 
diff --git a/compiler/vm.nim b/compiler/vm.nim
index bb8c6bbf2..34caec83f 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -13,7 +13,7 @@
 import ast except getstr
 
 import
-  strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
+  strutils, msgs, vmdef, vmgen, nimsets, types, passes,
   parser, vmdeps, idents, trees, renderer, options, transf, parseutils,
   vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl,
   modulegraphs, sighashes
diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim
index 723863ca7..425f728e7 100644
--- a/compiler/vmdef.nim
+++ b/compiler/vmdef.nim
@@ -10,8 +10,7 @@
 ## This module contains the type definitions for the new evaluation engine.
 ## An instruction is 1-3 int32s in memory, it is a register based VM.
 
-import ast, passes, msgs, idents, intsets, options, modulegraphs, lineinfos,
-  tables, btrees
+import ast, passes, idents, intsets, options, modulegraphs, lineinfos
 
 const
   byteExcess* = 128 # we use excess-K for immediates
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 5130f30c9..1b2538c79 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -7,7 +7,7 @@
 #    distribution, for details about the copyright.
 #
 
-import ast, types, msgs, os, streams, options, idents, lineinfos
+import ast, types, msgs, os, options, idents, lineinfos
 
 proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string =
   try:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 37706f1ea..a27cd62c6 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -28,10 +28,8 @@
 # this copy depends on the involved types.
 
 import
-  strutils, ast, astalgo, types, msgs, renderer, vmdef,
-  trees, intsets, magicsys, options, lowerings, lineinfos, transf
-import platform
-from os import splitFile
+  strutils, ast, types, msgs, renderer, vmdef,
+  intsets, magicsys, options, lowerings, lineinfos, transf
 
 const
   debugEchoCode* = defined(nimVMDebug)
diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim
index 04d3b7a16..b310701ca 100644
--- a/compiler/writetracking.nim
+++ b/compiler/writetracking.nim
@@ -15,8 +15,7 @@
 ##   * Computing an aliasing relation based on the assignments. This relation
 ##     is then used to compute the 'writes' and 'escapes' effects.
 
-import intsets, idents, ast, astalgo, trees, renderer, msgs, types, options,
-  lineinfos
+import intsets, idents, ast, trees, msgs, types, options, lineinfos
 
 const
   debug = false
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 0892ad434..fa8a682ad 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -1420,7 +1420,7 @@ proc boolVal*(n: NimNode): bool {.compileTime, noSideEffect.} =
   if n.kind == nnkIntLit: n.intVal != 0
   else: n == bindSym"true" # hacky solution for now
 
-macro expandMacros*(body: typed): typed =
+macro expandMacros*(body: typed): untyped =
   ## Expands one level of macro - useful for debugging.
   ## Can be used to inspect what happens when a macro call is expanded,
   ## without altering its result.
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index f0f498b9f..f1f27cc42 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -169,7 +169,7 @@
 include "system/inclrtl"
 
 import os, tables, strutils, times, heapqueue, lists, options, asyncstreams
-import options, math
+import options, math, std/monotimes
 import asyncfutures except callSoon
 
 import nativesockets, net, deques
@@ -184,7 +184,7 @@ export asyncstreams
 
 type
   PDispatcherBase = ref object of RootRef
-    timers*: HeapQueue[tuple[finishAt: float, fut: Future[void]]]
+    timers*: HeapQueue[tuple[finishAt: MonoTime, fut: Future[void]]]
     callbacks*: Deque[proc () {.gcsafe.}]
 
 proc processTimers(
@@ -192,7 +192,7 @@ proc processTimers(
 ): Option[int] {.inline.} =
   # Pop the timers in the order in which they will expire (smaller `finishAt`).
   var count = p.timers.len
-  let t = epochTime()
+  let t = getMonoTime()
   while count > 0 and t >= p.timers[0].finishAt:
     p.timers.pop().fut.complete()
     dec count
@@ -201,8 +201,8 @@ proc processTimers(
   # Return the number of miliseconds in which the next timer will expire.
   if p.timers.len == 0: return
 
-  let milisecs = (p.timers[0].finishAt - epochTime()) * 1000
-  return some(ceil(milisecs).int)
+  let millisecs = (p.timers[0].finishAt - getMonoTime()).inMilliseconds
+  return some(millisecs.int + 1)
 
 proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) =
   while p.callbacks.len > 0:
@@ -1778,7 +1778,11 @@ proc sleepAsync*(ms: int | float): owned(Future[void]) =
   ## ``ms`` milliseconds.
   var retFuture = newFuture[void]("sleepAsync")
   let p = getGlobalDispatcher()
-  p.timers.push((epochTime() + (ms / 1000), retFuture))
+  when ms is int:
+    p.timers.push((getMonoTime() + initDuration(milliseconds = ms), retFuture))
+  elif ms is float:
+    let ns = (ms * 1_000_000).int64
+    p.timers.push((getMonoTime() + initDuration(nanoseconds = ns), retFuture))
   return retFuture
 
 proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim
index 91b6b55e8..8b7703804 100644
--- a/lib/pure/collections/intsets.nim
+++ b/lib/pure/collections/intsets.nim
@@ -20,7 +20,7 @@
 
 
 import
-  hashes, math
+  hashes
 
 type
   BitScalar = uint
diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim
index 4f681f980..6fc5eb95b 100644
--- a/lib/pure/concurrency/cpuinfo.nim
+++ b/lib/pure/concurrency/cpuinfo.nim
@@ -11,10 +11,8 @@
 
 include "system/inclrtl"
 
-import strutils, os
-
 when not defined(windows):
-  import posix
+  import strutils, posix, os
 
 when defined(linux):
   import linux
diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index 8104470e3..48e192a59 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -44,10 +44,6 @@
 ## * `std/sha1 module <sha1.html>`_ for a sha1 encoder and decoder
 ## * `tables module <tables.html>`_ for hash tables
 
-
-import
-  strutils
-
 type
   Hash* = int  ## A hash value. Hash tables using these values should
                ## always have a size of a power of two and can use the ``and``
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index cef0d9607..9ae9819c3 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -176,7 +176,7 @@
 ##
 
 import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes,
-  math, random, httpcore, times, tables, streams
+  math, random, httpcore, times, tables, streams, std/monotimes
 import asyncnet, asyncdispatch, asyncfile
 import nativesockets
 
@@ -610,7 +610,7 @@ type
     contentTotal: BiggestInt
     contentProgress: BiggestInt
     oneSecondProgress: BiggestInt
-    lastProgressReport: float
+    lastProgressReport: MonoTime
     when SocketType is AsyncSocket:
       bodyStream: FutureStream[string]
       parseBodyFut: Future[void]
@@ -706,13 +706,13 @@ proc reportProgress(client: HttpClient | AsyncHttpClient,
                     progress: BiggestInt) {.multisync.} =
   client.contentProgress += progress
   client.oneSecondProgress += progress
-  if epochTime() - client.lastProgressReport >= 1.0:
+  if (getMonoTime() - client.lastProgressReport).inSeconds > 1:
     if not client.onProgressChanged.isNil:
       await client.onProgressChanged(client.contentTotal,
                                      client.contentProgress,
                                      client.oneSecondProgress)
       client.oneSecondProgress = 0
-      client.lastProgressReport = epochTime()
+      client.lastProgressReport = getMonoTime()
 
 proc recvFull(client: HttpClient | AsyncHttpClient, size: int, timeout: int,
               keep: bool): Future[int] {.multisync.} =
@@ -784,7 +784,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient,
   client.contentTotal = 0
   client.contentProgress = 0
   client.oneSecondProgress = 0
-  client.lastProgressReport = 0
+  client.lastProgressReport = MonoTime()
 
   when client is AsyncHttpClient:
     assert(not client.bodyStream.finished)
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index fa2ddb6f2..1ef08f547 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -142,8 +142,8 @@ runnableExamples:
   doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}"""
 
 import
-  hashes, tables, strutils, lexbase, streams, unicode, macros, parsejson,
-  typetraits, options
+  hashes, tables, strutils, lexbase, streams, macros, parsejson,
+  options
 
 export
   tables.`$`
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 8ae40a799..6e2f5d049 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -65,7 +65,8 @@
 ##     echo("Client connected from: ", address)
 
 {.deadCodeElim: on.}  # dce option deprecated
-import nativesockets, os, strutils, parseutils, times, sets, options
+import nativesockets, os, strutils, parseutils, times, sets, options,
+  std/monotimes
 export Port, `$`, `==`
 export Domain, SockType, Protocol
 
@@ -1077,7 +1078,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ReadIOEffect]
       # Save the error in case it gets reset.
       socket.lastError = osLastError()
 
-proc waitFor(socket: Socket, waited: var float, timeout, size: int,
+proc waitFor(socket: Socket, waited: var Duration, timeout, size: int,
              funcName: string): int {.tags: [TimeEffect].} =
   ## determines the amount of characters that can be read. Result will never
   ## be larger than ``size``. For unbuffered sockets this will be ``1``.
@@ -1092,7 +1093,7 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
     result = socket.bufLen - socket.currPos
     result = min(result, size)
   else:
-    if timeout - int(waited * 1000.0) < 1:
+    if timeout - waited.inMilliseconds < 1:
       raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
 
     when defineSsl:
@@ -1104,17 +1105,17 @@ proc waitFor(socket: Socket, waited: var float, timeout, size: int,
         if sslPending != 0:
           return min(sslPending, size)
 
-    var startTime = epochTime()
-    let selRet = select(socket, timeout - int(waited * 1000.0))
+    var startTime = getMonoTime()
+    let selRet = select(socket, (timeout - waited.inMilliseconds).int)
     if selRet < 0: raiseOSError(osLastError())
     if selRet != 1:
       raise newException(TimeoutError, "Call to '" & funcName & "' timed out.")
-    waited += (epochTime() - startTime)
+    waited += (getMonoTIme() - startTime)
 
 proc recv*(socket: Socket, data: pointer, size: int, timeout: int): int {.
   tags: [ReadIOEffect, TimeEffect].} =
   ## overload with a ``timeout`` parameter in milliseconds.
-  var waited = 0.0 # number of seconds already waited
+  var waited: Duration # duration already waited
 
   var read = 0
   while read < size:
@@ -1223,7 +1224,7 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1,
     if flags.isDisconnectionError(lastError): setLen(line.string, 0); return
     socket.socketError(n, lastError = lastError)
 
-  var waited = 0.0
+  var waited: Duration
 
   setLen(line.string, 0)
   while true:
@@ -1307,7 +1308,7 @@ proc skip*(socket: Socket, size: int, timeout = -1) =
   ## bytes takes longer than specified a TimeoutError exception will be raised.
   ##
   ## Returns the number of skipped bytes.
-  var waited = 0.0
+  var waited: Duration
   var dummy = alloc(size)
   var bytesSkipped = 0
   while bytesSkipped != size:
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index ee40a24c7..9ee6a4d4f 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -1849,7 +1849,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1",
           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.string):
+    for x in walkFiles(result):
       result = x
     if not existsFile(result) and not existsDir(result):
       raise newException(OSError, "file '" & result & "' does not exist")
diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim
index eb65b6f57..0d386bf18 100644
--- a/lib/pure/terminal.nim
+++ b/lib/pure/terminal.nim
@@ -19,7 +19,7 @@
 import macros
 import strformat
 from strutils import toLowerAscii, `%`
-import colors, tables
+import colors
 
 when defined(windows):
   import winlean
diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim
index bbd378740..35c364221 100644
--- a/lib/pure/unicode.nim
+++ b/lib/pure/unicode.nim
@@ -1281,7 +1281,7 @@ when isMainModule:
     compared = (someString == $someRunes)
   doAssert compared == true
 
-  proc test_replacements(word: string): string =
+  proc testReplacements(word: string): string =
     case word
     of "two":
       return "2"
@@ -1294,8 +1294,8 @@ when isMainModule:
     else:
       return "12345"
 
-  doAssert translate("two not alpha foo βeta", test_replacements) == "2 12345 αlpha BAR beta"
-  doAssert translate("  two not foo βeta  ", test_replacements) == "  2 12345 BAR beta  "
+  doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta"
+  doAssert translate("  two not foo βeta  ", testReplacements) == "  2 12345 BAR beta  "
 
   doAssert title("foo bar") == "Foo Bar"
   doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma"
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index a6104428d..4cbd2f734 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -190,7 +190,7 @@ proc defaultConsoleFormatter*(): <//>ConsoleOutputFormatter =
     var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
     var colorOutput  = isatty(stdout)
     if existsEnv("NIMTEST_COLOR"):
-      let colorEnv = getenv("NIMTEST_COLOR")
+      let colorEnv = getEnv("NIMTEST_COLOR")
       if colorEnv == "never":
         colorOutput = false
       elif colorEnv == "always":
@@ -349,7 +349,7 @@ proc glob(matcher, filter: string): bool =
   let beforeAndAfter = filter.split('*', maxsplit=1)
   if beforeAndAfter.len == 1:
     # "foo*"
-    return matcher.startswith(beforeAndAfter[0])
+    return matcher.startsWith(beforeAndAfter[0])
 
   if matcher.len < filter.len - 1:
     return false  # "12345" should not match "123*345"
@@ -366,8 +366,8 @@ proc matchFilter(suiteName, testName, filter: string): bool =
 
   if suiteAndTestFilters.len == 1:
     # no suite specified
-    let test_f = suiteAndTestFilters[0]
-    return glob(testName, test_f)
+    let testFilter = suiteAndTestFilters[0]
+    return glob(testName, testFilter)
 
   return glob(suiteName, suiteAndTestFilters[0]) and glob(testName, suiteAndTestFilters[1])
 
@@ -627,7 +627,7 @@ macro check*(conditions: untyped): untyped =
             #   Ident !"v"
             #   IntLit 2
             result.check[i] = exp[i][1]
-          if exp[i].typekind notin {ntyTypeDesc}:
+          if exp[i].typeKind notin {ntyTypeDesc}:
             let arg = newIdentNode(":p" & $counter)
             result.assigns.add getAst(asgn(arg, paramAst))
             result.printOuts.add getAst(print(argStr, arg))
@@ -640,7 +640,7 @@ macro check*(conditions: untyped): untyped =
   of nnkCallKinds:
 
     let (assigns, check, printOuts) = inspectArgs(checked)
-    let lineinfo = newStrLitNode(checked.lineinfo)
+    let lineinfo = newStrLitNode(checked.lineInfo)
     let callLit = checked.toStrLit
     result = quote do:
       block:
@@ -657,7 +657,7 @@ macro check*(conditions: untyped): untyped =
         result.add(newCall(!"check", node))
 
   else:
-    let lineinfo = newStrLitNode(checked.lineinfo)
+    let lineinfo = newStrLitNode(checked.lineInfo)
     let callLit = checked.toStrLit
 
     result = quote do:
@@ -712,7 +712,7 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped =
   for i in countup(1, exp.len - 2):
     errorTypes.add(exp[i])
 
-  result = getAst(expectBody(errorTypes, exp.lineinfo, body))
+  result = getAst(expectBody(errorTypes, exp.lineInfo, body))
 
 proc disableParamFiltering* =
   ## disables filtering tests with the command line params
diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim
index e0b27c231..aa6784c82 100644
--- a/nimpretty/nimpretty.nim
+++ b/nimpretty/nimpretty.nim
@@ -25,7 +25,7 @@ const
 Usage:
   nimpretty [options] file.nim
 Options:
-  --output:file         set the output file (default: overwrite the input file)
+  --out:file            set the output file (default: overwrite the input file)
   --indent:N[=0]        set the number of spaces that is used for indentation
                         --indent:0 means autodetection (default behaviour)
   --maxLineLen:N        set the desired maximum line length (default: 80)
@@ -79,7 +79,7 @@ proc main =
       of "help", "h": writeHelp()
       of "version", "v": writeVersion()
       of "backup": backup = parseBool(val)
-      of "output", "o": outfile = val
+      of "output", "o", "out": outfile = val
       of "indent": opt.indWidth = parseInt(val)
       of "maxlinelen": opt.maxLineLen = parseInt(val)
       else: writeHelp()
diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim
index 7dde99ca5..91ab530e4 100644
--- a/nimpretty/tests/exhaustive.nim
+++ b/nimpretty/tests/exhaustive.nim
@@ -761,3 +761,64 @@ var rows2 = await pool.rows(sql"""
     "BBBB"
   ]
 )
+
+
+# bug #11699
+
+const keywords = @[
+  "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords1 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords2 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+if true:
+  let keywords3 = @[
+    "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5", "bar5", "foo6", "bar6", "foo7",
+    "zzz", "ggg", "ddd",
+  ]
+
+const b = true
+let fooB =
+  if true:
+    if b: 7 else: 8
+  else: ord(b)
+
+let foo = if cond:
+            if b: T else: F
+          else: b
+
+let a =
+  [[aaadsfas, bbb],
+   [ccc, ddd]]
+
+let b = [
+  [aaa, bbb],
+  [ccc, ddd]
+]
+
+# bug #11616
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(if typ.isUnion: "cUnion"
+                                               else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+proc f =
+  # doesn't break the code, but leaving indentation as is would be nice.
+  let x = if true: callingProcWhatever()
+          else: callingADifferentProc()
diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim
index 79f0ad05a..247ece887 100644
--- a/nimpretty/tests/expected/exhaustive.nim
+++ b/nimpretty/tests/expected/exhaustive.nim
@@ -769,3 +769,69 @@ var rows2 = await pool.rows(sql"""
     "BBBB"
   ]
 )
+
+
+# bug #11699
+
+const keywords = @[
+  "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo", "bar", "foo",
+  "bar", "foo", "bar",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords1 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+let keywords2 = @[
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+  "bar5", "foo6", "bar6", "foo7",
+  "zzz", "ggg", "ddd",
+]
+
+if true:
+  let keywords3 = @[
+    "foo1", "bar1", "foo2", "bar2", "foo3", "bar3", "foo4", "bar4", "foo5",
+    "bar5", "foo6", "bar6", "foo7",
+    "zzz", "ggg", "ddd",
+  ]
+
+const b = true
+let fooB =
+  if true:
+    if b: 7 else: 8
+  else: ord(b)
+
+let foo = if cond:
+            if b: T else: F
+          else: b
+
+let a =
+  [[aaadsfas, bbb],
+   [ccc, ddd]]
+
+let b = [
+  [aaa, bbb],
+  [ccc, ddd]
+]
+
+# bug #11616
+proc newRecordGen(ctx: Context; typ: TypRef): PNode =
+  result = nkTypeDef.t(
+    newId(typ.optSym.name, true, pragmas = [id(if typ.isUnion: "cUnion"
+                                               else: "cStruct")]),
+    empty(),
+    nkObjectTy.t(
+      empty(),
+      empty(),
+      nkRecList.t(
+        typ.recFields.map(newRecFieldGen))))
+
+proc f =
+  # doesn't break the code, but leaving indentation as is would be nice.
+  let x = if true: callingProcWhatever()
+          else: callingADifferentProc()
diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim
index a1ad96f2b..6bf52f3aa 100644
--- a/tests/concepts/t3330.nim
+++ b/tests/concepts/t3330.nim
@@ -1,49 +1,63 @@
 discard """
 errormsg: "type mismatch: got <Bar[system.int]>"
-disabled: "true"
+disabled: "32bit"
 nimout: '''
-t3330.nim(63, 4) Error: type mismatch: got <Bar[system.int]>
+t3330.nim(78, 4) Error: type mismatch: got <Bar[system.int]>
 but expected one of:
 proc test(foo: Foo[int])
-t3330.nim(48, 8) Hint: Non-matching candidates for add(k, string, T)
+  first type mismatch at position: 1
+  required type for foo: Foo[int]
+  but expression 'bar' is of type: Bar[system.int]
+t3330.nim(63, 8) Hint: Non-matching candidates for add(k, string, T)
 proc add[T](x: var seq[T]; y: openArray[T])
   first type mismatch at position: 1
-  required type: var seq[T]
+  required type for x: var seq[T]
   but expression 'k' is of type: Alias
 proc add(result: var string; x: float)
   first type mismatch at position: 1
-  required type: var string
+  required type for result: var string
   but expression 'k' is of type: Alias
 proc add(x: var string; y: string)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
 proc add(x: var string; y: cstring)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
 proc add[T](x: var seq[T]; y: T)
   first type mismatch at position: 1
-  required type: var seq[T]
+  required type for x: var seq[T]
   but expression 'k' is of type: Alias
 proc add(result: var string; x: int64)
   first type mismatch at position: 1
-  required type: var string
+  required type for result: var string
   but expression 'k' is of type: Alias
 proc add(x: var string; y: char)
   first type mismatch at position: 1
-  required type: var string
+  required type for x: var string
   but expression 'k' is of type: Alias
 
-t3330.nim(48, 8) template/generic instantiation of `add` from here
-t3330.nim(55, 6) Foo: 'bar.value' cannot be assigned to
-t3330.nim(48, 8) template/generic instantiation of `add` from here
-t3330.nim(56, 6) Foo: 'bar.x' cannot be assigned to
+t3330.nim(63, 8) template/generic instantiation of `add` from here
+t3330.nim(70, 6) Foo: 'bar.value' cannot be assigned to
+t3330.nim(63, 8) template/generic instantiation of `add` from here
+t3330.nim(71, 6) Foo: 'bar.x' cannot be assigned to
 
 expression: test(bar)'''
 """
 
+# Note: currently disabled on 32bit because the candidates are presented in
+# different order on travis with `NIM_COMPILE_TO_CPP=false CPU=i386`;
+# a possible fix would be to sort the candidates by proc signature or
+# declaration location
+
+
+
+
+
+
 
+## line 60
 type
   Foo[T] = concept k
     add(k, string, T)
diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim
index 4ec848732..f3d70320f 100644
--- a/tests/concepts/texplain.nim
+++ b/tests/concepts/texplain.nim
@@ -2,69 +2,101 @@ discard """
   cmd: "nim c --verbosity:0 --colors:off $file"
   nimout: '''
 Hint: texplain [Processing]
-texplain.nim(118, 10) Hint: Non-matching candidates for e(y)
+texplain.nim(158, 10) Hint: Non-matching candidates for e(y)
 proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'y' is of type: MatchingType
 
-texplain.nim(121, 7) Hint: Non-matching candidates for e(10)
+texplain.nim(161, 7) Hint: Non-matching candidates for e(10)
 proc e(o: ExplainedConcept): int
-texplain.nim(84, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(84, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(84, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(84, 5) ExplainedConcept: concept predicate failed
-texplain.nim(85, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(85, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(85, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(84, 5) ExplainedConcept: concept predicate failed
-
-texplain.nim(124, 10) Hint: Non-matching candidates for e(10)
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(124, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(124, 6) ExplainedConcept: undeclared field: '.'
+texplain.nim(124, 6) ExplainedConcept: expression '.' cannot be called
+texplain.nim(124, 5) ExplainedConcept: concept predicate failed
+texplain.nim(125, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(125, 6) ExplainedConcept: undeclared field: '.'
+texplain.nim(125, 6) ExplainedConcept: expression '.' cannot be called
+texplain.nim(124, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(164, 10) Hint: Non-matching candidates for e(10)
 proc e(o: ExplainedConcept): int
-texplain.nim(84, 6) ExplainedConcept: undeclared field: 'foo'
-texplain.nim(84, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(84, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(84, 5) ExplainedConcept: concept predicate failed
-texplain.nim(85, 6) ExplainedConcept: undeclared field: 'bar'
-texplain.nim(85, 6) ExplainedConcept: undeclared field: '.'
-texplain.nim(85, 6) ExplainedConcept: expression '.' cannot be called
-texplain.nim(84, 5) ExplainedConcept: concept predicate failed
-
-texplain.nim(128, 20) Error: type mismatch: got <NonMatchingType>
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression '10' is of type: int literal(10)
+texplain.nim(124, 6) ExplainedConcept: undeclared field: 'foo'
+texplain.nim(124, 6) ExplainedConcept: undeclared field: '.'
+texplain.nim(124, 6) ExplainedConcept: expression '.' cannot be called
+texplain.nim(124, 5) ExplainedConcept: concept predicate failed
+texplain.nim(125, 6) ExplainedConcept: undeclared field: 'bar'
+texplain.nim(125, 6) ExplainedConcept: undeclared field: '.'
+texplain.nim(125, 6) ExplainedConcept: expression '.' cannot be called
+texplain.nim(124, 5) ExplainedConcept: concept predicate failed
+
+texplain.nim(168, 20) Error: type mismatch: got <NonMatchingType>
 but expected one of:
 proc e(o: ExplainedConcept): int
-texplain.nim(128, 9) template/generic instantiation of `assert` from here
-texplain.nim(84, 5) ExplainedConcept: concept predicate failed
+  first type mismatch at position: 1
+  required type for o: ExplainedConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(168, 9) template/generic instantiation of `assert` from here
+texplain.nim(124, 5) ExplainedConcept: concept predicate failed
 proc e(i: int): int
+  first type mismatch at position: 1
+  required type for i: int
+  but expression 'n' is of type: NonMatchingType
 
 expression: e(n)
-texplain.nim(129, 20) Error: type mismatch: got <NonMatchingType>
+texplain.nim(169, 20) Error: type mismatch: got <NonMatchingType>
 but expected one of:
 proc r(o: RegularConcept): int
-texplain.nim(129, 9) template/generic instantiation of `assert` from here
-texplain.nim(88, 5) RegularConcept: concept predicate failed
+  first type mismatch at position: 1
+  required type for o: RegularConcept
+  but expression 'n' is of type: NonMatchingType
+texplain.nim(169, 9) template/generic instantiation of `assert` from here
+texplain.nim(128, 5) RegularConcept: concept predicate failed
 proc r[T](a: SomeNumber; b: T; c: auto)
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'n' is of type: NonMatchingType
 proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'n' is of type: NonMatchingType
 
 expression: r(n)
-texplain.nim(130, 20) Hint: Non-matching candidates for r(y)
+texplain.nim(170, 20) Hint: Non-matching candidates for r(y)
 proc r[T](a: SomeNumber; b: T; c: auto)
+  first type mismatch at position: 1
+  required type for a: SomeNumber
+  but expression 'y' is of type: MatchingType
 proc r(i: string): int
+  first type mismatch at position: 1
+  required type for i: string
+  but expression 'y' is of type: MatchingType
 
-texplain.nim(138, 2) Error: type mismatch: got <MatchingType>
+texplain.nim(178, 2) Error: type mismatch: got <MatchingType>
 but expected one of:
 proc f(o: NestedConcept)
-texplain.nim(88, 6) RegularConcept: undeclared field: 'foo'
-texplain.nim(88, 6) RegularConcept: undeclared field: '.'
-texplain.nim(88, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(88, 5) RegularConcept: concept predicate failed
-texplain.nim(89, 6) RegularConcept: undeclared field: 'bar'
-texplain.nim(89, 6) RegularConcept: undeclared field: '.'
-texplain.nim(89, 6) RegularConcept: expression '.' cannot be called
-texplain.nim(88, 5) RegularConcept: concept predicate failed
-texplain.nim(92, 5) NestedConcept: concept predicate failed
-
-expression: f(y)
-'''
+  first type mismatch at position: 1
+  required type for o: NestedConcept
+  but expression 'y' is of type: MatchingType
+texplain.nim(128, 6) RegularConcept: undeclared field: 'foo'
+texplain.nim(128, 6) RegularConcept: undeclared field: '.'
+texplain.nim(128, 6) RegularConcept: expression '.' cannot be called
+texplain.nim(128, 5) RegularConcept: concept predicate failed
+texplain.nim(129, 6) RegularConcept: undeclared field: 'bar'
+texplain.nim(129, 6) RegularConcept: undeclared field: '.'
+texplain.nim(129, 6) RegularConcept: expression '.' cannot be called
+texplain.nim(128, 5) RegularConcept: concept predicate failed
+texplain.nim(132, 5) NestedConcept: concept predicate failed
+
+expression: f(y)'''
   errormsg: "type mismatch: got <MatchingType>"
-  line: 138
+  line: 178
 
   disabled: 32bit
 """
@@ -77,7 +109,15 @@ expression: f(y)
 
 
 
-# line 80 HERE
+
+
+
+
+
+
+
+
+# line 120 HERE
 
 type
   ExplainedConcept {.explain.} = concept o
diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim
index 5794dec1d..a726960c6 100644
--- a/tests/destructor/tdont_return_unowned_from_owned.nim
+++ b/tests/destructor/tdont_return_unowned_from_owned.nim
@@ -1,21 +1,33 @@
 discard """
   cmd: "nim check --newruntime --hints:off $file"
-  nimout: '''tdont_return_unowned_from_owned.nim(24, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(27, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type
-tdont_return_unowned_from_owned.nim(30, 6) Error: type mismatch: got <Obj>
+  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>
 but expected one of:
 proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.})
+  first type mismatch at position: 2
+  missing parameter: finalizer
 2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them
 
 expression: new(result)
-tdont_return_unowned_from_owned.nim(30, 6) Error: illformed AST:
-tdont_return_unowned_from_owned.nim(38, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(39, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref
-tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type
+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
 '''
   errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type"
-  line: 43
+  line: 55
 """
+
+
+
+
+
+
+
+
+
+## line 30
 # bug #11073
 type
   Obj = ref object
diff --git a/tests/errmsgs/t8434.nim b/tests/errmsgs/t8434.nim
index b37468111..ada38e9c0 100644
--- a/tests/errmsgs/t8434.nim
+++ b/tests/errmsgs/t8434.nim
@@ -4,7 +4,7 @@ discard """
 proc fun0[T1: int | float |
     object | array | seq](a1: T1; a2: int)
   first type mismatch at position: 1
-  required type: T1: int or float or object or array or seq[T]
+  required type for a1: T1: int or float or object or array or seq[T]
   but expression 'byte(1)' is of type: byte
 
 expression: fun0(byte(1), 0)
diff --git a/tests/errmsgs/tdetailed_position.nim b/tests/errmsgs/tdetailed_position.nim
index ce5b18bbd..ecece7972 100644
--- a/tests/errmsgs/tdetailed_position.nim
+++ b/tests/errmsgs/tdetailed_position.nim
@@ -6,7 +6,7 @@ nimout: '''
 but expected one of:
 proc main(a, b, c: string)
   first type mismatch at position: 1
-  required type: string
+  required type for a: string
   but expression '1' is of type: int literal(1)
 
 expression: main(1, 2, 3)
diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim
index 0ae60f200..e6a62204e 100644
--- a/tests/errmsgs/tgcsafety.nim
+++ b/tests/errmsgs/tgcsafety.nim
@@ -8,7 +8,7 @@ proc serve(server: AsyncHttpServer; port: Port;
           callback: proc (request: Request): Future[void] {.closure, gcsafe.};
           address = ""): owned(Future[void])
   first type mismatch at position: 3
-  required type: proc (request: Request): Future[system.void]{.closure, gcsafe.}
+  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>.}
   This expression is not GC-safe. Annotate the proc with {.gcsafe.} to get extended error information.
 
diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim
new file mode 100644
index 000000000..42a98a891
--- /dev/null
+++ b/tests/errmsgs/tsigmatch.nim
@@ -0,0 +1,172 @@
+discard """
+  cmd: "nim check --showAllMismatches:on --hints:off $file"
+  nimout: '''
+tsigmatch.nim(111, 4) Error: type mismatch: got <A, string>
+but expected one of:
+proc f(b: B)
+  first type mismatch at position: 1
+  required type for b: B
+  but expression 'A()' is of type: A
+proc f(a: A)
+  first type mismatch at position: 2
+  extra argument given
+
+expression: f(A(), "extra")
+tsigmatch.nim(125, 6) Error: type mismatch: got <tuple of (string, proc (){.gcsafe, locks: 0.})>
+but expected one of:
+proc foo(x: (string, proc ()))
+  first type mismatch at position: 1
+  required type for x: tuple of (string, proc (){.closure.})
+  but expression '("foobar", proc () = echo(["Hello!"]))' is of type: tuple of (string, proc (){.gcsafe, locks: 0.})
+
+expression: foo(("foobar", proc () = echo(["Hello!"])))
+tsigmatch.nim(132, 11) Error: type mismatch: got <proc (s: string): string{.noSideEffect, gcsafe, locks: 0.}>
+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.}
+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.}
+
+expression: foo(fun)
+tsigmatch.nim(143, 13) Error: type mismatch: got <array[0..0, proc (x: int){.gcsafe, locks: 0.}]>
+but expected one of:
+proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe, locks: 0.}])
+  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.}]
+
+expression: takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = 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)
+  first type mismatch at position: 2
+  named param already provided: a0
+
+expression: f(10, a0 = 5, "")
+tsigmatch.nim(156, 4) Error: type mismatch: got <string, string, string, string, string, float64, string>
+but expected one of:
+proc f(a1: int)
+  first type mismatch at position: 1
+  required type for a1: int
+  but expression '"asdf"' is of type: string
+proc f(a1: string; a2: varargs[string]; a3: float; a4: var string)
+  first type mismatch at position: 7
+  required type for a4: var string
+  but expression '"bad"' is immutable, not 'var'
+
+expression: f("asdf", "1", "2", "3", "4", 2.3, "bad")
+tsigmatch.nim(164, 4) Error: type mismatch: got <string, a0: int literal(12)>
+but expected one of:
+proc f(x: string; a0: var int)
+  first type mismatch at position: 2
+  required type for a0: var int
+  but expression 'a0 = 12' is immutable, not 'var'
+proc f(x: string; a0: string)
+  first type mismatch at position: 2
+  required type for a0: string
+  but expression 'a0 = 12' is of type: int literal(12)
+
+expression: f(foo, a0 = 12)
+tsigmatch.nim(171, 7) Error: type mismatch: got <Mystring, string>
+but expected one of:
+proc fun1(a1: MyInt; a2: Mystring)
+  first type mismatch at position: 1
+  required type for a1: MyInt
+  but expression 'default(Mystring)' is of type: Mystring
+proc fun1(a1: float; a2: Mystring)
+  first type mismatch at position: 1
+  required type for a1: float
+  but expression 'default(Mystring)' is of type: Mystring
+
+expression: fun1(default(Mystring), "asdf")
+'''
+  errormsg: "type mismatch"
+"""
+
+
+
+
+
+
+
+
+
+
+
+## line 100
+block:
+  # bug #11061 Type mismatch error "first type mismatch at" points to wrong argument/position
+  # Note: the error msg now gives correct position for mismatched argument
+  type
+    A = object of RootObj
+    B = object of A
+
+  proc f(b: B) = discard
+  proc f(a: A) = discard
+
+  f(A(), "extra")
+#[
+this one is similar but error msg was even more misleading, since the user
+would think float != float64 where in fact the issue is another param:
+first type mismatch at position: 1; required type: float; but expression 'x = 1.2' is of type: float64
+  proc f(x: string, a0 = 0, a1 = 0, a2 = 0) = discard
+  proc f(x: float, a0 = 0, a1 = 0, a2 = 0) = discard
+  f(x = float(1.2), a0 = 0, a0 = 0)
+]#
+
+block:
+  # bug #7808 Passing tuple with proc leads to confusing errors
+  # Note: the error message now shows `closure` which helps debugging the issue
+  proc foo(x: (string, proc ())) = x[1]()
+  foo(("foobar", proc () = echo("Hello!")))
+
+block:
+  # bug #8305 type mismatch error drops crucial pragma info when there's only 1 argument
+  proc fun(s: string): string {.  .} = discard
+  proc foo[T, S](op: proc (x: T): S {. cdecl .}): auto = 1
+  proc foo[T, S](op: proc (x: T): S {. safecall .}): auto = 1
+  echo foo(fun)
+
+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.}) =
+    echo "takes single Func"
+  proc takesFuncs(fs: openarray[proc (x: int) {.gcsafe, locks: 0.}]) =
+    echo "takes multiple Func"
+  takesFunc(proc (x: int) {.gcsafe, locks: 0.} = echo x)         # works
+  takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo x])      # fails
+
+block:
+  # bug https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970465
+  # better fix for removal of `errCannotBindXTwice` due to #3836
+  proc f(a0: uint8, b: string) = discard
+  f(10, a0 = 5, "")
+
+block:
+  # bug: https://github.com/nim-lang/Nim/issues/11061#issuecomment-508969796
+  # sigmatch gets confused with param/arg position after varargs
+  proc f(a1: int) = discard
+  proc f(a1: string, a2: varargs[string], a3: float, a4: var string) = discard
+  f("asdf", "1", "2", "3", "4", 2.3, "bad")
+
+block:
+  # bug: https://github.com/nim-lang/Nim/issues/11061#issuecomment-508970046
+  # err msg incorrectly said something is immutable
+  proc f(x: string, a0: var int) = discard
+  proc f(x: string, a0: string) = discard
+  var foo = ""
+  f(foo, a0 = 12)
+
+block:
+  type Mystring = string
+  type MyInt = int
+  proc fun1(a1: MyInt, a2: Mystring) = discard
+  proc fun1(a1: float, a2: Mystring) = discard
+  fun1(Mystring.default, "asdf")
+
diff --git a/tests/errmsgs/tunknown_named_parameter.nim b/tests/errmsgs/tunknown_named_parameter.nim
index 3051787ea..e9be23068 100644
--- a/tests/errmsgs/tunknown_named_parameter.nim
+++ b/tests/errmsgs/tunknown_named_parameter.nim
@@ -4,14 +4,14 @@ errormsg: "type mismatch: got <string, set[char], maxsplits: int literal(1)>"
 nimout: '''
 proc rsplit(s: string; sep: char; maxsplit: int = -1): seq[string]
   first type mismatch at position: 2
-  required type: char
+  required type for sep: char
   but expression '{':'}' is of type: set[char]
 proc rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[string]
   first type mismatch at position: 3
   unknown named parameter: maxsplits
 proc rsplit(s: string; sep: string; maxsplit: int = -1): seq[string]
   first type mismatch at position: 2
-  required type: string
+  required type for sep: string
   but expression '{':'}' is of type: set[char]
 
 expression: rsplit("abc:def", {':'}, maxsplits = 1)
diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim
index 5413515cb..7ce077003 100644
--- a/tests/errmsgs/twrong_at_operator.nim
+++ b/tests/errmsgs/twrong_at_operator.nim
@@ -1,11 +1,17 @@
 discard """
 errormsg: "type mismatch: got <array[0..0, type int]>"
-line: 16
+line: 22
 nimout: '''
-twrong_at_operator.nim(16, 30) Error: type mismatch: got <array[0..0, type int]>
+twrong_at_operator.nim(22, 30) Error: type mismatch: got <array[0..0, type int]>
 but expected one of:
 proc `@`[T](a: openArray[T]): seq[T]
+  first type mismatch at position: 1
+  required type for a: openarray[T]
+  but expression '[int]' is of type: array[0..0, type int]
 proc `@`[IDX, T](a: array[IDX, T]): seq[T]
+  first type mismatch at position: 1
+  required type for a: array[IDX, T]
+  but expression '[int]' is of type: array[0..0, type int]
 
 expression: @[int]
 '''
diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim
index bd973eed1..a90690591 100644
--- a/tests/metatype/ttypedesc3.nim
+++ b/tests/metatype/ttypedesc3.nim
@@ -5,6 +5,7 @@ proc Child
 method Base
 yield Base
 yield Child
+12
 '''
 """
 
@@ -27,3 +28,16 @@ when false:
 
 for s in Base.it: echo s
 for s in Child.it: echo s #<- bug #2662
+
+
+# bug #11747
+
+type
+  MyType = object
+    a: int32
+    b: int32
+    c: int32
+
+  MyRefType = ref MyType
+
+echo sizeof(MyRefType[])
diff --git a/tests/typerel/t7600_1.nim b/tests/typerel/t7600_1.nim
index e3a5fefa2..e9d01bd0d 100644
--- a/tests/typerel/t7600_1.nim
+++ b/tests/typerel/t7600_1.nim
@@ -1,8 +1,11 @@
 discard """
 errormsg: "type mismatch: got <Thin[system.int]>"
-nimout: '''t7600_1.nim(18, 6) Error: type mismatch: got <Thin[system.int]>
+nimout: '''t7600_1.nim(21, 6) Error: type mismatch: got <Thin[system.int]>
 but expected one of:
 proc test[T](x: Paper[T])
+  first type mismatch at position: 1
+  required type for x: Paper[test.T]
+  but expression 'tn' is of type: Thin[system.int]
 
 expression: test tn'''
 """
diff --git a/tests/typerel/t7600_2.nim b/tests/typerel/t7600_2.nim
index 7badb69cf..371707f4c 100644
--- a/tests/typerel/t7600_2.nim
+++ b/tests/typerel/t7600_2.nim
@@ -1,8 +1,11 @@
 discard """
 errormsg: "type mismatch: got <Thin>"
-nimout: '''t7600_2.nim(17, 6) Error: type mismatch: got <Thin>
+nimout: '''t7600_2.nim(20, 6) Error: type mismatch: got <Thin>
 but expected one of:
 proc test(x: Paper)
+  first type mismatch at position: 1
+  required type for x: Paper
+  but expression 'tn' is of type: Thin
 
 expression: test tn'''
 """
diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim
index 43cc04f30..398191658 100644
--- a/tests/varres/tprevent_forloopvar_mutations.nim
+++ b/tests/varres/tprevent_forloopvar_mutations.nim
@@ -1,10 +1,12 @@
 discard """
   errmsg: "type mismatch: got <int>"
-  line: 15
+  line: 17
   nimout: '''type mismatch: got <int>
 but expected one of:
 proc inc[T: Ordinal | uint | uint64](x: var T; y = 1)
-  for a 'var' type a variable needs to be passed, but 'i' is immutable
+  first type mismatch at position: 1
+  required type for x: var T: Ordinal or uint or uint64
+  but expression 'i' is immutable, not 'var'
 
 expression: inc i
 '''
[labl]) p.nested: gen(p, n.sons[0], cond) lineF(p, "if (!$1) break L$2;$n", [cond.res, labl]) p.nested: genStmt(p, n.sons[1]) lineF(p, "}$n", [labl]) setLen(p.blocks, length) proc moveInto(p: PProc, src: var TCompRes, dest: TCompRes) = if src.kind != resNone: if dest.kind != resNone: lineF(p, "$1 = $2;$n", [dest.rdLoc, src.rdLoc]) else: lineF(p, "$1;$n", [src.rdLoc]) src.kind = resNone src.res = nil proc genTry(p: PProc, n: PNode, r: var TCompRes) = # code to generate: # # ++excHandler; # var tmpFramePtr = framePtr; # try { # stmts; # --excHandler; # } catch (EXC) { # var prevJSError = lastJSError; lastJSError = EXC; # framePtr = tmpFramePtr; # --excHandler; # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { # stmts; # } else if (e.typ && e.typ == NTI32342) { # stmts; # } else { # stmts; # } # lastJSError = prevJSError; # } finally { # framePtr = tmpFramePtr; # stmts; # } genLineDir(p, n) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) inc(p.unique) var i = 1 var length = sonsLen(n) var catchBranchesExist = length > 1 and n.sons[i].kind == nkExceptBranch if catchBranchesExist: add(p.body, "++excHandler;\L") var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) line(p, tmpFramePtr & " = framePtr;\L") lineF(p, "try {$n", []) var a: TCompRes gen(p, n.sons[0], a) moveInto(p, a, r) var generalCatchBranchExists = false if catchBranchesExist: addf(p.body, "--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXC;$n --excHandler;$n", []) line(p, "framePtr = $1;$n" % [tmpFramePtr]) while i < length and n.sons[i].kind == nkExceptBranch: let blen = sonsLen(n.sons[i]) if blen == 1: # general except section: generalCatchBranchExists = true if i > 1: lineF(p, "else {$n", []) gen(p, n.sons[i].sons[0], a) moveInto(p, a, r) if i > 1: lineF(p, "}$n", []) else: var orExpr: Rope = nil var excAlias: PNode = nil useMagic(p, "isObj") for j in 0 .. blen - 2: var throwObj: PNode let it = n.sons[i].sons[j] if it.isInfixAs(): throwObj = it[1] excAlias = it[2] # If this is a ``except exc as sym`` branch there must be no following # nodes doAssert orExpr == nil elif it.kind == nkType: throwObj = it else: internalError(p.config, n.info, "genTryStmt") if orExpr != nil: add(orExpr, "||") # Generate the correct type checking code depending on whether this is a # NIM-native or a JS-native exception # if isJsObject(throwObj.typ): if isImportedException(throwObj.typ, p.config): addf(orExpr, "lastJSError instanceof $1", [throwObj.typ.sym.loc.r]) else: addf(orExpr, "isObj(lastJSError.m_type, $1)", [genTypeInfo(p, throwObj.typ)]) if i > 1: line(p, "else ") lineF(p, "if (lastJSError && ($1)) {$n", [orExpr]) # 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) gen(p, n.sons[i].sons[blen - 1], a) moveInto(p, a, r) lineF(p, "}$n", []) inc(i) if catchBranchesExist: if not generalCatchBranchExists: useMagic(p, "reraiseException") line(p, "else {\L") line(p, "\treraiseException();\L") line(p, "}\L") lineF(p, "lastJSError = prevJSError;$n") line(p, "} finally {\L") line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < length and n.sons[i].kind == nkFinally: genStmt(p, n.sons[i].sons[0]) line(p, "}\L") proc genRaiseStmt(p: PProc, n: PNode) = if n.sons[0].kind != nkEmpty: var a: TCompRes gen(p, n.sons[0], a) let typ = skipTypes(n.sons[0].typ, abstractPtrs) genLineDir(p, n) useMagic(p, "raiseException") lineF(p, "raiseException($1, $2);$n", [a.rdLoc, makeJSString(typ.sym.name.s)]) else: genLineDir(p, n) useMagic(p, "reraiseException") line(p, "reraiseException();\L") proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes genLineDir(p, n) gen(p, n.sons[0], cond) let stringSwitch = skipTypes(n.sons[0].typ, abstractVar).kind == tyString if stringSwitch: useMagic(p, "toJSStr") lineF(p, "switch (toJSStr($1)) {$n", [cond.rdLoc]) else: lineF(p, "switch ($1) {$n", [cond.rdLoc]) if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in 1 ..< sonsLen(n): let it = n.sons[i] case it.kind of nkOfBranch: for j in 0 .. sonsLen(it) - 2: let e = it.sons[j] if e.kind == nkRange: var v = copyNode(e.sons[0]) while v.intVal <= e.sons[1].intVal: gen(p, v, cond) lineF(p, "case $1:$n", [cond.rdLoc]) inc(v.intVal) else: if stringSwitch: case e.kind of nkStrLit..nkTripleStrLit: lineF(p, "case $1:$n", [makeJSString(e.strVal, false)]) else: internalError(p.config, e.info, "jsgen.genCaseStmt: 2") else: gen(p, e, cond) lineF(p, "case $1:$n", [cond.rdLoc]) p.nested: gen(p, lastSon(it), stmt) moveInto(p, stmt, r) lineF(p, "break;$n", []) of nkElse: lineF(p, "default: $n", []) p.nested: gen(p, it.sons[0], stmt) moveInto(p, stmt, r) lineF(p, "break;$n", []) else: internalError(p.config, it.info, "jsgen.genCaseStmt") lineF(p, "}$n", []) proc genBlock(p: PProc, n: PNode, r: var TCompRes) = inc(p.unique) let idx = len(p.blocks) if n.sons[0].kind != nkEmpty: # named block? if (n.sons[0].kind != nkSym): internalError(p.config, n.info, "genBlock") var sym = n.sons[0].sym sym.loc.k = locOther sym.position = idx+1 let labl = p.unique lineF(p, "L$1: do {$n", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet gen(p, n.sons[1], r) setLen(p.blocks, idx) lineF(p, "} while(false);$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int genLineDir(p, n) if n.sons[0].kind != nkEmpty: # named break? assert(n.sons[0].kind == nkSym) let sym = n.sons[0].sym assert(sym.loc.k == locOther) idx = sym.position-1 else: # an unnamed 'break' can only break a loop after 'transf' pass: idx = len(p.blocks) - 1 while idx >= 0 and not p.blocks[idx].isLoop: dec idx if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) p.body.add p.indentLine(nil) for i in 0 ..< sonsLen(n): let it = n[i] case it.kind of nkStrLit..nkTripleStrLit: p.body.add(it.strVal) of nkSym: let v = it.sym # for backwards compatibility we don't deref syms here :-( if false: discard else: var r: TCompRes gen(p, it, r) if it.typ.kind == tyPointer: # A fat pointer is disguised as an array r.res = r.address r.address = nil r.typ = etyNone elif r.typ == etyBaseIndex: # Deference first r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone p.body.add(r.rdLoc) else: var r: 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 toClose = 0 if not isEmptyType(n.typ): r.kind = resVal r.res = getTemp(p) for i in 0 ..< sonsLen(n): let it = n.sons[i] if sonsLen(it) != 1: if i > 0: lineF(p, "else {$n", []) inc(toClose) p.nested: gen(p, it.sons[0], cond) lineF(p, "if ($1) {$n", [cond.rdLoc]) gen(p, it.sons[1], stmt) else: # else part: lineF(p, "else {$n", []) p.nested: gen(p, it.sons[0], stmt) moveInto(p, stmt, r) lineF(p, "}$n", []) line(p, repeat('}', toClose) & "\L") proc generateHeader(p: PProc, typ: PType): Rope = result = nil for i in 1 ..< sonsLen(typ.n): assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if result != nil: add(result, ", ") var name = mangleName(p.module, param) add(result, name) if mapType(param.typ) == etyBaseIndex: add(result, ", ") add(result, name) add(result, "_Idx") proc countJsParams(typ: PType): int = for i in 1 ..< sonsLen(typ.n): assert(typ.n.sons[i].kind == nkSym) var param = typ.n.sons[i].sym if isCompileTimeOnly(param.typ): continue if mapType(param.typ) == etyBaseIndex: inc result, 2 else: inc result const nodeKindsNeedNoCopy = {nkCharLit..nkInt64Lit, nkStrLit..nkTripleStrLit, nkFloatLit..nkFloat64Lit, nkCurly, nkPar, nkStringToCString, nkObjConstr, nkTupleConstr, nkBracket, nkCStringToString, nkCall, nkPrefix, nkPostfix, nkInfix, nkCommand, nkHiddenCallConv, nkCallStrLit} 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 (skipTypes(y.typ, abstractInst).kind in {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc, tyOwned} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes var xtyp = mapType(p, x.typ) gen(p, x, a) genLineDir(p, y) gen(p, y, b) # we don't care if it's an etyBaseIndex (global) of a string, it's # still a string that needs to be copied properly: if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}: xtyp = etySeq case xtyp of etySeq: if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") lineF(p, "nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if y.kind == nkCall: let tmp = p.getTemp(false) lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) elif b.typ == etyBaseIndex: lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) else: internalError(p.config, x.info, "genAsgn") else: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) proc genAsgn(p: PProc, n: PNode) = genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=false) proc genFastAsgn(p: PProc, n: PNode) = # 'shallowCopy' always produced 'noCopyNeeded = true' here but this is wrong # for code like # while j >= pos: # dest[i].shallowCopy(dest[j]) # See bug #5933. So we try to be more compatible with the C backend semantics # here for 'shallowCopy'. This is an educated guess and might require further # changes later: let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString} genAsgnAux(p, n.sons[0], n.sons[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = var a, b: TCompRes gen(p, n.sons[1], a) gen(p, n.sons[2], b) var tmp = p.getTemp(false) if mapType(p, skipTypes(n.sons[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]) 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") proc genFieldAddr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.typ = etyBaseIndex let b = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, b.sons[0], a) if skipTypes(b.sons[0].typ, abstractVarRange).kind == tyTuple: r.res = makeJSString("Field" & $getFieldPosition(p, b.sons[1])) else: if b.sons[1].kind != nkSym: internalError(p.config, b.sons[1].info, "genFieldAddr") var f = b.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = makeJSString($f.loc.r) internalAssert p.config, a.typ != etyBaseIndex r.address = a.res r.kind = resExpr proc genFieldAccess(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) r.typ = mapType(n.typ) let otyp = skipTypes(n.sons[0].typ, abstractVarRange) template mkTemp(i: int) = if r.typ == etyBaseIndex: if needsTemp(p, n[i]): let tmp = p.getTemp r.address = "($1 = $2, $1)[0]" % [tmp, r.res] r.res = "$1[1]" % [tmp] r.tmpLoc = tmp else: r.address = "$1[0]" % [r.res] r.res = "$1[1]" % [r.res] if otyp.kind == tyTuple: r.res = ("$1.Field$2") % [r.res, getFieldPosition(p, n.sons[1]).rope] mkTemp(0) else: if n.sons[1].kind != nkSym: internalError(p.config, n.sons[1].info, "genFieldAccess") var f = n.sons[1].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) r.res = "$1.$2" % [r.res, f.loc.r] mkTemp(1) r.kind = resExpr proc genAddr(p: PProc, n: PNode, r: var TCompRes) proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = internalAssert p.config, n.kind == nkCheckedFieldExpr # nkDotExpr to access the requested field let accessExpr = n[0] # nkCall to check if the discriminant is valid var checkExpr = n[1] let negCheck = checkExpr[0].sym.magic == mNot if negCheck: checkExpr = checkExpr[^1] # Field symbol var field = accessExpr[1].sym internalAssert p.config, field.kind == skField if field.loc.r == nil: field.loc.r = mangleName(p.module, field) # Discriminant symbol let disc = checkExpr[2].sym internalAssert p.config, disc.kind == skField if disc.loc.r == nil: disc.loc.r = mangleName(p.module, disc) var setx: TCompRes gen(p, checkExpr[1], setx) var obj: TCompRes gen(p, accessExpr[0], obj) # Avoid evaluating the LHS twice (one to read the discriminant and one to read # the field) let tmp = p.getTemp() lineF(p, "var $1 = $2;$n", tmp, obj.res) useMagic(p, "raiseFieldError") useMagic(p, "makeNimstrLit") lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError(makeNimstrLit($5)); }$n", setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===", makeJSString(field.name.s)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex r.res = makeJSString($field.loc.r) r.address = tmp else: r.typ = etyNone r.res = "$1.$2" % [tmp, field.loc.r] r.kind = resExpr proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = var a, b: TCompRes first: BiggestInt r.typ = etyBaseIndex let m = if n.kind == nkHiddenAddr: n.sons[0] else: n gen(p, m.sons[0], a) gen(p, m.sons[1], b) #internalAssert p.config, a.typ != etyBaseIndex and b.typ != etyBaseIndex let (x, tmp) = maybeMakeTemp(p, m[0], a) r.address = x var typ = skipTypes(m.sons[0].typ, abstractPtrs) if typ.kind == tyArray: first = firstOrd(p.config, typ.sons[0]) else: first = 0 if optBoundsCheck in p.options: useMagic(p, "chckIndx") r.res = "chckIndx($1, $2, $3.length+$2-1)-$2" % [b.res, rope(first), tmp] elif first != 0: r.res = "($1)-$2" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n, r) of tyTuple: genFieldAddr(p, n, r) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = mapType(n.typ) if r.res == nil: internalError(p.config, n.info, "genArrayAccess") if ty.kind == tyCString: r.res = "$1.charCodeAt($2)" % [r.address, r.res] elif r.typ == etyBaseIndex: if needsTemp(p, n[0]): let tmp = p.getTemp r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] r.res = "$1[1]" % [tmp] r.tmpLoc = tmp else: let x = r.rdLoc r.address = "$1[0]" % [x] r.res = "$1[1]" % [x] else: r.res = "$1[$2]" % [r.address, r.res] r.kind = resExpr template isIndirect(x: PSym): bool = let v = x ({sfAddrTaken, sfGlobal} * v.flags != {} and #(mapType(v.typ) != etyObject) and {sfImportc, sfExportc} * v.flags == {} and v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) proc genAddr(p: PProc, n: PNode, r: var TCompRes) = case n.sons[0].kind of nkSym: let s = n.sons[0].sym if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") case s.kind of skVar, skLet, skResult: r.kind = resExpr let jsType = mapType(p, n.typ) if jsType == etyObject: # make addr() a no-op: r.typ = etyNone if isIndirect(s): r.res = s.loc.r & "[0]" else: r.res = s.loc.r r.address = nil 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.res = rope("0") else: # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n.sons[0], r) #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) else: internalError(p.config, n.info, "genAddr: 2") of nkCheckedFieldExpr: genCheckedFieldOp(p, n[0], n.typ, r) of nkDotExpr: if mapType(p, n.typ) == etyBaseIndex: genFieldAddr(p, n.sons[0], r) else: genFieldAccess(p, n.sons[0], r) of nkBracketExpr: var ty = skipTypes(n.sons[0].typ, abstractVarRange) if ty.kind in MappedToObject: gen(p, n.sons[0], r) else: let kindOfIndexedExpr = skipTypes(n.sons[0].sons[0].typ, abstractVarRange).kind case kindOfIndexedExpr of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: genArrayAddr(p, n.sons[0], r) of tyTuple: genFieldAddr(p, n.sons[0], r) else: internalError(p.config, n.sons[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') of nkObjDownConv: gen(p, n.sons[0], r) of nkHiddenDeref: gen(p, n.sons[0].sons[0], r) else: internalError(p.config, n.sons[0].info, "genAddr: " & $n.sons[0].kind) proc attachProc(p: PProc; content: Rope; s: PSym) = add(p.g.code, content) proc attachProc(p: PProc; s: PSym) = let newp = genProc(p, s) attachProc(p, newp, s) 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: add(owner.locals, newp) else: attachProc(p, newp, s) proc genCopyForParamIfNeeded(p: PProc, n: PNode) = let s = n.sym if p.prc == s.owner or needsNoCopy(p, n): return var owner = p.up while true: if owner == nil: 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)] add(owner.locals, owner.indentLine(copy)) return owner = owner.up 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 == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) if 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] else: r.address = "$1[0]" % [s.loc.r] r.res = "$1[1]" % [s.loc.r] else: r.address = s.loc.r r.res = s.loc.r & "_Idx" elif isIndirect(s): r.res = "$1[0]" % [s.loc.r] else: r.res = s.loc.r of skConst: genConstant(p, s) if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r of skProc, skFunc, skConverter, skMethod: 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 if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard elif s.kind == skMethod and s.getBody.kind == nkEmpty: # we cannot produce code for the dispatcher yet: discard elif sfForward in s.flags: p.g.forwarded.add(s) else: genProcForSymIfNeeded(p, s) else: if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) r.res = s.loc.r r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = let it = n.sons[0] let t = mapType(p, it.typ) if t == etyObject: gen(p, it, r) else: var a: TCompRes gen(p, it, a) r.kind = a.kind r.typ = mapType(p, n.typ) if r.typ == etyBaseIndex: let tmp = p.getTemp r.address = "($1 = $2, $1)[0]" % [tmp, a.rdLoc] r.res = "$1[1]" % [tmp] r.tmpLoc = tmp elif a.typ == etyBaseIndex: if a.tmpLoc != nil: r.tmpLoc = a.tmpLoc r.res = a.rdLoc else: internalError(p.config, n.info, "genDeref") proc genArgNoParam(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n, a) if a.typ == etyBaseIndex: add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) else: add(r.res, a.res) proc genArg(p: PProc, n: PNode, param: PSym, r: var TCompRes; emitted: ptr int = nil) = var a: TCompRes gen(p, n, a) if skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs} and a.typ == etyBaseIndex: add(r.res, "$1[$2]" % [a.address, a.res]) elif a.typ == etyBaseIndex: add(r.res, a.address) add(r.res, ", ") add(r.res, a.res) if emitted != nil: inc emitted[] elif n.typ.kind in {tyVar, tyPtr, tyRef, tyLent, tyOwned} and n.kind in nkCallKinds and mapType(param.typ) == etyBaseIndex: # this fixes bug #5608: let tmp = getTemp(p) add(r.res, "($1 = $2, $1[0]), $1[1]" % [tmp, a.rdLoc]) if emitted != nil: inc emitted[] else: add(r.res, a.res) proc genArgs(p: PProc, n: PNode, r: var TCompRes; start=1) = add(r.res, "(") var hasArgs = false var typ = skipTypes(n.sons[0].typ, abstractInst) assert(typ.kind == tyProc) assert(sonsLen(typ) == sonsLen(typ.n)) var emitted = start-1 for i in start ..< sonsLen(n): let it = n.sons[i] var paramType: PNode = nil if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) paramType = typ.n.sons[i] if paramType.typ.isCompileTimeOnly: continue if hasArgs: add(r.res, ", ") if paramType.isNil: genArgNoParam(p, it, r) else: genArg(p, it, paramType.sym, r, addr emitted) inc emitted hasArgs = true add(r.res, ")") when false: # XXX look into this: let jsp = countJsParams(typ) if emitted != jsp and tfVarargs notin typ.flags: localError(p.config, n.info, "wrong number of parameters emitted; expected: " & $jsp & " but got: " & $emitted) r.kind = resExpr proc genOtherArg(p: PProc; n: PNode; i: int; typ: PType; generated: var int; r: var TCompRes) = if i >= n.len: globalError(p.config, n.info, "wrong importcpp pattern; expected parameter at position " & $i & " but got only: " & $(n.len-1)) let it = n[i] var paramType: PNode = nil if i < sonsLen(typ): assert(typ.n.sons[i].kind == nkSym) paramType = typ.n.sons[i] if paramType.typ.isCompileTimeOnly: return if paramType.isNil: genArgNoParam(p, it, r) else: genArg(p, it, paramType.sym, r) inc generated proc genPatternCall(p: PProc; n: PNode; pat: string; typ: PType; r: var TCompRes) = var i = 0 var j = 1 r.kind = resExpr while i < pat.len: case pat[i] of '@': var generated = 0 for k in j ..< n.len: if generated > 0: add(r.res, ", ") genOtherArg(p, n, k, typ, generated, r) inc i of '#': var generated = 0 genOtherArg(p, n, j, typ, generated, r) inc j inc i of '\31': # unit separator add(r.res, "#") inc i of '\29': # group separator add(r.res, "@") inc i else: let start = i while i < pat.len: if pat[i] notin {'@', '#', '\31', '\29'}: inc(i) else: break if i - 1 >= start: add(r.res, substr(pat, start, i - 1)) proc genInfixCall(p: PProc, n: PNode, r: var TCompRes) = # don't call '$' here for efficiency: let f = n[0].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) if sfInfixCall in f.flags: let pat = n.sons[0].sym.loc.r.data internalAssert p.config, pat.len > 0 if pat.contains({'#', '(', '@'}): var typ = skipTypes(n.sons[0].typ, abstractInst) assert(typ.kind == tyProc) genPatternCall(p, n, pat, typ, r) return if n.len != 1: gen(p, n.sons[1], r) if r.typ == etyBaseIndex: if r.address == nil: globalError(p.config, n.info, "cannot invoke with infix syntax") r.res = "$1[$2]" % [r.address, r.res] r.address = nil r.typ = etyNone add(r.res, ".") var op: TCompRes gen(p, n.sons[0], op) add(r.res, op.res) genArgs(p, n, r, 2) proc genCall(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) genArgs(p, n, r) if n.typ != nil: let t = mapType(n.typ) if t == etyBaseIndex: let tmp = p.getTemp r.address = "($1 = $2, $1)[0]" % [tmp, r.rdLoc] r.res = "$1[1]" % [tmp] r.tmpLoc = tmp r.typ = t proc genEcho(p: PProc, n: PNode, r: var TCompRes) = let n = n[1].skipConv internalAssert p.config, n.kind == nkBracket useMagic(p, "toJSStr") # Used in rawEcho useMagic(p, "rawEcho") add(r.res, "rawEcho(") for i in 0 ..< sonsLen(n): let it = n.sons[i] if it.typ.isCompileTimeOnly: continue if i > 0: add(r.res, ", ") genArgNoParam(p, it, r) add(r.res, ")") r.kind = resExpr proc putToSeq(s: string, indirect: bool): Rope = result = rope(s) if indirect: result = "[$1]" % [result] proc createVar(p: PProc, typ: PType, indirect: bool): Rope proc createRecordVarAux(p: PProc, rec: PNode, excludedFieldIDs: IntSet, output: var Rope) = case rec.kind of nkRecList: for i in 0 ..< sonsLen(rec): createRecordVarAux(p, rec.sons[i], excludedFieldIDs, output) of nkRecCase: createRecordVarAux(p, rec.sons[0], excludedFieldIDs, output) for i in 1 ..< sonsLen(rec): createRecordVarAux(p, lastSon(rec.sons[i]), excludedFieldIDs, output) of nkSym: # Do not produce code for void types if isEmptyType(rec.sym.typ): return if rec.sym.id notin excludedFieldIDs: if output.len > 0: output.add(", ") output.addf("$#: ", [mangleName(p.module, rec.sym)]) output.add(createVar(p, rec.sym.typ, false)) else: internalError(p.config, rec.info, "createRecordVarAux") proc createObjInitList(p: PProc, typ: PType, excludedFieldIDs: IntSet, output: var Rope) = var t = typ if objHasTypeField(t): if output.len > 0: output.add(", ") addf(output, "m_type: $1", [genTypeInfo(p, t)]) while t != nil: t = t.skipTypes(skipPtrs) createRecordVarAux(p, t.n, excludedFieldIDs, output) t = t.sons[0] proc arrayTypeForElemType(typ: PType): string = # XXX This should also support tyEnum and tyBool case typ.kind of tyInt, tyInt32: "Int32Array" of tyInt16: "Int16Array" of tyInt8: "Int8Array" of tyUInt, tyUInt32: "Uint32Array" of tyUInt16: "Uint16Array" of tyUInt8: "Uint8Array" of tyFloat32: "Float32Array" of tyFloat64, tyFloat: "Float64Array" else: "" proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) of tyRange, tyGenericInst, tyAlias, tySink, tyOwned: result = createVar(p, lastSon(typ), indirect) of tySet: result = putToSeq("{}", indirect) of tyBool: result = putToSeq("false", indirect) of tyArray: let length = int(lengthOrd(p.config, t)) let e = elemType(t) let jsTyp = arrayTypeForElemType(e) if jsTyp.len > 0: result = "new $1($2)" % [rope(jsTyp), rope(length)] elif length > 32: useMagic(p, "arrayConstr") # XXX: arrayConstr depends on nimCopy. This line shouldn't be necessary. useMagic(p, "nimCopy") result = "arrayConstr($1, $2, $3)" % [rope(length), createVar(p, e, false), genTypeInfo(p, e)] else: result = rope("[") var i = 0 while i < length: if i > 0: add(result, ", ") add(result, createVar(p, e, false)) inc(i) add(result, "]") if indirect: result = "[$1]" % [result] of tyTuple: result = rope("{") for i in 0..<t.sonsLen: if i > 0: add(result, ", ") addf(result, "Field$1: $2", [i.rope, createVar(p, t.sons[i], false)]) add(result, "}") if indirect: result = "[$1]" % [result] of tyObject: var initList: Rope createObjInitList(p, t, initIntSet(), initList) result = ("{$1}") % [initList] if indirect: result = "[$1]" % [result] of tyVar, tyPtr, tyLent, tyRef, tyPointer: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: result = putToSeq("null", indirect) of tySequence, tyOpt, tyString, tyCString, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: result = createVar(p, lastSon t, indirect) else: internalError(p.config, "createVar: " & $t.kind) result = nil else: internalError(p.config, "createVar: " & $t.kind) result = nil template returnType: untyped = ~"" proc genVarInit(p: PProc, v: PSym, n: PNode) = var a: TCompRes s: Rope varCode: string varName = mangleName(p.module, v) useReloadingGuard = sfGlobal in v.flags and p.config.hcrOn if v.constraint.isNil: if useReloadingGuard: lineF(p, "var $1;$n", varName) lineF(p, "if ($1 === undefined) {$n", varName) varCode = $varName inc p.extraIndent else: varCode = "var $2" else: # Is this really a thought through feature? this basically unused # feature makes it impossible for almost all format strings in # this function to be checked at compile time. varCode = v.constraint.strVal if n.kind == nkEmpty: if not isIndirect(v) and v.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, v.typ) == etyBaseIndex: lineF(p, "var $1 = null;$n", [varName]) lineF(p, "var $1_Idx = 0;$n", [varName]) else: line(p, runtimeFormat(varCode & " = $3;$n", [returnType, varName, createVar(p, v.typ, isIndirect(v))])) else: gen(p, n, a) case mapType(p, v.typ) of etyObject, etySeq: if needsNoCopy(p, n): s = a.res else: useMagic(p, "nimCopy") s = "nimCopy(null, $1, $2)" % [a.res, genTypeInfo(p, n.typ)] of etyBaseIndex: let targetBaseIndex = {sfAddrTaken, sfGlobal} * v.flags == {} if a.typ == etyBaseIndex: if targetBaseIndex: line(p, runtimeFormat(varCode & " = $3, $2_Idx = $4;$n", [returnType, v.loc.r, a.address, a.res])) else: if isIndirect(v): line(p, runtimeFormat(varCode & " = [[$3, $4]];$n", [returnType, v.loc.r, a.address, a.res])) else: line(p, runtimeFormat(varCode & " = [$3, $4];$n", [returnType, v.loc.r, 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]) else: line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, a.res])) return else: s = a.res if isIndirect(v): line(p, runtimeFormat(varCode & " = [$3];$n", [returnType, v.loc.r, s])) else: line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) if useReloadingGuard: dec p.extraIndent lineF(p, "}$n") proc genVarStmt(p: PProc, n: PNode) = for i in 0 ..< sonsLen(n): var a = n.sons[i] if a.kind != nkCommentStmt: if a.kind == nkVarTuple: let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc) genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) assert(a.sons[0].kind == nkSym) var v = a.sons[0].sym if lfNoDecl notin v.loc.flags and sfImportc notin v.flags: genLineDir(p, a) genVarInit(p, v, a.sons[2]) proc genConstant(p: PProc, c: PSym) = if lfNoDecl notin c.loc.flags and not p.g.generatedSyms.containsOrIncl(c.id): let oldBody = p.body p.body = nil #genLineDir(p, c.ast) genVarInit(p, c, c.ast) add(p.g.constants, p.body) p.body = oldBody proc genNew(p: PProc, n: PNode) = var a: TCompRes gen(p, n.sons[1], a) var t = skipTypes(n.sons[1].typ, abstractVar).sons[0] if mapType(t) == etyObject: lineF(p, "$1 = $2;$n", [a.rdLoc, createVar(p, t, false)]) elif a.typ == etyBaseIndex: lineF(p, "$1 = [$3]; $2 = 0;$n", [a.address, a.res, createVar(p, t, false)]) else: lineF(p, "$1 = [[$2], 0];$n", [a.rdLoc, createVar(p, t, false)]) proc genNewSeq(p: PProc, n: PNode) = var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n.sons[1].typ, abstractVar).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n.sons[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") else: internalError(p.config, n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes gen(p, n.sons[1], a) r.kind = resExpr if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1].concat(" % [a.res]) else: r.res.add("($1 || []).concat(" % [a.res]) for i in 2 .. sonsLen(n) - 2: gen(p, n.sons[i], a) if skipTypes(n.sons[i].typ, abstractVarRange).kind == tyChar: r.res.add("[$1]," % [a.res]) else: r.res.add("$1 || []," % [a.res]) gen(p, n.sons[sonsLen(n) - 1], a) if skipTypes(n.sons[sonsLen(n) - 1].typ, abstractVarRange).kind == tyChar: r.res.add("[$1])" % [a.res]) else: r.res.add("$1 || [])" % [a.res]) proc genToArray(p: PProc; n: PNode; r: var TCompRes) = # we map mArray to PHP's array constructor, a mild hack: var a, b: TCompRes r.kind = resExpr r.res = rope("array(") let x = skipConv(n[1]) if x.kind == nkBracket: for i in 0 ..< x.len: let it = x[i] if it.kind in {nkPar, nkTupleConstr} and it.len == 2: if i > 0: r.res.add(", ") gen(p, it[0], a) gen(p, it[1], b) r.res.add("$# => $#" % [a.rdLoc, b.rdLoc]) else: localError(p.config, it.info, "'toArray' needs tuple constructors") else: localError(p.config, x.info, "'toArray' needs an array literal") r.res.add(")") proc genReprAux(p: PProc, n: PNode, r: var TCompRes, magic: string, typ: Rope = nil) = useMagic(p, magic) add(r.res, magic & "(") var a: TCompRes gen(p, n.sons[1], a) if magic == "reprAny": # the pointer argument in reprAny is expandend to # (pointedto, pointer), so we need to fill it if a.address.isNil: add(r.res, a.res) add(r.res, ", null") else: add(r.res, "$1, $2" % [a.address, a.res]) else: add(r.res, a.res) if not typ.isNil: add(r.res, ", ") add(r.res, typ) add(r.res, ")") proc genRepr(p: PProc, n: PNode, r: var TCompRes) = let t = skipTypes(n.sons[1].typ, abstractVarRange) case t.kind: of tyInt..tyInt64, tyUInt..tyUInt64: genReprAux(p, n, r, "reprInt") of tyChar: genReprAux(p, n, r, "reprChar") of tyBool: genReprAux(p, n, r, "reprBool") of tyFloat..tyFloat128: genReprAux(p, n, r, "reprFloat") of tyString: genReprAux(p, n, r, "reprStr") of tyEnum, tyOrdinal: genReprAux(p, n, r, "reprEnum", genTypeInfo(p, t)) of tySet: genReprAux(p, n, r, "reprSet", genTypeInfo(p, t)) of tyEmpty, tyVoid: localError(p.config, n.info, "'repr' doesn't support 'void' type") of tyPointer: genReprAux(p, n, r, "reprPointer") of tyOpenArray, tyVarargs: genReprAux(p, n, r, "reprJSONStringify") else: genReprAux(p, n, r, "reprAny", genTypeInfo(p, t)) proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes let t = skipTypes(n.sons[2].typ, abstractVarRange+{tyRef, tyPtr, tyLent, tyTypeDesc, tyOwned}) gen(p, n.sons[1], x) if tfFinal in t.flags: r.res = "($1.m_type == $2)" % [x.res, genTypeInfo(p, t)] else: useMagic(p, "isObj") r.res = "isObj($1.m_type, $2)" % [x.res, genTypeInfo(p, t)] r.kind = resExpr 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") gen(p, n.sons[1], x) if x.typ == etyBaseIndex: lineF(p, "$1 = null, $2 = 0;$n", [x.address, x.res]) else: let (a, tmp) = maybeMakeTemp(p, n[1], x) lineF(p, "$1 = genericReset($3, $2);$n", [a, genTypeInfo(p, n.sons[1].typ), tmp]) proc genMagic(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes line, filen: Rope var op = n.sons[0].sym.magic case op of mOr: genOr(p, n.sons[1], n.sons[2], r) of mAnd: genAnd(p, n.sons[1], n.sons[2], r) of mAddI..mStrToStr: arith(p, n, r, op) of mRepr: genRepr(p, n, r) of mSwap: genSwap(p, n) of mUnaryLt: # XXX: range checking? if not (optOverflowCheck in p.options): unaryExpr(p, n, r, "", "$1 - 1") else: unaryExpr(p, n, r, "subInt", "subInt($1, 1)") of mAppendStrCh: binaryExpr(p, n, r, "addChar", "if ($1 != null) { addChar($3, $2); } else { $3 = [$2]; }") of mAppendStrStr: var lhs, rhs: TCompRes gen(p, n[1], lhs) gen(p, n[2], rhs) let rhsIsLit = n[2].kind in nkStrKinds let (a, tmp) = maybeMakeTemp(p, n[1], lhs) if skipTypes(n.sons[1].typ, abstractVarRange).kind == tyCString: r.res = "if ($1 != null) { $4 += $2; } else { $4 = $2$3; }" % [ a, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] else: r.res = "if ($1 != null) { $4 = ($4).concat($2); } else { $4 = $2$3; }" % [ lhs.rdLoc, rhs.rdLoc, if rhsIsLit: nil else: ~".slice()", tmp] r.kind = resExpr of mAppendSeqElem: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let (a, tmp) = maybeMakeTemp(p, n[1], x) if mapType(n[2].typ) == etyBaseIndex: let c = "[$1, $2]" % [y.address, y.res] r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] elif needsNoCopy(p, n[2]): r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, y.rdLoc, tmp] else: useMagic(p, "nimCopy") let c = getTemp(p, defineInLocals=false) lineF(p, "var $1 = nimCopy(null, $2, $3);$n", [c, y.rdLoc, genTypeInfo(p, n[2].typ)]) r.res = "if ($1 != null) { $3.push($2); } else { $3 = [$2]; }" % [a, c, tmp] r.kind = resExpr of mConStrStr: genConStrStr(p, n, r) of mEqStr: binaryExpr(p, n, r, "eqStrings", "eqStrings($1, $2)") of mLeStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) <= 0)") of mLtStr: binaryExpr(p, n, r, "cmpStrings", "(cmpStrings($1, $2) < 0)") of mIsNil: # we want to accept undefined, so we == if mapType(n[1].typ) != etyBaseIndex: unaryExpr(p, n, r, "", "($1 == null)") else: var x: TCompRes gen(p, n[1], x) r.res = "($# == null && $# === 0)" % [x.address, x.res] of mEnumToStr: genRepr(p, n, r) of mNew, mNewFinalize: genNew(p, n) of mChr: gen(p, n.sons[1], r) of mArrToSeq: if needsNoCopy(p, n.sons[1]): gen(p, n.sons[1], r) else: var x: TCompRes gen(p, n.sons[1], x) useMagic(p, "nimCopy") r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] of mDestroy: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: unaryExpr(p, n, r, "", "($1 != null ? $2.length : 0)") of mXLenStr, mXLenSeq: unaryExpr(p, n, r, "", "$1.length") of mHigh: unaryExpr(p, n, r, "", "($1 != null ? ($2.length-1) : -1)") of mInc: if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "+", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 += $2") else: binaryExpr(p, n, r, "addInt", "$1 = addInt($3, $2)") of ast.mDec: if n[1].typ.skipTypes(abstractRange).kind in {tyUInt..tyUInt64}: binaryUintExpr(p, n, r, "-", true) else: if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)") of mSetLengthStr: binaryExpr(p, n, r, "mnewString", "($1 == null ? $3 = mnewString($2) : $3.length = $2)") of mSetLengthSeq: var x, y: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) let t = skipTypes(n.sons[1].typ, abstractVar).sons[0] let (a, tmp) = maybeMakeTemp(p, n[1], x) let (b, tmp2) = maybeMakeTemp(p, n[2], y) r.res = """if ($1 === null) $4 = []; if ($4.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") of mLtSet: binaryExpr(p, n, r, "SetLt", "SetLt($1, $2)") of mLeSet: binaryExpr(p, n, r, "SetLe", "SetLe($1, $2)") of mEqSet: binaryExpr(p, n, r, "SetEq", "SetEq($1, $2)") of mMulSet: binaryExpr(p, n, r, "SetMul", "SetMul($1, $2)") of mPlusSet: binaryExpr(p, n, r, "SetPlus", "SetPlus($1, $2)") of mMinusSet: binaryExpr(p, n, r, "SetMinus", "SetMinus($1, $2)") of mIncl: binaryExpr(p, n, r, "", "$1[$2] = true") of mExcl: binaryExpr(p, n, r, "", "delete $1[$2]") of mInSet: binaryExpr(p, n, r, "", "($1[$2] != undefined)") 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: genReset(p, n) of mEcho: genEcho(p, n, r) of mNLen..mNError, mSlurp, mStaticExec: localError(p.config, n.info, errXMustBeCompileTime % n.sons[0].sym.name.s) of mCopyStr: binaryExpr(p, n, r, "", "($1.slice($2))") of mNewString: unaryExpr(p, n, r, "mnewString", "mnewString($1)") of mNewStringOfCap: unaryExpr(p, n, r, "mnewString", "mnewString(0)") of mDotDot: genProcForSymIfNeeded(p, n.sons[0].sym) genCall(p, n, r) of mParseBiggestFloat: useMagic(p, "nimParseBiggestFloat") genCall(p, n, r) of mSlice: # arr.slice([begin[, end]]): 'end' is exclusive var x, y, z: TCompRes gen(p, n.sons[1], x) gen(p, n.sons[2], y) gen(p, n.sons[3], z) r.res = "($1.slice($2, $3+1))" % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr 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 useMagic(p, "setConstr") r.res = rope("setConstr(") r.kind = resExpr for i in 0 ..< sonsLen(n): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkRange: gen(p, it.sons[0], a) gen(p, it.sons[1], b) addf(r.res, "[$1, $2]", [a.res, b.res]) else: gen(p, it, a) add(r.res, a.res) add(r.res, ")") # emit better code for constant sets: if isDeepConstExpr(n): inc(p.g.unique) let tmp = rope("ConstSet") & rope(p.g.unique) addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res]) r.res = tmp proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("[") r.kind = resExpr for i in 0 ..< sonsLen(n): if i > 0: add(r.res, ", ") gen(p, n.sons[i], a) if a.typ == etyBaseIndex: addf(r.res, "[$1, $2]", [a.address, a.res]) else: if not needsNoCopy(p, n[i]): let typ = n[i].typ.skipTypes(abstractInst) useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] add(r.res, a.res) add(r.res, "]") proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.res = rope("{") r.kind = resExpr for i in 0 ..< sonsLen(n): if i > 0: add(r.res, ", ") var it = n.sons[i] if it.kind == nkExprColonExpr: it = it.sons[1] gen(p, it, a) let typ = it.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: addf(r.res, "Field$#: [$#, $#]", [i.rope, a.address, a.res]) else: if not needsNoCopy(p, it): useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] addf(r.res, "Field$#: $#", [i.rope, a.res]) r.res.add("}") proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.kind = resExpr var initList : Rope var fieldIDs = initIntSet() for i in 1 ..< sonsLen(n): if i > 1: add(initList, ", ") var it = n.sons[i] internalAssert p.config, it.kind == nkExprColonExpr let val = it.sons[1] gen(p, val, a) var f = it.sons[0].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) fieldIDs.incl(f.id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: addf(initList, "$#: [$#, $#]", [f.loc.r, a.address, a.res]) else: if not needsNoCopy(p, val): useMagic(p, "nimCopy") a.res = "nimCopy(null, $1, $2)" % [a.rdLoc, genTypeInfo(p, typ)] addf(initList, "$#: $#", [f.loc.r, a.res]) let t = skipTypes(n.typ, abstractInst + skipPtrs) createObjInitList(p, t, fieldIDs, initList) r.res = ("{$1}") % [initList] proc genConv(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) gen(p, n.sons[1], r) if dest.kind == src.kind: # no-op conversion return case dest.kind: of tyBool: r.res = "(!!($1))" % [r.res] r.kind = resExpr of tyInt: r.res = "(($1)|0)" % [r.res] else: # TODO: What types must we handle here? discard proc upConv(p: PProc, n: PNode, r: var TCompRes) = gen(p, n.sons[0], r) # XXX proc genRangeChck(p: PProc, n: PNode, r: var TCompRes, magic: string) = var a, b: TCompRes gen(p, n.sons[0], r) if optRangeCheck in p.options: gen(p, n.sons[1], a) gen(p, n.sons[2], b) useMagic(p, "chckRange") r.res = "chckRange($1, $2, $3)" % [r.res, a.res, b.res] r.kind = resExpr proc convStrToCStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkCStringToString: gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) if r.res == nil: internalError(p.config, n.info, "convStrToCStr") useMagic(p, "toJSStr") r.res = "toJSStr($1)" % [r.res] r.kind = resExpr proc convCStrToStr(p: PProc, n: PNode, r: var TCompRes) = # we do an optimization here as this is likely to slow down # much of the code otherwise: if n.sons[0].kind == nkStringToCString: gen(p, n.sons[0].sons[0], r) else: gen(p, n.sons[0], r) if r.res == nil: internalError(p.config, n.info, "convCStrToStr") useMagic(p, "cstrToNimstr") r.res = "cstrToNimstr($1)" % [r.res] r.kind = resExpr proc genReturnStmt(p: PProc, n: PNode) = if p.procDef == nil: internalError(p.config, n.info, "genReturnStmt") p.beforeRetNeeded = true if n.sons[0].kind != nkEmpty: genStmt(p, n.sons[0]) else: genLineDir(p, n) lineF(p, "break BeforeRet;$n", []) proc frameCreate(p: PProc; procname, filename: Rope): Rope = const frameFmt = "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" result = p.indentLine(frameFmt % [procname, filename]) result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) proc frameDestroy(p: PProc): Rope = result = p.indentLine rope(("framePtr = F.prev;") & "\L") proc genProcBody(p: PProc, prc: PSym): Rope = if hasFrameInfo(p): result = frameCreate(p, makeJSString(prc.owner.name.s & '.' & prc.name.s), makeJSString(toFilename(p.config, prc.info))) else: result = nil if p.beforeRetNeeded: result.add p.indentLine(~"BeforeRet: do {$n") result.add p.body result.add p.indentLine(~"} while (false);$n") else: add(result, p.body) if prc.typ.callConv == ccSysCall: result = ("try {$n$1} catch (e) {$n" & " alert(\"Unhandled exception:\\n\" + e.message + \"\\n\"$n}") % [result] if hasFrameInfo(p): add(result, frameDestroy(p)) proc optionalLine(p: Rope): Rope = if p == nil: return nil else: return p & "\L" proc genProc(oldProc: PProc, prc: PSym): Rope = var resultSym: PSym a: TCompRes #if gVerbosity >= 3: # echo "BEGIN generating code for: " & prc.name.s var p = newProc(oldProc.g, oldProc.module, prc.ast, prc.options) p.up = oldProc var returnStmt: Rope = nil var resultAsgn: Rope = nil var name = mangleName(p.module, prc) let header = generateHeader(p, prc.typ) if prc.typ.sons[0] != nil and sfPure notin prc.flags: resultSym = prc.ast.sons[resultPos].sym let mname = mangleName(p.module, resultSym) if not isIndirect(resultSym) and resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and mapType(p, resultSym.typ) == etyBaseIndex: 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.sons[resultPos], a) if mapType(p, resultSym.typ) == etyBaseIndex: returnStmt = "return [$#, $#];$n" % [a.address, a.res] else: returnStmt = "return $#;$n" % [a.res] let transformedBody = transformBody(oldProc.module.graph, prc, cache = false) p.nested: genStmt(p, transformedBody) var def: Rope if not prc.constraint.isNil: def = runtimeFormat(prc.constraint.strVal & " {$n$#$#$#$#$#", [ returnType, name, header, optionalLine(p.globals), optionalLine(p.locals), optionalLine(resultAsgn), optionalLine(genProcBody(p, prc)), optionalLine(p.indentLine(returnStmt))]) else: result = ~"\L" if p.config.hcrOn: # Here, we introduce thunks that create the equivalent of a jump table # for all global functions, because references to them may be stored # in JavaScript variables. The added indirection ensures that such # references will end up calling the reloaded code. var thunkName = name name = name & "IMLP" result.add("function $#() { return $#.apply(this, arguments); }$n" % [thunkName, name]) def = "function $#($#) {$n$#$#$#$#$#" % [ name, header, optionalLine(p.globals), optionalLine(p.locals), optionalLine(resultAsgn), optionalLine(genProcBody(p, prc)), optionalLine(p.indentLine(returnStmt))] dec p.extraIndent result.add p.indentLine(def) result.add p.indentLine(~"}$n") #if gVerbosity >= 3: # echo "END generated code for: " & prc.name.s proc genStmt(p: PProc, n: PNode) = var r: TCompRes gen(p, n, r) if r.res != nil: lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = for it in n.sons: case whichPragma(it) of wEmit: genAsmOrEmitStmt(p, it.sons[1]) else: discard proc genCast(p: PProc, n: PNode, r: var TCompRes) = var dest = skipTypes(n.typ, abstractVarRange) var src = skipTypes(n.sons[1].typ, abstractVarRange) gen(p, n.sons[1], r) if dest.kind == src.kind: # no-op conversion return let toInt = (dest.kind in tyInt..tyInt32) let toUint = (dest.kind in tyUInt..tyUInt32) 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] elif toInt: if fromInt: let trimmer = unsignedTrimmer(dest.size) r.res = "($1 $2)" % [r.res, trimmer] 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] elif (src.kind == tyPtr and mapType(p, src) == etyObject) and dest.kind == tyPointer: r.address = r.res r.res = ~"null" r.typ = etyBaseIndex elif (dest.kind == tyPtr and mapType(p, dest) == etyObject) and src.kind == tyPointer: r.res = r.address r.typ = etyObject proc gen(p: PProc, n: PNode, r: var TCompRes) = r.typ = etyNone if r.kind != resCallee: r.kind = resNone #r.address = nil r.res = nil case n.kind of nkSym: genSym(p, n, r) of nkCharLit..nkUInt64Lit: if n.typ.kind == tyBool: r.res = if n.intVal == 0: rope"false" else: rope"true" else: r.res = rope(n.intVal) r.kind = resExpr of nkNilLit: if isEmptyType(n.typ): discard elif mapType(p, n.typ) == etyBaseIndex: r.typ = etyBaseIndex r.address = rope"null" r.res = rope"0" r.kind = resExpr else: r.res = rope"null" r.kind = resExpr of nkStrLit..nkTripleStrLit: if skipTypes(n.typ, abstractVarRange).kind == tyString: if n.strVal.len != 0: useMagic(p, "makeNimstrLit") r.res = "makeNimstrLit($1)" % [makeJSString(n.strVal)] else: r.res = rope"[]" else: r.res = makeJSString(n.strVal, false) r.kind = resExpr of nkFloatLit..nkFloat64Lit: let f = n.floatVal case classify(f) of fcNan: r.res = rope"NaN" of fcNegZero: r.res = rope"-0.0" of fcZero: r.res = rope"0.0" of fcInf: r.res = rope"Infinity" of fcNegInf: r.res = rope"-Infinity" else: r.res = rope(f.toStrMaxPrecision) r.kind = resExpr of nkCallKinds: if isEmptyType(n.typ): genLineDir(p, n) if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone): genMagic(p, n, r) elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and n.len >= 1: genInfixCall(p, n, r) else: genCall(p, n, r) of nkClosure: gen(p, n[0], r) of nkCurly: genSetConstr(p, n, r) of nkBracket: genArrayConstr(p, n, r) of nkPar, nkTupleConstr: genTupleConstr(p, n, r) of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: genAddr(p, n, r) of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r) of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r) of nkObjDownConv: gen(p, n.sons[0], r) of nkObjUpConv: upConv(p, n, r) of nkCast: genCast(p, n, r) of nkChckRangeF: genRangeChck(p, n, r, "chckRangeF") of nkChckRange64: genRangeChck(p, n, r, "chckRange64") of nkChckRange: genRangeChck(p, n, r, "chckRange") of nkStringToCString: convStrToCStr(p, n, r) of nkCStringToString: convCStrToStr(p, n, r) of nkEmpty: discard of nkLambdaKinds: let s = n.sons[namePos].sym discard mangleName(p.module, s) r.res = s.loc.r if lfNoDecl in s.loc.flags or s.magic != mNone: discard elif not p.g.generatedSyms.containsOrIncl(s.id): add(p.locals, genProc(p, s)) of nkType: r.res = genTypeInfo(p, n.typ) of nkStmtList, nkStmtListExpr: # this shows the distinction is nice for backends and should be kept # in the frontend let isExpr = not isEmptyType(n.typ) for i in 0 ..< sonsLen(n) - isExpr.ord: genStmt(p, n.sons[i]) if isExpr: gen(p, lastSon(n), r) of nkBlockStmt, nkBlockExpr: genBlock(p, n, r) of nkIfStmt, nkIfExpr: genIf(p, n, r) of nkWhen: # This is "when nimvm" node gen(p, n.sons[1].sons[0], r) of nkWhileStmt: genWhileStmt(p, n) of nkVarSection, nkLetSection: genVarStmt(p, n) of nkConstSection: discard of nkForStmt, nkParForStmt: internalError(p.config, n.info, "for statement not eliminated") of nkCaseStmt: genCaseJS(p, n, r) of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: genAsgn(p, n) of nkFastAsgn: genFastAsgn(p, n) of nkDiscardStmt: if n.sons[0].kind != nkEmpty: genLineDir(p, n) gen(p, n.sons[0], r) of nkAsmStmt: genAsmOrEmitStmt(p, n) of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n.sons[namePos].sym if {sfExportc, sfCompilerProc} * s.flags == {sfExportc}: genSym(p, n.sons[namePos], r) r.res = nil of nkGotoState, nkState: internalError(p.config, n.info, "first class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) proc newModule(g: ModuleGraph; module: PSym): BModule = new(result) result.module = module result.sigConflicts = initCountTable[SigHash]() if g.backend == nil: g.backend = newGlobals() result.graph = g result.config = g.config proc genHeader(): Rope = result = ( "/* Generated by the Nim Compiler v$1 */$n" & "/* (c) " & copyrightYear & " Andreas Rumpf */$n$n" & "var framePtr = null;$n" & "var excHandler = 0;$n" & "var lastJSError = null;$n" & "if (typeof Int8Array === 'undefined') Int8Array = Array;$n" & "if (typeof Int16Array === 'undefined') Int16Array = Array;$n" & "if (typeof Int32Array === 'undefined') Int32Array = Array;$n" & "if (typeof Uint8Array === 'undefined') Uint8Array = Array;$n" & "if (typeof Uint16Array === 'undefined') Uint16Array = Array;$n" & "if (typeof Uint32Array === 'undefined') Uint32Array = Array;$n" & "if (typeof Float32Array === 'undefined') Float32Array = Array;$n" & "if (typeof Float64Array === 'undefined') Float64Array = Array;$n") % [rope(VersionAsString)] proc addHcrInitGuards(p: PProc, n: PNode, moduleLoadedVar: Rope, inInitGuard: var bool) = if n.kind == nkStmtList: for child in n: addHcrInitGuards(p, child, moduleLoadedVar, inInitGuard) else: let stmtShouldExecute = n.kind in { nkProcDef, nkFuncDef, nkMethodDef,nkConverterDef, nkVarSection, nkLetSection} or nfExecuteOnReload in n.flags if inInitGuard: if stmtShouldExecute: dec p.extraIndent line(p, "}\L") inInitGuard = false else: if not stmtShouldExecute: lineF(p, "if ($1 == undefined) {$n", [moduleLoadedVar]) inc p.extraIndent inInitGuard = true genStmt(p, n) proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: add(p.body, frameCreate(p, makeJSString("module " & p.module.module.name.s), makeJSString(toFilename(p.config, p.module.module.info)))) let transformedN = transformStmt(p.module.graph, p.module.module, n) 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) lineF(p, "var $1;$n", [moduleLoadedVar]) var inGuardedBlock = false addHcrInitGuards(p, transformedN, moduleLoadedVar, inGuardedBlock) if inGuardedBlock: dec p.extraIndent line(p, "}\L") lineF(p, "$1 = true;$n", [moduleLoadedVar]) else: genStmt(p, transformedN) if optStackTrace in p.options: add(p.body, frameDestroy(p)) proc myProcess(b: PPassContext, n: PNode): PNode = result = n let m = BModule(b) if passes.skipCodegen(m.config, n): return n if m.module == nil: internalError(m.config, n.info, "myProcess") let globals = PGlobals(m.graph.backend) var p = newProc(globals, m, nil, m.module.options) p.unique = globals.unique genModule(p, n) add(p.g.code, p.locals) add(p.g.code, p.body) proc wholeCode(graph: ModuleGraph; m: BModule): Rope = let globals = PGlobals(graph.backend) for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) attachProc(p, prc) var disp = generateMethodDispatchers(graph) for i in 0..sonsLen(disp)-1: let prc = disp.sons[i].sym if not globals.generatedSyms.containsOrIncl(prc.id): var p = newProc(globals, m, nil, m.module.options) 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 != nil: result = s.loc.r else: result = rope(s.name.s) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = result = myProcess(b, n) var m = BModule(b) if sfMainModule in m.module.flags: for destructorCall in graph.globalDestructors: n.add destructorCall if passes.skipCodegen(m.config, n): return n if sfMainModule in m.module.flags: let code = wholeCode(graph, m) let outFile = m.config.prepareToWriteOutput() discard writeRopeIfNotEqual(genHeader() & code, outFile) proc myOpen(graph: ModuleGraph; s: PSym): PPassContext = result = newModule(graph, s) const JSgenPass* = makePass(myOpen, myProcess, myClose)