summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ccgcalls.nim38
-rw-r--r--compiler/cgen.nim2
-rw-r--r--compiler/installer.ini2
-rw-r--r--compiler/jsgen.nim11
-rw-r--r--compiler/platform.nim7
-rw-r--r--compiler/pragmas.nim544
-rw-r--r--compiler/sem.nim6
-rw-r--r--compiler/semtypes.nim3
-rw-r--r--compiler/vm.nim15
-rw-r--r--compiler/vmgen.nim27
-rw-r--r--lib/core/macros.nim18
-rw-r--r--lib/pure/asyncdispatch.nim20
-rw-r--r--lib/pure/asyncnet.nim2
-rw-r--r--lib/pure/httpclient.nim2
-rw-r--r--lib/pure/marshal.nim2
-rw-r--r--lib/pure/unittest.nim144
-rw-r--r--lib/system.nim19
-rw-r--r--lib/system/platforms.nim6
-rw-r--r--lib/system/repr.nim5
-rw-r--r--lib/system/sets.nim6
-rw-r--r--tests/async/tasyncconnect.nim33
-rw-r--r--tests/async/tasynceverror.nim65
-rw-r--r--tests/metatype/tstatic_ones.nim28
-rw-r--r--tests/pragmas/tsym_as_pragma.nim8
-rw-r--r--tests/stdlib/tunittest.nim14
-rw-r--r--tests/template/t_otemplates.nim678
-rw-r--r--tests/template/twrongsymkind.nim20
-rw-r--r--web/news.txt2
28 files changed, 1037 insertions, 690 deletions
diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 2dacc25e9..86ecc9db8 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -159,6 +159,17 @@ proc genArgNoParam(p: BProc, n: PNode): Rope =
     initLocExprSingleUse(p, n, a)
     result = rdLoc(a)
 
+template genParamLoop(params) {.dirty.} =
+  if i < sonsLen(typ):
+    assert(typ.n.sons[i].kind == nkSym)
+    let paramType = typ.n.sons[i]
+    if not paramType.typ.isCompileTimeOnly:
+      if params != nil: add(params, ~", ")
+      add(params, genArg(p, ri.sons[i], paramType.sym, ri))
+  else:
+    if params != nil: add(params, ~", ")
+    add(params, genArgNoParam(p, ri.sons[i]))
+
 proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var op: TLoc
   # this is a hotspot in the compiler
@@ -170,13 +181,7 @@ proc genPrefixCall(p: BProc, le, ri: PNode, d: var TLoc) =
   assert(sonsLen(typ) == sonsLen(typ.n))
   var length = sonsLen(ri)
   for i in countup(1, length - 1):
