summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgstmts.nim4
-rw-r--r--compiler/cgmeth.nim9
-rw-r--r--compiler/jsgen.nim1
-rw-r--r--compiler/jstypes.nim2
-rw-r--r--compiler/vm.nim10
-rw-r--r--compiler/vmdeps.nim7
-rw-r--r--doc/tut1.rst12
-rw-r--r--lib/pure/asyncdispatch.nim12
-rw-r--r--lib/pure/asyncfile.nim10
-rw-r--r--lib/pure/asyncnet.nim9
-rw-r--r--lib/pure/collections/tables.nim41
-rw-r--r--lib/pure/httpclient.nim8
-rw-r--r--lib/pure/net.nim358
-rw-r--r--lib/pure/osproc.nim6
-rw-r--r--lib/pure/parseutils.nim2
-rw-r--r--lib/pure/strutils.nim2
-rw-r--r--lib/system/atomics.nim20
-rw-r--r--lib/system/excpt.nim9
-rw-r--r--lib/system/syslocks.nim110
-rw-r--r--lib/upcoming/asyncdispatch.nim12
-rw-r--r--nimsuggest/nimsuggest.nim2
-rw-r--r--tests/async/tasyncfilewrite.nim17
-rw-r--r--tests/collections/ttables.nim15
-rw-r--r--tests/macros/tgettypeinst.nim65
-rw-r--r--tests/stdlib/thttpclient.nim20
-rw-r--r--tests/testament/specs.nim11
-rw-r--r--tests/testament/tester.nim6
-rw-r--r--tools/nimgrab.nim6
-rw-r--r--web/news/e031_version_0_16_2.rst2
29 files changed, 514 insertions, 274 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index 80500f508..1bb26c48d 100644
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -332,7 +332,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
 
   var alreadyPoppedCnt = p.inExceptBlock
   for i in countup(1, howManyTrys):
-    if not p.module.compileToCpp:
+    if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
       # Pop safe points generated by try
       if alreadyPoppedCnt > 0:
         dec alreadyPoppedCnt
@@ -354,7 +354,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
   for i in countdown(howManyTrys-1, 0):
     p.nestedTryStmts.add(stack[i])
 
-  if not p.module.compileToCpp:
+  if not p.module.compileToCpp or optNoCppExceptions in gGlobalOptions:
     # Pop exceptions that was handled by the
     # except-blocks we are in
     for i in countdown(howManyExcepts-1, 0):
diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim
index e14306e56..1165ec932 100644
--- a/compiler/cgmeth.nim
+++ b/compiler/cgmeth.nim
@@ -233,6 +233,12 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
   var disp = newNodeI(nkIfStmt, base.info)
   var ands = getSysSym("and")
   var iss = getSysSym("of")
