summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/core/macros.nim15
-rw-r--r--lib/packages/docutils/rstgen.nim12
-rw-r--r--lib/pure/collections/sequtils.nim26
-rw-r--r--lib/pure/collections/sets.nim12
-rw-r--r--lib/pure/encodings.nim8
-rw-r--r--lib/pure/includes/oserr.nim2
-rw-r--r--lib/pure/ioselects/ioselectors_epoll.nim29
-rw-r--r--lib/pure/ioselects/ioselectors_kqueue.nim27
-rw-r--r--lib/pure/ioselects/ioselectors_poll.nim25
-rw-r--r--lib/pure/ioselects/ioselectors_select.nim9
-rw-r--r--lib/pure/json.nim35
-rw-r--r--lib/pure/mimetypes.nim1
-rw-r--r--lib/pure/net.nim2
-rw-r--r--lib/pure/os.nim8
-rw-r--r--lib/pure/ospaths.nim7
-rw-r--r--lib/pure/random.nim17
-rw-r--r--lib/pure/selectors.nim9
-rw-r--r--lib/pure/times.nim35
-rw-r--r--lib/system.nim10
-rw-r--r--lib/system/alloc.nim26
-rw-r--r--lib/system/ansi_c.nim3
-rw-r--r--lib/system/gc.nim5
-rw-r--r--lib/system/gc_common.nim3
-rw-r--r--lib/system/memtracker.nim12
-rw-r--r--lib/system/sysio.nim8
-rw-r--r--lib/system/sysstr.nim79
26 files changed, 298 insertions, 127 deletions
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 8a1be3720..d4e8ada0a 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -382,16 +382,24 @@ type
 
 {.deprecated: [TBindSymRule: BindSymRule].}
 
-proc bindSym*(ident: static[string], rule: BindSymRule = brClosed): NimNode {.
+proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {.
               magic: "NBindSym", noSideEffect.}
   ## creates a node that binds `ident` to a symbol node. The bound symbol
   ## may be an overloaded symbol.
+  ## if `ident` is a NimNode, it must have nkIdent kind.
   ## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brOpen`` either an ``nkOpenSymChoice`` tree is
   ## returned or ``nkSym`` if the symbol is not ambiguous.
   ## If ``rule == brForceOpen`` always an ``nkOpenSymChoice`` tree is
   ## returned even if the symbol is not ambiguous.
+  ##
+  ## experimental feature:
+  ## use {.experimental: "dynamicBindSym".} to activate it
+  ## if called from template / regular code, `ident` and `rule` must be
+  ## constant expression / literal value.
+  ## if called from macros / compile time procs / static blocks,
+  ## `ident` and `rule` can be VM computed value.
 
 proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {.
   magic: "NGenSym", noSideEffect.}
@@ -425,6 +433,7 @@ proc getColumn(arg: NimNode): int {.magic: "NLineInfo", noSideEffect.}
 proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.}
 
 proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} =
+  ## returns ``LineInfo`` of ``n``, using absolute path for ``filename``
   result.filename = n.getFile
   result.line = n.getLine
   result.column = n.getColumn
@@ -1284,7 +1293,7 @@ proc customPragmaNode(n: NimNode): NimNode =
   let
     typ = n.getTypeInst()
 
-  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: 
+  if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy:
     return typ[1][1]
   elif typ.typeKind == ntyTypeDesc:
     let impl = typ[1].getImpl()
@@ -1319,6 +1328,8 @@ proc customPragmaNode(n: NimNode): NimNode =
           if identDefs.kind == nnkRecCase:
             identDefsStack.add(identDefs[0])
             for i in 1..<identDefs.len:
+              # if it is and empty branch, skip
+              if identDefs[i][0].kind == nnkNilLit: continue
               if identDefs[i][1].kind == nnkIdentDefs:
                 identDefsStack.add(identDefs[i][1])
               else: # nnkRecList
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index ef456f093..43a429a17 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -449,10 +449,11 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
         desc = if not symbols[j].linkDesc.isNil: symbols[j].linkDesc else: ""
       if desc.len > 0:
         result.addf("""<li><a class="reference external"
-          title="$3" href="$1">$2</a></li>
+          title="$3" data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text, desc])
       else:
-        result.addf("""<li><a class="reference external" href="$1">$2</a></li>
+        result.addf("""<li><a class="reference external" 
+          data-doc-search-tag="$2" href="$1">$2</a></li>
           """, [url, text])
       inc j
     result.add("</ul></dd>\n")
@@ -493,6 +494,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
   # Build a list of levels and extracted titles to make processing easier.
   var
     titleRef: string
+    titleTag: string
     levels: seq[tuple[level: int, text: string]]
     L = 0
     level = 1
@@ -519,10 +521,12 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
     let link = entries[L].link
     if link.isDocumentationTitle:
       titleRef = link
+      titleTag = levels[L].text
     else:
       result.add(level.indentToLevel(levels[L].level))
-      result.add("<li><a href=\"" & link & "\">" &
-        levels[L].text & "</a></li>\n")
+      result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2"> 
+        $3</a></li>
+        """, [titleTag & " : " & levels[L].text, link, levels[L].text])
     inc L
   result.add(level.indentToLevel(1) & "</ul>\n")
   assert(not titleRef.isNil,
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index 44c59c627..db33e41af 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -854,20 +854,20 @@ when isMainModule:
     doAssert numbers.distribute(6)[0] == @[1, 2]
     doAssert numbers.distribute(6)[5] == @[7]
     let a = @[1, 2, 3, 4, 5, 6, 7]
-    doAssert a.distribute(1, true)   == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(1, false)  == @[@[1, 2, 3, 4, 5, 6, 7]]
-    doAssert a.distribute(2, true)   == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(2, false)  == @[@[1, 2, 3, 4], @[5, 6, 7]]
-    doAssert a.distribute(3, true)   == @[@[1, 2, 3], @[4, 5], @[6, 7]]
-    doAssert a.distribute(3, false)  == @[@[1, 2, 3], @[4, 5, 6], @[7]]
-    doAssert a.distribute(4, true)   == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(4, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
-    doAssert a.distribute(5, true)   == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
-    doAssert a.distribute(5, false)  == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
-    doAssert a.distribute(6, true)   == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
-    doAssert a.distribute(6, false)  == @[
+    doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]]
+    doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]]
+    doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]]
+    doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]]
+    doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]]
+    doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]]
+    doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]]
+    doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]]
+    doAssert a.distribute(6, false) == @[
       @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]]
-    doAssert a.distribute(8, false)  == a.distribute(8, true)
+    doAssert a.distribute(8, false) == a.distribute(8, true)
     doAssert a.distribute(90, false) == a.distribute(90, true)
     var b = @[0]
     for f in 1 .. 25: b.add(f)
diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim
index 59c90bc2b..fdc3b4b03 100644
--- a/lib/pure/collections/sets.nim
+++ b/lib/pure/collections/sets.nim
@@ -347,6 +347,18 @@ proc excl*[A](s: var HashSet[A], other: HashSet[A]) =
   assert other.isValid, "The set `other` needs to be initialized."
   for item in other: discard exclImpl(s, item)
 
+proc pop*[A](s: var HashSet[A]): A =
+  ## Remove and return an arbitrary element from the set `s`.
+  ##
+  ## Raises KeyError if the set `s` is empty.
+  ##
+  for h in 0..high(s.data):
+    if isFilled(s.data[h].hcode):
+      result = s.data[h].key
+      excl(s, result)
+      return result
+  raise newException(KeyError, "set is empty")
+
 proc containsOrIncl*[A](s: var HashSet[A], key: A): bool =
   ## Includes `key` in the set `s` and tells if `key` was added to `s`.
   ##
diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim
index c67cd7579..3c1cf73f4 100644
--- a/lib/pure/encodings.nim
+++ b/lib/pure/encodings.nim
@@ -332,7 +332,7 @@ when defined(windows):
     if s.len == 0: return ""
     # educated guess of capacity:
     var cap = s.len + s.len shr 2
-    result = newStringOfCap(cap*2)
+    result = newString(cap*2)
     # convert to utf-16 LE
     var m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
                                 lpMultiByteStr = cstring(s),
@@ -347,7 +347,7 @@ when defined(windows):
                                 lpWideCharStr = nil,
                                 cchWideChar = cint(0))
       # and do the conversion properly:
-      result = newStringOfCap(cap*2)
+      result = newString(cap*2)
       m = multiByteToWideChar(codePage = c.src, dwFlags = 0'i32,
                               lpMultiByteStr = cstring(s),
                               cbMultiByte = cint(s.len),
@@ -364,7 +364,7 @@ when defined(windows):
     if int(c.dest) == 1200: return
     # otherwise the fun starts again:
     cap = s.len + s.len shr 2
-    var res = newStringOfCap(cap)
+    var res = newString(cap)
     m = wideCharToMultiByte(
       codePage = c.dest,
       dwFlags = 0'i32,
@@ -382,7 +382,7 @@ when defined(windows):
         lpMultiByteStr = nil,
         cbMultiByte = cint(0))
       # and do the conversion properly:
-      res = newStringOfCap(cap)
+      res = newString(cap)
       m = wideCharToMultiByte(
         codePage = c.dest,
         dwFlags = 0'i32,
diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim
index eb350cbd4..493e8e174 100644
--- a/lib/pure/includes/oserr.nim
+++ b/lib/pure/includes/oserr.nim
@@ -60,7 +60,7 @@ proc raiseOSError*(errorCode: OSErrorCode; additionalInfo = "") {.noinline.} =
   if additionalInfo.len == 0:
     e.msg = osErrorMsg(errorCode)
   else:
-    e.msg = osErrorMsg(errorCode) & "\nAdditional info: " & additionalInfo
+    e.msg = osErrorMsg(errorCode) & "\nAdditional info: '" & additionalInfo & "'"
   if e.msg == "":
     e.msg = "unknown OS error"
   raise e
diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim
index 36145abc7..8b3f14f34 100644
--- a/lib/pure/ioselects/ioselectors_epoll.nim
+++ b/lib/pure/ioselects/ioselectors_epoll.nim
@@ -92,6 +92,9 @@ proc newSelector*[T](): Selector[T] =
     result.maxFD = maxFD
     result.fds = newSeq[SelectorKey[T]](maxFD)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
 proc close*[T](s: Selector[T]) =
   let res = posix.close(s.epollFD)
   when hasThreadSupport:
@@ -100,12 +103,6 @@ proc close*[T](s: Selector[T]) =
   if res != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
   let fdci = eventfd(0, 0)
   if fdci == -1:
@@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0, "Descriptor $# already registered" % $fdi)
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Descriptor $# already registered" % $fdi)
   s.setKey(fdi, events, 0, data)
   if events != {}:
     var epv = EpollEvent(events: EPOLLRDHUP)
@@ -152,7 +149,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle, events: set[Event]
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
   if pkey.events != events:
@@ -180,7 +177,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the selector!" % $fdi)
   if pkey.events != {}:
     when not defined(android):
@@ -243,7 +240,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.efd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   var epv = EpollEvent()
   if epoll_ctl(s.epollFD, EPOLL_CTL_DEL, fdi.cint, addr epv) != 0:
@@ -262,7 +259,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
   setNonBlocking(fdi.cint)
 
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var events = {Event.Timer}
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
@@ -307,7 +304,7 @@ when not defined(android):
     setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -334,7 +331,7 @@ when not defined(android):
     setNonBlocking(fdi.cint)
 
     s.checkFd(fdi)
-    doAssert(s.fds[fdi].ident == 0)
+    doAssert(s.fds[fdi].ident == InvalidIdent)
 
     var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
     epv.data.u64 = fdi.uint
@@ -347,7 +344,7 @@ when not defined(android):
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = int(ev.efd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   s.setKey(fdi, {Event.User}, 0, data)
   var epv = EpollEvent(events: EPOLLIN or EPOLLRDHUP)
   epv.data.u64 = ev.efd.uint
@@ -381,7 +378,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
       let fdi = int(resTable[i].data.u64)
       let pevents = resTable[i].events
       var pkey = addr(s.fds[fdi])
-      doAssert(pkey.ident != 0)
+      doAssert(pkey.ident != InvalidIdent)
       var rkey = ReadyKey(fd: fdi, events: {})
 
       if (pevents and EPOLLERR) != 0 or (pevents and EPOLLHUP) != 0:
@@ -482,7 +479,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim
index 10e23c072..142e988d0 100644
--- a/lib/pure/ioselects/ioselectors_kqueue.nim
+++ b/lib/pure/ioselects/ioselectors_kqueue.nim
@@ -114,6 +114,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.changes = newSeqOfCap[KEvent](MAX_KQUEUE_EVENTS)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
   result.sock = usock
   result.kqFD = kqFD
   result.maxFD = maxFD.int
@@ -128,12 +131,6 @@ proc close*[T](s: Selector[T]) =
   if res1 != 0 or res2 != 0:
     raiseIOSelectorsError(osLastError())
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 proc newSelectEvent*(): SelectEvent =
   var fds: array[2, cint]
   if posix.pipe(fds) != 0:
@@ -221,7 +218,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   let fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   s.setKey(fdi, events, 0, data)
 
   if events != {}:
@@ -242,7 +239,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor $# is not registered in the queue!" % $fdi)
   doAssert(pkey.events * maskEvents == {})
 
@@ -269,7 +266,7 @@ proc registerTimer*[T](s: Selector[T], timeout: int, oneshot: bool,
                        data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   let events = if oneshot: {Event.Timer, Event.Oneshot} else: {Event.Timer}
   let flags: cushort = if oneshot: EV_ONESHOT or EV_ADD else: EV_ADD
@@ -291,7 +288,7 @@ proc registerSignal*[T](s: Selector[T], signal: int,
                         data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   s.setKey(fdi, {Event.Signal}, signal, data)
   var nmask, omask: Sigset
@@ -315,7 +312,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
                          data: T): int {.discardable.} =
   let fdi = getUnique(s)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
 
   var kflags: cushort = EV_ONESHOT or EV_ADD
   setKey(s, fdi, {Event.Process, Event.Oneshot}, pid, data)
@@ -331,7 +328,7 @@ proc registerProcess*[T](s: Selector[T], pid: int,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   let fdi = ev.rfd.int
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   setKey(s, fdi, {Event.User}, 0, data)
 
   modifyKQueue(s, fdi.uint, EVFILT_READ, EV_ADD, 0, 0, nil)
@@ -374,7 +371,7 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
 
   if pkey.events != {}:
@@ -434,7 +431,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
   modifyKQueue(s, uint(fdi), EVFILT_READ, EV_DELETE, 0, 0, nil)
   when not declared(CACHE_EVENTS):
@@ -593,7 +590,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim
index c36750c8d..9d708b0c1 100644
--- a/lib/pure/ioselects/ioselectors_poll.nim
+++ b/lib/pure/ioselects/ioselectors_poll.nim
@@ -70,6 +70,9 @@ proc newSelector*[T](): Selector[T] =
     result.fds = newSeq[SelectorKey[T]](maxFD)
     result.pollfds = newSeq[TPollFd](maxFD)
 
+  for i in 0 ..< maxFD:
+    result.fds[i].ident = InvalidIdent
+
 proc close*[T](s: Selector[T]) =
   when hasThreadSupport:
     deinitLock(s.lock)
@@ -77,12 +80,6 @@ proc close*[T](s: Selector[T]) =
     deallocSharedArray(s.pollfds)
     deallocShared(cast[pointer](s))
 
-template clearKey[T](key: ptr SelectorKey[T]) =
-  var empty: T
-  key.ident = 0
-  key.events = {}
-  key.data = empty
-
 template pollAdd[T](s: Selector[T], sock: cint, events: set[Event]) =
   withPollLock(s):
     var pollev: cshort = 0
@@ -135,7 +132,7 @@ proc registerHandle*[T](s: Selector[T], fd: int | SocketHandle,
                         events: set[Event], data: T) =
   var fdi = int(fd)
   s.checkFd(fdi)
-  doAssert(s.fds[fdi].ident == 0)
+  doAssert(s.fds[fdi].ident == InvalidIdent)
   setKey(s, fdi, events, 0, data)
   if events != {}: s.pollAdd(fdi.cint, events)
 
@@ -146,7 +143,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
   doAssert(pkey.events * maskEvents == {})
 
@@ -162,7 +159,7 @@ proc updateHandle*[T](s: Selector[T], fd: int | SocketHandle,
 
 proc registerEvent*[T](s: Selector[T], ev: SelectEvent, data: T) =
   var fdi = int(ev.rfd)
-  doAssert(s.fds[fdi].ident == 0, "Event is already registered in the queue!")
+  doAssert(s.fds[fdi].ident == InvalidIdent, "Event is already registered in the queue!")
   var events = {Event.User}
   setKey(s, fdi, events, 0, data)
   events.incl(Event.Read)
@@ -172,9 +169,9 @@ proc unregister*[T](s: Selector[T], fd: int|SocketHandle) =
   let fdi = int(fd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0,
+  doAssert(pkey.ident != InvalidIdent,
            "Descriptor [" & $fdi & "] is not registered in the queue!")
-  pkey.ident = 0
+  pkey.ident = InvalidIdent
   pkey.events = {}
   s.pollRemove(fdi.cint)
 
@@ -182,9 +179,9 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) =
   let fdi = int(ev.rfd)
   s.checkFd(fdi)
   var pkey = addr(s.fds[fdi])
-  doAssert(pkey.ident != 0, "Event is not registered in the queue!")
+  doAssert(pkey.ident != InvalidIdent, "Event is not registered in the queue!")
   doAssert(Event.User in pkey.events)
-  pkey.ident = 0
+  pkey.ident = InvalidIdent
   pkey.events = {}
   s.pollRemove(fdi.cint)
 
@@ -270,7 +267,7 @@ template isEmpty*[T](s: Selector[T]): bool =
   (s.count == 0)
 
 proc contains*[T](s: Selector[T], fd: SocketHandle|int): bool {.inline.} =
-  return s.fds[fd.int].ident != 0
+  return s.fds[fd.int].ident != InvalidIdent
 
 proc getData*[T](s: Selector[T], fd: SocketHandle|int): var T =
   let fdi = int(fd)
diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim
index 7ed250307..cd6a72b44 100644
--- a/lib/pure/ioselects/ioselectors_select.nim
+++ b/lib/pure/ioselects/ioselectors_select.nim
@@ -99,6 +99,9 @@ proc newSelector*[T](): Selector[T] =
     result = Selector[T]()
     result.fds = newSeq[SelectorKey[T]](FD_SETSIZE)
 
+  for i in 0 ..< FD_SETSIZE:
+    result.fds[i].ident = InvalidIdent
+
   IOFD_ZERO(addr result.rSet)
   IOFD_ZERO(addr result.wSet)
   IOFD_ZERO(addr result.eSet)
@@ -195,7 +198,7 @@ proc setSelectKey[T](s: Selector[T], fd: SocketHandle, events: set[Event],
   var i = 0
   let fdi = int(fd)
   while i < FD_SETSIZE:
-    if s.fds[i].ident == 0:
+    if s.fds[i].ident == InvalidIdent:
       var pkey = addr(s.fds[i])
       pkey.ident = fdi
       pkey.events = events
@@ -221,7 +224,7 @@ proc delKey[T](s: Selector[T], fd: SocketHandle) =
   var i = 0
   while i < FD_SETSIZE:
     if s.fds[i].ident == fd.int:
-      s.fds[i].ident = 0
+      s.fds[i].ident = InvalidIdent
       s.fds[i].events = {}
       s.fds[i].data = empty
       break
@@ -335,7 +338,7 @@ proc selectInto*[T](s: Selector[T], timeout: int,
     var k = 0
 
     while (i < FD_SETSIZE) and (k < count):
-      if s.fds[i].ident != 0:
+      if s.fds[i].ident != InvalidIdent:
         var flag = false
         var pkey = addr(s.fds[i])
         var rkey = ReadyKey(fd: int(pkey.ident), events: {})
diff --git a/lib/pure/json.nim b/lib/pure/json.nim
index 67f92dffe..b9279b18c 100644
--- a/lib/pure/json.nim
+++ b/lib/pure/json.nim
@@ -1004,6 +1004,13 @@ proc processElseBranch(recCaseNode, elseBranch, jsonNode, kindType,
       exprColonExpr.add(ifStmt)
 
 proc createConstructor(typeSym, jsonNode: NimNode): NimNode {.compileTime.}
+
+proc detectDistinctType(typeSym: NimNode): NimNode =
+  let
+    typeImpl = getTypeImpl(typeSym)
+    typeInst = getTypeInst(typeSym)
+  result = if typeImpl.typeKind == ntyDistinct: typeImpl else: typeInst
+
 proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
   ## Process a field from a ``RecList``.
   ##
@@ -1022,8 +1029,8 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
     # Add the field value.
     # -> jsonNode["`field`"]
     let indexedJsonNode = createJsonIndexer(jsonNode, $field)
-    exprColonExpr.add(createConstructor(getTypeInst(field), indexedJsonNode))
-
+    let typeNode = detectDistinctType(field)
+    exprColonExpr.add(createConstructor(typeNode, indexedJsonNode))
   of nnkRecCase:
     # A "case" field that introduces a variant.
     let exprColonExpr = newNimNode(nnkExprColonExpr)
@@ -1248,7 +1255,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
       let seqT = typeSym[1]
       let forLoopI = genSym(nskForVar, "i")
       let indexerNode = createJsonIndexer(jsonNode, forLoopI)
-      let constructorNode = createConstructor(seqT, indexerNode)
+      let constructorNode = createConstructor(detectDistinctType(seqT), indexerNode)
 
       # Create a statement expression containing a for loop.
       result = quote do:
@@ -1284,7 +1291,10 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
 
     # Handle all other types.
     let obj = getType(typeSym)
-    if obj.kind == nnkBracketExpr:
+    let typeNode = getTypeImpl(typeSym)
+    if typeNode.typeKind == ntyDistinct:
+      result = createConstructor(typeNode, jsonNode)
+    elif obj.kind == nnkBracketExpr:
       # When `Sym "Foo"` turns out to be a `ref object`.
       result = createConstructor(obj, jsonNode)
     else:
@@ -1295,6 +1305,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
     # TODO: The fact that `jsonNode` here works to give a good line number
     # is weird. Specifying typeSym should work but doesn't.
     error("Use a named tuple instead of: " & $toStrLit(typeSym), jsonNode)
+  of nnkDistinctTy:
+    var baseType = typeSym
+    # solve nested distinct types
+    while baseType.typeKind == ntyDistinct:
+      let impl = getTypeImpl(baseType[0])
+      if impl.typeKind != ntyDistinct:
+        baseType = baseType[0]
+        break
+      baseType = impl
+    let ret = createConstructor(baseType, jsonNode)
+    let typeInst = getTypeInst(typeSym)
+    result = quote do:
+      (
+        `typeInst`(`ret`)
+      )
   else:
     doAssert false, "Unable to create constructor for: " & $typeSym.kind
 
@@ -1418,7 +1443,7 @@ macro to*(node: JsonNode, T: typedesc): untyped =
   ##     doAssert data.person.age == 21
   ##     doAssert data.list == @[1, 2, 3, 4]
 
-  let typeNode = getTypeInst(T)
+  let typeNode = getTypeImpl(T)
   expectKind(typeNode, nnkBracketExpr)
   doAssert(($typeNode[0]).normalize == "typedesc")
 
diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim
index ff69ba61e..8f5f3a183 100644
--- a/lib/pure/mimetypes.nim
+++ b/lib/pure/mimetypes.nim
@@ -231,6 +231,7 @@ const mimes* = {
     "xcf": "application/x-xcf",
     "fig": "application/x-xfig",
     "xpi": "application/x-xpinstall",
+    "wasm": "application/wasm",
     "amr": "audio/amr",
     "awb": "audio/amr-wb",
     "amr": "audio/amr",
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index 5d2efebee..771e7de10 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -41,7 +41,7 @@
 ## immediately.
 ##
 ## .. code-block:: Nim
-##   var socket = newSocket()
+##   var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
 ##   socket.sendTo("192.168.0.1", Port(27960), "status\n")
 ##
 ## Creating a server
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index 84f492c9d..9cc83c372 100644
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -971,7 +971,7 @@ proc rawCreateDir(dir: string): bool =
     elif errno in {EEXIST, ENOSYS}:
       result = false
     else:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
   elif defined(posix):
     let res = mkdir(dir, 0o777)
     if res == 0'i32:
@@ -980,7 +980,7 @@ proc rawCreateDir(dir: string): bool =
       result = false
     else:
       #echo res
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
   else:
     when useWinUnicode:
       wrapUnary(res, createDirectoryW, dir)
@@ -992,7 +992,7 @@ proc rawCreateDir(dir: string): bool =
     elif getLastError() == 183'i32:
       result = false
     else:
-      raiseOSError(osLastError())
+      raiseOSError(osLastError(), dir)
 
 proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   tags: [WriteDirEffect, ReadDirEffect].} =
@@ -1005,7 +1005,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1",
   if result:
     # path already exists - need to check that it is indeed a directory
     if not existsDir(dir):
-      raise newException(IOError, "Failed to create the directory")
+      raise newException(IOError, "Failed to create '" & dir & "'")
 
 proc createDir*(dir: string) {.rtl, extern: "nos$1",
   tags: [WriteDirEffect, ReadDirEffect].} =
diff --git a/lib/pure/ospaths.nim b/lib/pure/ospaths.nim
index bc523e551..d0c5ef03f 100644
--- a/lib/pure/ospaths.nim
+++ b/lib/pure/ospaths.nim
@@ -147,7 +147,7 @@ else: # UNIX-like operating system
     DirSep* = '/'
     AltSep* = DirSep
     PathSep* = ':'
-    FileSystemCaseSensitive* = true
+    FileSystemCaseSensitive* = when defined(macosx): false else: true
     ExeExt* = ""
     ScriptExt* = ""
     DynlibFormat* = when defined(macosx): "lib$1.dylib" else: "lib$1.so"
@@ -410,6 +410,11 @@ proc cmpPaths*(pathA, pathB: string): int {.
   ## | 0 iff pathA == pathB
   ## | < 0 iff pathA < pathB
   ## | > 0 iff pathA > pathB
+  runnableExamples:
+    when defined(macosx):
+      doAssert cmpPaths("foo", "Foo") == 0
+    elif defined(posix):
+      doAssert cmpPaths("foo", "Foo") > 0
   if FileSystemCaseSensitive:
     result = cmp(pathA, pathB)
   else:
diff --git a/lib/pure/random.nim b/lib/pure/random.nim
index 01ea9c845..e565fccf8 100644
--- a/lib/pure/random.nim
+++ b/lib/pure/random.nim
@@ -110,7 +110,7 @@ proc random*[T](a: openArray[T]): T {.deprecated.} =
   ## Use ``rand`` instead.
   result = a[random(a.low..a.len)]
 
-proc rand*(r: var Rand; max: int): int {.benign.} =
+proc rand*(r: var Rand; max: Natural): int {.benign.} =
   ## Returns a random number in the range 0..max. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
@@ -128,7 +128,7 @@ proc rand*(max: int): int {.benign.} =
   ## number, i.e. a tickcount.
   rand(state, max)
 
-proc rand*(r: var Rand; max: float): float {.benign.} =
+proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
   ## Returns a random number in the range 0..max. The sequence of
   ## random number is always the same, unless `randomize` is called
   ## which initializes the random number generator with a "random"
@@ -218,4 +218,17 @@ when isMainModule:
     doAssert rand(0) == 0
     doAssert rand("a") == 'a'
 
+    when compileOption("rangeChecks"):
+      try:
+        discard rand(-1)
+        doAssert false
+      except RangeError:
+        discard
+
+      try:
+        discard rand(-1.0)
+        doAssert false
+      except RangeError:
+        discard
+
   main()
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 935250e08..e4c2b2124 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -261,6 +261,9 @@ else:
       param: int
       data: T
 
+  const
+    InvalidIdent = -1
+
   proc raiseIOSelectorsError[T](message: T) =
     var msg = ""
     when T is string:
@@ -302,6 +305,12 @@ else:
         if posix.sigprocmask(SIG_UNBLOCK, newmask, oldmask) == -1:
           raiseIOSelectorsError(osLastError())
 
+  template clearKey[T](key: ptr SelectorKey[T]) =
+    var empty: T
+    key.ident = InvalidIdent
+    key.events = {}
+    key.data = empty
+
   when defined(linux):
     include ioselects/ioselectors_epoll
   elif bsdPlatform:
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index bc4de7ee4..cdb7a4466 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -181,10 +181,27 @@ elif defined(windows):
 type
   Month* = enum ## Represents a month. Note that the enum starts at ``1``, so ``ord(month)`` will give
                 ## the month number in the range ``[1..12]``.
-    mJan = 1, mFeb, mMar, mApr, mMay, mJun, mJul, mAug, mSep, mOct, mNov, mDec
+    mJan = (1, "January")
+    mFeb = "February"
+    mMar = "March"
+    mApr = "April"
+    mMay = "May"
+    mJun = "June"
+    mJul = "July"
+    mAug = "August"
+    mSep = "September"
+    mOct = "October"
+    mNov = "November"
+    mDec = "December"
 
   WeekDay* = enum ## Represents a weekday.
-    dMon, dTue, dWed, dThu, dFri, dSat, dSun
+    dMon = "Monday"
+    dTue = "Tuesday"
+    dWed = "Wednesday"
+    dThu = "Thursday"
+    dFri = "Friday"
+    dSat = "Saturday"
+    dSun = "Sunday"
 
   MonthdayRange* = range[1..31]
   HourRange* = range[0..23]
@@ -1074,20 +1091,6 @@ proc getClockStr*(): string {.rtl, extern: "nt$1", tags: [TimeEffect].} =
   result = intToStr(ti.hour, 2) & ':' & intToStr(ti.minute, 2) &
     ':' & intToStr(ti.second, 2)
 
-proc `$`*(day: WeekDay): string =
-  ## Stringify operator for ``WeekDay``.
-  const lookup: array[WeekDay, string] = ["Monday", "Tuesday", "Wednesday",
-     "Thursday", "Friday", "Saturday", "Sunday"]
-  return lookup[day]
-
-proc `$`*(m: Month): string =
-  ## Stringify operator for ``Month``.
-  const lookup: array[Month, string] = ["January", "February", "March",
-      "April", "May", "June", "July", "August", "September", "October",
-      "November", "December"]
-  return lookup[m]
-
-
 proc toParts* (ti: TimeInterval): TimeIntervalParts =
   ## Converts a `TimeInterval` into an array consisting of its time units,
   ## starting with nanoseconds and ending with years
diff --git a/lib/system.nim b/lib/system.nim
index f96f97dd6..0cfa39e19 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1465,8 +1465,8 @@ when defined(nimdoc):
     ##
     ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
     ## have any compile time effect. If you need to stop the compiler inside a
-    ## macro, use the `error <manual.html#error-pragma>`_ or `fatal
-    ## <manual.html#fatal-pragma>`_ pragmas.
+    ## macro, use the `error <manual.html#pragmas-error-pragma>`_ or `fatal
+    ## <manual.html#pragmas-fatal-pragma>`_ pragmas.
 
 elif defined(genode):
   include genode/env
@@ -2429,7 +2429,7 @@ iterator fieldPairs*[T: tuple|object](x: T): RootObj {.
   ## When you iterate over objects with different field types you have to use
   ## the compile time ``when`` instead of a runtime ``if`` to select the code
   ## you want to run for each type. To perform the comparison use the `is
-  ## operator <manual.html#is-operator>`_. Example:
+  ## operator <manual.html#generics-is-operator>`_. Example:
   ##
   ## .. code-block:: Nim
   ##
@@ -3771,7 +3771,9 @@ template doAssert*(cond: bool, msg = "") =
   ## same as `assert` but is always turned on and not affected by the
   ## ``--assertions`` command line switch.
   bind instantiationInfo
-  {.line: instantiationInfo().}:
+  # NOTE: `true` is correct here; --excessiveStackTrace:on will control whether
+  # or not to output full paths.
+  {.line: instantiationInfo(-1, true).}:
     if not cond:
       raiseAssert(astToStr(cond) & ' ' &
                   instantiationInfo(-1, false).fileName & '(' &
diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim
index 95becde22..b090117a9 100644
--- a/lib/system/alloc.nim
+++ b/lib/system/alloc.nim
@@ -116,6 +116,8 @@ type
     nextChunkSize: int
     bottomData: AvlNode
     heapLinks: HeapLinks
+    when defined(nimTypeNames):
+      allocCounter, deallocCounter: int
 
 const
   fsLookupTable: array[byte, int8] = [
@@ -434,8 +436,9 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
         a.nextChunkSize = PageSize*4
       else:
         a.nextChunkSize = min(roundup(usedMem shr 2, PageSize), a.nextChunkSize * 2)
-  var size = size
+        a.nextChunkSize = min(a.nextChunkSize, MaxBigChunkSize)
 
+  var size = size
   if size > a.nextChunkSize:
     result = cast[PBigChunk](osAllocPages(size))
   else:
@@ -451,10 +454,10 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk =
   a.addHeapLink(result, size)
   when defined(debugHeapLinks):
     cprintf("owner: %p; result: %p; next pointer %p; size: %ld\n", addr(a),
-      result, result.heapLink, result.origSize)
+      result, result.heapLink, result.size)
 
   when defined(memtracker):
-    trackLocation(addr result.origSize, sizeof(int))
+    trackLocation(addr result.size, sizeof(int))
 
   sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1")
   #zeroMem(result, size)
@@ -524,7 +527,7 @@ proc updatePrevSize(a: var MemRegion, c: PBigChunk,
 proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk =
   result = cast[PBigChunk](cast[ByteAddress](c) +% size)
   result.size = c.size - size
-  track("result.origSize", addr result.origSize, sizeof(int))
+  track("result.size", addr result.size, sizeof(int))
   # XXX check if these two nil assignments are dead code given
   # addChunkToMatrix's implementation:
   result.next = nil
@@ -599,7 +602,7 @@ proc getBigChunk(a: var MemRegion, size: int): PBigChunk =
       splitChunk(a, result, size)
   # set 'used' to to true:
   result.prevSize = 1
-  track("setUsedToFalse", addr result.origSize, sizeof(int))
+  track("setUsedToFalse", addr result.size, sizeof(int))
 
   incl(a, a.chunkStarts, pageIndex(result))
   dec(a.freeMem, size)
@@ -737,6 +740,8 @@ when false:
         result = nil
 
 proc rawAlloc(a: var MemRegion, requestedSize: int): pointer =
+  when defined(nimTypeNames):
+    inc(a.allocCounter)
   sysAssert(allocInv(a), "rawAlloc: begin")
   sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken")
   sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small")
@@ -810,6 +815,8 @@ proc rawAlloc0(a: var MemRegion, requestedSize: int): pointer =
   zeroMem(result, requestedSize)
 
 proc rawDealloc(a: var MemRegion, p: pointer) =
+  when defined(nimTypeNames):
+    inc(a.deallocCounter)
   #sysAssert(isAllocatedPtr(a, p), "rawDealloc: no allocated pointer")
   sysAssert(allocInv(a), "rawDealloc: begin")
   var c = pageAddr(p)
@@ -961,7 +968,7 @@ proc deallocOsPages(a: var MemRegion) =
       let (p, size) = it.chunks[i]
       when defined(debugHeapLinks):
         cprintf("owner %p; dealloc A: %p size: %ld; next: %p\n", addr(a),
-          it, it.origSize, next)
+          it, it.size, next)
       sysAssert size >= PageSize, "origSize too small"
       osDeallocPages(p, size)
     it = next
@@ -975,6 +982,10 @@ proc getOccupiedMem(a: MemRegion): int {.inline.} =
   result = a.occ
   # a.currMem - a.freeMem
 
+when defined(nimTypeNames):
+  proc getMemCounters(a: MemRegion): (int, int) {.inline.} =
+    (a.allocCounter, a.deallocCounter)
+
 # ---------------------- thread memory region -------------------------------
 
 template instantiateForRegion(allocator: untyped) =
@@ -1018,6 +1029,9 @@ template instantiateForRegion(allocator: untyped) =
   proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem()
   proc getMaxMem*(): int = return getMaxMem(allocator)
 
+  when defined(nimTypeNames):
+    proc getMemCounters*(): (int, int) = getMemCounters(allocator)
+
   # -------------------- shared heap region ----------------------------------
   when hasThreadSupport:
     var sharedHeap: MemRegion
diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim
index 4d21f8747..f593d4c99 100644
--- a/lib/system/ansi_c.nim
+++ b/lib/system/ansi_c.nim
@@ -27,6 +27,9 @@ proc c_strcmp(a, b: cstring): cint {.
   importc: "strcmp", header: "<string.h>", noSideEffect.}
 proc c_strlen(a: cstring): csize {.
   importc: "strlen", header: "<string.h>", noSideEffect.}
+proc c_abort() {.
+  importc: "abort", header: "<stdlib.h>", noSideEffect.}
+
 
 when defined(linux) and defined(amd64):
   type
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index 425963f3f..74ac68eea 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -548,7 +548,10 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer =
   gcTrace(res, csAllocated)
   track("growObj old", ol, 0)
   track("growObj new", res, newsize)
-  when reallyDealloc:
+  when defined(nimIncrSeqV3):
+    # since we steal the old seq's contents, we set the old length to 0.
+    cast[PGenericSeq](old).len = 0
+  elif reallyDealloc:
     sysAssert(allocInv(gch.region), "growObj before dealloc")
     if ol.refcount shr rcShift <=% 1:
       # free immediately to save space:
diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim
index 711a610bf..dcea0c4cc 100644
--- a/lib/system/gc_common.nim
+++ b/lib/system/gc_common.nim
@@ -57,6 +57,9 @@ when defined(nimTypeNames):
     for i in 0 .. n-1:
       c_fprintf(stdout, "[Heap] %s: #%ld; bytes: %ld\n", a[i][0], a[i][1], a[i][2])
     c_fprintf(stdout, "[Heap] total number of bytes: %ld\n", totalAllocated)
+    when defined(nimTypeNames):
+      let (allocs, deallocs) = getMemCounters()
+      c_fprintf(stdout, "[Heap] allocs/deallocs: %ld/%ld\n", allocs, deallocs)
 
   when defined(nimGcRefLeak):
     proc oomhandler() =
diff --git a/lib/system/memtracker.nim b/lib/system/memtracker.nim
index ae0297438..1b1f18039 100644
--- a/lib/system/memtracker.nim
+++ b/lib/system/memtracker.nim
@@ -73,12 +73,12 @@ proc addEntry(entry: LogEntry) =
       let x = cast[proc() {.nimcall, tags: [], gcsafe, locks: 0.}](writeStackTrace)
       x()
       quit 1
-      if gLog.count > high(gLog.data):
-        gLogger(gLog)
-        gLog.count = 0
-      gLog.data[gLog.count] = entry
-      inc gLog.count
-      gLog.disabled = false
+      #if gLog.count > high(gLog.data):
+      #  gLogger(gLog)
+      #  gLog.count = 0
+      #gLog.data[gLog.count] = entry
+      #inc gLog.count
+      #gLog.disabled = false
 
 proc memTrackerWrite(address: pointer; size: int; file: cstring; line: int) {.compilerProc.} =
   addEntry LogEntry(op: "write", address: address,
diff --git a/lib/system/sysio.nim b/lib/system/sysio.nim
index 2ece56916..7a10849dd 100644
--- a/lib/system/sysio.nim
+++ b/lib/system/sysio.nim
@@ -417,12 +417,18 @@ proc setStdIoUnbuffered() =
     discard c_setvbuf(stdin, nil, IONBF, 0)
 
 when declared(stdout):
+  when defined(windows) and compileOption("threads"):
+    var echoLock: SysLock
+    initSysLock echoLock
+
   proc echoBinSafe(args: openArray[string]) {.compilerProc.} =
     # flockfile deadlocks some versions of Android 5.x.x
     when not defined(windows) and not defined(android) and not defined(nintendoswitch):
       proc flockfile(f: File) {.importc, noDecl.}
       proc funlockfile(f: File) {.importc, noDecl.}
       flockfile(stdout)
+    when defined(windows) and compileOption("threads"):
+      acquireSys echoLock
     for s in args:
       discard c_fwrite(s.cstring, s.len, 1, stdout)
     const linefeed = "\n" # can be 1 or more chars
@@ -430,5 +436,7 @@ when declared(stdout):
     discard c_fflush(stdout)
     when not defined(windows) and not defined(android) and not defined(nintendoswitch):
       funlockfile(stdout)
+    when defined(windows) and compileOption("threads"):
+      releaseSys echoLock
 
 {.pop.}
diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim
index 8e9e18b05..19c2c62ad 100644
--- a/lib/system/sysstr.nim
+++ b/lib/system/sysstr.nim
@@ -184,8 +184,13 @@ proc addChar(s: NimString, c: char): NimString =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[NimString](growObj(result,
-        sizeof(TGenericSeq) + r + 1))
+      when defined(nimIncrSeqV3):
+        result = rawNewStringNoInit(r)
+        result.len = s.len
+        copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
+      else:
+        result = cast[NimString](growObj(result,
+          sizeof(TGenericSeq) + r + 1))
       result.reserved = r
   result.data[result.len] = c
   result.data[result.len+1] = '\0'
@@ -228,8 +233,13 @@ proc resizeString(dest: NimString, addlen: int): NimString {.compilerRtl.} =
   elif dest.len + addlen <= dest.space:
     result = dest
   else: # slow path:
-    var sp = max(resize(dest.space), dest.len + addlen)
-    result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
+    let sp = max(resize(dest.space), dest.len + addlen)
+    when defined(nimIncrSeqV3):
+      result = rawNewStringNoInit(sp)
+      result.len = dest.len
+      copyMem(addr result.data[0], unsafeAddr(dest.data[0]), dest.len+1)
+    else:
+      result = cast[NimString](growObj(dest, sizeof(TGenericSeq) + sp + 1))
     result.reserved = sp
     #result = rawNewString(sp)
     #copyMem(result, dest, dest.len + sizeof(TGenericSeq))
@@ -246,13 +256,21 @@ proc appendChar(dest: NimString, c: char) {.compilerproc, inline.} =
   inc(dest.len)
 
 proc setLengthStr(s: NimString, newLen: int): NimString {.compilerRtl.} =
-  var n = max(newLen, 0)
+  let n = max(newLen, 0)
   if s == nil:
     result = mnewString(newLen)
   elif n <= s.space:
     result = s
   else:
-    result = resizeString(s, n)
+    let sp = max(resize(s.space), newLen)
+    when defined(nimIncrSeqV3):
+      result = rawNewStringNoInit(sp)
+      result.len = s.len
+      copyMem(addr result.data[0], unsafeAddr(s.data[0]), s.len+1)
+      zeroMem(addr result.data[s.len], newLen - s.len)
+      result.reserved = sp
+    else:
+      result = resizeString(s, n)
   result.len = n
   result.data[n] = '\0'
 
@@ -282,6 +300,9 @@ proc incrSeqV2(seq: PGenericSeq, elemSize: int): PGenericSeq {.compilerProc.} =
                                GenericSeqSize))
     result.reserved = r
 
+template `+!`(p: pointer, s: int): pointer =
+  cast[pointer](cast[int](p) +% s)
+
 proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
   if s == nil:
     result = cast[PGenericSeq](newSeq(typ, 1))
@@ -290,9 +311,16 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerProc.} =
     result = s
     if result.len >= result.space:
       let r = resize(result.space)
-      result = cast[PGenericSeq](growObj(result, typ.base.size * r +
+      when defined(nimIncrSeqV3):
+        result = cast[PGenericSeq](newSeq(typ, r))
+        result.len = s.len
+        copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * typ.base.size)
+        # since we steal the content from 's', it's crucial to set s's len to 0.
+        s.len = 0
+      else:
+        result = cast[PGenericSeq](growObj(result, typ.base.size * r +
                                 GenericSeqSize))
-      result.reserved = r
+        result.reserved = r
 
 proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
     compilerRtl, inl.} =
@@ -336,10 +364,43 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, newLen: int): PGenericSeq {.
 
 proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {.
     compilerRtl.} =
+  sysAssert typ.kind == tySequence, "setLengthSeqV2: type is not a seq"
   if s == nil:
     result = cast[PGenericSeq](newSeq(typ, newLen))
   else:
-    result = setLengthSeq(s, typ.base.size, newLen)
+    when defined(nimIncrSeqV3):
+      let elemSize = typ.base.size
+      if s.space < newLen:
+        let r = max(resize(s.space), newLen)
+        result = cast[PGenericSeq](newSeq(typ, r))
+        copyMem(result +! GenericSeqSize, s +! GenericSeqSize, s.len * elemSize)
+        # since we steal the content from 's', it's crucial to set s's len to 0.
+        s.len = 0
+      elif newLen < s.len:
+        result = s
+        # we need to decref here, otherwise the GC leaks!
+        when not defined(boehmGC) and not defined(nogc) and
+            not defined(gcMarkAndSweep) and not defined(gogc) and
+            not defined(gcRegions):
+          if ntfNoRefs notin typ.base.flags:
+            for i in newLen..result.len-1:
+              forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +%
+                                GenericSeqSize +% (i*%elemSize)),
+                                extGetCellType(result).base, waZctDecRef)
+
+        # XXX: zeroing out the memory can still result in crashes if a wiped-out
+        # cell is aliased by another pointer (ie proc parameter or a let variable).
+        # This is a tough problem, because even if we don't zeroMem here, in the
+        # presence of user defined destructors, the user will expect the cell to be
+        # "destroyed" thus creating the same problem. We can destoy the cell in the
+        # finalizer of the sequence, but this makes destruction non-deterministic.
+        zeroMem(cast[pointer](cast[ByteAddress](result) +% GenericSeqSize +%
+              (newLen*%elemSize)), (result.len-%newLen) *% elemSize)
+      else:
+        result = s
+      result.len = newLen
+    else:
+      result = setLengthSeq(s, typ.base.size, newLen)
 
 # --------------- other string routines ----------------------------------
 proc add*(result: var string; x: int64) =