-    if ri.sons[i].typ.isCompileTimeOnly: continue
-    if params != nil: add(params, ~", ")
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      add(params, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
-    else:
-      add(params, genArgNoParam(p, ri.sons[i]))
+    genParamLoop(params)
   fixupCall(p, le, ri, d, op.r, params)
 
 proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
@@ -198,13 +203,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
   var length = sonsLen(ri)
   for i in countup(1, length - 1):
     assert(sonsLen(typ) == sonsLen(typ.n))
-    if ri.sons[i].typ.isCompileTimeOnly: continue
-    if i < sonsLen(typ):
-      assert(typ.n.sons[i].kind == nkSym)
-      add(pl, genArg(p, ri.sons[i], typ.n.sons[i].sym, ri))
-    else:
-      add(pl, genArgNoParam(p, ri.sons[i]))
-    if i < length - 1: add(pl, ~", ")
+    genParamLoop(pl)
 
   template genCallPattern {.dirty.} =
     lineF(p, cpsStmts, callPattern & ";$n", [op.r, pl, pl.addComma, rawProc])
@@ -241,13 +240,14 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
     genCallPattern()
 
 proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope =
-  if ri.sons[i].typ.isCompileTimeOnly:
-    result = nil
-  elif i < sonsLen(typ):
+  if i < sonsLen(typ):
     # 'var T' is 'T&' in C++. This means we ignore the request of
     # any nkHiddenAddr when it's a 'var T'.
-    assert(typ.n.sons[i].kind == nkSym)
-    if typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
+    let paramType = typ.n.sons[i]
+    assert(paramType.kind == nkSym)
+    if paramType.typ.isCompileTimeOnly:
+      result = nil
+    elif typ.sons[i].kind == tyVar and ri.sons[i].kind == nkHiddenAddr:
       result = genArgNoParam(p, ri.sons[i][0])
     else:
       result = genArgNoParam(p, ri.sons[i]) #, typ.n.sons[i].sym)
diff --git a/compiler/cgen.nim b/compiler/cgen.nim
index bfc80b2a1..2e95918cc 100644
--- a/compiler/cgen.nim
+++ b/compiler/cgen.nim
@@ -950,7 +950,7 @@ proc genMainProc(m: BModule) =
     gBreakpoints.add(m.genFilenames)
 
   let initStackBottomCall =
-    if platform.targetOS == osStandalone: "".rope
+    if platform.targetOS == osStandalone or gSelectedGC == gcNone: "".rope
     else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N")
   inc(m.labels)
   appcg(m, m.s[cfsProcs], PreMainBody, [
diff --git a/compiler/installer.ini b/compiler/installer.ini
index 52a3d0886..c8af38886 100644
--- a/compiler/installer.ini
+++ b/compiler/installer.ini
@@ -6,7 +6,7 @@ Name: "Nim"
 Version: "$version"
 Platforms: """
   windows: i386;amd64
-  linux: i386;amd64;powerpc64;arm;sparc;mips;powerpc
+  linux: i386;amd64;powerpc64;arm;sparc;mips;mipsel;powerpc;powerpc64el;arm64
   macosx: i386;amd64;powerpc64
   solaris: i386;amd64;sparc
   freebsd: i386;amd64
diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim
index f32441d98..89a1b84a2 100644
--- a/compiler/jsgen.nim
+++ b/compiler/jsgen.nim
@@ -1045,9 +1045,18 @@ proc genArg(p: PProc, n: PNode, r: var TCompRes) =
 proc genArgs(p: PProc, n: PNode, r: var TCompRes) =
   add(r.res, "(")
   var hasArgs = false
+
+  var typ = skipTypes(n.sons[0].typ, abstractInst)
+  assert(typ.kind == tyProc)
+  assert(sonsLen(typ) == sonsLen(typ.n))
+
   for i in countup(1, sonsLen(n) - 1):
     let it = n.sons[i]
-    if it.typ.isCompileTimeOnly: continue
+    if i < sonsLen(typ):
+      assert(typ.n.sons[i].kind == nkSym)
+      let paramType = typ.n.sons[i]
+      if paramType.typ.isCompileTimeOnly: continue
+
     if hasArgs: add(r.res, ", ")
     genArg(p, it, r)
     hasArgs = true
diff --git a/compiler/platform.nim b/compiler/platform.nim
index 4dd5d8836..c4e7d453a 100644
--- a/compiler/platform.nim
+++ b/compiler/platform.nim
@@ -158,8 +158,8 @@ type
   TSystemCPU* = enum # Also add CPU for in initialization section and
                      # alias conditionals to condsyms (end of module).
     cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64,
-    cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuArm,
-    cpuJS, cpuNimrodVM, cpuAVR
+    cpuPowerpc64el, cpuSparc, cpuVm, cpuIa64, cpuAmd64, cpuMips, cpuMipsel,
+    cpuArm, cpuArm64, cpuJS, cpuNimrodVM, cpuAVR
 
 type
   TEndian* = enum
@@ -175,12 +175,15 @@ const
     (name: "alpha", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "powerpc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "powerpc64", intSize: 64, endian: bigEndian, floatSize: 64,bit: 64),
+    (name: "powerpc64el", intSize: 64, endian: littleEndian, floatSize: 64,bit: 64),
     (name: "sparc", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "vm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "ia64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "amd64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "mips", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
+    (name: "mipsel", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
     (name: "arm", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32),
+    (name: "arm64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64),
     (name: "js", intSize: 32, endian: bigEndian,floatSize: 64,bit: 32),
     (name: "nimrodvm", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32),
     (name: "avr", intSize: 16, endian: littleEndian, floatSize: 32, bit: 16)]
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index c771155af..128a90138 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -591,280 +591,282 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
                   validPragmas: TSpecialWords): bool =
   var it = n.sons[i]
   var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
-  if key.kind == nkIdent:
-    var userPragma = strTableGet(c.userPragmas, key.ident)
-    if userPragma != nil:
-      inc c.instCounter
-      if c.instCounter > 100:
-        globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
-      pragma(c, sym, userPragma.ast, validPragmas)
-      # ensure the pragma is also remember for generic instantiations in other
-      # modules:
-      n.sons[i] = userPragma.ast
-      dec c.instCounter
-    else:
-      var k = whichKeyword(key.ident)
-      if k in validPragmas:
-        case k
-        of wExportc:
-          makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
-          incl(sym.flags, sfUsed) # avoid wrong hints
-        of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
-        of wImportCompilerProc:
-          processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
-        of wExtern: setExternName(sym, expectStrLit(c, it))
-        of wImmediate:
-          if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
-          else: invalidPragma(it)
-        of wDirty:
-          if sym.kind == skTemplate: incl(sym.flags, sfDirty)
-          else: invalidPragma(it)
-        of wImportCpp:
-          processImportCpp(sym, getOptionalStr(c, it, "$1"))
-        of wImportObjC:
-          processImportObjC(sym, getOptionalStr(c, it, "$1"))
-        of wAlign:
-          if sym.typ == nil: invalidPragma(it)
-          var align = expectIntLit(c, it)
-          if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.align = align.int16
-        of wSize:
-          if sym.typ == nil: invalidPragma(it)
-          var size = expectIntLit(c, it)
-          if not isPowerOfTwo(size) or size <= 0 or size > 8:
-            localError(it.info, errPowerOfTwoExpected)
-          else:
-            sym.typ.size = size
-        of wNodecl:
-          noVal(it)
-          incl(sym.loc.flags, lfNoDecl)
-        of wPure, wAsmNoStackFrame:
-          noVal(it)
-          if sym != nil:
-            if k == wPure and sym.kind in routineKinds: invalidPragma(it)
-            else: incl(sym.flags, sfPure)
-        of wVolatile:
-          noVal(it)
-          incl(sym.flags, sfVolatile)
-        of wRegister:
-          noVal(it)
-          incl(sym.flags, sfRegister)
-        of wThreadVar:
-          noVal(it)
-          incl(sym.flags, sfThread)
-        of wDeadCodeElim: pragmaDeadCodeElim(c, it)
-        of wNoForward: pragmaNoForward(c, it)
-        of wMagic: processMagic(c, it, sym)
-        of wCompileTime:
-          noVal(it)
-          incl(sym.flags, sfCompileTime)
-          incl(sym.loc.flags, lfNoDecl)
-        of wGlobal:
-          noVal(it)
-          incl(sym.flags, sfGlobal)
-          incl(sym.flags, sfPure)
-        of wMerge:
-          # only supported for backwards compat, doesn't do anything anymore
-          noVal(it)
-        of wConstructor:
-          noVal(it)
-          incl(sym.flags, sfConstructor)
-        of wHeader:
-          var lib = getLib(c, libHeader, getStrLitNode(c, it))
-          addToLib(lib, sym)
-          incl(sym.flags, sfImportc)
-          incl(sym.loc.flags, lfHeader)
-          incl(sym.loc.flags, lfNoDecl)
-          # implies nodecl, because otherwise header would not make sense
-          if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
-        of wDestructor:
-          sym.flags.incl sfOverriden
-          if sym.name.s.normalize != "destroy":
-            localError(n.info, errGenerated, "destructor has to be named 'destroy'")
-        of wOverride:
-          sym.flags.incl sfOverriden
-        of wNosideeffect:
-          noVal(it)
-          incl(sym.flags, sfNoSideEffect)
-          if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
-        of wSideeffect:
-          noVal(it)
-          incl(sym.flags, sfSideEffect)
-        of wNoreturn:
-          noVal(it)
-          incl(sym.flags, sfNoReturn)
-        of wDynlib:
-          processDynLib(c, it, sym)
-        of wCompilerproc:
-          noVal(it)           # compilerproc may not get a string!
-          if sfFromGeneric notin sym.flags: markCompilerProc(sym)
-        of wProcVar:
-          noVal(it)
-          incl(sym.flags, sfProcvar)
-        of wDeprecated:
-          if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
-          elif sym != nil: incl(sym.flags, sfDeprecated)
-          else: incl(c.module.flags, sfDeprecated)
-        of wVarargs:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfVarargs)
-        of wBorrow:
-          if sym.kind == skType:
-            typeBorrow(sym, it)
-          else:
-            noVal(it)
-            incl(sym.flags, sfBorrow)
-        of wFinal:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfFinal)
-        of wInheritable:
-          noVal(it)
-          if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
-          else: incl(sym.typ.flags, tfInheritable)
-        of wAcyclic:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfAcyclic)
-        of wShallow:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfShallow)
-        of wThread:
-          noVal(it)
-          incl(sym.flags, sfThread)
-          incl(sym.flags, sfProcvar)
-          if sym.typ != nil: incl(sym.typ.flags, tfThread)
-        of wGcSafe:
-          noVal(it)
-          if sym.kind != skType: incl(sym.flags, sfThread)
-          if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
-          else: invalidPragma(it)
-        of wPacked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfPacked)
-        of wHint: message(it.info, hintUser, expectStrLit(c, it))
-        of wWarning: message(it.info, warnUser, expectStrLit(c, it))
-        of wError:
-          if sym != nil and sym.isRoutine:
-            # This is subtle but correct: the error *statement* is only
-            # allowed for top level statements. Seems to be easier than
-            # distinguishing properly between
-            # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
-            noVal(it)
-            incl(sym.flags, sfError)
-          else:
-            localError(it.info, errUser, expectStrLit(c, it))
-        of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
-        of wDefine: processDefine(c, it)
-        of wUndef: processUndef(c, it)
-        of wCompile: processCompile(c, it)
-        of wLink: processCommonLink(c, it, linkNormal)
-        of wLinksys: processCommonLink(c, it, linkSys)
-        of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
-        of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
-        of wBreakpoint: pragmaBreakpoint(c, it)
-        of wWatchPoint: pragmaWatchpoint(c, it)
-        of wPush:
-          processPush(c, n, i + 1)
-          result = true
-        of wPop: processPop(c, it)
-        of wPragma:
-          processPragma(c, n, i)
-          result = true
-        of wDiscardable:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfDiscardable)
-        of wNoInit:
-          noVal(it)
-          if sym != nil: incl(sym.flags, sfNoInit)
-        of wCodegenDecl: processCodegenDecl(c, it, sym)
-        of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
-           wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
-           wLinedir, wStacktrace, wLinetrace, wOptimization,
-           wCallconv,
-           wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
-           wPatterns:
-          if processOption(c, it):
-            # calling conventions (boring...):
-            localError(it.info, errOptionExpected)
-        of FirstCallConv..LastCallConv:
-          assert(sym != nil)
-          if sym.typ == nil: invalidPragma(it)
-          else: sym.typ.callConv = wordToCallConv(k)
-        of wEmit: pragmaEmit(c, it)
-        of wUnroll: pragmaUnroll(c, it)
-        of wLinearScanEnd, wComputedGoto: noVal(it)
-        of wEffects:
-          # is later processed in effect analysis:
-          noVal(it)
-        of wIncompleteStruct:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfIncompleteStruct)
-        of wUnchecked:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUncheckedArray)
-        of wUnion:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfUnion)
-        of wRequiresInit:
-          noVal(it)
-          if sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfNeedsInit)
-        of wByRef:
-          noVal(it)
-          if sym == nil or sym.typ == nil:
-            if processOption(c, it): localError(it.info, errOptionExpected)
-          else:
-            incl(sym.typ.flags, tfByRef)
-        of wByCopy:
-          noVal(it)
-          if sym.kind != skType or sym.typ == nil: invalidPragma(it)
-          else: incl(sym.typ.flags, tfByCopy)
-        of wInject, wGensym:
-          # We check for errors, but do nothing with these pragmas otherwise
-          # as they are handled directly in 'evalTemplate'.
-          noVal(it)
-          if sym == nil: invalidPragma(it)
-        of wLine: pragmaLine(c, it)
-        of wRaises, wTags: pragmaRaisesOrTags(c, it)
-        of wLocks:
-          if sym == nil: pragmaLockStmt(c, it)
-          elif sym.typ == nil: invalidPragma(it)
-          else: sym.typ.lockLevel = pragmaLocks(c, it)
-        of wGuard:
-          if sym == nil or sym.kind notin {skVar, skLet, skField}:
-            invalidPragma(it)
-          else:
-            sym.guard = pragmaGuard(c, it, sym.kind)
-        of wGoto:
-          if sym == nil or sym.kind notin {skVar, skLet}:
-            invalidPragma(it)
-          else:
-            sym.flags.incl sfGoto
-        of wInjectStmt:
-          if it.kind != nkExprColonExpr:
-            localError(it.info, errExprExpected)
-          else:
-            it.sons[1] = c.semExpr(c, it.sons[1])
-        of wExperimental:
-          noVal(it)
-          if isTopLevel(c):
-            c.module.flags.incl sfExperimental
-          else:
-            localError(it.info, "'experimental' pragma only valid as toplevel statement")
-        of wNoRewrite:
+  if key.kind == nkBracketExpr:
+    processNote(c, it)
+    return
+  let ident = considerQuotedIdent(key)
+  var userPragma = strTableGet(c.userPragmas, ident)
+  if userPragma != nil:
+    inc c.instCounter
+    if c.instCounter > 100:
+      globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
+    pragma(c, sym, userPragma.ast, validPragmas)
+    # ensure the pragma is also remember for generic instantiations in other
+    # modules:
+    n.sons[i] = userPragma.ast
+    dec c.instCounter
+  else:
+    var k = whichKeyword(ident)
+    if k in validPragmas:
+      case k
+      of wExportc:
+        makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
+        incl(sym.flags, sfUsed) # avoid wrong hints
+      of wImportc: makeExternImport(sym, getOptionalStr(c, it, "$1"))
+      of wImportCompilerProc:
+        processImportCompilerProc(sym, getOptionalStr(c, it, "$1"))
+      of wExtern: setExternName(sym, expectStrLit(c, it))
+      of wImmediate:
+        if sym.kind in {skTemplate, skMacro}: incl(sym.flags, sfImmediate)
+        else: invalidPragma(it)
+      of wDirty:
+        if sym.kind == skTemplate: incl(sym.flags, sfDirty)
+        else: invalidPragma(it)
+      of wImportCpp:
+        processImportCpp(sym, getOptionalStr(c, it, "$1"))
+      of wImportObjC:
+        processImportObjC(sym, getOptionalStr(c, it, "$1"))
+      of wAlign:
+        if sym.typ == nil: invalidPragma(it)
+        var align = expectIntLit(c, it)
+        if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.align = align.int16
+      of wSize:
+        if sym.typ == nil: invalidPragma(it)
+        var size = expectIntLit(c, it)
+        if not isPowerOfTwo(size) or size <= 0 or size > 8:
+          localError(it.info, errPowerOfTwoExpected)
+        else:
+          sym.typ.size = size
+      of wNodecl:
+        noVal(it)
+        incl(sym.loc.flags, lfNoDecl)
+      of wPure, wAsmNoStackFrame:
+        noVal(it)
+        if sym != nil:
+          if k == wPure and sym.kind in routineKinds: invalidPragma(it)
+          else: incl(sym.flags, sfPure)
+      of wVolatile:
+        noVal(it)
+        incl(sym.flags, sfVolatile)
+      of wRegister:
+        noVal(it)
+        incl(sym.flags, sfRegister)
+      of wThreadVar:
+        noVal(it)
+        incl(sym.flags, sfThread)
+      of wDeadCodeElim: pragmaDeadCodeElim(c, it)
+      of wNoForward: pragmaNoForward(c, it)
+      of wMagic: processMagic(c, it, sym)
+      of wCompileTime:
+        noVal(it)
+        incl(sym.flags, sfCompileTime)
+        incl(sym.loc.flags, lfNoDecl)
+      of wGlobal:
+        noVal(it)
+        incl(sym.flags, sfGlobal)
+        incl(sym.flags, sfPure)
+      of wMerge:
+        # only supported for backwards compat, doesn't do anything anymore
+        noVal(it)
+      of wConstructor:
+        noVal(it)
+        incl(sym.flags, sfConstructor)
+      of wHeader:
+        var lib = getLib(c, libHeader, getStrLitNode(c, it))
+        addToLib(lib, sym)
+        incl(sym.flags, sfImportc)
+        incl(sym.loc.flags, lfHeader)
+        incl(sym.loc.flags, lfNoDecl)
+        # implies nodecl, because otherwise header would not make sense
+        if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
+      of wDestructor:
+        sym.flags.incl sfOverriden
+        if sym.name.s.normalize != "destroy":
+          localError(n.info, errGenerated, "destructor has to be named 'destroy'")
+      of wOverride:
+        sym.flags.incl sfOverriden
+      of wNosideeffect:
+        noVal(it)
+        incl(sym.flags, sfNoSideEffect)
+        if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
+      of wSideeffect:
+        noVal(it)
+        incl(sym.flags, sfSideEffect)
+      of wNoreturn:
+        noVal(it)
+        incl(sym.flags, sfNoReturn)
+      of wDynlib:
+        processDynLib(c, it, sym)
+      of wCompilerproc:
+        noVal(it)           # compilerproc may not get a string!
+        if sfFromGeneric notin sym.flags: markCompilerProc(sym)
+      of wProcVar:
+        noVal(it)
+        incl(sym.flags, sfProcvar)
+      of wDeprecated:
+        if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
+        elif sym != nil: incl(sym.flags, sfDeprecated)
+        else: incl(c.module.flags, sfDeprecated)
+      of wVarargs:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfVarargs)
+      of wBorrow:
+        if sym.kind == skType:
+          typeBorrow(sym, it)
+        else:
           noVal(it)
+          incl(sym.flags, sfBorrow)
+      of wFinal:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfFinal)
+      of wInheritable:
+        noVal(it)
+        if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
+        else: incl(sym.typ.flags, tfInheritable)
+      of wAcyclic:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfAcyclic)
+      of wShallow:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfShallow)
+      of wThread:
+        noVal(it)
+        incl(sym.flags, sfThread)
+        incl(sym.flags, sfProcvar)
+        if sym.typ != nil: incl(sym.typ.flags, tfThread)
+      of wGcSafe:
+        noVal(it)
+        if sym.kind != skType: incl(sym.flags, sfThread)
+        if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
         else: invalidPragma(it)
+      of wPacked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfPacked)
+      of wHint: message(it.info, hintUser, expectStrLit(c, it))
+      of wWarning: message(it.info, warnUser, expectStrLit(c, it))
+      of wError:
+        if sym != nil and sym.isRoutine:
+          # This is subtle but correct: the error *statement* is only
+          # allowed for top level statements. Seems to be easier than
+          # distinguishing properly between
+          # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
+          noVal(it)
+          incl(sym.flags, sfError)
+        else:
+          localError(it.info, errUser, expectStrLit(c, it))
+      of wFatal: fatal(it.info, errUser, expectStrLit(c, it))
+      of wDefine: processDefine(c, it)
+      of wUndef: processUndef(c, it)
+      of wCompile: processCompile(c, it)
+      of wLink: processCommonLink(c, it, linkNormal)
+      of wLinksys: processCommonLink(c, it, linkSys)
+      of wPassl: extccomp.addLinkOption(expectStrLit(c, it))
+      of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
+      of wBreakpoint: pragmaBreakpoint(c, it)
+      of wWatchPoint: pragmaWatchpoint(c, it)
+      of wPush:
+        processPush(c, n, i + 1)
+        result = true
+      of wPop: processPop(c, it)
+      of wPragma:
+        processPragma(c, n, i)
+        result = true
+      of wDiscardable:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfDiscardable)
+      of wNoInit:
+        noVal(it)
+        if sym != nil: incl(sym.flags, sfNoInit)
+      of wCodegenDecl: processCodegenDecl(c, it, sym)
+      of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
+         wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
+         wLinedir, wStacktrace, wLinetrace, wOptimization,
+         wCallconv,
+         wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
+         wPatterns:
+        if processOption(c, it):
+          # calling conventions (boring...):
+          localError(it.info, errOptionExpected)
+      of FirstCallConv..LastCallConv:
+        assert(sym != nil)
+        if sym.typ == nil: invalidPragma(it)
+        else: sym.typ.callConv = wordToCallConv(k)
+      of wEmit: pragmaEmit(c, it)
+      of wUnroll: pragmaUnroll(c, it)
+      of wLinearScanEnd, wComputedGoto: noVal(it)
+      of wEffects:
+        # is later processed in effect analysis:
+        noVal(it)
+      of wIncompleteStruct:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfIncompleteStruct)
+      of wUnchecked:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUncheckedArray)
+      of wUnion:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfUnion)
+      of wRequiresInit:
+        noVal(it)
+        if sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfNeedsInit)
+      of wByRef:
+        noVal(it)
+        if sym == nil or sym.typ == nil:
+          if processOption(c, it): localError(it.info, errOptionExpected)
+        else:
+          incl(sym.typ.flags, tfByRef)
+      of wByCopy:
+        noVal(it)
+        if sym.kind != skType or sym.typ == nil: invalidPragma(it)
+        else: incl(sym.typ.flags, tfByCopy)
+      of wInject, wGensym:
+        # We check for errors, but do nothing with these pragmas otherwise
+        # as they are handled directly in 'evalTemplate'.
+        noVal(it)
+        if sym == nil: invalidPragma(it)
+      of wLine: pragmaLine(c, it)
+      of wRaises, wTags: pragmaRaisesOrTags(c, it)
+      of wLocks:
+        if sym == nil: pragmaLockStmt(c, it)
+        elif sym.typ == nil: invalidPragma(it)
+        else: sym.typ.lockLevel = pragmaLocks(c, it)
+      of wGuard:
+        if sym == nil or sym.kind notin {skVar, skLet, skField}:
+          invalidPragma(it)
+        else:
+          sym.guard = pragmaGuard(c, it, sym.kind)
+      of wGoto:
+        if sym == nil or sym.kind notin {skVar, skLet}:
+          invalidPragma(it)
+        else:
+          sym.flags.incl sfGoto
+      of wInjectStmt:
+        if it.kind != nkExprColonExpr:
+          localError(it.info, errExprExpected)
+        else:
+          it.sons[1] = c.semExpr(c, it.sons[1])
+      of wExperimental:
+        noVal(it)
+        if isTopLevel(c):
+          c.module.flags.incl sfExperimental
+        else:
+          localError(it.info, "'experimental' pragma only valid as toplevel statement")
+      of wNoRewrite:
+        noVal(it)
       else: invalidPragma(it)
-  else: processNote(c, it)
+    else: invalidPragma(it)
 
 proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
                       validPragmas: TSpecialWords) =
diff --git a/compiler/sem.nim b/compiler/sem.nim
index d23dd1543..041524f84 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -171,11 +171,15 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym =
   result = newSym(kind, considerQuotedIdent(n), getCurrOwner(), n.info)
 
 proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym =
+  proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLower
+
   # like newSymS, but considers gensym'ed symbols
   if n.kind == nkSym:
     # and sfGenSym in n.sym.flags:
     result = n.sym
-    internalAssert result.kind == kind
+    if result.kind != kind:
+      localError(n.info, "cannot use symbol of kind '" &
+                 $result.kind & "' as a '" & $kind & "'")
     # when there is a nested proc inside a template, semtmpl
     # will assign a wrong owner during the first pass over the
     # template; we must fix it here: see #909
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index b518f0fb9..3bb1284fc 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -23,6 +23,9 @@ proc newConstraint(c: PContext, k: TTypeKind): PType =
 
 proc semEnum(c: PContext, n: PNode, prev: PType): PType =
   if n.sonsLen == 0: return newConstraint(c, tyEnum)
+  elif n.sonsLen == 1:
+    # don't create an empty tyEnum; fixes #3052
+    return errorType(c)
   var
     counter, x: BiggestInt
     e: PSym
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 40d273ceb..d7495d77f 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -120,10 +120,10 @@ template decodeBx(k: expr) {.immediate, dirty.} =
 template move(a, b: expr) {.immediate, dirty.} = system.shallowCopy(a, b)
 # XXX fix minor 'shallowCopy' overloading bug in compiler
 
-proc createStrKeepNode(x: var TFullReg) =
+proc createStrKeepNode(x: var TFullReg; keepNode=true) =
   if x.node.isNil:
     x.node = newNode(nkStrLit)
-  elif x.node.kind == nkNilLit:
+  elif x.node.kind == nkNilLit and keepNode:
     when defined(useNodeIds):
       let id = x.node.id
     system.reset(x.node[])
@@ -385,6 +385,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
     #if c.traceActive:
     #  echo "PC ", pc, " ", c.code[pc].opcode, " ra ", ra
     #  message(c.debug[pc], warnUser, "Trace")
+
     case instr.opcode
     of opcEof: return regs[ra]
     of opcRet:
@@ -407,8 +408,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       decodeB(rkInt)
       regs[ra].intVal = regs[rb].intVal
     of opcAsgnStr:
-      decodeB(rkNode)
-      createStrKeepNode regs[ra]
+      decodeBC(rkNode)
+      createStrKeepNode regs[ra], rc != 0
       regs[ra].node.strVal = regs[rb].node.strVal
     of opcAsgnFloat:
       decodeB(rkFloat)
@@ -509,8 +510,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
       of rkNode:
         if regs[rb].node.kind == nkNilLit:
           stackTrace(c, tos, pc, errNilAccess)
-        assert regs[rb].node.kind == nkRefTy
-        regs[ra].node = regs[rb].node.sons[0]
+        if regs[rb].node.kind == nkRefTy:
+          regs[ra].node = regs[rb].node.sons[0]
+        else:
+          stackTrace(c, tos, pc, errGenerated, "limited VM support for 'ref'")
       else:
         stackTrace(c, tos, pc, errNilAccess)
     of opcWrDeref:
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 7abcbdb92..1bee9788a 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -74,8 +74,9 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) =
       result.addf("\t$#\tr$#, L$#", ($opc).substr(3), x.regA,
                   i+x.regBx-wordExcess)
     elif opc in {opcLdConst, opcAsgnConst}:
-      result.addf("\t$#\tr$#, $#", ($opc).substr(3), x.regA,
-        c.constants[x.regBx-wordExcess].renderTree)
+      let idx = x.regBx-wordExcess
+      result.addf("\t$#\tr$#, $# ($#)", ($opc).substr(3), x.regA,
+        c.constants[idx].renderTree, $idx)
     elif opc in {opcMarshalLoad, opcMarshalStore}:
       let y = c.code[i+1]
       result.addf("\t$#\tr$#, r$#, $#", ($opc).substr(3), x.regA, x.regB,
@@ -184,7 +185,7 @@ proc getTemp(cc: PCtx; typ: PType): TRegister =
       return TRegister(i)
 
   # if register pressure is high, we re-use more aggressively:
-  if c.maxSlots >= HighRegisterPressure:
+  if c.maxSlots >= HighRegisterPressure and false:
     for i in 0 .. c.maxSlots-1:
       if not c.slots[i].inUse:
         c.slots[i] = (inUse: true, kind: k)
@@ -1073,6 +1074,7 @@ const
     tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64}
 
 proc fitsRegister*(t: PType): bool =
+  assert t != nil
   t.skipTypes(abstractInst-{tyTypeDesc}).kind in {
     tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar}
 
@@ -1110,6 +1112,7 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
     if dest < 0: dest = c.getTemp(n.typ)
     if not isAddr:
       gABC(c, n, opc, dest, tmp)
+      assert n.typ != nil
       if gfAddrOf notin flags and fitsRegister(n.typ):
         c.gABC(n, opcNodeToReg, dest, dest)
     elif c.prc.slots[tmp].kind >= slotTempUnknown:
@@ -1143,7 +1146,7 @@ proc whichAsgnOpc(n: PNode; opc: TOpcode): TOpcode = opc
 proc genAsgn(c: PCtx; dest: TDest; ri: PNode; requiresCopy: bool) =
   let tmp = c.genx(ri)
   assert dest >= 0
-  gABC(c, ri, whichAsgnOpc(ri), dest, tmp)
+  gABC(c, ri, whichAsgnOpc(ri), dest, tmp, 1-ord(requiresCopy))
   c.freeTemp(tmp)
 
 proc setSlot(c: PCtx; v: PSym) =
@@ -1194,9 +1197,10 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
   # opcLdObj et al really means "load address". We sometimes have to create a
   # copy in order to not introduce false aliasing:
   # mylocal = a.b  # needs a copy of the data!
+  assert n.typ != nil
   if needsAdditionalCopy(n):
     var cc = c.getTemp(n.typ)
-    c.gABC(n, whichAsgnOpc(n), cc, value)
+    c.gABC(n, whichAsgnOpc(n), cc, value, 0)
     c.gABC(n, opc, dest, idx, cc)
     c.freeTemp(cc)
   else:
@@ -1241,10 +1245,11 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
       internalAssert s.position > 0 or (s.position == 0 and
                                         s.kind in {skParam,skResult})
       var dest: TRegister = s.position + ord(s.kind == skParam)
+      assert le.typ != nil
       if needsAdditionalCopy(le) and s.kind in {skResult, skVar, skParam}:
         var cc = c.getTemp(le.typ)
         gen(c, ri, cc)
-        c.gABC(le, whichAsgnOpc(le), dest, cc)
+        c.gABC(le, whichAsgnOpc(le), dest, cc, 1)
         c.freeTemp(cc)
       else:
         gen(c, ri, dest)
@@ -1303,6 +1308,7 @@ proc genRdVar(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
       if sfImportc in s.flags: c.importcSym(n.info, s)
       else: genGlobalInit(c, n, s)
     if dest < 0: dest = c.getTemp(n.typ)
+    assert s.typ != nil
     if gfAddrOf notin flags and fitsRegister(s.typ):
       var cc = c.getTemp(n.typ)
       c.gABx(n, opcLdGlobal, cc, s.position)
@@ -1426,6 +1432,7 @@ proc getNullValue(typ: PType, info: TLineInfo): PNode =
     globalError(info, "cannot create null element for: " & $t.kind)
 
 proc ldNullOpcode(t: PType): TOpcode =
+  assert t != nil
   if fitsRegister(t): opcLdNullReg else: opcLdNull
 
 proc genVarSection(c: PCtx; n: PNode) =
@@ -1453,7 +1460,7 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind != nkEmpty:
           let tmp = c.genx(a.sons[0], {gfAddrOf})
           let val = c.genx(a.sons[2])
-          c.preventFalseAlias(a, opcWrDeref, tmp, 0, val)
+          c.preventFalseAlias(a.sons[2], opcWrDeref, tmp, 0, val)
           c.freeTemp(val)
           c.freeTemp(tmp)
       else:
@@ -1461,13 +1468,15 @@ proc genVarSection(c: PCtx; n: PNode) =
         if a.sons[2].kind == nkEmpty:
           c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
         else:
+          assert s.typ != nil
           if not fitsRegister(s.typ):
             c.gABx(a, ldNullOpcode(s.typ), s.position, c.genType(s.typ))
           let le = a.sons[0]
+          assert le.typ != nil
           if not fitsRegister(le.typ) and s.kind in {skResult, skVar, skParam}:
             var cc = c.getTemp(le.typ)
             gen(c, a.sons[2], cc)
-            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc)
+            c.gABC(le, whichAsgnOpc(le), s.position.TRegister, cc, 1)
             c.freeTemp(cc)
           else:
             gen(c, a.sons[2], s.position.TRegister)
@@ -1694,7 +1703,7 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
     c.freeTemp(tmp1)
     c.freeTemp(tmp2)
     if dest >= 0:
-      gABC(c, n, whichAsgnOpc(n), dest, tmp0)
+      gABC(c, n, whichAsgnOpc(n), dest, tmp0, 1)
       c.freeTemp(tmp0)
     else:
       dest = tmp0
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index 7d1c15610..c89fa354a 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -592,10 +592,8 @@ proc newNilLit*(): NimNode {.compileTime.} =
   ## New nil literal shortcut
   result = newNimNode(nnkNilLit)
 
-proc high*(node: NimNode): int {.compileTime.} = len(node) - 1
-  ## Return the highest index available for a node
-proc last*(node: NimNode): NimNode {.compileTime.} = node[node.high]
-  ## Return the last item in nodes children. Same as `node[node.high()]`
+proc last*(node: NimNode): NimNode {.compileTime.} = node[<node.len]
+  ## Return the last item in nodes children. Same as `node[^1]`
 
 
 const
@@ -695,7 +693,7 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} =
   of nnkBlockStmt, nnkWhileStmt:
     someProc[1] = val
   of nnkForStmt:
-    someProc[high(someProc)] = val
+    someProc[len(someProc)-1] = val
   else:
     badNodeKind someProc.kind, "body="
 
@@ -722,7 +720,7 @@ proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
   ## Create a new ident node from a string
 
 iterator children*(n: NimNode): NimNode {.inline.}=
-  for i in 0 .. high(n):
+  for i in 0 ..< n.len:
     yield n[i]
 
 template findChild*(n: NimNode; cond: expr): NimNode {.
@@ -742,16 +740,16 @@ template findChild*(n: NimNode; cond: expr): NimNode {.
 
 proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} =
   ## Insert node B into A at pos
-  if high(a) < pos:
+  if len(a)-1 < pos:
     ## add some empty nodes first
-    for i in high(a)..pos-2:
+    for i in len(a)-1..pos-2:
       a.add newEmptyNode()
     a.add b
   else:
     ## push the last item onto the list again
     ## and shift each item down to pos up one
-    a.add(a[a.high])
-    for i in countdown(high(a) - 2, pos):
+    a.add(a[a.len-1])
+    for i in countdown(len(a) - 3, pos):
       a[i + 1] = a[i]
     a[pos] = b
 
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 2ce31b4e8..7523b29d5 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -972,9 +972,9 @@ else:
       let data = PData(info.key.data)
       assert data.fd == info.key.fd.AsyncFD
       #echo("In poll ", data.fd.cint)
-      if EvError in info.events:
-        closeSocket(data.fd)
-        continue
+      # There may be EvError here, but we handle them in callbacks,
+      # so that exceptions can be raised from `send(...)` and
+      # `recv(...)` routines.
 
       if EvRead in info.events:
         # Callback may add items to ``data.readCBs`` which causes issues if
@@ -1013,9 +1013,17 @@ else:
     var retFuture = newFuture[void]("connect")
 
     proc cb(fd: AsyncFD): bool =
-      # We have connected.
-      retFuture.complete()
-      return true
+      var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
+      if ret == 0:
+          # We have connected.
+          retFuture.complete()
+          return true
+      elif ret == EINTR:
+          # interrupted, keep waiting
+          return false
+      else:
+          retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
+          return true
 
     assert getSockDomain(socket.SocketHandle) == domain
     var aiList = getAddrInfo(address, port, domain)
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index cfd3d7666..9139200f3 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -453,6 +453,7 @@ proc close*(socket: AsyncSocket) =
   when defined(ssl):
     if socket.isSSL:
       let res = SslShutdown(socket.sslHandle)
+      SSLFree(socket.sslHandle)
       if res == 0:
         discard
       elif res != 1:
@@ -567,4 +568,3 @@ when not defined(testing) and isMainModule:
     var f = accept(sock)
     f.callback = onAccept
   runForever()
-
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index e6b8088c5..98687359b 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -402,7 +402,7 @@ proc request*(url: string, httpMethod: string, extraHeaders = "",
 
   headers.add(" HTTP/1.1\c\L")
 
-  add(headers, "Host: " & r.hostname & "\c\L")
+  add(headers, "Host: " & parseUri(url).hostname & "\c\L")
   if userAgent != "":
     add(headers, "User-Agent: " & userAgent & "\c\L")
   if proxy != nil and proxy.auth != "":
diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim
index 49f049e46..173cd1e81 100644
--- a/lib/pure/marshal.nim
+++ b/lib/pure/marshal.nim
@@ -17,7 +17,7 @@
 ## .. code-block:: nim
 ##
 ##   type
-##     A = object
+##     A = object of RootObj
 ##     B = object of A
 ##       f: int
 ##
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index de52cbbd1..c459023a9 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -9,7 +9,7 @@
 
 ## :Author: Zahary Karadjov
 ##
-## This module implements boilerplate to make testing easy.
+## This module implements boilerplate to make unit testing easy.
 ##
 ## Example:
 ##
@@ -41,27 +41,69 @@ when not defined(ECMAScript):
   import terminal
 
 type
-  TestStatus* = enum OK, FAILED
-  OutputLevel* = enum PRINT_ALL, PRINT_FAILURES, PRINT_NONE
+  TestStatus* = enum OK, FAILED ## The status of a test when it is done.
+  OutputLevel* = enum  ## The output verbosity of the tests.
+    PRINT_ALL,         ## Print as much as possible.
+    PRINT_FAILURES,    ## Print only the failed tests.
+    PRINT_NONE         ## Print nothing.
 
 {.deprecated: [TTestStatus: TestStatus, TOutputLevel: OutputLevel]}
 
-var
-  abortOnError* {.threadvar.}: bool
-  outputLevel* {.threadvar.}: OutputLevel
-  colorOutput* {.threadvar.}: bool
+var ## Global unittest settings!
+
+  abortOnError* {.threadvar.}: bool ## Set to true in order to quit
+                                    ## immediately on fail. Default is false,
+                                    ## unless the ``NIMTEST_ABORT_ON_ERROR``
+                                    ## environment variable is set for
+                                    ## the non-js target.
+  outputLevel* {.threadvar.}: OutputLevel ## Set the verbosity of test results.
+                                          ## Default is ``PRINT_ALL``, unless
+                                          ## the ``NIMTEST_OUTPUT_LVL`` environment
+                                          ## variable is set for the non-js target.
+
+  colorOutput* {.threadvar.}: bool ## Have test results printed in color.
+                                   ## Default is true for the non-js target
+                                   ## unless, the environment variable
+                                   ## ``NIMTEST_NO_COLOR`` is set.
 
   checkpoints {.threadvar.}: seq[string]
 
 checkpoints = @[]
 
-template testSetupIMPL*: stmt {.immediate, dirty.} = discard
+template testSetupIMPL*: stmt {.immediate, dirty.} = discard      #Should this be public or even exist?
 template testTeardownIMPL*: stmt {.immediate, dirty.} = discard
 
 proc shouldRun(testName: string): bool =
   result = true
 
 template suite*(name: expr, body: stmt): stmt {.immediate, dirty.} =
+  ## Declare a test suite identified by `name` with optional ``setup``
+  ## and/or ``teardown`` section.
+  ##
+  ## A test suite is a series of one or more related tests sharing a
+  ## common fixture (``setup``, ``teardown``). The fixture is executed
+  ## for EACH test.
+  ##
+  ## .. code-block:: nim
+  ##  suite "test suite for addition":
+  ##    setup:
+  ##      let result = 4
+  ##
+  ##    test "2 + 2 = 4":
+  ##      check(2+2 == result)
+  ##
+  ##    test "2 + -2 != 4":
+  ##      check(2+2 != result)
+  ##
+  ##    # No teardown needed
+  ##
+  ## The suite will run the individual test cases in the order in which
+  ## they were listed. With default global settings the above code prints:
+  ##
+  ## .. code-block::
+  ##
+  ##  [OK] 2 + 2 = 4
+  ##  [OK] (2 + -2) != 4
   block:
     template setup(setupBody: stmt): stmt {.immediate, dirty.} =
       template testSetupIMPL: stmt {.immediate, dirty.} = setupBody
@@ -87,6 +129,19 @@ proc testDone(name: string, s: TestStatus) =
       rawPrint()
 
 template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
+  ## Define a single test case identified by `name`.
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  test "roses are red":
+  ##    let roses = "red"
+  ##    check(roses == "red")
+  ##
+  ## The above code outputs:
+  ##
+  ## .. code-block::
+  ##
+  ##  [OK] roses are red
   bind shouldRun, checkpoints, testDone
 
   if shouldRun(name):
@@ -108,10 +163,32 @@ template test*(name: expr, body: stmt): stmt {.immediate, dirty.} =
       testDone name, testStatusIMPL
 
 proc checkpoint*(msg: string) =
+  ## Set a checkpoint identified by `msg`. Upon test failure all
+  ## checkpoints encountered so far are printed out. Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  checkpoint("Checkpoint A")
+  ##  check((42, "the Answer to life and everything") == (1, "a"))
+  ##  checkpoint("Checkpoint B")
+  ##
+  ## outputs "Checkpoint A" once it fails.
   checkpoints.add(msg)
   # TODO: add support for something like SCOPED_TRACE from Google Test
 
 template fail* =
+  ## Print out the checkpoints encountered so far and quit if ``abortOnError``
+  ## is true. Otherwise, erase the checkpoints and indicate the test has
+  ## failed (change exit code and test status). This template is useful
+  ## for debugging, but is otherwise mostly used internally. Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  checkpoint("Checkpoint A")
+  ##  complicatedProcInThread()
+  ##  fail()
+  ##
+  ## outputs "Checkpoint A" before quitting.
   bind checkpoints
   for msg in items(checkpoints):
     echo msg
@@ -127,8 +204,23 @@ template fail* =
   checkpoints = @[]
 
 macro check*(conditions: stmt): stmt {.immediate.} =
+  ## Verify if a statement or a list of statements is true.
+  ## A helpful error message and set checkpoints are printed out on
+  ## failure (if ``outputLevel`` is not ``PRINT_NONE``).
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  import strutils
+  ##
+  ##  check("AKB48".toLower() == "akb48")
+  ##
+  ##  let teams = {'A', 'K', 'B', '4', '8'}
+  ##
+  ##  check:
+  ##    "AKB48".toLower() == "akb48"
+  ##    'C' in teams
   let checked = callsite()[1]
-
   var
     argsAsgns = newNimNode(nnkStmtList)
     argsPrintOuts = newNimNode(nnkStmtList)
@@ -143,7 +235,7 @@ macro check*(conditions: stmt): stmt {.immediate.} =
       checkpoint(name & " was " & $value)
 
   proc inspectArgs(exp: NimNode) =
-    for i in 1 .. <exp.len:
+    for i in countup(1, exp.len - 1):
       if exp[i].kind notin nnkLiterals:
         inc counter
         var arg = newIdentNode(":p" & $counter)
@@ -194,11 +286,34 @@ macro check*(conditions: stmt): stmt {.immediate.} =
     result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit))
 
 template require*(conditions: stmt): stmt {.immediate, dirty.} =
+  ## Same as `check` except any failed test causes the program to quit
+  ## immediately. Any teardown statements are not executed and the failed
+  ## test output is not generated.
+  let savedAbortOnError = abortOnError
   block:
-    const AbortOnError {.inject.} = true
+    abortOnError = true
     check conditions
+  abortOnError = savedAbortOnError
 
 macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
+  ## Test if `body` raises an exception found in the passed `exceptions`.
+  ## The test passes if the raised exception is part of the acceptable
+  ## exceptions. Otherwise, it fails.
+  ## Example:
+  ##
+  ## .. code-block:: nim
+  ##
+  ##  import math
+  ##  proc defectiveRobot() =
+  ##    randomize()
+  ##    case random(1..4)
+  ##    of 1: raise newException(OSError, "CANNOT COMPUTE!")
+  ##    of 2: discard parseInt("Hello World!")
+  ##    of 3: raise newException(IOError, "I can't do that Dave.")
+  ##    else: assert 2 + 2 == 5
+  ##
+  ##  expect IOError, OSError, ValueError, AssertionError:
+  ##    defectiveRobot()
   let exp = callsite()
   template expectBody(errorTypes, lineInfoLit: expr,
                       body: stmt): NimNode {.dirty.} =
@@ -208,6 +323,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
       fail()
     except errorTypes:
       discard
+    except:
+      checkpoint(lineInfoLit & ": Expect Failed, unexpected exception was thrown.")
+      fail()
 
   var body = exp[exp.len - 1]
 
@@ -219,9 +337,9 @@ macro expect*(exceptions: varargs[expr], body: stmt): stmt {.immediate.} =
 
 
 when declared(stdout):
-  ## Reading settings
+  # Reading settings
+  # On a terminal this branch is executed
   var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string
-
   abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR")
   colorOutput  = not existsEnv("NIMTEST_NO_COLOR")
 
diff --git a/lib/system.nim b/lib/system.nim
index c5b0e0cc7..dd1a0721a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -176,10 +176,16 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it in ``a``.
 
-proc new*(T: typedesc): ref T =
+proc new*(T: typedesc): auto =
   ## creates a new object of type ``T`` and returns a safe (traced)
   ## reference to it as result value
-  new(result)
+  when (T is ref):
+      var r: T
+  else:
+      var r: ref T
+  new(r)
+  return r
+
 
 proc internalNew*[T](a: var ref T) {.magic: "New", noSideEffect.}
   ## leaked implementation detail. Do not use.
@@ -1150,7 +1156,8 @@ const
 
   hostCPU* {.magic: "HostCPU".}: string = ""
     ## a string that describes the host CPU. Possible values:
-    ## "i386", "alpha", "powerpc", "powerpc64", "sparc", "amd64", "mips", "arm".
+    ## "i386", "alpha", "powerpc", "powerpc64", "powerpc64el", "sparc",
+    ## "amd64", "mips", "mipsel", "arm", "arm64".
 
   seqShallowFlag = low(int)
 
@@ -2173,7 +2180,11 @@ proc `$`*[T: tuple|object](x: T): string =
     if not firstElement: result.add(", ")
     result.add(name)
     result.add(": ")
-    result.add($value)
+    when compiles(value.isNil):
+      if value.isNil: result.add "nil"
+      else: result.add($value)
+    else:
+      result.add($value)
     firstElement = false
   result.add(")")
 
diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim
index 47a01d5fe..0a7045fae 100644
--- a/lib/system/platforms.nim
+++ b/lib/system/platforms.nim
@@ -18,11 +18,14 @@ type
     alpha,                     ## Alpha processor
     powerpc,                   ## 32 bit PowerPC
     powerpc64,                 ## 64 bit PowerPC
+    powerpc64el,               ## Little Endian 64 bit PowerPC
     sparc,                     ## Sparc based processor
     ia64,                      ## Intel Itanium
     amd64,                     ## x86_64 (AMD64); 64 bit x86 compatible CPU
     mips,                      ## Mips based processor
+    mipsel,                    ## Little Endian Mips based processor
     arm,                       ## ARM based processor
+    arm64,                     ## ARM64 based processor
     vm,                        ## Some Virtual machine: Nim's VM or JavaScript
     avr                        ## AVR based processor
 
@@ -63,11 +66,14 @@ const
                elif defined(alpha): CpuPlatform.alpha
                elif defined(powerpc): CpuPlatform.powerpc
                elif defined(powerpc64): CpuPlatform.powerpc64
+               elif defined(powerpc64el): CpuPlatform.powerpc64el
                elif defined(sparc): CpuPlatform.sparc
                elif defined(ia64): CpuPlatform.ia64
                elif defined(amd64): CpuPlatform.amd64
                elif defined(mips): CpuPlatform.mips
+               elif defined(mipsel): CpuPlatform.mipsel
                elif defined(arm): CpuPlatform.arm
+               elif defined(arm64): CpuPlatform.arm64
                elif defined(vm): CpuPlatform.vm
                elif defined(avr): CpuPlatform.avr
                else: CpuPlatform.none
diff --git a/lib/system/repr.nim b/lib/system/repr.nim
index 74396f424..b4188527f 100644
--- a/lib/system/repr.nim
+++ b/lib/system/repr.nim
@@ -256,7 +256,10 @@ when not defined(useNimRtl):
     of tyBool: add result, reprBool(cast[ptr bool](p)[])
     of tyChar: add result, reprChar(cast[ptr char](p)[])
     of tyString: reprStrAux(result, cast[ptr string](p)[])
-    of tyCString: reprStrAux(result, $(cast[ptr cstring](p)[]))
+    of tyCString:
+      let cs = cast[ptr cstring](p)[]
+      if cs.isNil: add result, "nil"
+      else: reprStrAux(result, $cs)
     of tyRange: reprAux(result, p, typ.base, cl)
     of tyProc, tyPointer:
       if cast[PPointer](p)[] == nil: add result, "nil"
diff --git a/lib/system/sets.nim b/lib/system/sets.nim
index 22d6d57c0..66877de30 100644
--- a/lib/system/sets.nim
+++ b/lib/system/sets.nim
@@ -19,9 +19,9 @@ proc countBits32(n: int32): int {.compilerproc.} =
   v = (v and 0x33333333'i32) +% ((v shr 2'i32) and 0x33333333'i32)
   result = ((v +% (v shr 4'i32) and 0xF0F0F0F'i32) *% 0x1010101'i32) shr 24'i32
 
-proc countBits64(n: int64): int {.compilerproc.} = 
-  result = countBits32(toU32(n and 0xffff'i64)) +
-           countBits32(toU32(n shr 16'i64))
+proc countBits64(n: int64): int {.compilerproc.} =
+  result = countBits32(toU32(n and 0xffffffff'i64)) +
+           countBits32(toU32(n shr 32'i64))
 
 proc cardSet(s: NimSet, len: int): int {.compilerproc.} =
   result = 0
diff --git a/tests/async/tasyncconnect.nim b/tests/async/tasyncconnect.nim
new file mode 100644
index 000000000..bc63b8e82
--- /dev/null
+++ b/tests/async/tasyncconnect.nim
@@ -0,0 +1,33 @@
+discard """
+  file: "tasyncconnect.nim"
+  exitcode: 1
+  outputsub: "Error: unhandled exception: Connection refused [Exception]"
+"""
+
+import
+    asyncdispatch,
+    posix
+
+
+const
+    testHost = "127.0.0.1"
+    testPort = Port(17357)
+
+
+when defined(windows) or defined(nimdoc):
+    discard
+else:
+    proc testAsyncConnect() {.async.} =
+        var s = newAsyncRawSocket()
+
+        await s.connect(testHost, testPort)
+
+        var peerAddr: SockAddr
+        var addrSize = Socklen(sizeof(peerAddr))
+        var ret = SocketHandle(s).getpeername(addr(peerAddr), addr(addrSize))
+
+        if ret < 0:
+            echo("`connect(...)` failed but no exception was raised.")
+            quit(2)
+
+    waitFor(testAsyncConnect())
diff --git a/tests/async/tasynceverror.nim b/tests/async/tasynceverror.nim
new file mode 100644
index 000000000..3b81680cb
--- /dev/null
+++ b/tests/async/tasynceverror.nim
@@ -0,0 +1,65 @@
+discard """
+  file: "tasynceverror.nim"
+  exitcode: 1
+  outputsub: "Error: unhandled exception: Connection reset by peer [Exception]"
+"""
+
+import
+    asyncdispatch,
+    asyncnet,
+    rawsockets,
+    os
+
+
+const
+    testHost = "127.0.0.1"
+    testPort = Port(17357)
+
+
+when defined(windows) or defined(nimdoc):
+    discard
+else:
+    proc createListenSocket(host: string, port: Port): TAsyncFD =
+        result = newAsyncRawSocket()
+
+        SocketHandle(result).setSockOptInt(SOL_SOCKET, SO_REUSEADDR, 1)
+
+        var aiList = getAddrInfo(host, port, AF_INET)
+        if SocketHandle(result).bindAddr(aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
+          dealloc(aiList)
+          raiseOSError(osLastError())
+        dealloc(aiList)
+
+        if SocketHandle(result).listen(1) < 0'i32:
+            raiseOSError(osLastError())
+
+
+    proc testAsyncSend() {.async.} =
+        var
+            ls = createListenSocket(testHost, testPort)
+            s = newAsyncSocket()
+
+        await s.connect(testHost, testPort)
+        
+        var ps = await ls.accept()
+        SocketHandle(ls).close()
+
+        await ps.send("test 1", flags={})
+        s.close()
+        # This send should raise EPIPE
+        await ps.send("test 2", flags={})
+        SocketHandle(ps).close()
+
+
+    # The bug was, when the poll function handled EvError for us,
+    # our callbacks may never get executed, thus making the event
+    # loop block indefinitely. This is a timer to keep everything
+    # rolling. 400 ms is an arbitrary value, should be enough though.
+    proc timer() {.async.} =
+        await sleepAsync(400)
+        echo("Timer expired.")
+        quit(2)
+
+
+    asyncCheck(testAsyncSend())
+    waitFor(timer())
diff --git a/tests/metatype/tstatic_ones.nim b/tests/metatype/tstatic_ones.nim
new file mode 100644
index 000000000..73a88447d
--- /dev/null
+++ b/tests/metatype/tstatic_ones.nim
@@ -0,0 +1,28 @@
+discard """
+  output: "@[2, 2, 2, 2, 2]"
+"""
+
+# bug #3144
+
+type IntArray[N: static[int]] = array[N, int]
+
+proc `$`(a: IntArray): string = $(@(a))
+
+proc `+=`[N: static[int]](a: var IntArray[N], b: IntArray[N]) =
+  for i in 0 .. < N:
+    a[i] += b[i]
+
+proc zeros(N: static[int]): IntArray[N] =
+  for i in 0 .. < N:
+    result[i] = 0
+
+proc ones(N: static[int]): IntArray[N] =
+  for i in 0 .. < N:
+    result[i] = 1
+
+proc sum[N: static[int]](vs: seq[IntArray[N]]): IntArray[N] =
+  result = zeros(N)
+  for v in vs:
+    result += v
+
+echo sum(@[ones(5), ones(5)])
diff --git a/tests/pragmas/tsym_as_pragma.nim b/tests/pragmas/tsym_as_pragma.nim
new file mode 100644
index 000000000..f6ba44dc9
--- /dev/null
+++ b/tests/pragmas/tsym_as_pragma.nim
@@ -0,0 +1,8 @@
+
+# bug #3171
+
+template newDataWindow(): stmt =
+    let eventClosure = proc (closure: pointer): bool {.closure, cdecl.} =
+        discard
+
+newDataWindow()
diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim
index b23b3cdab..fb9b02243 100644
--- a/tests/stdlib/tunittest.nim
+++ b/tests/stdlib/tunittest.nim
@@ -19,3 +19,17 @@ import options
 test "unittest typedescs":
   check(none(int) == none(int))
   check(none(int) != some(1))
+
+
+import math
+from strutils import parseInt
+proc defectiveRobot() =
+  randomize()
+  case random(1..4)
+  of 1: raise newException(OSError, "CANNOT COMPUTE!")
+  of 2: discard parseInt("Hello World!")
+  of 3: raise newException(IOError, "I can't do that Dave.")
+  else: assert 2 + 2 == 5
+test "unittest expect":
+  expect IOError, OSError, ValueError, AssertionError:
+    defectiveRobot()
diff --git a/tests/template/t_otemplates.nim b/tests/template/t_otemplates.nim
index db535d818..6c419f72f 100644
--- a/tests/template/t_otemplates.nim
+++ b/tests/template/t_otemplates.nim
@@ -2,344 +2,344 @@ discard """
   output: "Success"
 """
 
-# Ref:

-# http://nim-lang.org/macros.html

-# http://nim-lang.org/parseutils.html

-

-

-# Imports

-import tables, parseutils, macros, strutils

-import annotate

-export annotate

-

-

-# Fields

-const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}

-

-

-# Procedure Declarations

-proc parse_template(node: NimNode, value: string) {.compiletime.}

-

-

-# Procedure Definitions

-proc substring(value: string, index: int, length = -1): string {.compiletime.} =

-    ## Returns a string at most `length` characters long, starting at `index`.

-    return if length < 0:    value.substr(index)

-           elif length == 0: ""

-           else:             value.substr(index, index + length-1)

-

-

-proc parse_thru_eol(value: string, index: int): int {.compiletime.} =

-    ## Reads until and past the end of the current line, unless

-    ## a non-whitespace character is encountered first

-    var remainder: string

-    var read = value.parseUntil(remainder, {0x0A.char}, index)

-    if remainder.skipWhitespace() == read:

-        return read + 1

-

-

-proc trim_after_eol(value: var string) {.compiletime.} =

-    ## Trims any whitespace at end after \n

-    var toTrim = 0

-    for i in countdown(value.len-1, 0):

-        # If \n, return

-        if value[i] in [' ', '\t']: inc(toTrim)

-        else: break

-

-    if toTrim > 0:

-        value = value.substring(0, value.len - toTrim)

-

-

-proc trim_eol(value: var string) {.compiletime.} =

-    ## Removes everything after the last line if it contains nothing but whitespace

-    for i in countdown(value.len - 1, 0):

-        # If \n, trim and return

-        if value[i] == 0x0A.char:

-            value = value.substr(0, i)

-            break

-

-        # This is the first character

-        if i == 0:

-            value = ""

-            break

-

-        # Skip change

-        if not (value[i] in [' ', '\t']): break

-

-

-proc detect_indent(value: string, index: int): int {.compiletime.} =

-    ## Detects how indented the line at `index` is.

-    # Seek to the beginning of the line.

-    var lastChar = index

-    for i in countdown(index, 0):

-        if value[i] == 0x0A.char:

-            # if \n, return the indentation level

-            return lastChar - i

-        elif not (value[i] in [' ', '\t']):

-            # if non-whitespace char, decrement lastChar

-            dec(lastChar)

-

-

-proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} =

-    ## Parses until ending " or ' is reached.

-    inc(i)

-    if i < value.len-1:

-        inc(i, value.skipUntil({'\\', strType}, i))

-

-

-proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} =

-    ## Reads until all opened braces are closed

-    ## ignoring any strings "" or ''

-    var remainder   = value.substring(index)

-    var open_braces = opened

-    result = 0

-

-    while result < remainder.len:

-        var c = remainder[result]

-

-        if   c == open:  inc(open_braces)

-        elif c == close: dec(open_braces)

-        elif c == '"':   remainder.parse_thru_string(result)

-        elif c == '\'':  remainder.parse_thru_string(result, '\'')

-

-        if open_braces == 0: break

-        else: inc(result)

-

-

-iterator parse_stmt_list(value: string, index: var int): string =

-    ## Parses unguided ${..} block

-    var read        = value.parse_to_close(index, open='{', close='}')

-    var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char })

-

-    for expression in expressions:

-        let value = expression.strip

-        if value.len > 0:

-            yield value

-

-    #Increment index & parse thru EOL

-    inc(index, read + 1)

-    inc(index, value.parse_thru_eol(index))

-

-

-iterator parse_compound_statements(value, identifier: string, index: int): string =

-    ## Parses through several statements, i.e. if {} elif {} else {}

-    ## and returns the initialization of each as an empty statement

-    ## i.e. if x == 5 { ... } becomes if x == 5: nil.

-

-    template get_next_ident(expected): stmt =

-        var nextIdent: string

-        discard value.parseWhile(nextIdent, {'$'} + identChars, i)

-

-        var next: string

-        var read: int

-

-        if nextIdent == "case":

-            # We have to handle case a bit differently

-            read = value.parseUntil(next, '$', i)

-            inc(i, read)

-            yield next.strip(leading=false) & "\n"

-

-        else:

-            read = value.parseUntil(next, '{', i)

-

-            if nextIdent in expected:

-                inc(i, read)

-                # Parse until closing }, then skip whitespace afterwards

-                read = value.parse_to_close(i, open='{', close='}')

-                inc(i, read + 1)

-                inc(i, value.skipWhitespace(i))

-                yield next & ": nil\n"

-

-            else: break

-

-

-    var i = index

-    while true:

-        # Check if next statement would be valid, given the identifier

-        if identifier in ["if", "when"]:

-            get_next_ident([identifier, "$elif", "$else"])

-

-        elif identifier == "case":

-            get_next_ident(["case", "$of", "$elif", "$else"])

-

-        elif identifier == "try":

-            get_next_ident(["try", "$except", "$finally"])

-

-

-proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} =

-    ## Parses if/when/try /elif /else /except /finally statements

-

-    # Build up complex statement string

-    var stmtString = newString(0)

-    var numStatements = 0

-    for statement in value.parse_compound_statements(identifier, index):

-        if statement[0] == '$': stmtString.add(statement.substr(1))

-        else:                   stmtString.add(statement)

-        inc(numStatements)

-

-    # Parse stmt string

-    result = parseExpr(stmtString)

-

-    var resultIndex = 0

-

-    # Fast forward a bit if this is a case statement

-    if identifier == "case":

-        inc(resultIndex)

-

-    while resultIndex < numStatements:

-

-        # Detect indentation

-        let indent = detect_indent(value, index)

-

-        # Parse until an open brace `{`

-        var read = value.skipUntil('{', index)

-        inc(index, read + 1)

-

-        # Parse through EOL

-        inc(index, value.parse_thru_eol(index))

-

-        # Parse through { .. }

-        read = value.parse_to_close(index, open='{', close='}', opened=1)

-

-        # Add parsed sub-expression into body

-        var body       = newStmtList()

-        var stmtString = value.substring(index, read)

-        trim_after_eol(stmtString)

-        stmtString = reindent(stmtString, indent)

-        parse_template(body, stmtString)

-        inc(index, read + 1)

-

-        # Insert body into result

-        var stmtIndex = macros.high(result[resultIndex])

-        result[resultIndex][stmtIndex] = body

-

-        # Parse through EOL again & increment result index

-        inc(index, value.parse_thru_eol(index))

-        inc(resultIndex)

-

-

-proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} =

-    ## Parses for/while

-

-    # Detect indentation

-    let indent = detect_indent(value, index)

-

-    # Parse until an open brace `{`

-    var splitValue: string

-    var read = value.parseUntil(splitValue, '{', index)

-    result   = parseExpr(splitValue & ":nil")

-    inc(index, read + 1)

-

-    # Parse through EOL

-    inc(index, value.parse_thru_eol(index))

-

-    # Parse through { .. }

-    read = value.parse_to_close(index, open='{', close='}', opened=1)

-

-    # Add parsed sub-expression into body

-    var body       = newStmtList()

-    var stmtString = value.substring(index, read)

-    trim_after_eol(stmtString)

-    stmtString = reindent(stmtString, indent)

-    parse_template(body, stmtString)

-    inc(index, read + 1)

-

-    # Insert body into result

-    var stmtIndex = macros.high(result)

-    result[stmtIndex] = body

-

-    # Parse through EOL again

-    inc(index, value.parse_thru_eol(index))

-

-

-proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =

-    ## Parses a string until a $ symbol is encountered, if

-    ## two $$'s are encountered in a row, a split will happen

-    ## removing one of the $'s from the resulting output

-    var splitValue: string

-    var read = value.parseUntil(splitValue, '$', index)

-    var insertionPoint = node.len

-

-    inc(index, read + 1)

-    if index < value.len:

-

-        case value[index]

-        of '$':

-            # Check for duplicate `$`, meaning this is an escaped $

-            node.add newCall("add", ident("result"), newStrLitNode("$"))

-            inc(index)

-

-        of '(':

-            # Check for open `(`, which means parse as simple single-line expression.

-            trim_eol(splitValue)

-            read = value.parse_to_close(index) + 1

-            node.add newCall("add", ident("result"),

-                newCall(bindSym"strip", parseExpr("$" & value.substring(index, read)))

-            )

-            inc(index, read)

-

-        of '{':

-            # Check for open `{`, which means open statement list

-            trim_eol(splitValue)

-            for s in value.parse_stmt_list(index):

-                node.add parseExpr(s)

-

-        else:

-            # Otherwise parse while valid `identChars` and make expression w/ $

-            var identifier: string

-            read = value.parseWhile(identifier, identChars, index)

-

-            if identifier in ["for", "while"]:

-                ## for/while means open simple statement

-                trim_eol(splitValue)

-                node.add value.parse_simple_statement(index)

-

-            elif identifier in ["if", "when", "case", "try"]:

-                ## if/when/case/try means complex statement

-                trim_eol(splitValue)

-                node.add value.parse_complex_stmt(identifier, index)

-

-            elif identifier.len > 0:

-                ## Treat as simple variable

-                node.add newCall("add", ident("result"), newCall("$", ident(identifier)))

-                inc(index, read)

-

-        result = true

-

-    # Insert

-    if splitValue.len > 0:

-        node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue))

-

-

-proc parse_template(node: NimNode, value: string) =

-    ## Parses through entire template, outputing valid

-    ## Nim code into the input `node` AST.

-    var index = 0

-    while index < value.len and

-          parse_until_symbol(node, value, index): nil

-

-

-macro tmpli*(body: expr): stmt =

-    result = newStmtList()

-

-    result.add parseExpr("result = \"\"")

-

-    var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal

-                else: body[1].strVal

-

-    parse_template(result, reindent(value))

-

-

-macro tmpl*(body: expr): stmt =

-    result = newStmtList()

-

-    var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal

-                else: body[1].strVal

-

-    parse_template(result, reindent(value))

-

-

-# Run tests

-when isMainModule:

+# Ref:
+# http://nim-lang.org/macros.html
+# http://nim-lang.org/parseutils.html
+
+
+# Imports
+import tables, parseutils, macros, strutils
+import annotate
+export annotate
+
+
+# Fields
+const identChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
+
+
+# Procedure Declarations
+proc parse_template(node: NimNode, value: string) {.compiletime.}
+
+
+# Procedure Definitions
+proc substring(value: string, index: int, length = -1): string {.compiletime.} =
+    ## Returns a string at most `length` characters long, starting at `index`.
+    return if length < 0:    value.substr(index)
+           elif length == 0: ""
+           else:             value.substr(index, index + length-1)
+
+
+proc parse_thru_eol(value: string, index: int): int {.compiletime.} =
+    ## Reads until and past the end of the current line, unless
+    ## a non-whitespace character is encountered first
+    var remainder: string
+    var read = value.parseUntil(remainder, {0x0A.char}, index)
+    if remainder.skipWhitespace() == read:
+        return read + 1
+
+
+proc trim_after_eol(value: var string) {.compiletime.} =
+    ## Trims any whitespace at end after \n
+    var toTrim = 0
+    for i in countdown(value.len-1, 0):
+        # If \n, return
+        if value[i] in [' ', '\t']: inc(toTrim)
+        else: break
+
+    if toTrim > 0:
+        value = value.substring(0, value.len - toTrim)
+
+
+proc trim_eol(value: var string) {.compiletime.} =
+    ## Removes everything after the last line if it contains nothing but whitespace
+    for i in countdown(value.len - 1, 0):
+        # If \n, trim and return
+        if value[i] == 0x0A.char:
+            value = value.substr(0, i)
+            break
+
+        # This is the first character
+        if i == 0:
+            value = ""
+            break
+
+        # Skip change
+        if not (value[i] in [' ', '\t']): break
+
+
+proc detect_indent(value: string, index: int): int {.compiletime.} =
+    ## Detects how indented the line at `index` is.
+    # Seek to the beginning of the line.
+    var lastChar = index
+    for i in countdown(index, 0):
+        if value[i] == 0x0A.char:
+            # if \n, return the indentation level
+            return lastChar - i
+        elif not (value[i] in [' ', '\t']):
+            # if non-whitespace char, decrement lastChar
+            dec(lastChar)
+
+
+proc parse_thru_string(value: string, i: var int, strType = '"') {.compiletime.} =
+    ## Parses until ending " or ' is reached.
+    inc(i)
+    if i < value.len-1:
+        inc(i, value.skipUntil({'\\', strType}, i))
+
+
+proc parse_to_close(value: string, index: int, open='(', close=')', opened=0): int {.compiletime.} =
+    ## Reads until all opened braces are closed
+    ## ignoring any strings "" or ''
+    var remainder   = value.substring(index)
+    var open_braces = opened
+    result = 0
+
+    while result < remainder.len:
+        var c = remainder[result]
+
+        if   c == open:  inc(open_braces)
+        elif c == close: dec(open_braces)
+        elif c == '"':   remainder.parse_thru_string(result)
+        elif c == '\'':  remainder.parse_thru_string(result, '\'')
+
+        if open_braces == 0: break
+        else: inc(result)
+
+
+iterator parse_stmt_list(value: string, index: var int): string =
+    ## Parses unguided ${..} block
+    var read        = value.parse_to_close(index, open='{', close='}')
+    var expressions = value.substring(index + 1, read - 1).split({ ';', 0x0A.char })
+
+    for expression in expressions:
+        let value = expression.strip
+        if value.len > 0:
+            yield value
+
+    #Increment index & parse thru EOL
+    inc(index, read + 1)
+    inc(index, value.parse_thru_eol(index))
+
+
+iterator parse_compound_statements(value, identifier: string, index: int): string =
+    ## Parses through several statements, i.e. if {} elif {} else {}
+    ## and returns the initialization of each as an empty statement
+    ## i.e. if x == 5 { ... } becomes if x == 5: nil.
+
+    template get_next_ident(expected): stmt =
+        var nextIdent: string
+        discard value.parseWhile(nextIdent, {'$'} + identChars, i)
+
+        var next: string
+        var read: int
+
+        if nextIdent == "case":
+            # We have to handle case a bit differently
+            read = value.parseUntil(next, '$', i)
+            inc(i, read)
+            yield next.strip(leading=false) & "\n"
+
+        else:
+            read = value.parseUntil(next, '{', i)
+
+            if nextIdent in expected:
+                inc(i, read)
+                # Parse until closing }, then skip whitespace afterwards
+                read = value.parse_to_close(i, open='{', close='}')
+                inc(i, read + 1)
+                inc(i, value.skipWhitespace(i))
+                yield next & ": nil\n"
+
+            else: break
+
+
+    var i = index
+    while true:
+        # Check if next statement would be valid, given the identifier
+        if identifier in ["if", "when"]:
+            get_next_ident([identifier, "$elif", "$else"])
+
+        elif identifier == "case":
+            get_next_ident(["case", "$of", "$elif", "$else"])
+
+        elif identifier == "try":
+            get_next_ident(["try", "$except", "$finally"])
+
+
+proc parse_complex_stmt(value, identifier: string, index: var int): NimNode {.compiletime.} =
+    ## Parses if/when/try /elif /else /except /finally statements
+
+    # Build up complex statement string
+    var stmtString = newString(0)
+    var numStatements = 0
+    for statement in value.parse_compound_statements(identifier, index):
+        if statement[0] == '$': stmtString.add(statement.substr(1))
+        else:                   stmtString.add(statement)
+        inc(numStatements)
+
+    # Parse stmt string
+    result = parseExpr(stmtString)
+
+    var resultIndex = 0
+
+    # Fast forward a bit if this is a case statement
+    if identifier == "case":
+        inc(resultIndex)
+
+    while resultIndex < numStatements:
+
+        # Detect indentation
+        let indent = detect_indent(value, index)
+
+        # Parse until an open brace `{`
+        var read = value.skipUntil('{', index)
+        inc(index, read + 1)
+
+        # Parse through EOL
+        inc(index, value.parse_thru_eol(index))
+
+        # Parse through { .. }
+        read = value.parse_to_close(index, open='{', close='}', opened=1)
+
+        # Add parsed sub-expression into body
+        var body       = newStmtList()
+        var stmtString = value.substring(index, read)
+        trim_after_eol(stmtString)
+        stmtString = reindent(stmtString, indent)
+        parse_template(body, stmtString)
+        inc(index, read + 1)
+
+        # Insert body into result
+        var stmtIndex = result[resultIndex].len-1
+        result[resultIndex][stmtIndex] = body
+
+        # Parse through EOL again & increment result index
+        inc(index, value.parse_thru_eol(index))
+        inc(resultIndex)
+
+
+proc parse_simple_statement(value: string, index: var int): NimNode {.compiletime.} =
+    ## Parses for/while
+
+    # Detect indentation
+    let indent = detect_indent(value, index)
+
+    # Parse until an open brace `{`
+    var splitValue: string
+    var read = value.parseUntil(splitValue, '{', index)
+    result   = parseExpr(splitValue & ":nil")
+    inc(index, read + 1)
+
+    # Parse through EOL
+    inc(index, value.parse_thru_eol(index))
+
+    # Parse through { .. }
+    read = value.parse_to_close(index, open='{', close='}', opened=1)
+
+    # Add parsed sub-expression into body
+    var body       = newStmtList()
+    var stmtString = value.substring(index, read)
+    trim_after_eol(stmtString)
+    stmtString = reindent(stmtString, indent)
+    parse_template(body, stmtString)
+    inc(index, read + 1)
+
+    # Insert body into result
+    var stmtIndex = result.len-1
+    result[stmtIndex] = body
+
+    # Parse through EOL again
+    inc(index, value.parse_thru_eol(index))
+
+
+proc parse_until_symbol(node: NimNode, value: string, index: var int): bool {.compiletime.} =
+    ## Parses a string until a $ symbol is encountered, if
+    ## two $$'s are encountered in a row, a split will happen
+    ## removing one of the $'s from the resulting output
+    var splitValue: string
+    var read = value.parseUntil(splitValue, '$', index)
+    var insertionPoint = node.len
+
+    inc(index, read + 1)
+    if index < value.len:
+
+        case value[index]
+        of '$':
+            # Check for duplicate `$`, meaning this is an escaped $
+            node.add newCall("add", ident("result"), newStrLitNode("$"))
+            inc(index)
+
+        of '(':
+            # Check for open `(`, which means parse as simple single-line expression.
+            trim_eol(splitValue)
+            read = value.parse_to_close(index) + 1
+            node.add newCall("add", ident("result"),
+                newCall(bindSym"strip", parseExpr("$" & value.substring(index, read)))
+            )
+            inc(index, read)
+
+        of '{':
+            # Check for open `{`, which means open statement list
+            trim_eol(splitValue)
+            for s in value.parse_stmt_list(index):
+                node.add parseExpr(s)
+
+        else:
+            # Otherwise parse while valid `identChars` and make expression w/ $
+            var identifier: string
+            read = value.parseWhile(identifier, identChars, index)
+
+            if identifier in ["for", "while"]:
+                ## for/while means open simple statement
+                trim_eol(splitValue)
+                node.add value.parse_simple_statement(index)
+
+            elif identifier in ["if", "when", "case", "try"]:
+                ## if/when/case/try means complex statement
+                trim_eol(splitValue)
+                node.add value.parse_complex_stmt(identifier, index)
+
+            elif identifier.len > 0:
+                ## Treat as simple variable
+                node.add newCall("add", ident("result"), newCall("$", ident(identifier)))
+                inc(index, read)
+
+        result = true
+
+    # Insert
+    if splitValue.len > 0:
+        node.insert insertionPoint, newCall("add", ident("result"), newStrLitNode(splitValue))
+
+
+proc parse_template(node: NimNode, value: string) =
+    ## Parses through entire template, outputing valid
+    ## Nim code into the input `node` AST.
+    var index = 0
+    while index < value.len and
+          parse_until_symbol(node, value, index): nil
+
+
+macro tmpli*(body: expr): stmt =
+    result = newStmtList()
+
+    result.add parseExpr("result = \"\"")
+
+    var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
+                else: body[1].strVal
+
+    parse_template(result, reindent(value))
+
+
+macro tmpl*(body: expr): stmt =
+    result = newStmtList()
+
+    var value = if body.kind in nnkStrLit..nnkTripleStrLit: body.strVal
+                else: body[1].strVal
+
+    parse_template(result, reindent(value))
+
+
+# Run tests
+when isMainModule:
     include otests
     echo "Success"
diff --git a/tests/template/twrongsymkind.nim b/tests/template/twrongsymkind.nim
new file mode 100644
index 000000000..be3d8c652
--- /dev/null
+++ b/tests/template/twrongsymkind.nim
@@ -0,0 +1,20 @@
+discard """
+  errormsg: "cannot use symbol of kind 'var' as a 'param'"
+  line: 20
+"""
+
+# bug #3158
+
+type
+  MyData = object
+      x: int
+
+template newDataWindow(data: ref MyData): stmt =
+    proc testProc(data: ref MyData) =
+        echo "Hello, ", data.x
+    testProc(data)
+
+var d: ref MyData
+new(d)
+d.x = 10
+newDataWindow(d)
diff --git a/web/news.txt b/web/news.txt
index a5bc600b9..b72ae56d2 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -42,6 +42,8 @@ News
   - ``sequtils.delete`` doesn't take confusing default arguments anymore.
   - ``system.free`` was an error-prone alias to ``system.dealloc`` and has
     been removed.
+  - ``macros.high`` never worked and the manual says ``high`` cannot be
+    overloaded, so we removed it with no deprecation cycle.
 
 
   Library additions