+  for col in countup(1, paramLen - 1):
+    if contains(relevantCols, col):
+      let param = base.typ.n.sons[col].sym
+      if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
+        addSon(nilchecks, newTree(nkCall,
+            newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
   for meth in countup(0, high(methods)):
     var curr = methods[meth]      # generate condition:
     var cond: PNode = nil
@@ -242,9 +248,6 @@ proc genDispatcher(methods: TSymSeq, relevantCols: IntSet): PSym =
         addSon(isn, newSymNode(iss))
         let param = base.typ.n.sons[col].sym
         addSon(isn, newSymNode(param))
-        if param.typ.skipTypes(abstractInst).kind in {tyRef, tyPtr}:
-          addSon(nilchecks, newTree(nkCall,
-                newSymNode(getCompilerProc"chckNilDisp"), newSymNode(param)))
         addSon(isn, newNodeIT(nkType, base.info, curr.typ.sons[col]))
         if cond != nil:
           var a = newNodeIT(nkCall, base.info, getSysType(tyBool))
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index eb3fb9f47..ee35356c9 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -2272,7 +2272,6 @@ proc myProcess(b: PPassContext, n: PNode): PNode =
   genModule(p, n)
   add(p.g.code, p.locals)
   add(p.g.code, p.body)
-  globals.unique = p.unique
 
 proc wholeCode(graph: ModuleGraph; m: BModule): Rope =
   for prc in globals.forwarded:
diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim
index f49bd7668..ae30861e7 100644
--- a/compiler/jstypes.nim
+++ b/compiler/jstypes.nim
@@ -59,7 +59,7 @@ proc genObjectFields(p: PProc, typ: PType, n: PNode): Rope =
         u = rope(lengthOrd(field.typ))
       else: internalError(n.info, "genObjectFields(nkRecCase)")
       if result != nil: add(result, ", " & tnl)
-      addf(result, "[SetConstr($1), $2]",
+      addf(result, "[setConstr($1), $2]",
            [u, genObjectFields(p, typ, lastSon(b))])
     result = ("{kind: 3, offset: \"$1\", len: $3, " &
         "typ: $2, name: $4, sons: [$5]}") % [
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 6a9545193..3c475cf57 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1651,8 +1651,16 @@ proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
   for i in 0 .. <gp.len:
     if sfImmediate notin sym.flags:
       let idx = sym.typ.len + i
-      tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+      if idx < n.len:
+        tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+      else:
+        dec(evalMacroCounter)
+        c.callsite = nil
+        localError(n.info, "expected " & $gp.len &
+                   " generic parameter(s)")
     elif gp[i].sym.typ.kind in {tyStatic, tyTypeDesc}:
+      dec(evalMacroCounter)
+      c.callsite = nil
       globalError(n.info, "static[T] or typedesc nor supported for .immediate macros")
   # temporary storage:
   #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim
index 8c7388643..b2b1ec92b 100644
--- a/compiler/vmdeps.nim
+++ b/compiler/vmdeps.nim
@@ -175,7 +175,12 @@ proc mapTypeToAstX(t: PType; info: TLineInfo;
           result.add mapTypeToAst(t.sons[i], info)
     else:
       result = mapTypeToAstX(t.lastSon, info, inst, allowRecursion)
-  of tyGenericBody, tyOrdinal:
+  of tyGenericBody:
+    if inst:
+      result = mapTypeToAstX(t.lastSon, info, inst, true)
+    else:
+      result = mapTypeToAst(t.lastSon, info)
+  of tyOrdinal:
     result = mapTypeToAst(t.lastSon, info)
   of tyDistinct:
     if inst:
diff --git a/doc/tut1.rst b/doc/tut1.rst
index 06ee84c0d..436b3880d 100644
--- a/doc/tut1.rst
+++ b/doc/tut1.rst
@@ -599,7 +599,7 @@ Result variable
 A procedure that returns a value has an implicit ``result`` variable declared
 that represents the return value. A ``return`` statement with no expression is a
 shorthand for ``return result``. The ``result`` value is always returned
-automatically at the end a procedure if there is no ``return`` statement at
+automatically at the end of a procedure if there is no ``return`` statement at
 the exit.
 
 .. code-block:: nim
@@ -1074,8 +1074,8 @@ at runtime by 0, the second by 1 and so on. For example:
     Direction = enum
       north, east, south, west
 
-  var x = south      # `x` is of type `Direction`; its value is `south`
-  echo x           # writes "south" to `stdout`
+  var x = south     # `x` is of type `Direction`; its value is `south`
+  echo x            # writes "south" to `stdout`
 
 All the comparison operators can be used with enumeration types.
 
@@ -1132,11 +1132,11 @@ A subrange type is a range of values from an integer or enumeration type
 
 .. code-block:: nim
   type
-    Subrange = range[0..5]
+    MySubrange = range[0..5]
 
 
-``Subrange`` is a subrange of ``int`` which can only hold the values 0
-to 5. Assigning any other value to a variable of type ``Subrange`` is a
+``MySubrange`` is a subrange of ``int`` which can only hold the values 0
+to 5. Assigning any other value to a variable of type ``MySubrange`` is a
 compile-time or runtime error. Assignments from the base type to one of its
 subrange types (and vice versa) are allowed.
 
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 6a877be30..1697384e0 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, times, heapqueue, options
+import os, tables, strutils, times, heapqueue, options
 
 import nativesockets, net, deques
 
@@ -242,6 +242,11 @@ when defined(windows) or defined(nimdoc):
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
     let p = getGlobalDispatcher()
@@ -931,6 +936,11 @@ else:
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc update(fd: AsyncFD, events: set[Event]) =
     let p = getGlobalDispatcher()
     assert fd.SocketHandle in p.selector
diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim
index 9bd060e30..8fb30075c 100644
--- a/lib/pure/asyncfile.nim
+++ b/lib/pure/asyncfile.nim
@@ -339,13 +339,17 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == size.int32
-            f.offset.inc(size)
             retFuture.complete()
           else:
             retFuture.fail(newException(OSError, osErrorMsg(errcode)))
     )
+    # passing -1 here should work according to MSDN, but doesn't. For more
+    # information see
+    # http://stackoverflow.com/questions/33650899/does-asynchronous-file-
+    #   appending-in-windows-preserve-order
     ol.offset = DWord(f.offset and 0xffffffff)
     ol.offsetHigh = DWord(f.offset shr 32)
+    f.offset.inc(size)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
     let ret = writeFile(f.fd.Handle, buf, size.int32, nil,
@@ -364,7 +368,6 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] =
         retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
       else:
         assert bytesWritten == size.int32
-        f.offset.inc(size)
         retFuture.complete()
   else:
     var written = 0
@@ -410,7 +413,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
         if not retFuture.finished:
           if errcode == OSErrorCode(-1):
             assert bytesCount == data.len.int32
-            f.offset.inc(data.len)
             retFuture.complete()
           else:
             retFuture.fail(newException(OSError, osErrorMsg(errcode)))
@@ -420,6 +422,7 @@ proc write*(f: AsyncFile, data: string): Future[void] =
     )
     ol.offset = DWord(f.offset and 0xffffffff)
     ol.offsetHigh = DWord(f.offset shr 32)
+    f.offset.inc(data.len)
 
     # According to MSDN we're supposed to pass nil to lpNumberOfBytesWritten.
     let ret = writeFile(f.fd.Handle, buffer, data.len.int32, nil,
@@ -441,7 +444,6 @@ proc write*(f: AsyncFile, data: string): Future[void] =
         retFuture.fail(newException(OSError, osErrorMsg(osLastError())))
       else:
         assert bytesWritten == data.len.int32
-        f.offset.inc(data.len)
         retFuture.complete()
   else:
     var written = 0
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 11b7998b2..9f73bc3cf 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -647,9 +647,12 @@ when defineSsl:
     sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut)
 
   proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket,
-                            handshake: SslHandshakeType) =
+                            handshake: SslHandshakeType,
+                            hostname: string = nil) =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
+    ## ``hostname`` should be specified so that the client knows which hostname
+    ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
     ## an SSL handshake immediately.
@@ -660,6 +663,10 @@ when defineSsl:
 
     case handshake
     of handshakeAsClient:
+      if not hostname.isNil and not isIpAddress(hostname):
+        # Set the SNI address for this connection. This call can fail if
+        # we're not using TLSv1+.
+        discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
       sslSetConnectState(socket.sslHandle)
     of handshakeAsServer:
       sslSetAcceptState(socket.sslHandle)
diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim
index 969802cfc..323af5a38 100644
--- a/lib/pure/collections/tables.nim
+++ b/lib/pure/collections/tables.nim
@@ -269,6 +269,18 @@ proc del*[A, B](t: var Table[A, B], key: A) =
   ## deletes `key` from hash table `t`.
   delImpl()
 
+proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool =
+  ## Deletes the ``key`` from the table.
+  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
+  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## unchanged.
+  var hc: Hash
+  var index = rawGet(t, key, hc)
+  result = index >= 0
+  if result:
+    shallowCopy(val, t.data[index].val)
+    delImplIdx(t, index)
+
 proc enlarge[A, B](t: var Table[A, B]) =
   var n: KeyValuePairSeq[A, B]
   newSeq(n, len(t.data) * growthFactor)
@@ -424,6 +436,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) =
   ## deletes `key` from hash table `t`.
   t[].del(key)
 
+proc take*[A, B](t: TableRef[A, B], key: A, val: var B): bool =
+  ## Deletes the ``key`` from the table.
+  ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the
+  ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is
+  ## unchanged.
+  result = t[].take(key, val)
+
 proc newTable*[A, B](initialSize=64): TableRef[A, B] =
   new(result)
   result[] = initTable[A, B](initialSize)
@@ -625,7 +644,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool =
   while ht >= 0 and hs >= 0:
     var nxtt = t.data[ht].next
     var nxts = s.data[hs].next
-    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode): 
+    if isFilled(t.data[ht].hcode) and isFilled(s.data[hs].hcode):
       if (s.data[hs].key != t.data[ht].key) and (s.data[hs].val != t.data[ht].val):
         return false
     ht = nxtt
@@ -829,7 +848,7 @@ proc clear*[A](t: CountTableRef[A]) =
 proc clear*[A](t: var CountTable[A]) =
   ## Resets the table so that it is empty.
   clearImpl()
-  
+
 iterator pairs*[A](t: CountTable[A]): (A, int) =
   ## iterates over any (key, value) pair in the table `t`.
   for h in 0..high(t.data):
@@ -1256,17 +1275,17 @@ when isMainModule:
     var b = newOrderedTable[string, string](initialSize=2)
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
-    assert a == b    
+    assert a == b
 
   block: #5482
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()  
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
     var b = newOrderedTable[string, string](initialSize=2)
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
-    assert a == b    
+    assert a == b
 
   block: #5487
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()  
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
     var b = newOrderedTable[string, string]() # notice, default size!
     b.add("wrong?", "foo")
     b.add("wrong?", "foo2")
@@ -1279,13 +1298,13 @@ when isMainModule:
     b.add("wrong?", "foo2")
     assert a == b
 
-  block: 
-    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() 
-    var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable() 
+  block:
+    var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable()
+    var b = [("wrong?","foo"), ("wrong?", "foo2")].newOrderedTable()
     var c = newOrderedTable[string, string]() # notice, default size!
     c.add("wrong?", "foo")
-    c.add("wrong?", "foo2")    
+    c.add("wrong?", "foo2")
     assert a == b
     assert a == c
-    
+
 
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 1b8a20b65..4f43177a8 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -512,7 +512,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
         raise newException(HttpRequestError,
                            "The proxy server rejected a CONNECT request, " &
                            "so a secure connection could not be established.")
-      sslContext.wrapConnectedSocket(s, handshakeAsClient)
+      sslContext.wrapConnectedSocket(s, handshakeAsClient, hostUrl.hostname)
     else:
       raise newException(HttpRequestError, "SSL support not available. Cannot connect via proxy over SSL")
   else:
@@ -1060,7 +1060,8 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
     when defined(ssl):
       if isSsl:
         try:
-          client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
+          client.sslContext.wrapConnectedSocket(
+            client.socket, handshakeAsClient, url.hostname)
         except:
           client.socket.close()
           raise getCurrentException()
@@ -1102,7 +1103,8 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: string,
         raise newException(HttpRequestError,
                            "The proxy server rejected a CONNECT request, " &
                            "so a secure connection could not be established.")
-      client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
+      client.sslContext.wrapConnectedSocket(
+        client.socket, handshakeAsClient, requestUrl.hostname)
       client.proxy = nil
     else:
       raise newException(HttpRequestError,
diff --git a/lib/pure/net.nim b/lib/pure/net.nim
index d175bd537..629e916fa 100644
--- a/lib/pure/net.nim
+++ b/lib/pure/net.nim
@@ -237,6 +237,180 @@ proc newSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
     raiseOSError(osLastError())
   result = newSocket(fd, domain, sockType, protocol, buffered)
 
+proc parseIPv4Address(address_str: string): IpAddress =
+  ## Parses IPv4 adresses
+  ## Raises EInvalidValue on errors
+  var
+    byteCount = 0
+    currentByte:uint16 = 0
+    seperatorValid = false
+
+  result.family = IpAddressFamily.IPv4
+
+  for i in 0 .. high(address_str):
+    if address_str[i] in strutils.Digits: # Character is a number
+      currentByte = currentByte * 10 +
+        cast[uint16](ord(address_str[i]) - ord('0'))
+      if currentByte > 255'u16:
+        raise newException(ValueError,
+          "Invalid IP Address. Value is out of range")
+      seperatorValid = true
+    elif address_str[i] == '.': # IPv4 address separator
+      if not seperatorValid or byteCount >= 3:
+        raise newException(ValueError,
+          "Invalid IP Address. The address consists of too many groups")
+      result.address_v4[byteCount] = cast[uint8](currentByte)
+      currentByte = 0
+      byteCount.inc
+      seperatorValid = false
+    else:
+      raise newException(ValueError,
+        "Invalid IP Address. Address contains an invalid character")
+
+  if byteCount != 3 or not seperatorValid:
+    raise newException(ValueError, "Invalid IP Address")
+  result.address_v4[byteCount] = cast[uint8](currentByte)
+
+proc parseIPv6Address(address_str: string): IpAddress =
+  ## Parses IPv6 adresses
+  ## Raises EInvalidValue on errors
+  result.family = IpAddressFamily.IPv6
+  if address_str.len < 2:
+    raise newException(ValueError, "Invalid IP Address")
+
+  var
+    groupCount = 0
+    currentGroupStart = 0
+    currentShort:uint32 = 0
+    seperatorValid = true
+    dualColonGroup = -1
+    lastWasColon = false
+    v4StartPos = -1
+    byteCount = 0
+
+  for i,c in address_str:
+    if c == ':':
+      if not seperatorValid:
+        raise newException(ValueError,
+          "Invalid IP Address. Address contains an invalid seperator")
+      if lastWasColon:
+        if dualColonGroup != -1:
+          raise newException(ValueError,
+            "Invalid IP Address. Address contains more than one \"::\" seperator")
+        dualColonGroup = groupCount
+        seperatorValid = false
+      elif i != 0 and i != high(address_str):
+        if groupCount >= 8:
+          raise newException(ValueError,
+            "Invalid IP Address. The address consists of too many groups")
+        result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
+        result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
+        currentShort = 0
+        groupCount.inc()
+        if dualColonGroup != -1: seperatorValid = false
+      elif i == 0: # only valid if address starts with ::
+        if address_str[1] != ':':
+          raise newException(ValueError,
+            "Invalid IP Address. Address may not start with \":\"")
+      else: # i == high(address_str) - only valid if address ends with ::
+        if address_str[high(address_str)-1] != ':':
+          raise newException(ValueError,
+            "Invalid IP Address. Address may not end with \":\"")
+      lastWasColon = true
+      currentGroupStart = i + 1
+    elif c == '.': # Switch to parse IPv4 mode
+      if i < 3 or not seperatorValid or groupCount >= 7:
+        raise newException(ValueError, "Invalid IP Address")
+      v4StartPos = currentGroupStart
+      currentShort = 0
+      seperatorValid = false
+      break
+    elif c in strutils.HexDigits:
+      if c in strutils.Digits: # Normal digit
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
+      elif c >= 'a' and c <= 'f': # Lower case hex
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
+      else: # Upper case hex
+        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
+      if currentShort > 65535'u32:
+        raise newException(ValueError,
+          "Invalid IP Address. Value is out of range")
+      lastWasColon = false
+      seperatorValid = true
+    else:
+      raise newException(ValueError,
+        "Invalid IP Address. Address contains an invalid character")
+
+
+  if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
+    if seperatorValid: # Copy remaining data
+      if groupCount >= 8:
+        raise newException(ValueError,
+          "Invalid IP Address. The address consists of too many groups")
+      result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
+      result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
+      groupCount.inc()
+  else: # Must parse IPv4 address
+    for i,c in address_str[v4StartPos..high(address_str)]:
+      if c in strutils.Digits: # Character is a number
+        currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
+        if currentShort > 255'u32:
+          raise newException(ValueError,
+            "Invalid IP Address. Value is out of range")
+        seperatorValid = true
+      elif c == '.': # IPv4 address separator
+        if not seperatorValid or byteCount >= 3:
+          raise newException(ValueError, "Invalid IP Address")
+        result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
+        currentShort = 0
+        byteCount.inc()
+        seperatorValid = false
+      else: # Invalid character
+        raise newException(ValueError,
+          "Invalid IP Address. Address contains an invalid character")
+
+    if byteCount != 3 or not seperatorValid:
+      raise newException(ValueError, "Invalid IP Address")
+    result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
+    groupCount += 2
+
+  # Shift and fill zeros in case of ::
+  if groupCount > 8:
+    raise newException(ValueError,
+      "Invalid IP Address. The address consists of too many groups")
+  elif groupCount < 8: # must fill
+    if dualColonGroup == -1:
+      raise newException(ValueError,
+        "Invalid IP Address. The address consists of too few groups")
+    var toFill = 8 - groupCount # The number of groups to fill
+    var toShift = groupCount - dualColonGroup # Nr of known groups after ::
+    for i in 0..2*toShift-1: # shift
+      result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
+    for i in 0..2*toFill-1: # fill with 0s
+      result.address_v6[dualColonGroup*2+i] = 0
+  elif dualColonGroup != -1:
+    raise newException(ValueError,
+      "Invalid IP Address. The address consists of too many groups")
+
+proc parseIpAddress*(address_str: string): IpAddress =
+  ## Parses an IP address
+  ## Raises EInvalidValue on error
+  if address_str == nil:
+    raise newException(ValueError, "IP Address string is nil")
+  if address_str.contains(':'):
+    return parseIPv6Address(address_str)
+  else:
+    return parseIPv4Address(address_str)
+
+proc isIpAddress*(address_str: string): bool {.tags: [].} =
+  ## Checks if a string is an IP address
+  ## Returns true if it is, false otherwise
+  try:
+    discard parseIpAddress(address_str)
+  except ValueError:
+    return false
+  return true
+
 when defineSsl:
   CRYPTO_malloc_init()
   SslLibraryInit()
@@ -438,9 +612,12 @@ when defineSsl:
       raiseSSLError()
 
   proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket,
-                            handshake: SslHandshakeType) =
+                            handshake: SslHandshakeType,
+                            hostname: string = nil) =
     ## Wraps a connected socket in an SSL context. This function effectively
     ## turns ``socket`` into an SSL socket.
+    ## ``hostname`` should be specified so that the client knows which hostname
+    ## the server certificate should be validated against.
     ##
     ## This should be called on a connected socket, and will perform
     ## an SSL handshake immediately.
@@ -450,6 +627,10 @@ when defineSsl:
     wrapSocket(ctx, socket)
     case handshake
     of handshakeAsClient:
+      if not hostname.isNil and not isIpAddress(hostname):
+        # Discard result in case OpenSSL version doesn't support SNI, or we're
+        # not using TLSv1+
+        discard SSL_set_tlsext_host_name(socket.sslHandle, hostname)
       let ret = SSLConnect(socket.sslHandle)
       socketError(socket, ret)
     of handshakeAsServer:
@@ -1302,181 +1483,6 @@ proc `$`*(address: IpAddress): string =
             mask = mask shr 4
           printedLastGroup = true
 
-proc parseIPv4Address(address_str: string): IpAddress =
-  ## Parses IPv4 adresses
-  ## Raises EInvalidValue on errors
-  var
-    byteCount = 0
-    currentByte:uint16 = 0
-    seperatorValid = false
-
-  result.family = IpAddressFamily.IPv4
-
-  for i in 0 .. high(address_str):
-    if address_str[i] in strutils.Digits: # Character is a number
-      currentByte = currentByte * 10 +
-        cast[uint16](ord(address_str[i]) - ord('0'))
-      if currentByte > 255'u16:
-        raise newException(ValueError,
-          "Invalid IP Address. Value is out of range")
-      seperatorValid = true
-    elif address_str[i] == '.': # IPv4 address separator
-      if not seperatorValid or byteCount >= 3:
-        raise newException(ValueError,
-          "Invalid IP Address. The address consists of too many groups")
-      result.address_v4[byteCount] = cast[uint8](currentByte)
-      currentByte = 0
-      byteCount.inc
-      seperatorValid = false
-    else:
-      raise newException(ValueError,
-        "Invalid IP Address. Address contains an invalid character")
-
-  if byteCount != 3 or not seperatorValid:
-    raise newException(ValueError, "Invalid IP Address")
-  result.address_v4[byteCount] = cast[uint8](currentByte)
-
-proc parseIPv6Address(address_str: string): IpAddress =
-  ## Parses IPv6 adresses
-  ## Raises EInvalidValue on errors
-  result.family = IpAddressFamily.IPv6
-  if address_str.len < 2:
-    raise newException(ValueError, "Invalid IP Address")
-
-  var
-    groupCount = 0
-    currentGroupStart = 0
-    currentShort:uint32 = 0
-    seperatorValid = true
-    dualColonGroup = -1
-    lastWasColon = false
-    v4StartPos = -1
-    byteCount = 0
-
-  for i,c in address_str:
-    if c == ':':
-      if not seperatorValid:
-        raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid seperator")
-      if lastWasColon:
-        if dualColonGroup != -1:
-          raise newException(ValueError,
-            "Invalid IP Address. Address contains more than one \"::\" seperator")
-        dualColonGroup = groupCount
-        seperatorValid = false
-      elif i != 0 and i != high(address_str):
-        if groupCount >= 8:
-          raise newException(ValueError,
-            "Invalid IP Address. The address consists of too many groups")
-        result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
-        result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
-        currentShort = 0
-        groupCount.inc()
-        if dualColonGroup != -1: seperatorValid = false
-      elif i == 0: # only valid if address starts with ::
-        if address_str[1] != ':':
-          raise newException(ValueError,
-            "Invalid IP Address. Address may not start with \":\"")
-      else: # i == high(address_str) - only valid if address ends with ::
-        if address_str[high(address_str)-1] != ':':
-          raise newException(ValueError,
-            "Invalid IP Address. Address may not end with \":\"")
-      lastWasColon = true
-      currentGroupStart = i + 1
-    elif c == '.': # Switch to parse IPv4 mode
-      if i < 3 or not seperatorValid or groupCount >= 7:
-        raise newException(ValueError, "Invalid IP Address")
-      v4StartPos = currentGroupStart
-      currentShort = 0
-      seperatorValid = false
-      break
-    elif c in strutils.HexDigits:
-      if c in strutils.Digits: # Normal digit
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('0'))
-      elif c >= 'a' and c <= 'f': # Lower case hex
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('a')) + 10
-      else: # Upper case hex
-        currentShort = (currentShort shl 4) + cast[uint32](ord(c) - ord('A')) + 10
-      if currentShort > 65535'u32:
-        raise newException(ValueError,
-          "Invalid IP Address. Value is out of range")
-      lastWasColon = false
-      seperatorValid = true
-    else:
-      raise newException(ValueError,
-        "Invalid IP Address. Address contains an invalid character")
-
-
-  if v4StartPos == -1: # Don't parse v4. Copy the remaining v6 stuff
-    if seperatorValid: # Copy remaining data
-      if groupCount >= 8:
-        raise newException(ValueError,
-          "Invalid IP Address. The address consists of too many groups")
-      result.address_v6[groupCount*2] = cast[uint8](currentShort shr 8)
-      result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF)
-      groupCount.inc()
-  else: # Must parse IPv4 address
-    for i,c in address_str[v4StartPos..high(address_str)]:
-      if c in strutils.Digits: # Character is a number
-        currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0'))
-        if currentShort > 255'u32:
-          raise newException(ValueError,
-            "Invalid IP Address. Value is out of range")
-        seperatorValid = true
-      elif c == '.': # IPv4 address separator
-        if not seperatorValid or byteCount >= 3:
-          raise newException(ValueError, "Invalid IP Address")
-        result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
-        currentShort = 0
-        byteCount.inc()
-        seperatorValid = false
-      else: # Invalid character
-        raise newException(ValueError,
-          "Invalid IP Address. Address contains an invalid character")
-
-    if byteCount != 3 or not seperatorValid:
-      raise newException(ValueError, "Invalid IP Address")
-    result.address_v6[groupCount*2 + byteCount] = cast[uint8](currentShort)
-    groupCount += 2
-
-  # Shift and fill zeros in case of ::
-  if groupCount > 8:
-    raise newException(ValueError,
-      "Invalid IP Address. The address consists of too many groups")
-  elif groupCount < 8: # must fill
-    if dualColonGroup == -1:
-      raise newException(ValueError,
-        "Invalid IP Address. The address consists of too few groups")
-    var toFill = 8 - groupCount # The number of groups to fill
-    var toShift = groupCount - dualColonGroup # Nr of known groups after ::
-    for i in 0..2*toShift-1: # shift
-      result.address_v6[15-i] = result.address_v6[groupCount*2-i-1]
-    for i in 0..2*toFill-1: # fill with 0s
-      result.address_v6[dualColonGroup*2+i] = 0
-  elif dualColonGroup != -1:
-    raise newException(ValueError,
-      "Invalid IP Address. The address consists of too many groups")
-
-
-proc parseIpAddress*(address_str: string): IpAddress =
-  ## Parses an IP address
-  ## Raises EInvalidValue on error
-  if address_str == nil:
-    raise newException(ValueError, "IP Address string is nil")
-  if address_str.contains(':'):
-    return parseIPv6Address(address_str)
-  else:
-    return parseIPv4Address(address_str)
-
-proc isIpAddress*(address_str: string): bool {.tags: [].} =
-  ## Checks if a string is an IP address
-  ## Returns true if it is, false otherwise
-  try:
-    discard parseIpAddress(address_str)
-  except ValueError:
-    return false
-  return true
-
 proc dial*(address: string, port: Port,
            protocol = IPPROTO_TCP, buffered = true): Socket
            {.tags: [ReadIOEffect, WriteIOEffect].} =
diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim
index 82a0c0c65..c94a65a63 100644
--- a/lib/pure/osproc.nim
+++ b/lib/pure/osproc.nim
@@ -335,7 +335,8 @@ proc execProcesses*(cmds: openArray[string],
       if afterRunEvent != nil: afterRunEvent(i, p)
       close(p)
 
-proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
+proc select*(readfds: var seq[Process], timeout = 500): int
+  {.benign, deprecated.}
   ## `select` with a sensible Nim interface. `timeout` is in milliseconds.
   ## Specify -1 for no timeout. Returns the number of processes that are
   ## ready to read from. The processes that are ready to be read from are
@@ -343,6 +344,9 @@ proc select*(readfds: var seq[Process], timeout = 500): int {.benign.}
   ##
   ## **Warning**: This function may give unexpected or completely wrong
   ## results on Windows.
+  ##
+  ## **Deprecated since version 0.17.0**: This procedure isn't cross-platform
+  ## and so should not be used in newly written code.
 
 when not defined(useNimRtl):
   proc execProcess(command: string,
diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim
index 8d53a0360..b78e8d000 100644
--- a/lib/pure/parseutils.nim
+++ b/lib/pure/parseutils.nim
@@ -201,7 +201,7 @@ proc parseWhile*(s: string, token: var string, validChars: set[char],
 
 proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
   ## Finds the first occurrence of ``first``, then returns everything from there
-  ## up to ``second``(if ``second`` is '\0', then ``first`` is used).
+  ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
   var i = skipUntil(s, first, start)+1+start
   result = ""
   discard s.parseUntil(result, if second == '\0': first else: second, i)
diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim
index 9383675f4..458c22f3a 100644
--- a/lib/pure/strutils.nim
+++ b/lib/pure/strutils.nim
@@ -1881,6 +1881,8 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
   ## of significant digits to be printed.
   ## `precision`'s default value is the maximum number of meaningful digits
   ## after the decimal point for Nim's ``float`` type.
+  ##
+  ## If ``precision == 0``, it tries to format it nicely.
   result = formatBiggestFloat(f, format, precision, decimalSep)
 
 proc trimZeros*(x: var string) {.noSideEffect.} =
diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim
index 3a43729dc..885b01621 100644
--- a/lib/system/atomics.nim
+++ b/lib/system/atomics.nim
@@ -165,8 +165,22 @@ when someGcc and hasThreadSupport:
 
   template fence*() = atomicThreadFence(ATOMIC_SEQ_CST)
 elif defined(vcc) and hasThreadSupport:
-  proc addAndFetch*(p: ptr int, val: int): int {.
-    importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
+  when defined(cpp):
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd64(static_cast<NI volatile *>(#), #)",
+        header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importcpp: "_InterlockedExchangeAdd(static_cast<NI volatile *>(#), #)",
+        header: "<intrin.h>".}
+  else:
+    when sizeof(int) == 8:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd64", header: "<intrin.h>".}
+    else:
+      proc addAndFetch*(p: ptr int, val: int): int {.
+        importc: "_InterlockedExchangeAdd", header: "<intrin.h>".}
 
   proc fence*() {.importc: "_ReadWriteBarrier", header: "<intrin.h>".}
 
@@ -180,6 +194,7 @@ proc atomicInc*(memLoc: var int, x: int = 1): int =
     result = atomic_add_fetch(memLoc.addr, x, ATOMIC_RELAXED)
   elif defined(vcc) and hasThreadSupport:
     result = addAndFetch(memLoc.addr, x)
+    inc(result, x)
   else:
     inc(memLoc, x)
     result = memLoc
@@ -192,6 +207,7 @@ proc atomicDec*(memLoc: var int, x: int = 1): int =
       result = atomic_add_fetch(memLoc.addr, -x, ATOMIC_RELAXED)
   elif defined(vcc) and hasThreadSupport:
     result = addAndFetch(memLoc.addr, -x)
+    dec(result, x)
   else:
     dec(memLoc, x)
     result = memLoc
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index bdcb4c1f6..55f283d2d 100644
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -299,8 +299,13 @@ proc raiseExceptionAux(e: ref Exception) =
 proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} =
   if e.name.isNil: e.name = ename
   when hasSomeStackTrace:
-    e.trace = ""
-    rawWriteStackTrace(e.trace)
+    if e.trace.isNil:
+      e.trace = ""
+      rawWriteStackTrace(e.trace)
+    elif framePtr != nil:
+      e.trace.add "[[reraised from:\n"
+      auxWriteStackTrace(framePtr, e.trace)
+      e.trace.add "]]\n"
   raiseExceptionAux(e)
 
 proc reraiseException() {.compilerRtl.} =
diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim
index fb354880f..f61b887ad 100644
--- a/lib/system/syslocks.nim
+++ b/lib/system/syslocks.nim
@@ -99,7 +99,7 @@ elif defined(genode):
 
 else:
   type
-    SysLock {.importc: "pthread_mutex_t", pure, final,
+    SysLockObj {.importc: "pthread_mutex_t", pure, final,
                header: """#include <sys/types.h>
                           #include <pthread.h>""".} = object
       when defined(linux) and defined(amd64):
@@ -111,7 +111,7 @@ else:
       when defined(linux) and defined(amd64):
         abi: array[4 div sizeof(cint), cint]  # actually a cint
 
-    SysCond {.importc: "pthread_cond_t", pure, final,
+    SysCondObj {.importc: "pthread_cond_t", pure, final,
                header: """#include <sys/types.h>
                           #include <pthread.h>""".} = object
       when defined(linux) and defined(amd64):
@@ -119,8 +119,62 @@ else:
 
     SysLockType = distinct cint
 
-  proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) {.
+  proc initSysLockAux(L: var SysLockObj, attr: ptr SysLockAttr) {.
     importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.}
+  proc deinitSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
+
+  proc acquireSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_lock", header: "<pthread.h>".}
+  proc tryAcquireSysAux(L: var SysLockObj): cint {.noSideEffect,
+    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
+
+  proc releaseSysAux(L: var SysLockObj) {.noSideEffect,
+    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
+
+  when defined(ios):
+    # iOS will behave badly if sync primitives are moved in memory. In order
+    # to prevent this once and for all, we're doing an extra malloc when
+    # initializing the primitive.
+    type
+      SysLock = ptr SysLockObj
+      SysCond = ptr SysCondObj
+
+    when not declared(c_malloc):
+      proc c_malloc(size: csize): pointer {.
+        importc: "malloc", header: "<stdlib.h>".}
+      proc c_free(p: pointer) {.
+        importc: "free", header: "<stdlib.h>".}
+
+    proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      L = cast[SysLock](c_malloc(sizeof(SysLockObj)))
+      initSysLockAux(L[], attr)
+
+    proc deinitSys(L: var SysLock) =
+      deinitSysAux(L[])
+      c_free(L)
+
+    template acquireSys(L: var SysLock) =
+      acquireSysAux(L[])
+    template tryAcquireSys(L: var SysLock): bool =
+      tryAcquireSysAux(L[]) == 0'i32
+    template releaseSys(L: var SysLock) =
+      releaseSysAux(L[])
+  else:
+    type
+      SysLock = SysLockObj
+      SysCond = SysCondObj
+
+    template initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) =
+      initSysLockAux(L, attr)
+    template deinitSys(L: var SysLock) =
+      deinitSysAux(L)
+    template acquireSys(L: var SysLock) =
+      acquireSysAux(L)
+    template tryAcquireSys(L: var SysLock): bool =
+      tryAcquireSysAux(L) == 0'i32
+    template releaseSys(L: var SysLock) =
+      releaseSysAux(L)
 
   when insideRLocksModule:
     proc SysLockType_Reentrant: SysLockType =
@@ -130,27 +184,39 @@ else:
     proc setSysLockType(a: var SysLockAttr, t: SysLockType) {.
       importc: "pthread_mutexattr_settype", header: "<pthread.h>", noSideEffect.}
 
-  proc acquireSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_lock", header: "<pthread.h>".}
-  proc tryAcquireSysAux(L: var SysLock): cint {.noSideEffect,
-    importc: "pthread_mutex_trylock", header: "<pthread.h>".}
-
-  proc tryAcquireSys(L: var SysLock): bool {.inline.} =
-    result = tryAcquireSysAux(L) == 0'i32
-
-  proc releaseSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
-  proc deinitSys(L: var SysLock) {.noSideEffect,
-    importc: "pthread_mutex_destroy", header: "<pthread.h>".}
-
-  when not insideRLocksModule:
-    proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) {.
+  else:
+    proc initSysCondAux(cond: var SysCondObj, cond_attr: pointer) {.
       importc: "pthread_cond_init", header: "<pthread.h>", noSideEffect.}
-    proc waitSysCond(cond: var SysCond, lock: var SysLock) {.
+    proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect,
+      importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+    proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {.
       importc: "pthread_cond_wait", header: "<pthread.h>", noSideEffect.}
-    proc signalSysCond(cond: var SysCond) {.
+    proc signalSysCondAux(cond: var SysCondObj) {.
       importc: "pthread_cond_signal", header: "<pthread.h>", noSideEffect.}
-    proc deinitSysCond(cond: var SysCond) {.noSideEffect,
-      importc: "pthread_cond_destroy", header: "<pthread.h>".}
+
+    when defined(ios):
+      proc initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
+        cond = cast[SysCond](c_malloc(sizeof(SysCondObj)))
+        initSysCondAux(cond[], cond_attr)
+
+      proc deinitSysCond(cond: var SysCond) =
+        deinitSysCondAux(cond[])
+        c_free(cond)
+
+      template waitSysCond(cond: var SysCond, lock: var SysLock) =
+        waitSysCondAux(cond[], lock[])
+      template signalSysCond(cond: var SysCond) =
+        signalSysCondAux(cond[])
+    else:
+      template initSysCond(cond: var SysCond, cond_attr: pointer = nil) =
+        initSysCondAux(cond, cond_attr)
+      template deinitSysCond(cond: var SysCond) =
+        deinitSysCondAux(cond)
+
+      template waitSysCond(cond: var SysCond, lock: var SysLock) =
+        waitSysCondAux(cond, lock)
+      template signalSysCond(cond: var SysCond) =
+        signalSysCondAux(cond)
 
 {.pop.}
diff --git a/lib/upcoming/asyncdispatch.nim b/lib/upcoming/asyncdispatch.nim
index 7c0497cc6..1623d8375 100644
--- a/lib/upcoming/asyncdispatch.nim
+++ b/lib/upcoming/asyncdispatch.nim
@@ -9,7 +9,7 @@
 
 include "system/inclrtl"
 
-import os, oids, tables, strutils, times, heapqueue, lists, options
+import os, tables, strutils, times, heapqueue, lists, options
 
 import nativesockets, net, deques
 
@@ -219,6 +219,11 @@ when defined(windows) or defined(nimdoc):
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     ## Registers ``fd`` with the dispatcher.
     let p = getGlobalDispatcher()
@@ -1080,6 +1085,11 @@ else:
     if gDisp.isNil: gDisp = newDispatcher()
     result = gDisp
 
+  proc setGlobalDispatcher*(disp: PDispatcher) =
+    if not gDisp.isNil:
+      assert gDisp.callbacks.len == 0
+    gDisp = disp
+
   proc register*(fd: AsyncFD) =
     let p = getGlobalDispatcher()
     var data = newAsyncData()
diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim
index c1adb87a1..93a418dd7 100644
--- a/nimsuggest/nimsuggest.nim
+++ b/nimsuggest/nimsuggest.nim
@@ -595,6 +595,8 @@ proc handleCmdLine(cache: IdentCache; config: ConfigRef) =
       raise newException(IOError,
           "Cannot find Nim standard library: Nim compiler not in PATH")
     gPrefixDir = binaryPath.splitPath().head.parentDir()
+    if not dirExists(gPrefixDir / "lib"): gPrefixDir = ""
+
     #msgs.writelnHook = proc (line: string) = log(line)
     myLog("START " & gProjectFull)
 
diff --git a/tests/async/tasyncfilewrite.nim b/tests/async/tasyncfilewrite.nim
new file mode 100644
index 000000000..cda612bae
--- /dev/null
+++ b/tests/async/tasyncfilewrite.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''string 1
+string 2
+string 3'''
+"""
+# bug #5532
+import os, asyncfile, asyncdispatch
+
+removeFile("test.txt")
+let f = openAsync("test.txt", fmWrite)
+var futs = newSeq[Future[void]]()
+for i in 1..3:
+  futs.add(f.write("string " & $i & "\n"))
+waitFor(all(futs))
+f.close()
+echo readFile("test.txt")
+
diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim
index a2988e841..01dab44fc 100644
--- a/tests/collections/ttables.nim
+++ b/tests/collections/ttables.nim
@@ -235,6 +235,21 @@ block withKeyTest:
   except KeyError:
     discard
 
+block takeTest:
+  var t = initTable[string, int]()
+  t["key"] = 123
+
+  var val = 0
+  assert(t.take("key", val))
+  assert(val == 123)
+
+  val = -1
+  assert(not t.take("key", val))
+  assert(val == -1)
+
+  assert(not t.take("otherkey", val))
+  assert(val == -1)
+
 proc orderedTableSortTest() =
   var t = initOrderedTable[string, int](2)
   for key, val in items(data): t[key] = val
diff --git a/tests/macros/tgettypeinst.nim b/tests/macros/tgettypeinst.nim
index 255eff949..f89aa5e0b 100644
--- a/tests/macros/tgettypeinst.nim
+++ b/tests/macros/tgettypeinst.nim
@@ -21,49 +21,67 @@ proc symToIdent(x: NimNode): NimNode =
       for c in x:
         result.add symToIdent(c)
 
+# check getTypeInst and getTypeImpl for given symbol x
 macro testX(x,inst0: typed; recurse: static[bool]; implX: stmt): typed =
+  # check that getTypeInst(x) equals inst0
   let inst = x.getTypeInst
-  let impl = x.getTypeImpl
-  let inst0r = inst0.symToIdent.treeRepr
   let instr = inst.symToIdent.treeRepr
-  #echo inst0r
+  let inst0r = inst0.symToIdent.treeRepr
   #echo instr
+  #echo inst0r
   doAssert(instr == inst0r)
+
+  # check that getTypeImpl(x) is correct
+  #  if implX is nil then compare to inst0
+  #  else we expect implX to be a type definition
+  #       and we extract the implementation from that
+  let impl = x.getTypeImpl
   var impl0 =
     if implX.kind == nnkNilLit: inst0
     else: implX[0][2]
-  let impl0r = impl0.symToIdent.treerepr
   let implr = impl.symToIdent.treerepr
-  #echo impl0r
+  let impl0r = impl0.symToIdent.treerepr
   #echo implr
+  #echo impl0r
   doAssert(implr == impl0r)
-  template echoString(s:string) = echo s.replace("\n","\n  ")
+
   result = newStmtList()
+  #template echoString(s: string) = echo s.replace("\n","\n  ")
   #result.add getAst(echoString("  " & inst0.repr))
   #result.add getAst(echoString("  " & inst.repr))
   #result.add getAst(echoString("  " & impl0.repr))
   #result.add getAst(echoString("  " & impl.repr))
+
   if recurse:
-    template testDecl(n, m :typed) =
+    # now test using a newly formed variable of type getTypeInst(x)
+    template testDecl(n,m: typed) =
       testV(n, false):
         type _ = m
     result.add getAst(testDecl(inst.symToIdent, impl.symToIdent))
 
+# test with a variable (instance) of type
 template testV(inst, recurse, impl) =
   block:
     #echo "testV(" & astToStr(inst) & ", " & $recurse & "):" & astToStr(impl)
     var x: inst
     testX(x, inst, recurse, impl)
-template testT(inst, recurse) =
+
+# test with a newly created typedesc (myType)
+# using the passed type as the implementation
+template testT(impl, recurse) =
   block:
-    type myType = inst
+    type myType = impl
     testV(myType, recurse):
-      type _ = inst
+      type _ = impl
 
+# test a built-in type whose instance is equal to the implementation
 template test(inst) =
   testT(inst, false)
   testV(inst, true, nil)
-template test(inst, impl) = testV(inst, true, impl)
+
+# test a custom type with provided implementation
+template test(inst, impl) =
+  testV(inst, true, impl)
 
 type
   Model = object of RootObj
@@ -87,9 +105,12 @@ type
     value:T
   Foo[N:static[int],T] = object
   Bar[N:static[int],T] = object
-    #baz:Foo[N+1,GenericObject[T]]
+    #baz:Foo[N+1,GenericObject[T]]  # currently fails
     baz:Foo[N,GenericObject[T]]
 
+  Generic[T] = seq[int]
+  Concrete = Generic[int]
+
 test(bool)
 test(char)
 test(int)
@@ -97,13 +118,17 @@ test(float)
 test(ptr int)
 test(ref int)
 test(array[1..10,Bar[2,Foo[3,float]]])
+test(array[MyEnum,Bar[2,Foo[3,float]]])
 test(distinct Bar[2,Foo[3,float]])
 test(tuple[a:int,b:Foo[-1,float]])
-#test(MyEnum):
-#  type _ = enum
-#    valueA, valueB, valueC
-test(set[MyEnum])
 test(seq[int])
+test(set[MyEnum])
+test(proc (a: int, b: Foo[2,float]))
+test(proc (a: int, b: Foo[2,float]): Bar[3,int])
+
+test(MyEnum):
+  type _ = enum
+    valueA, valueB, valueC
 test(Bar[2,Foo[3,float]]):
   type _ = object
     baz: Foo[2, GenericObject[Foo[3, float]]]
@@ -118,8 +143,12 @@ test(Tree):
     value: int
     left: ref Tree
     right: ref Tree
-test(proc (a: int, b: Foo[2,float]))
-test(proc (a: int, b: Foo[2,float]): Bar[3,int])
+test(Concrete):
+  type _ = Generic[int]
+test(Generic[int]):
+  type _ = seq[int]
+test(Generic[float]):
+  type _ = seq[int]
 
 # bug #4862
 static:
diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim
index 40324d92a..c6a857edb 100644
--- a/tests/stdlib/thttpclient.nim
+++ b/tests/stdlib/thttpclient.nim
@@ -104,15 +104,17 @@ proc syncTest() =
 
   client.close()
 
-  # Timeout test.
-  client = newHttpClient(timeout = 1)
-  try:
-    resp = client.request("http://example.com/")
-    doAssert false, "TimeoutError should have been raised."
-  except TimeoutError:
-    discard
-  except:
-    doAssert false, "TimeoutError should have been raised."
+  when false:
+    # Disabled for now because it causes troubles with AppVeyor
+    # Timeout test.
+    client = newHttpClient(timeout = 1)
+    try:
+      resp = client.request("http://example.com/")
+      doAssert false, "TimeoutError should have been raised."
+    except TimeoutError:
+      discard
+    except:
+      doAssert false, "TimeoutError should have been raised."
 
 proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
   let fd = newNativeSocket(AF_INET6)
diff --git a/tests/testament/specs.nim b/tests/testament/specs.nim
index 91e8b2ec7..ab24acc70 100644
--- a/tests/testament/specs.nim
+++ b/tests/testament/specs.nim
@@ -9,8 +9,11 @@
 
 import parseutils, strutils, os, osproc, streams, parsecfg
 
-const
-  cmdTemplate* = r"compiler" / "nim $target --lib:lib --hints:on -d:testing $options $file"
+
+var compilerPrefix* = "compiler" / "nim "
+
+proc cmdTemplate*(): string =
+  compilerPrefix & "$target --lib:lib --hints:on -d:testing $options $file"
 
 type
   TTestAction* = enum
@@ -100,7 +103,7 @@ proc specDefaults*(result: var TSpec) =
   result.outp = ""
   result.nimout = ""
   result.ccodeCheck = ""
-  result.cmd = cmdTemplate
+  result.cmd = cmdTemplate()
   result.line = 0
   result.column = 0
   result.tfile = ""
@@ -173,7 +176,7 @@ proc parseSpec*(filename: string): TSpec =
         raise newException(ValueError, "cannot interpret as a bool: " & e.value)
     of "cmd":
       if e.value.startsWith("nim "):
-        result.cmd = "compiler" / e.value
+        result.cmd = compilerPrefix & e.value[4..^1]
       else:
         result.cmd = e.value
     of "ccodecheck": result.ccodeCheck = e.value
diff --git a/tests/testament/tester.nim b/tests/testament/tester.nim
index 2d758ef0d..0f74de013 100644
--- a/tests/testament/tester.nim
+++ b/tests/testament/tester.nim
@@ -34,6 +34,7 @@ Options:
   --failing                 only show failing/ignored tests
   --pedantic                return non-zero status code if there are failures
   --targets:"c c++ js objc" run tests for specified targets (default: all)
+  --nim:path                use a particular nim executable (default: compiler/nim)
 """ % resultsFile
 
 type
@@ -367,7 +368,7 @@ proc testNoSpec(r: var TResults, test: TTest) =
   # does not extract the spec because the file is not supposed to have any
   #let tname = test.name.addFileExt(".nim")
   inc(r.total)
-  let given = callCompiler(cmdTemplate, test.name, test.options, test.target)
+  let given = callCompiler(cmdTemplate(), test.name, test.options, test.target)
   r.addResult(test, "", given.msg, given.err)
   if given.err == reSuccess: inc(r.passed)
 
@@ -376,7 +377,7 @@ proc testC(r: var TResults, test: TTest) =
   let tname = test.name.addFileExt(".c")
   inc(r.total)
   styledEcho "Processing ", fgCyan, extractFilename(tname)
-  var given = callCCompiler(cmdTemplate, test.name & ".c", test.options, test.target)
+  var given = callCCompiler(cmdTemplate(), test.name & ".c", test.options, test.target)
   if given.err != reSuccess:
     r.addResult(test, "", given.msg, given.err)
   elif test.action == actionRun:
@@ -424,6 +425,7 @@ proc main() =
     of "failing": optFailing = true
     of "pedantic": optPedantic = true
     of "targets": targets = parseTargets(p.val.string)
+    of "nim": compilerPrefix = p.val.string
     else: quit Usage
     p.next()
   if p.kind != cmdArgument: quit Usage
diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim
index 69824369e..937a5dcd4 100644
--- a/tools/nimgrab.nim
+++ b/tools/nimgrab.nim
@@ -7,7 +7,11 @@ when defined(windows):
     proc progress(status: DownloadStatus, progress: uint, total: uint,
                   message: string) {.procvar, gcsafe.} =
       echo "Downloading " & url
-      echo clamp(int(progress.BiggestInt*100 div total.BiggestInt), 0, 100), "%"
+      let t = total.BiggestInt
+      if t != 0:
+        echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%"
+      else:
+        echo "0%"
 
     downloadToFile(url, file, {optUseCache}, progress)
     echo "100%"
diff --git a/web/news/e031_version_0_16_2.rst b/web/news/e031_version_0_16_2.rst
index 63884700f..4f49bd8d9 100644
--- a/web/news/e031_version_0_16_2.rst
+++ b/web/news/e031_version_0_16_2.rst
@@ -45,6 +45,8 @@ Changes affecting backwards compatibility
   AST that is the same as what is used to define an enum.  Previously the AST
   returned had a repeated ``EnumTy`` node and was missing the initial pragma
   node (which is currently empty for an enum).
+- ``macros.getTypeImpl`` now correctly returns the implementation for a symbol
+  of type ``tyGenericBody``.
 - If the dispatcher parameter's value used in multi method is ``nil``,
   a ``NilError`` exception is raised. The old behavior was that the method
   would be a ``nop`` then.