summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--compiler/ast.nim5
-rw-r--r--compiler/docgen.nim27
-rw-r--r--compiler/evaltempl.nim50
-rw-r--r--compiler/semcall.nim27
-rw-r--r--compiler/semdata.nim2
-rw-r--r--compiler/semexprs.nim7
-rw-r--r--compiler/seminst.nim25
-rw-r--r--compiler/semstmts.nim12
-rw-r--r--compiler/semtypes.nim11
-rw-r--r--compiler/sigmatch.nim110
-rw-r--r--compiler/vm.nim32
-rw-r--r--compiler/vmgen.nim23
-rw-r--r--config/nimdoc.cfg2
-rw-r--r--doc/basicopt.txt2
-rw-r--r--doc/idetools.txt2
-rw-r--r--doc/manual/types.txt130
-rw-r--r--doc/tut1.txt4
-rw-r--r--doc/tut2.txt54
-rw-r--r--lib/pure/asyncdispatch.nim4
-rw-r--r--lib/pure/asynchttpserver.nim2
-rw-r--r--lib/pure/httpclient.nim185
-rw-r--r--lib/pure/md5.nim270
-rw-r--r--lib/pure/selectors.nim11
-rw-r--r--lib/pure/unittest.nim29
-rw-r--r--lib/pure/uri.nim49
-rw-r--r--lib/system/gc.nim36
-rw-r--r--lib/system/gc2.nim2
-rw-r--r--lib/system/mmdisp.nim6
-rw-r--r--lib/wrappers/mysql.nim2
-rw-r--r--lib/wrappers/openssl.nim2
-rw-r--r--tests/gc/closureleak.nim4
-rw-r--r--tests/gc/cyclecollector.nim21
-rw-r--r--tests/gc/gctest.nim3
-rw-r--r--tests/generics/t1050.nim16
-rw-r--r--tests/metatype/tsemistatic.nim8
-rw-r--r--tests/metatype/tstaticparams.nim86
-rw-r--r--tests/metatype/tusertypeclasses.nim27
-rw-r--r--tests/misc/tvarious.nim108
-rw-r--r--tests/static/tstaticparammacro.nim22
-rw-r--r--tests/testament/categories.nim13
-rw-r--r--tools/niminst/nsis.tmpl2
-rw-r--r--tools/nimweb.nim8
-rw-r--r--tools/website.tmpl4
-rw-r--r--web/index.txt34
-rw-r--r--web/question.txt1
46 files changed, 991 insertions, 490 deletions
diff --git a/.gitignore b/.gitignore
index 462df4efc..d804fb8f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,4 @@ xcuserdata/
 /testresults.html
 /testresults.json
 testament.db
+/csources/
diff --git a/compiler/ast.nim b/compiler/ast.nim
index d85dbf42c..a071060d4 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -399,6 +399,7 @@ const
   tyPureObject* = tyTuple
   GcTypeKinds* = {tyRef, tySequence, tyString}
   tyError* = tyProxy # as an errornous node should match everything
+  tyUnknown* = tyFromExpr
 
   tyUnknownTypes* = {tyError, tyFromExpr}
 
@@ -1340,6 +1341,10 @@ proc skipTypes*(t: PType, kinds: TTypeKinds): PType =
   result = t
   while result.kind in kinds: result = lastSon(result)
 
+proc safeSkipTypes*(t: PType, kinds: TTypeKinds): PType =
+  result = if t != nil: t.skipTypes(kinds)
+           else: nil
+
 proc isGCedMem*(t: PType): bool {.inline.} =
   result = t.kind in {tyString, tyRef, tySequence} or
            t.kind == tyProc and t.callConv == ccClosure
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 35acf1379..3f4f39c27 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -23,6 +23,7 @@ type
     id: int                  # for generating IDs
     toc, section: TSections
     indexValFilename: string
+    analytics: string  # Google Analytics javascript, "" if doesn't exist
     seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML.
 
   PDoc* = ref TDocumentor ## Alias to type less.
@@ -61,6 +62,23 @@ proc newDocumentor*(filename: string, config: StringTableRef): PDoc =
   initRstGenerator(result[], (if gCmd != cmdRst2tex: outHtml else: outLatex),
                    options.gConfigVars, filename, {roSupportRawDirective},
                    docgenFindFile, compilerMsgHandler)
+
+  if config.hasKey("doc.googleAnalytics"):
+    result.analytics = """
+<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', '$1', 'auto');
+  ga('send', 'pageview');
+
+</script>
+    """ % [config["doc.googleAnalytics"]]
+  else:
+    result.analytics = ""
+
   result.seenSymbols = newStringTable(modeCaseInsensitive)
   result.id = 100
 
@@ -562,10 +580,10 @@ proc genOutFile(d: PDoc): PRope =
     # XXX what is this hack doing here? 'optCompileOnly' means raw output!?
     code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
         "tableofcontents", "moduledesc", "date", "time",
-        "content", "author", "version"],
+        "content", "author", "version", "analytics"],
         [title.toRope, toc, d.modDesc, toRope(getDateStr()),
                      toRope(getClockStr()), content, d.meta[metaAuthor].toRope,
-                     d.meta[metaVersion].toRope])
+                     d.meta[metaVersion].toRope, d.analytics.toRope])
   else:
     code = content
   result = code
@@ -630,7 +648,8 @@ proc commandBuildIndex*() =
 
   let code = ropeFormatNamedVars(getConfigVar("doc.file"), ["title",
       "tableofcontents", "moduledesc", "date", "time",
-      "content", "author", "version"],
+      "content", "author", "version", "analytics"],
       ["Index".toRope, nil, nil, toRope(getDateStr()),
-                   toRope(getClockStr()), content, nil, nil])
+                   toRope(getClockStr()), content, nil, nil, nil])
+  # no analytics because context is not available
   writeRope(code, getOutFile("theindex", HtmlExt))
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 78cc691c0..ecb898d8a 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -25,16 +25,22 @@ proc copyNode(ctx: TemplCtx, a, b: PNode): PNode =
   if ctx.instLines: result.info = b.info
 
 proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
+  template handleParam(param) =
+    let x = param
+    if x.kind == nkArgList:
+      for y in items(x): result.add(y)
+    else:
+      result.add copyTree(x)
+
   case templ.kind
   of nkSym:
     var s = templ.sym
     if s.owner.id == c.owner.id:
-      if s.kind == skParam:
-        let x = actual.sons[s.position]
-        if x.kind == nkArgList:
-          for y in items(x): result.add(y)
-        else:
-          result.add copyTree(x)
+      case s.kind
+      of skParam:
+        handleParam actual.sons[s.position]
+      of skGenericParam:
+        handleParam actual.sons[s.owner.typ.len + s.position - 1]
       else:
         internalAssert sfGenSym in s.flags
         var x = PSym(idTableGet(c.mapping, s))
@@ -56,21 +62,31 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) =
 proc evalTemplateArgs(n: PNode, s: PSym): PNode =
   # if the template has zero arguments, it can be called without ``()``
   # `n` is then a nkSym or something similar
-  var a: int
-  case n.kind
-  of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
-    a = sonsLen(n)
-  else: a = 0
-  var f = s.typ.sonsLen
-  if a > f: globalError(n.info, errWrongNumberOfArguments)
+  var totalParams = case n.kind
+    of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit: <n.len
+    else: 0
+
+  var
+    genericParams = s.ast[genericParamsPos].len
+    expectedRegularParams = <s.typ.len
+    givenRegularParams = totalParams - genericParams
+
+  if totalParams > expectedRegularParams + genericParams:
+    globalError(n.info, errWrongNumberOfArguments)
 
   result = newNodeI(nkArgList, n.info)
-  for i in countup(1, f - 1):
-    var arg = if i < a: n.sons[i] else: copyTree(s.typ.n.sons[i].sym.ast)
-    if arg == nil or arg.kind == nkEmpty:
+  for i in 1 .. givenRegularParams:
+    result.addSon n.sons[i]
+
+  for i in givenRegularParams+1 .. expectedRegularParams:
+    let default = s.typ.n.sons[i].sym.ast
+    if default.kind == nkEmpty:
       localError(n.info, errWrongNumberOfArguments)
-    addSon(result, arg)
+    result.addSon default.copyTree
 
+  for i in 1 .. genericParams:
+    result.addSon n.sons[givenRegularParams + i]
+  
 var evalTemplateCounter* = 0
   # to prevent endless recursion in templates instantiation
 
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 3971b8ff5..961c61c57 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -277,16 +277,27 @@ proc semResolvedCall(c: PContext, n: PNode, x: TCandidate): PNode =
   styleCheckUse(n.sons[0].info, finalCallee)
   if finalCallee.ast == nil:
     internalError(n.info, "calleeSym.ast is nil") # XXX: remove this check!
-  if finalCallee.ast.sons[genericParamsPos].kind != nkEmpty:
-    # a generic proc!
-    if not x.proxyMatch:
+  if x.hasFauxMatch:
+    result = x.call
+    result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
+    if containsGenericType(result.typ) or x.fauxMatch == tyUnknown:
+      result.typ = newTypeS(x.fauxMatch, c)
+    return
+  let gp = finalCallee.ast.sons[genericParamsPos]
+  if gp.kind != nkEmpty:
+    if x.calleeSym.kind notin {skMacro, skTemplate}:
       finalCallee = generateInstance(c, x.calleeSym, x.bindings, n.info)
     else:
-      result = x.call
-      result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
-      result.typ = finalCallee.typ.sons[0]
-      if containsGenericType(result.typ): result.typ = errorType(c)
-      return
+      # For macros and templates, the resolved generic params
+      # are added as normal params.
+      for s in instantiateGenericParamList(c, gp, x.bindings):
+        case s.kind
+          of skConst:
+            x.call.add s.ast
+          of skType:
+            x.call.add newSymNode(s, n.info)
+          else:
+            internalAssert false
   result = x.call
   instGenericConvertersSons(c, result, x)
   result.sons[0] = newSymNode(finalCallee, result.sons[0].info)
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index f876770c0..623f9b633 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -268,7 +268,7 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType =
 
 template rangeHasStaticIf*(t: PType): bool =
   # this accepts the ranges's node
-  t.n[1].kind == nkStaticExpr
+  t.n != nil and t.n.len > 1 and t.n[1].kind == nkStaticExpr
 
 template getStaticTypeFromRange*(t: PType): PType =
   t.n[1][0][1].typ
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 9bf04c955..5e61c4a0b 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -229,7 +229,7 @@ proc semConv(c: PContext, n: PNode): PNode =
     return n
 
   result = newNodeI(nkConv, n.info)
-  var targetType = semTypeNode(c, n.sons[0], nil)
+  var targetType = semTypeNode(c, n.sons[0], nil).skipTypes({tyTypeDesc})
   maybeLiftType(targetType, c, n[0].info)
   result.addSon copyTree(n.sons[0])
   var op = semExprWithType(c, n.sons[1])
@@ -780,7 +780,6 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     if tfNoSideEffect notin t.flags: incl(c.p.owner.flags, sfSideEffect)
   elif t != nil and t.kind == tyTypeDesc:
     if n.len == 1: return semObjConstr(c, n, flags)
-    let destType = t.skipTypes({tyTypeDesc, tyGenericInst})
     return semConv(c, n)
   else:
     result = overloadedCallOpr(c, n)
@@ -926,8 +925,8 @@ const
 proc readTypeParameter(c: PContext, typ: PType,
                        paramName: PIdent, info: TLineInfo): PNode =
   let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
-           else: (internalAssert(typ.kind == tyCompositeTypeClass); typ.sons[1])
-  #debug ty
+           else: (internalAssert(typ.kind == tyCompositeTypeClass);
+                  typ.sons[1].skipGenericAlias)
   let tbody = ty.sons[0]
   for s in countup(0, tbody.len-2):
     let tParam = tbody.sons[s]
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 81a4465c5..dd60e0881 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -10,14 +10,10 @@
 # This module implements the instantiation of generic procs.
 # included from sem.nim
 
-proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
-                                 entry: var TInstantiation) = 
-  if n.kind != nkGenericParams: 
-    internalError(n.info, "instantiateGenericParamList; no generic params")
-  newSeq(entry.concreteTypes, n.len)
+iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym =
+  internalAssert n.kind == nkGenericParams
   for i, a in n.pairs:
-    if a.kind != nkSym: 
-      internalError(a.info, "instantiateGenericParamList; no symbol")
+    internalAssert a.kind == nkSym
     var q = a.sym
     if q.typ.kind notin {tyTypeDesc, tyGenericParam, tyStatic, tyIter}+tyTypeClasses:
       continue
@@ -42,8 +38,7 @@ proc instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable,
       #t = ReplaceTypeVarsT(cl, t)
     s.typ = t
     if t.kind == tyStatic: s.ast = t.n
-    addDecl(c, s)
-    entry.concreteTypes[i] = t
+    yield s
 
 proc sameInstantiation(a, b: TInstantiation): bool =
   if a.concreteTypes.len == b.concreteTypes.len:
@@ -196,7 +191,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   ## The `pt` parameter is a type-unsafe mapping table used to link generic
   ## parameters to their concrete types within the generic instance.
   # no need to instantiate generic templates/macros:
-  if fn.kind in {skTemplate, skMacro}: return fn
+  internalAssert fn.kind notin {skMacro, skTemplate}
   # generates an instantiated proc
   if c.instCounter > 1000: internalError(fn.ast.info, "nesting too deep")
   inc(c.instCounter)
@@ -213,12 +208,18 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
   result.ast = n
   pushOwner(result)
   openScope(c)
-  internalAssert n.sons[genericParamsPos].kind != nkEmpty
+  let gp = n.sons[genericParamsPos]
+  internalAssert gp.kind != nkEmpty
   n.sons[namePos] = newSymNode(result)
   pushInfoContext(info)
   var entry = TInstantiation.new
   entry.sym = result
-  instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
+  newSeq(entry.concreteTypes, gp.len)
+  var i = 0
+  for s in instantiateGenericParamList(c, gp, pt):
+    addDecl(c, s)
+    entry.concreteTypes[i] = s.typ
+    inc i
   pushProcCon(c, result)
   instantiateProcType(c, pt, result, info)
   n.sons[genericParamsPos] = ast.emptyNode
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 3b0332939..1396ef374 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -1262,10 +1262,14 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       return
     else:
       n.sons[i] = semExpr(c, n.sons[i])
-      if c.inTypeClass > 0 and n[i].typ != nil and n[i].typ.kind == tyBool:
-        let verdict = semConstExpr(c, n[i])
-        if verdict.intVal == 0:
-          localError(result.info, "type class predicate failed")
+      if c.inTypeClass > 0 and n[i].typ != nil:
+        case n[i].typ.kind
+        of tyBool:
+          let verdict = semConstExpr(c, n[i])
+          if verdict.intVal == 0:
+            localError(result.info, "type class predicate failed")
+        of tyUnknown: continue
+        else: discard
       if n.sons[i].typ == enforceVoidContext or usesResult(n.sons[i]):
         voidContext = true
         n.typ = enforceVoidContext
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index eb15c3809..deb4b1288 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -225,11 +225,10 @@ proc semArrayIndex(c: PContext, n: PNode): PType =
     elif e.kind == nkSym and e.typ.kind == tyStatic:
       if e.sym.ast != nil:
         return semArrayIndex(c, e.sym.ast)
-      internalAssert c.inGenericContext > 0
       if not isOrdinalType(e.typ.lastSon):
         localError(n[1].info, errOrdinalTypeExpected)
       result = makeRangeWithStaticExpr(c, e)
-      result.flags.incl tfUnresolved
+      if c.inGenericContext >0: result.flags.incl tfUnresolved
     elif e.kind in nkCallKinds and hasGenericArguments(e):
       if not isOrdinalType(e.typ):
         localError(n[1].info, errOrdinalTypeExpected)
@@ -782,10 +781,12 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
   of tyGenericBody:
     result = newTypeS(tyGenericInvokation, c)
     result.rawAddSon(paramType)
+      
     for i in 0 .. paramType.sonsLen - 2:
-      result.rawAddSon newTypeS(tyAnything, c)
-      # result.rawAddSon(copyType(paramType.sons[i], getCurrOwner(), true))
-
+      let dummyType = if paramType.sons[i].kind == tyStatic: tyUnknown
+                      else: tyAnything
+      result.rawAddSon newTypeS(dummyType, c)
+      
     if paramType.lastSon.kind == tyUserTypeClass:
       result.kind = tyUserTypeClassInst
       result.rawAddSon paramType.lastSon
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index b58818a29..721f7e318 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -39,7 +39,9 @@ type
     bindings*: TIdTable      # maps types to types
     baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                              # for example
-    proxyMatch*: bool        # to prevent instantiations
+    fauxMatch*: TTypeKind    # the match was successful only due to the use
+                             # of error or wildcard (unknown) types.
+                             # this is used to prevent instantiations.
     genericConverter*: bool  # true if a generic converter needs to
                              # be instantiated
     coerceDistincts*: bool   # this is an explicit coercion that can strip away
@@ -66,6 +68,8 @@ const
     
 proc markUsed*(info: TLineInfo, s: PSym)
 
+template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
+
 proc initCandidateAux(ctx: PContext,
                       c: var TCandidate, callee: PType) {.inline.} =
   c.c = ctx
@@ -109,9 +113,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
     for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
       var formalTypeParam = typeParams.sons[i-1].typ
       var bound = binding[i].typ
-      if bound != nil and formalTypeParam.kind != tyTypeDesc:
+      internalAssert bound != nil
+      if formalTypeParam.kind == tyTypeDesc:
+        if bound.kind != tyTypeDesc:
+          bound = makeTypeDesc(ctx, bound)
+      else:
         bound = bound.skipTypes({tyTypeDesc})
-      assert bound != nil
       put(c.bindings, formalTypeParam, bound)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
@@ -462,9 +469,23 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
       var
         typeParamName = ff.base.sons[i-1].sym.name
         typ = ff.sons[i]
-        param = newSym(skType, typeParamName, body.sym, body.sym.info)
-        
-      param.typ = makeTypeDesc(c, typ)
+        param: PSym
+
+      template paramSym(kind): expr =
+        newSym(kind, typeParamName, body.sym, body.sym.info)
+
+      case typ.kind
+      of tyStatic:
+        param = paramSym skConst
+        param.typ = typ.base
+        param.ast = typ.n
+      of tyUnknown:
+        param = paramSym skVar
+        param.typ = typ
+      else:
+        param = paramSym skType
+        param.typ = makeTypeDesc(c, typ)
+      
       addDecl(c, param)
 
   for param in body.n[0]:
@@ -626,6 +647,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
   of tyRange:
     if a.kind == f.kind:
+      if f.base.kind == tyNone: return isGeneric
       result = typeRel(c, base(f), base(a))
       # bugfix: accept integer conversions here
       #if result < isGeneric: result = isNone
@@ -672,22 +694,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         else:
           fRange = prev
       result = typeRel(c, f.sons[1], a.sons[1])
-      if result < isGeneric:
-        result = isNone
-      elif tfUnresolved in fRange.flags and
-           rangeHasStaticIf(fRange):
-        # This is a range from an array instantiated with a generic
-        # static param. We must extract the static param here and bind
-        # it to the size of the currently supplied array.
-        var
-          rangeStaticT = fRange.getStaticTypeFromRange
-          replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
-          inputUpperBound = a.sons[0].n[1].intVal
-        # we must correct for the off-by-one discrepancy between
-        # ranges and static params:
-        replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
-        put(c.bindings, rangeStaticT, replacementT)
-        result = isGeneric
+      if result < isGeneric: return isNone
+      if rangeHasStaticIf(fRange):
+        if tfUnresolved in fRange.flags:
+          # This is a range from an array instantiated with a generic
+          # static param. We must extract the static param here and bind
+          # it to the size of the currently supplied array.
+          var
+            rangeStaticT = fRange.getStaticTypeFromRange
+            replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
+            inputUpperBound = a.sons[0].n[1].intVal
+          # we must correct for the off-by-one discrepancy between
+          # ranges and static params:
+          replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
+          put(c.bindings, rangeStaticT, replacementT)
+          return isGeneric
+
+        let len = tryResolvingStaticExpr(c, fRange.n[1])
+        if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
+          return # if we get this far, the result is already good
+        else:
+          return isNone
       elif lengthOrd(fRange) != lengthOrd(a):
         result = isNone
     else: discard
@@ -945,7 +972,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           else:
             internalAssert a.sons != nil and a.sons.len > 0
             c.typedescMatched = true
-            result = typeRel(c, f.base, a.base)
+            result = typeRel(c, f.base, a.skipTypes({tyGenericParam, tyTypeDesc}))
         else:
           result = isNone
       else:
@@ -977,15 +1004,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       result = typeRel(c, x, a) # check if it fits
   
   of tyStatic:
-    if aOrig.kind == tyStatic:
-      result = typeRel(c, f.lastSon, a)
-      if result != isNone and f.n != nil:
-        if not exprStructuralEquivalent(f.n, a.n):
-          result = isNone
-      if result != isNone: put(c.bindings, f, aOrig)
+    let prev = PType(idTableGet(c.bindings, f))
+    if prev == nil:
+      if aOrig.kind == tyStatic:
+        result = typeRel(c, f.lastSon, a)
+        if result != isNone and f.n != nil:
+          if not exprStructuralEquivalent(f.n, aOrig.n):
+            result = isNone
+        if result != isNone: put(c.bindings, f, aOrig)
+      else:
+        result = isNone
     else:
-      result = isNone
-
+      result = typeRel(c, prev, aOrig)
+      
   of tyTypeDesc:
     var prev = PType(idTableGet(c.bindings, f))
     if prev == nil:
@@ -1024,6 +1055,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
+    if f.n == nil: return isGeneric
     let reevaluated = tryResolvingStaticExpr(c, f.n)
     case reevaluated.typ.kind
     of tyTypeDesc:
@@ -1045,10 +1077,10 @@ proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
   initCandidate(c, m, f)
   result = typeRel(m, f, a)
 
-proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, 
-                         f: PType): PType = 
+proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
+                         f: PType): PType =
   result = PType(idTableGet(m.bindings, f))
-  if result == nil: 
+  if result == nil:
     result = generateTypeInstance(c, m.bindings, arg, f)
   if result == nil:
     internalError(arg.info, "getInstantiatedType")
@@ -1058,7 +1090,7 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
                   c: PContext): PNode = 
   result = newNodeI(kind, arg.info)
   if containsGenericType(f):
-    if not m.proxyMatch:
+    if not m.hasFauxMatch:
       result.typ = getInstantiatedType(c, arg, m, f)
     else:
       result.typ = errorType(c)
@@ -1130,7 +1162,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     arg = argSemantized
     argType = argType
     c = m.c
- 
+  
   if tfHasStatic in fMaybeStatic.flags:
     # XXX: When implicit statics are the default
     # this will be done earlier - we just have to
@@ -1144,7 +1176,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
       return argSemantized
 
     if argType.kind == tyStatic:
-      if m.callee.kind == tyGenericBody:
+      if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags:
         result = newNodeI(nkType, argOrig.info)
         result.typ = makeTypeFromExpr(c, arg)
         return
@@ -1237,9 +1269,9 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
       result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
   of isNone:
     # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
-    if a.kind == tyProxy:
+    if a.kind in {tyProxy, tyUnknown}:
       inc(m.genericMatches)
-      m.proxyMatch = true
+      m.fauxMatch = a.kind
       return copyTree(arg)
     result = userConvMatch(c, m, f, a, arg) 
     # check for a base type match, which supports varargs[T] without []
diff --git a/compiler/vm.nim b/compiler/vm.nim
index ad0d3b0a1..4072ed765 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1417,12 +1417,20 @@ proc evalStaticStmt*(module: PSym, e: PNode, prc: PSym) =
 proc setupCompileTimeVar*(module: PSym, n: PNode) =
   discard evalConstExprAux(module, nil, n, emStaticStmt)
 
-proc setupMacroParam(x: PNode): PNode =
-  result = x
-  if result.kind in {nkHiddenSubConv, nkHiddenStdConv}: result = result.sons[1]
-  result = canonValue(result)
-  result.flags.incl nfIsRef
-  result.typ = x.typ
+proc setupMacroParam(x: PNode, typ: PType): TFullReg =
+  case typ.kind
+  of tyStatic:
+    putIntoReg(result, x)
+  of tyTypeDesc:
+    putIntoReg(result, x)
+  else:
+    result.kind = rkNode
+    var n = x
+    if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n.sons[1]
+    n = n.canonValue
+    n.flags.incl nfIsRef
+    n.typ = x.typ
+    result.node = n
 
 var evalMacroCounter: int
 
@@ -1442,6 +1450,7 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
 
   c.callsite = nOrig
   let start = genProc(c, sym)
+  # c.echoCode start
 
   var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil)
   let maxSlots = sym.offset
@@ -1457,9 +1466,14 @@ proc evalMacroCall*(module: PSym, n, nOrig: PNode, sym: PSym): PNode =
   tos.slots[0].kind = rkNode
   tos.slots[0].node = newNodeIT(nkEmpty, n.info, sym.typ.sons[0])
   # setup parameters:
-  for i in 1 .. < min(tos.slots.len, L):
-    tos.slots[i].kind = rkNode
-    tos.slots[i].node = setupMacroParam(n.sons[i])
+  for i in 1.. <sym.typ.len:
+    tos.slots[i] = setupMacroParam(n.sons[i], sym.typ.sons[i])
+
+  let gp = sym.ast[genericParamsPos]
+  for i in 0 .. <gp.len:
+    let idx = sym.typ.len + i
+    tos.slots[idx] = setupMacroParam(n.sons[idx], gp[i].sym.typ)
+
   # temporary storage:
   #for i in L .. <maxSlots: tos.slots[i] = newNode(nkEmpty)
   result = rawExecute(c, start, tos).regToNode
diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim
index 9a3fc260a..8444af7ba 100644
--- a/compiler/vmgen.nim
+++ b/compiler/vmgen.nim
@@ -164,7 +164,8 @@ proc getSlotKind(t: PType): TSlotKind =
 const
   HighRegisterPressure = 40
 
-proc getTemp(c: PCtx; typ: PType): TRegister =
+proc getTemp(c: PCtx; tt: PType): TRegister =
+  let typ = tt.safeSkipTypes({tyStatic})
   let c = c.prc
   # we prefer the same slot kind here for efficiency. Unfortunately for
   # discardable return types we may not know the desired type. This can happen
@@ -685,7 +686,7 @@ proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) =
   if dest < 0: dest = c.getTemp(n.typ)
   c.gABC(n, opc, dest, tmp)
   c.gABx(n, opc, 0, genType(c, n.typ))
-  c.gABx(n, opc, 0, genType(c, arg.typ))
+  c.gABx(n, opc, 0, genType(c, arg.typ.skipTypes({tyStatic})))
   c.freeTemp(tmp)
 
 proc genCard(c: PCtx; n: PNode; dest: var TDest) =
@@ -1085,7 +1086,8 @@ proc genAddrDeref(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
     c.freeTemp(tmp)
 
 proc whichAsgnOpc(n: PNode): TOpcode =
-  case n.typ.skipTypes(abstractRange-{tyTypeDesc}).kind
+  let toSkip = abstractRange-{tyTypeDesc}
+  case n.typ.skipTypes(toSkip).kind
   of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64:
     opcAsgnInt
   of tyString, tyCString:
@@ -1559,6 +1561,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) =
         c.gABx(n, opcLdConst, dest, lit)
     of skType:
       genTypeLit(c, s.typ, dest)
+    of skGenericParam:
+      if c.prc.sym.kind == skMacro:
+        genRdVar(c, n, dest, flags)
+      else:
+        internalError(n.info, "cannot generate code for: " & s.name.s)
     else:
       internalError(n.info, "cannot generate code for: " & s.name.s)
   of nkCallKinds:
@@ -1690,6 +1697,14 @@ proc genParams(c: PCtx; params: PNode) =
     c.prc.slots[i] = (inUse: true, kind: slotFixedLet)
   c.prc.maxSlots = max(params.len, 1)
 
+proc genGenericParams(c: PCtx; gp: PNode) =
+  var base = c.prc.maxSlots
+  for i in 0.. <gp.len:
+    var param = gp.sons[i].sym
+    param.position = base + i # XXX: fix this earlier; make it consistent with templates
+    c.prc.slots[base + i] = (inUse: true, kind: slotFixedLet)
+  c.prc.maxSlots = base + gp.len
+
 proc finalJumpTarget(c: PCtx; pc, diff: int) =
   internalAssert(-0x7fff < diff and diff < 0x7fff)
   let oldInstr = c.code[pc]
@@ -1761,6 +1776,8 @@ proc genProc(c: PCtx; s: PSym): int =
     c.prc = p
     # iterate over the parameters and allocate space for them:
     genParams(c, s.typ.n)
+    if s.kind == skMacro and s.ast[genericParamsPos].kind != nkEmpty:
+      genGenericParams(c, s.ast[genericParamsPos])
     if tfCapturesEnv in s.typ.flags:
       #let env = s.ast.sons[paramsPos].lastSon.sym
       #assert env.position == 2
diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg
index 3abae7388..3375460cc 100644
--- a/config/nimdoc.cfg
+++ b/config/nimdoc.cfg
@@ -86,6 +86,7 @@ $moduledesc
 $content
 """
 
+# * $analytics: Google analytics location, includes <script> tags
 doc.file = """<?xml version="1.0" encoding="utf-8" ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -1243,6 +1244,7 @@ dt pre > span.Operator ~ span.Identifier, dt pre > span.Operator ~ span.Operator
     </div>
   </div>
 </div>
+$analytics
 </body>
 </html>
 """
diff --git a/doc/basicopt.txt b/doc/basicopt.txt
index ffb374f18..e366b2718 100644
--- a/doc/basicopt.txt
+++ b/doc/basicopt.txt
@@ -37,4 +37,4 @@ Options:
   --advanced                show advanced command line switches
   -h, --help                show this help
 
-Note: Even single letter options require the colon: -p:PATH.
+Note, single letter options that take an argument require a colon. E.g. -p:PATH.
diff --git a/doc/idetools.txt b/doc/idetools.txt
index 5429d0c07..e04f530f4 100644
--- a/doc/idetools.txt
+++ b/doc/idetools.txt
@@ -145,7 +145,7 @@ The ``--usages`` idetools switch lists all usages of the symbol at
 a position. IDEs can use this to find all the places in the file
 where the symbol is used and offer the user to rename it in all
 places at the same time. Again, a pure string based search and
-replace may catch symbols out of the scope of a funcion/loop.
+replace may catch symbols out of the scope of a function/loop.
 
 For this kind of query the IDE will most likely ignore all the
 type/signature info provided by idetools and concentrate on the
diff --git a/doc/manual/types.txt b/doc/manual/types.txt
index b028752e8..94611bbbb 100644
--- a/doc/manual/types.txt
+++ b/doc/manual/types.txt
@@ -40,7 +40,7 @@ These integer types are pre-defined:
 
 ``int``
   the generic signed integer type; its size is platform dependent and has the
-  same size as a pointer. This type should be used in general. An integer 
+  same size as a pointer. This type should be used in general. An integer
   literal that has no type suffix is of this type.
 
 intXX
@@ -51,7 +51,7 @@ intXX
 
 ``uint``
   the generic `unsigned integer`:idx: type; its size is platform dependent and
-  has the same size as a pointer. An integer literal with the type 
+  has the same size as a pointer. An integer literal with the type
   suffix ``'u`` is of this type.
 
 uintXX
@@ -59,15 +59,15 @@ uintXX
   (example: uint16 is a 16 bit wide unsigned integer).
   The current implementation supports ``uint8``, ``uint16``, ``uint32``,
   ``uint64``. Literals of these types have the suffix 'uXX.
-  Unsigned operations all wrap around; they cannot lead to over- or 
+  Unsigned operations all wrap around; they cannot lead to over- or
   underflow errors.
 
 
 In addition to the usual arithmetic operators for signed and unsigned integers
-(``+ - *`` etc.) there are also operators that formally work on *signed* 
-integers but treat their arguments as *unsigned*: They are mostly provided 
-for backwards compatibility with older versions of the language that lacked 
-unsigned integer types. These unsigned operations for signed integers use 
+(``+ - *`` etc.) there are also operators that formally work on *signed*
+integers but treat their arguments as *unsigned*: They are mostly provided
+for backwards compatibility with older versions of the language that lacked
+unsigned integer types. These unsigned operations for signed integers use
 the ``%`` suffix as convention:
 
 
@@ -98,7 +98,7 @@ operation                meaning
 kinds of integer types are used: the smaller type is converted to the larger.
 
 A `narrowing type conversion`:idx: converts a larger to a smaller type (for
-example ``int32 -> int16``. A `widening type conversion`:idx: converts a 
+example ``int32 -> int16``. A `widening type conversion`:idx: converts a
 smaller type to a larger type (for example ``int16 -> int32``). In Nim only
 widening type conversions are *implicit*:
 
@@ -111,7 +111,7 @@ widening type conversions are *implicit*:
 
 However, ``int`` literals are implicitly convertible to a smaller integer type
 if the literal's value fits this smaller type and such a conversion is less
-expensive than other implicit conversions, so ``myInt16 + 34`` produces 
+expensive than other implicit conversions, so ``myInt16 + 34`` produces
 an ``int16`` result.
 
 For further details, see `Convertible relation`_.
@@ -137,12 +137,12 @@ determined). Assignments from the base type to one of its subrange types
 A subrange type has the same size as its base type (``int`` in the example).
 
 Nim requires `interval arithmetic`:idx: for subrange types over a set
-of built-in operators that involve constants: ``x %% 3`` is of 
-type ``range[0..2]``. The following built-in operators for integers are 
+of built-in operators that involve constants: ``x %% 3`` is of
+type ``range[0..2]``. The following built-in operators for integers are
 affected by this rule: ``-``, ``+``, ``*``, ``min``, ``max``, ``succ``,
 ``pred``, ``mod``, ``div``, ``%%``, ``and`` (bitwise ``and``).
 
-Bitwise ``and`` only produces a ``range`` if one of its operands is a 
+Bitwise ``and`` only produces a ``range`` if one of its operands is a
 constant *x* so that (x+1) is a number of two.
 (Bitwise ``and`` is then a ``%%`` operation.)
 
@@ -155,7 +155,7 @@ This means that the following code is accepted:
   of 9: echo "C"
   of 10: echo "D"
   # note: no ``else`` required as (x and 3) + 7 has the type: range[7..10]
-  
+
 
 Pre-defined floating point types
 --------------------------------
@@ -186,17 +186,17 @@ The IEEE standard defines five types of floating-point exceptions:
   for example 0.0/0.0, sqrt(-1.0), and log(-37.8).
 * Division by zero: divisor is zero and dividend is a finite nonzero number,
   for example 1.0/0.0.
-* Overflow: operation produces a result that exceeds the range of the exponent, 
+* Overflow: operation produces a result that exceeds the range of the exponent,
   for example MAXDOUBLE+0.0000000000001e308.
-* Underflow: operation produces a result that is too small to be represented 
+* Underflow: operation produces a result that is too small to be represented
   as a normal number, for example, MINDOUBLE * MINDOUBLE.
-* Inexact: operation produces a result that cannot be represented with infinite 
+* Inexact: operation produces a result that cannot be represented with infinite
   precision, for example, 2.0 / 3.0, log(1.1) and 0.1 in input.
 
-The IEEE exceptions are either ignored at runtime or mapped to the 
+The IEEE exceptions are either ignored at runtime or mapped to the
 Nim exceptions: `FloatInvalidOpError`:idx:, `FloatDivByZeroError`:idx:,
 `FloatOverflowError`:idx:, `FloatUnderflowError`:idx:,
-and `FloatInexactError`:idx:. 
+and `FloatInexactError`:idx:.
 These exceptions inherit from the `FloatingPointError`:idx: base class.
 
 Nim provides the pragmas `NaNChecks`:idx: and `InfChecks`:idx: to control
@@ -212,7 +212,7 @@ whether the IEEE exceptions are ignored or trap a Nim exception:
 In the current implementation ``FloatDivByZeroError`` and ``FloatInexactError``
 are never raised. ``FloatOverflowError`` is raised instead of
 ``FloatDivByZeroError``.
-There is also a `floatChecks`:idx: pragma that is a short-cut for the 
+There is also a `floatChecks`:idx: pragma that is a short-cut for the
 combination of ``NaNChecks`` and ``InfChecks`` pragmas. ``floatChecks`` are
 turned off as default.
 
@@ -303,7 +303,7 @@ and ``pred`` are not available for them either.
 
 
 The compiler supports the built-in stringify operator ``$`` for enumerations.
-The stringify's result can be controlled by explicitly giving the string 
+The stringify's result can be controlled by explicitly giving the string
 values to use:
 
 .. code-block:: nim
@@ -315,12 +315,12 @@ values to use:
       valueC = 2,
       valueD = (3, "abc")
 
-As can be seen from the example, it is possible to both specify a field's 
+As can be seen from the example, it is possible to both specify a field's
 ordinal value and its string value by using a tuple. It is also
 possible to only specify one of them.
 
 An enum can be marked with the ``pure`` pragma so that it's fields are not
-added to the current scope, so they always need to be accessed 
+added to the current scope, so they always need to be accessed
 via ``MyEnum.value``:
 
 .. code-block:: nim
@@ -328,7 +328,7 @@ via ``MyEnum.value``:
   type
     MyEnum {.pure.} = enum
       valueA, valueB, valueC, valueD
-  
+
   echo valueA # error: Unknown identifier
   echo MyEnum.valueA # works
 
@@ -364,22 +364,22 @@ cstring type
 ------------
 The ``cstring`` type represents a pointer to a zero-terminated char array
 compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy
-interfacing with C. The index operation ``s[i]`` means the i-th *char* of 
+interfacing with C. The index operation ``s[i]`` means the i-th *char* of
 ``s``; however no bounds checking for ``cstring`` is performed making the
 index operation unsafe.
 
-A Nim ``string`` is implicitly convertible 
+A Nim ``string`` is implicitly convertible
 to ``cstring`` for convenience. If a Nim string is passed to a C-style
 variadic proc, it is implicitly converted to ``cstring`` too:
 
 .. code-block:: nim
-  proc printf(formatstr: cstring) {.importc: "printf", varargs, 
+  proc printf(formatstr: cstring) {.importc: "printf", varargs,
                                     header: "<stdio.h>".}
-  
+
   printf("This works %s", "as expected")
 
 Even though the conversion is implicit, it is not *safe*: The garbage collector
-does not consider a ``cstring`` to be a root and may collect the underlying 
+does not consider a ``cstring`` to be a root and may collect the underlying
 memory. However in practice this almost never happens as the GC considers
 stack roots conservatively. One can use the builtin procs ``GC_ref`` and
 ``GC_unref`` to keep the string data alive for the rare cases where it does
@@ -390,7 +390,7 @@ string from a cstring:
 
 .. code-block:: nim
   var str: string = "Hello!"
-  var cstr: cstring = s
+  var cstr: cstring = str
   var newstr: string = $cstr
 
 
@@ -410,9 +410,9 @@ integers from 0 to ``len(A)-1``. An array expression may be constructed by the
 array constructor ``[]``.
 
 Sequences are similar to arrays but of dynamic length which may change
-during runtime (like strings). Sequences are implemented as growable arrays, 
+during runtime (like strings). Sequences are implemented as growable arrays,
 allocating pieces of memory as items are added. A sequence ``S`` is always
-indexed by integers from 0 to ``len(S)-1`` and its bounds are checked. 
+indexed by integers from 0 to ``len(S)-1`` and its bounds are checked.
 Sequences can be constructed by the array constructor ``[]`` in conjunction
 with the array to sequence operator ``@``. Another way to allocate space for a
 sequence is to call the built-in ``newSeq`` procedure.
@@ -452,11 +452,11 @@ Open arrays
 
 Often fixed size arrays turn out to be too inflexible; procedures should
 be able to deal with arrays of different sizes. The `openarray`:idx: type
-allows this; it can only be used for parameters. Openarrays are always 
-indexed with an ``int`` starting at position 0. The ``len``, ``low`` 
-and ``high`` operations are available for open arrays too. Any array with 
-a compatible base type can be passed to an openarray parameter, the index 
-type does not matter. In addition to arrays sequences can also be passed 
+allows this; it can only be used for parameters. Openarrays are always
+indexed with an ``int`` starting at position 0. The ``len``, ``low``
+and ``high`` operations are available for open arrays too. Any array with
+a compatible base type can be passed to an openarray parameter, the index
+type does not matter. In addition to arrays sequences can also be passed
 to an open array parameter.
 
 The openarray type cannot be nested: multidimensional openarrays are not
@@ -467,7 +467,7 @@ Varargs
 -------
 
 A ``varargs`` parameter is an openarray parameter that additionally
-allows to pass a variable number of arguments to a procedure. The compiler 
+allows to pass a variable number of arguments to a procedure. The compiler
 converts the list of arguments to an array implicitly:
 
 .. code-block:: nim
@@ -494,7 +494,7 @@ type conversions in this context:
   # is transformed to:
   myWriteln(stdout, [$123, $"def", $4.0])
 
-In this example ``$`` is applied to any argument that is passed to the 
+In this example ``$`` is applied to any argument that is passed to the
 parameter ``a``. (Note that ``$`` applied to strings is a nop.)
 
 
@@ -531,7 +531,7 @@ in future versions of the compiler.
   person = (creditCard: "Peter", id: 20)
 
 The implementation aligns the fields for best access performance. The alignment
-is compatible with the way the C compiler does it. 
+is compatible with the way the C compiler does it.
 
 For consistency  with ``object`` declarations, tuples in a ``type`` section
 can also be defined with indentation instead of ``[]``:
@@ -571,7 +571,7 @@ Object construction
 -------------------
 
 Objects can also be created with an `object construction expression`:idx: that
-has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is 
+has the syntax ``T(fieldA: valueA, fieldB: valueB, ...)`` where ``T`` is
 an ``object`` type or a ``ref object`` type:
 
 .. code-block:: nim
@@ -617,10 +617,10 @@ An example:
   # the following statement raises an `EInvalidField` exception, because
   # n.kind's value does not fit and the ``nkString`` branch is not active:
   n.strVal = ""
-  
+
   # invalid: would change the active object branch:
   n.kind = nkInt
-  
+
   var x = PNode(kind: nkAdd, leftOp: PNode(kind: nkInt, intVal: 4),
                              rightOp: PNode(kind: nkInt, intVal: 2))
   # valid: does not change the active object branch:
@@ -660,8 +660,8 @@ untraced references are *unsafe*. However for certain low-level operations
 Traced references are declared with the **ref** keyword, untraced references
 are declared with the **ptr** keyword.
 
-An empty subscript ``[]`` notation can be used to derefer a reference, 
-the ``addr`` procedure returns the address of an item. An address is always 
+An empty subscript ``[]`` notation can be used to derefer a reference,
+the ``addr`` procedure returns the address of an item. An address is always
 an untraced reference.
 Thus the usage of ``addr`` is an *unsafe* feature.
 
@@ -680,7 +680,7 @@ dereferencing operations for reference types:
   var
     n: PNode
   new(n)
-  n.data = 9 
+  n.data = 9
   # no need to write n[].data; in fact n[].data is highly discouraged!
 
 In order to simplify structural type checking, recursive tuples are not valid:
@@ -751,20 +751,20 @@ details like this when mixing garbage collected data with unmanaged memory.
 Not nil annotation
 ------------------
 
-All types for that ``nil`` is a valid value can be annotated to 
+All types for that ``nil`` is a valid value can be annotated to
 exclude ``nil`` as a valid value with the ``not nil`` annotation:
 
 .. code-block:: nim
   type
     PObject = ref TObj not nil
     TProc = (proc (x, y: int)) not nil
-    
+
   proc p(x: PObject) =
     echo "not nil"
-  
+
   # compiler catches this:
   p(nil)
-  
+
   # and also this:
   var x: PObject
   p(x)
@@ -851,22 +851,22 @@ Examples:
 
   forEach(printItem)  # this will NOT compile because calling conventions differ
 
-  
+
 .. code-block:: nim
 
   type
     TOnMouseMove = proc (x, y: int) {.closure.}
-  
+
   proc onMouseMove(mouseX, mouseY: int) =
     # has default calling convention
     echo "x: ", mouseX, " y: ", mouseY
-  
+
   proc setOnMouseMove(mouseMoveEvent: TOnMouseMove) = discard
-  
+
   # ok, 'onMouseMove' has the default calling convention, which is compatible
   # to 'closure':
   setOnMouseMove(onMouseMove)
-  
+
 
 A subtle issue with procedural types is that the calling convention of the
 procedure influences the type compatibility: procedural types are only
@@ -932,7 +932,7 @@ of the following conditions hold:
 3) The procedure has a calling convention that differs from ``nimcall``.
 4) The procedure is anonymous.
 
-The rules' purpose is to prevent the case that extending a non-``procvar`` 
+The rules' purpose is to prevent the case that extending a non-``procvar``
 procedure with default parameters breaks client code.
 
 The default calling convention is ``nimcall``, unless it is an inner proc (a
@@ -964,7 +964,7 @@ types are a perfect tool to model different currencies:
   type
     TDollar = distinct int
     TEuro = distinct int
-  
+
   var
     d: TDollar
     e: TEuro
@@ -989,7 +989,7 @@ number without unit; and the same holds for division:
 
   proc `*` (x: int, y: TDollar): TDollar =
     result = TDollar(x * int(y))
-    
+
   proc `div` ...
 
 This quickly gets tedious. The implementations are trivial and the compiler
@@ -1014,7 +1014,7 @@ currency. This can be solved with templates_.
   template additive(typ: typedesc): stmt =
     proc `+` *(x, y: typ): typ {.borrow.}
     proc `-` *(x, y: typ): typ {.borrow.}
-    
+
     # unary operators:
     proc `+` *(x: typ): typ {.borrow.}
     proc `-` *(x: typ): typ {.borrow.}
@@ -1036,7 +1036,7 @@ currency. This can be solved with templates_.
     additive(typ)
     multiplicative(typ, base)
     comparable(typ)
-    
+
   defineCurrency(TDollar, int)
   defineCurrency(TEuro, int)
 
@@ -1127,21 +1127,21 @@ modules like `db_sqlite <db_sqlite.html>`_.
 Void type
 ---------
 
-The ``void`` type denotes the absense of any type. Parameters of 
+The ``void`` type denotes the absense of any type. Parameters of
 type ``void`` are treated as non-existent, ``void`` as a return type means that
 the procedure does not return a value:
 
 .. code-block:: nim
   proc nothing(x, y: void): void =
     echo "ha"
-  
+
   nothing() # writes "ha" to stdout
 
 The ``void`` type is particularly useful for generic code:
 
 .. code-block:: nim
   proc callProc[T](p: proc (x: T), x: T) =
-    when T is void: 
+    when T is void:
       p()
     else:
       p(x)
@@ -1151,13 +1151,13 @@ The ``void`` type is particularly useful for generic code:
 
   callProc[int](intProc, 12)
   callProc[void](emptyProc)
-  
+
 However, a ``void`` type cannot be inferred in generic code:
 
 .. code-block:: nim
-  callProc(emptyProc) 
+  callProc(emptyProc)
   # Error: type mismatch: got (proc ())
-  # but expected one of: 
+  # but expected one of:
   # callProc(p: proc (T), x: T)
 
 The ``void`` type is only valid for parameters and return types; other symbols
diff --git a/doc/tut1.txt b/doc/tut1.txt
index 4c32fa0ae..5901dd02a 100644
--- a/doc/tut1.txt
+++ b/doc/tut1.txt
@@ -1070,7 +1070,7 @@ Operation             Comment
 ``dec(x, n)``         decrements `x` by `n`; `n` is an integer
 ``succ(x)``           returns the successor of `x`
 ``succ(x, n)``        returns the `n`'th successor of `x`
-``prec(x)``           returns the predecessor of `x`
+``pred(x)``           returns the predecessor of `x`
 ``pred(x, n)``        returns the `n`'th predecessor of `x`
 -----------------     --------------------------------------------------------
 
@@ -1323,7 +1323,7 @@ define operators which accept TSlice objects to define ranges.
     a = "Nim is a progamming language"
     b = "Slices are useless."
 
-  echo a[10..15] # --> 'a prog'
+  echo a[7..12] # --> 'a prog'
   b[11.. -2] = "useful"
   echo b # --> 'Slices are useful.'
 
diff --git a/doc/tut2.txt b/doc/tut2.txt
index 6e239d681..2ae0f18f6 100644
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -35,7 +35,7 @@ Object Oriented Programming
 ===========================
 
 While Nim's support for object oriented programming (OOP) is minimalistic,
-powerful OOP technics can be used. OOP is seen as *one* way to design a
+powerful OOP techniques can be used. OOP is seen as *one* way to design a
 program, not *the only* way. Often a procedural approach leads to simpler
 and more efficient code. In particular, prefering composition over inheritance
 is often the better design.
@@ -56,7 +56,7 @@ Objects have access to their type at runtime. There is an
 
 .. code-block:: nim
   type
-    TPerson = object of TObject
+    TPerson = object of RootObj
       name*: string  # the * means that `name` is accessible from other modules
       age: int       # no * means that the field is hidden from other modules
 
@@ -76,10 +76,10 @@ never *equivalent*. New object types can only be defined within a type
 section.
 
 Inheritance is done with the ``object of`` syntax. Multiple inheritance is
-currently not supported. If an object type has no suitable ancestor, ``TObject``
-can be used as its ancestor, but this is only a convention. Objects that have 
-no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma 
-to introduce new object roots apart from ``system.TObject``. (This is used
+currently not supported. If an object type has no suitable ancestor, ``RootObj``
+can be used as its ancestor, but this is only a convention. Objects that have
+no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma
+to introduce new object roots apart from ``system.RootObj``. (This is used
 in the GTK wrapper for instance.)
 
 
@@ -199,7 +199,7 @@ This method call syntax is not restricted to objects, it can be used
 for any type:
 
 .. code-block:: nim
-  
+
   echo("abc".len) # is the same as echo(len("abc"))
   echo("abc".toUpper())
   echo({'a', 'b', 'c'}.card)
@@ -212,7 +212,7 @@ So "pure object oriented" code is easy to write:
 
 .. code-block:: nim
   import strutils
-  
+
   stdout.writeln("Give a list of numbers (separated by spaces): ")
   stdout.write(stdin.readLine.split.map(parseInt).max.`$`)
   stdout.writeln(" is the maximum!")
@@ -226,9 +226,9 @@ the same. But setting a value is different; for this a special setter syntax
 is needed:
 
 .. code-block:: nim
-  
+
   type
-    TSocket* = object of TObject
+    TSocket* = object of RootObj
       FHost: int # cannot be accessed from the outside of the module
                  # the `F` prefix is a convention to avoid clashes since
                  # the accessors are named `host`
@@ -236,7 +236,7 @@ is needed:
   proc `host=`*(s: var TSocket, value: int) {.inline.} =
     ## setter of hostAddr
     s.FHost = value
-  
+
   proc host*(s: TSocket): int {.inline.} =
     ## getter of hostAddr
     s.FHost
@@ -284,7 +284,7 @@ Procedures always use static dispatch. For dynamic dispatch replace the
 
 .. code-block:: nim
   type
-    PExpr = ref object of TObject ## abstract base class for an expression
+    PExpr = ref object of RootObj ## abstract base class for an expression
     PLiteral = ref object of PExpr
       x: int
     PPlusExpr = ref object of PExpr
@@ -294,15 +294,15 @@ Procedures always use static dispatch. For dynamic dispatch replace the
   method eval(e: PExpr): int =
     # override this base method
     quit "to override!"
-  
+
   method eval(e: PLiteral): int = e.x
   method eval(e: PPlusExpr): int = eval(e.a) + eval(e.b)
-  
+
   proc newLit(x: int): PLiteral = PLiteral(x: x)
   proc newPlus(a, b: PExpr): PPlusExpr = PPlusExpr(a: a, b: b)
-  
+
   echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4)))
-  
+
 Note that in the example the constructors ``newLit`` and ``newPlus`` are procs
 because they should use static binding, but ``eval`` is a method because it
 requires dynamic binding.
@@ -313,19 +313,19 @@ dispatching:
 .. code-block:: nim
 
   type
-    TThing = object of TObject
+    TThing = object of RootObj
     TUnit = object of TThing
       x: int
-      
+
   method collide(a, b: TThing) {.inline.} =
     quit "to override!"
-    
+
   method collide(a: TThing, b: TUnit) {.inline.} =
     echo "1"
-  
+
   method collide(a: TUnit, b: TThing) {.inline.} =
     echo "2"
-  
+
   var
     a, b: TUnit
   collide(a, b) # output: 2
@@ -526,7 +526,7 @@ containers:
         yield n.data
         add(stack, n.ri)  # push right subtree onto the stack
         n = n.le          # and follow the left pointer
-      
+
   var
     root: PBinaryTree[string] # instantiate a PBinaryTree with ``string``
   add(root, newNode("hello")) # instantiates ``newNode`` and ``add``
@@ -578,7 +578,7 @@ simple proc for logging:
 
   proc log(msg: string) {.inline.} =
     if debug: stdout.writeln(msg)
-  
+
   var
     x = 4
   log("x has the value: " & $x)
@@ -595,7 +595,7 @@ Turning the ``log`` proc into a template solves this problem:
 
   template log(msg: string) =
     if debug: stdout.writeln(msg)
-  
+
   var
     x = 4
   log("x has the value: " & $x)
@@ -622,11 +622,11 @@ via a special ``:`` syntax:
         close(f)
     else:
       quit("cannot open: " & fn)
-      
+
   withFile(txt, "ttempl3.txt", fmWrite):
     txt.writeln("line 1")
     txt.writeln("line 2")
-  
+
 In the example the two ``writeln`` statements are bound to the ``body``
 parameter. The ``withFile`` template contains boilerplate code and helps to
 avoid a common bug: to forget to close the file. Note how the
@@ -739,7 +739,7 @@ Term rewriting macros
 ---------------------
 
 Term rewriting macros can be used to enhance the compilation process
-with user defined optimizations; see this `document <trmacros.html>`_ for 
+with user defined optimizations; see this `document <trmacros.html>`_ for
 further information.
 
 
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 4bdce9cfb..c4abdf9f3 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -878,6 +878,10 @@ else:
       let data = PData(info.key.data)
       assert data.fd == info.key.fd.TAsyncFD
       #echo("In poll ", data.fd.cint)
+      if EvError in info.events:
+        closeSocket(data.fd)
+        continue
+
       if EvRead in info.events:
         # Callback may add items to ``data.readCBs`` which causes issues if
         # we are iterating over ``data.readCBs`` at the same time. We therefore
diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim
index 0b18e6bcc..4c48350aa 100644
--- a/lib/pure/asynchttpserver.nim
+++ b/lib/pure/asynchttpserver.nim
@@ -17,6 +17,8 @@
 ## as the response body.
 ##
 ## .. code-block::nim
+##    import asynchttpserver, asyncdispatch
+##
 ##    var server = newAsyncHttpServer()
 ##    proc cb(req: Request) {.async.} =
 ##      await req.respond(Http200, "Hello World")
diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim
index 3afb625ee..bfdfed72c 100644
--- a/lib/pure/httpclient.nim
+++ b/lib/pure/httpclient.nim
@@ -32,21 +32,12 @@
 ## the server.
 ##
 ## .. code-block:: Nim
-##   var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
-##   var body: string = "--xyz\c\L"
-##   # soap 1.2 output
-##   body.add("Content-Disposition: form-data; name=\"output\"\c\L")
-##   body.add("\c\Lsoap12\c\L")
+##   var data = newMultipartData()
+##   data["output"] = "soap12"
+##   data["uploaded_file"] = ("test.html", "text/html",
+##     "<html><head></head><body><p>test</p></body></html>")
 ##
-##   # html
-##   body.add("--xyz\c\L")
-##   body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
-##            " filename=\"test.html\"\c\L")
-##   body.add("Content-Type: text/html\c\L")
-##   body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
-##   body.add("--xyz--")
-##
-##   echo(postContent("http://validator.w3.org/check", headers, body))
+##   echo postContent("http://validator.w3.org/check", multipart=data)
 ##
 ## Asynchronous HTTP requests
 ## ==========================
@@ -88,7 +79,7 @@
 ## constructor should be used for this purpose. However,
 ## currently only basic authentication is supported.
 
-import net, strutils, uri, parseutils, strtabs, base64, os
+import net, strutils, uri, parseutils, strtabs, base64, os, mimetypes, math
 import asyncnet, asyncdispatch
 import rawsockets
 
@@ -103,6 +94,10 @@ type
     url*: Uri
     auth*: string
 
+  MultipartEntries* = openarray[tuple[name, content: string]]
+  MultipartData* = ref object
+    content: seq[string]
+
   ProtocolError* = object of IOError   ## exception that is raised when server
                                        ## does not conform to the implemented
                                        ## protocol
@@ -282,6 +277,109 @@ proc newProxy*(url: string, auth = ""): Proxy =
   ## Constructs a new ``TProxy`` object.
   result = Proxy(url: parseUri(url), auth: auth)
 
+proc newMultipartData*: MultipartData =
+  ## Constructs a new ``MultipartData`` object.
+  MultipartData(content: @[])
+
+proc add*(p: var MultipartData, name, content: string, filename: string = nil,
+          contentType: string = nil) =
+  ## Add a value to the multipart data. Raises a `ValueError` exception if
+  ## `name`, `filename` or `contentType` contain newline characters.
+
+  if {'\c','\L'} in name:
+    raise newException(ValueError, "name contains a newline character")
+  if filename != nil and {'\c','\L'} in filename:
+    raise newException(ValueError, "filename contains a newline character")
+  if contentType != nil and {'\c','\L'} in contentType:
+    raise newException(ValueError, "contentType contains a newline character")
+
+  var str = "Content-Disposition: form-data; name=\"" & name & "\""
+  if filename != nil:
+    str.add("; filename=\"" & filename & "\"")
+  str.add("\c\L")
+  if contentType != nil:
+    str.add("Content-Type: " & contentType & "\c\L")
+  str.add("\c\L" & content & "\c\L")
+
+  p.content.add(str)
+
+proc add*(p: var MultipartData, xs: MultipartEntries): MultipartData
+         {.discardable.} =
+  ## Add a list of multipart entries to the multipart data `p`. All values are
+  ## added without a filename and without a content type.
+  ##
+  ## .. code-block:: Nim
+  ##   data.add({"action": "login", "format": "json"})
+  for name, content in xs.items:
+    p.add(name, content)
+  result = p
+
+proc newMultipartData*(xs: MultipartEntries): MultipartData =
+  ## Create a new multipart data object and fill it with the entries `xs`
+  ## directly.
+  ##
+  ## .. code-block:: Nim
+  ##   var data = newMultipartData({"action": "login", "format": "json"})
+  result = MultipartData(content: @[])
+  result.add(xs)
+
+proc addFiles*(p: var MultipartData, xs: openarray[tuple[name, file: string]]):
+              MultipartData {.discardable.} =
+  ## Add files to a multipart data object. The file will be opened from your
+  ## disk, read and sent with the automatically determined MIME type. Raises an
+  ## `IOError` if the file cannot be opened or reading fails. To manually
+  ## specify file content, filename and MIME type, use `[]=` instead.
+  ##
+  ## .. code-block:: Nim
+  ##   data.addFiles({"uploaded_file": "public/test.html"})
+  var m = newMimetypes()
+  for name, file in xs.items:
+    var contentType: string
+    let (dir, fName, ext) = splitFile(file)
+    if ext.len > 0:
+      contentType = m.getMimetype(ext[1..ext.high], nil)
+    p.add(name, readFile(file), fName & ext, contentType)
+  result = p
+
+proc `[]=`*(p: var MultipartData, name, content: string) =
+  ## Add a multipart entry to the multipart data `p`. The value is added
+  ## without a filename and without a content type.
+  ##
+  ## .. code-block:: Nim
+  ##   data["username"] = "NimUser"
+  p.add(name, content)
+
+proc `[]=`*(p: var MultipartData, name: string,
+            file: tuple[name, contentType, content: string]) =
+  ## Add a file to the multipart data `p`, specifying filename, contentType and
+  ## content manually.
+  ##
+  ## .. code-block:: Nim
+  ##   data["uploaded_file"] = ("test.html", "text/html",
+  ##     "<html><head></head><body><p>test</p></body></html>")
+  p.add(name, file.content, file.name, file.contentType)
+
+proc format(p: MultipartData): tuple[header, body: string] =
+  if p == nil or p.content == nil or p.content.len == 0:
+    return ("", "")
+
+  # Create boundary that is not in the data to be formatted
+  var bound: string
+  while true:
+    bound = $random(int.high)
+    var found = false
+    for s in p.content:
+      if bound in s:
+        found = true
+    if not found:
+      break
+
+  result.header = "Content-Type: multipart/form-data; boundary=" & bound & "\c\L"
+  result.body = ""
+  for s in p.content:
+    result.body.add("--" & bound & "\c\L" & s)
+  result.body.add("--" & bound & "--\c\L")
+
 proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
               body = "",
               sslContext: SSLContext = defaultSSLContext,
@@ -294,7 +392,9 @@ proc request*(url: string, httpMethod = httpGET, extraHeaders = "",
   var r = if proxy == nil: parseUri(url) else: proxy.url
   var headers = substr($httpMethod, len("http"))
   if proxy == nil:
-    headers.add(" /" & r.path & r.query)
+    headers.add(" " & r.path)
+    if r.query.len > 0:
+      headers.add("?" & r.query)
   else:
     headers.add(" " & url)
 
@@ -387,15 +487,30 @@ proc post*(url: string, extraHeaders = "", body = "",
            maxRedirects = 5,
            sslContext: SSLContext = defaultSSLContext,
            timeout = -1, userAgent = defUserAgent,
-           proxy: Proxy = nil): Response =
+           proxy: Proxy = nil,
+           multipart: MultipartData = nil): Response =
   ## | POSTs ``body`` to the ``url`` and returns a ``Response`` object.
   ## | This proc adds the necessary Content-Length header.
   ## | This proc also handles redirection.
   ## | Extra headers can be specified and must be separated by ``\c\L``.
   ## | An optional timeout can be specified in miliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
-  var xh = extraHeaders & "Content-Length: " & $len(body) & "\c\L"
-  result = request(url, httpPOST, xh, body, sslContext, timeout, userAgent,
+  ## | The optional ``multipart`` parameter can be used to create
+  ## ``multipart/form-data`` POSTs comfortably.
+  let (mpHeaders, mpBody) = format(multipart)
+
+  template withNewLine(x): expr =
+    if x.len > 0 and not x.endsWith("\c\L"):
+      x & "\c\L"
+    else:
+      x
+
+  var xb = mpBody.withNewLine() & body.withNewLine()
+
+  var xh = extraHeaders.withNewLine() & mpHeaders.withNewLine() &
+    withNewLine("Content-Length: " & $len(xb))
+
+  result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
                    proxy)
   var lastUrl = ""
   for i in 1..maxRedirects:
@@ -410,14 +525,17 @@ proc postContent*(url: string, extraHeaders = "", body = "",
                   maxRedirects = 5,
                   sslContext: SSLContext = defaultSSLContext,
                   timeout = -1, userAgent = defUserAgent,
-                  proxy: Proxy = nil): string =
+                  proxy: Proxy = nil,
+                  multipart: MultipartData = nil): string =
   ## | POSTs ``body`` to ``url`` and returns the response's body as a string
   ## | Raises exceptions for the status codes ``4xx`` and ``5xx``
   ## | Extra headers can be specified and must be separated by ``\c\L``.
   ## | An optional timeout can be specified in miliseconds, if reading from the
   ## server takes longer than specified an ETimeout exception will be raised.
+  ## | The optional ``multipart`` parameter can be used to create
+  ## ``multipart/form-data`` POSTs comfortably.
   var r = post(url, extraHeaders, body, maxRedirects, sslContext, timeout,
-               userAgent, proxy)
+               userAgent, proxy, multipart)
   if r.status[0] in {'4','5'}:
     raise newException(HttpRequestError, r.status)
   else:
@@ -442,7 +560,9 @@ proc generateHeaders(r: Uri, httpMethod: HttpMethod,
                      headers: StringTableRef): string =
   result = substr($httpMethod, len("http"))
   # TODO: Proxies
-  result.add(" /" & r.path & r.query)
+  result.add(" " & r.path)
+  if r.query.len > 0:
+    result.add("?" & r.query)
   result.add(" HTTP/1.1\c\L")
 
   add(result, "Host: " & r.hostname & "\c\L")
@@ -706,18 +826,9 @@ when isMainModule:
     #var r = get("http://validator.w3.org/check?uri=http%3A%2F%2Fgoogle.com&
     #  charset=%28detect+automatically%29&doctype=Inline&group=0")
 
-    var headers: string = "Content-Type: multipart/form-data; boundary=xyz\c\L"
-    var body: string = "--xyz\c\L"
-    # soap 1.2 output
-    body.add("Content-Disposition: form-data; name=\"output\"\c\L")
-    body.add("\c\Lsoap12\c\L")
-
-    # html
-    body.add("--xyz\c\L")
-    body.add("Content-Disposition: form-data; name=\"uploaded_file\";" &
-             " filename=\"test.html\"\c\L")
-    body.add("Content-Type: text/html\c\L")
-    body.add("\c\L<html><head></head><body><p>test</p></body></html>\c\L")
-    body.add("--xyz--")
-
-    echo(postContent("http://validator.w3.org/check", headers, body))
+    var data = newMultipartData()
+    data["output"] = "soap12"
+    data["uploaded_file"] = ("test.html", "text/html",
+      "<html><head></head><body><p>test</p></body></html>")
+
+    echo postContent("http://validator.w3.org/check", multipart=data)
diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim
index 3b5453957..c6228af50 100644
--- a/lib/pure/md5.nim
+++ b/lib/pure/md5.nim
@@ -9,18 +9,20 @@
 
 ## Module for computing MD5 checksums.
 
-type 
-  MD5State = array[0..3, int32]
-  MD5Block = array[0..15, int32]
-  MD5CBits = array[0..7, int8]
-  MD5Digest* = array[0..15, int8]
-  MD5Buffer = array[0..63, int8]
-  MD5Context* {.final.} = object 
+import unsigned
+
+type
+  MD5State = array[0..3, uint32]
+  MD5Block = array[0..15, uint32]
+  MD5CBits = array[0..7, uint8]
+  MD5Digest* = array[0..15, uint8]
+  MD5Buffer = array[0..63, uint8]
+  MD5Context* {.final.} = object
     state: MD5State
-    count: array[0..1, int32]
+    count: array[0..1, uint32]
     buffer: MD5Buffer
 
-const 
+const
   padding: cstring = "\x80\0\0\0" &
                      "\0\0\0\0\0\0\0\0" &
                      "\0\0\0\0\0\0\0\0" &
@@ -31,60 +33,60 @@ const
                      "\0\0\0\0\0\0\0\0" &
                      "\0\0\0\0"
 
-proc F(x, y, z: int32): int32 {.inline.} = 
+proc F(x, y, z: uint32): uint32 {.inline.} =
   result = (x and y) or ((not x) and z)
 
-proc G(x, y, z: int32): int32 {.inline.} = 
+proc G(x, y, z: uint32): uint32 {.inline.} =
   result = (x and z) or (y and (not z))
 
-proc H(x, y, z: int32): int32 {.inline.} = 
+proc H(x, y, z: uint32): uint32 {.inline.} =
   result = x xor y xor z
 
-proc I(x, y, z: int32): int32 {.inline.} = 
+proc I(x, y, z: uint32): uint32 {.inline.} =
   result = y xor (x or (not z))
 
-proc rot(x: var int32, n: int8) {.inline.} = 
-  x = toU32(x shl ze(n)) or (x shr toU32(32 -% ze(n)))
+proc rot(x: var uint32, n: uint8) {.inline.} =
+  x = (x shl n) or (x shr (32'u32 - n))
 
-proc FF(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% F(b, c, d) +% x +% ac
+proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + F(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc GG(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% G(b, c, d) +% x +% ac
+proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + G(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc HH(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% H(b, c, d) +% x +% ac
+proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + H(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc II(a: var int32, b, c, d, x: int32, s: int8, ac: int32) = 
-  a = a +% I(b, c, d) +% x +% ac
+proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) =
+  a = a + I(b, c, d) + x + ac
   rot(a, s)
-  a = a +% b
+  a = a + b
 
-proc encode(dest: var MD5Block, src: cstring) = 
+proc encode(dest: var MD5Block, src: cstring) =
   var j = 0
   for i in 0..high(dest):
-    dest[i] = toU32(ord(src[j]) or 
-                ord(src[j+1]) shl 8 or
-                ord(src[j+2]) shl 16 or
-                ord(src[j+3]) shl 24)
+    dest[i] = uint32(ord(src[j])) or
+              uint32(ord(src[j+1])) shl 8 or
+              uint32(ord(src[j+2])) shl 16 or
+              uint32(ord(src[j+3])) shl 24
     inc(j, 4)
 
-proc decode(dest: var openArray[int8], src: openArray[int32]) = 
+proc decode(dest: var openArray[uint8], src: openArray[uint32]) =
   var i = 0
   for j in 0..high(src):
-    dest[i] = toU8(src[j] and 0xff'i32)
-    dest[i+1] = toU8(src[j] shr 8'i32 and 0xff'i32)
-    dest[i+2] = toU8(src[j] shr 16'i32 and 0xff'i32)
-    dest[i+3] = toU8(src[j] shr 24'i32 and 0xff'i32)
+    dest[i] = src[j] and 0xff'u32
+    dest[i+1] = src[j] shr 8 and 0xff'u32
+    dest[i+2] = src[j] shr 16 and 0xff'u32
+    dest[i+3] = src[j] shr 24 and 0xff'u32
     inc(i, 4)
 
-proc transform(buffer: pointer, state: var MD5State) = 
+proc transform(buffer: pointer, state: var MD5State) =
   var
     myBlock: MD5Block
   encode(myBlock, cast[cstring](buffer))
@@ -92,111 +94,111 @@ proc transform(buffer: pointer, state: var MD5State) =
   var b = state[1]
   var c = state[2]
   var d = state[3]
-  FF(a, b, c, d, myBlock[0], 7'i8, 0xD76AA478'i32)
-  FF(d, a, b, c, myBlock[1], 12'i8, 0xE8C7B756'i32)
-  FF(c, d, a, b, myBlock[2], 17'i8, 0x242070DB'i32)
-  FF(b, c, d, a, myBlock[3], 22'i8, 0xC1BDCEEE'i32)
-  FF(a, b, c, d, myBlock[4], 7'i8, 0xF57C0FAF'i32)
-  FF(d, a, b, c, myBlock[5], 12'i8, 0x4787C62A'i32)
-  FF(c, d, a, b, myBlock[6], 17'i8, 0xA8304613'i32)
-  FF(b, c, d, a, myBlock[7], 22'i8, 0xFD469501'i32)
-  FF(a, b, c, d, myBlock[8], 7'i8, 0x698098D8'i32)
-  FF(d, a, b, c, myBlock[9], 12'i8, 0x8B44F7AF'i32)
-  FF(c, d, a, b, myBlock[10], 17'i8, 0xFFFF5BB1'i32)
-  FF(b, c, d, a, myBlock[11], 22'i8, 0x895CD7BE'i32)
-  FF(a, b, c, d, myBlock[12], 7'i8, 0x6B901122'i32)
-  FF(d, a, b, c, myBlock[13], 12'i8, 0xFD987193'i32)
-  FF(c, d, a, b, myBlock[14], 17'i8, 0xA679438E'i32)
-  FF(b, c, d, a, myBlock[15], 22'i8, 0x49B40821'i32)
-  GG(a, b, c, d, myBlock[1], 5'i8, 0xF61E2562'i32)
-  GG(d, a, b, c, myBlock[6], 9'i8, 0xC040B340'i32)
-  GG(c, d, a, b, myBlock[11], 14'i8, 0x265E5A51'i32)
-  GG(b, c, d, a, myBlock[0], 20'i8, 0xE9B6C7AA'i32)
-  GG(a, b, c, d, myBlock[5], 5'i8, 0xD62F105D'i32)
-  GG(d, a, b, c, myBlock[10], 9'i8, 0x02441453'i32)
-  GG(c, d, a, b, myBlock[15], 14'i8, 0xD8A1E681'i32)
-  GG(b, c, d, a, myBlock[4], 20'i8, 0xE7D3FBC8'i32)
-  GG(a, b, c, d, myBlock[9], 5'i8, 0x21E1CDE6'i32)
-  GG(d, a, b, c, myBlock[14], 9'i8, 0xC33707D6'i32)
-  GG(c, d, a, b, myBlock[3], 14'i8, 0xF4D50D87'i32)
-  GG(b, c, d, a, myBlock[8], 20'i8, 0x455A14ED'i32)
-  GG(a, b, c, d, myBlock[13], 5'i8, 0xA9E3E905'i32)
-  GG(d, a, b, c, myBlock[2], 9'i8, 0xFCEFA3F8'i32)
-  GG(c, d, a, b, myBlock[7], 14'i8, 0x676F02D9'i32)
-  GG(b, c, d, a, myBlock[12], 20'i8, 0x8D2A4C8A'i32)
-  HH(a, b, c, d, myBlock[5], 4'i8, 0xFFFA3942'i32)
-  HH(d, a, b, c, myBlock[8], 11'i8, 0x8771F681'i32)
-  HH(c, d, a, b, myBlock[11], 16'i8, 0x6D9D6122'i32)
-  HH(b, c, d, a, myBlock[14], 23'i8, 0xFDE5380C'i32)
-  HH(a, b, c, d, myBlock[1], 4'i8, 0xA4BEEA44'i32)
-  HH(d, a, b, c, myBlock[4], 11'i8, 0x4BDECFA9'i32)
-  HH(c, d, a, b, myBlock[7], 16'i8, 0xF6BB4B60'i32)
-  HH(b, c, d, a, myBlock[10], 23'i8, 0xBEBFBC70'i32)
-  HH(a, b, c, d, myBlock[13], 4'i8, 0x289B7EC6'i32)
-  HH(d, a, b, c, myBlock[0], 11'i8, 0xEAA127FA'i32)
-  HH(c, d, a, b, myBlock[3], 16'i8, 0xD4EF3085'i32)
-  HH(b, c, d, a, myBlock[6], 23'i8, 0x04881D05'i32)
-  HH(a, b, c, d, myBlock[9], 4'i8, 0xD9D4D039'i32)
-  HH(d, a, b, c, myBlock[12], 11'i8, 0xE6DB99E5'i32)
-  HH(c, d, a, b, myBlock[15], 16'i8, 0x1FA27CF8'i32)
-  HH(b, c, d, a, myBlock[2], 23'i8, 0xC4AC5665'i32)
-  II(a, b, c, d, myBlock[0], 6'i8, 0xF4292244'i32)
-  II(d, a, b, c, myBlock[7], 10'i8, 0x432AFF97'i32)
-  II(c, d, a, b, myBlock[14], 15'i8, 0xAB9423A7'i32)
-  II(b, c, d, a, myBlock[5], 21'i8, 0xFC93A039'i32)
-  II(a, b, c, d, myBlock[12], 6'i8, 0x655B59C3'i32)
-  II(d, a, b, c, myBlock[3], 10'i8, 0x8F0CCC92'i32)
-  II(c, d, a, b, myBlock[10], 15'i8, 0xFFEFF47D'i32)
-  II(b, c, d, a, myBlock[1], 21'i8, 0x85845DD1'i32)
-  II(a, b, c, d, myBlock[8], 6'i8, 0x6FA87E4F'i32)
-  II(d, a, b, c, myBlock[15], 10'i8, 0xFE2CE6E0'i32)
-  II(c, d, a, b, myBlock[6], 15'i8, 0xA3014314'i32)
-  II(b, c, d, a, myBlock[13], 21'i8, 0x4E0811A1'i32)
-  II(a, b, c, d, myBlock[4], 6'i8, 0xF7537E82'i32)
-  II(d, a, b, c, myBlock[11], 10'i8, 0xBD3AF235'i32)
-  II(c, d, a, b, myBlock[2], 15'i8, 0x2AD7D2BB'i32)
-  II(b, c, d, a, myBlock[9], 21'i8, 0xEB86D391'i32)
-  state[0] = state[0] +% a
-  state[1] = state[1] +% b
-  state[2] = state[2] +% c
-  state[3] = state[3] +% d
-  
-proc md5Init*(c: var MD5Context) = 
-  ## initializes a MD5Context  
-  c.state[0] = 0x67452301'i32
-  c.state[1] = 0xEFCDAB89'i32
-  c.state[2] = 0x98BADCFE'i32
-  c.state[3] = 0x10325476'i32
-  c.count[0] = 0'i32
-  c.count[1] = 0'i32
+  FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32)
+  FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32)
+  FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32)
+  FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32)
+  FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32)
+  FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32)
+  FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32)
+  FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32)
+  FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32)
+  FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32)
+  FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32)
+  FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32)
+  FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32)
+  FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32)
+  FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32)
+  FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32)
+  GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32)
+  GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32)
+  GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32)
+  GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32)
+  GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32)
+  GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32)
+  GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32)
+  GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32)
+  GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32)
+  GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32)
+  GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32)
+  GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32)
+  GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32)
+  GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32)
+  GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32)
+  GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32)
+  HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32)
+  HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32)
+  HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32)
+  HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32)
+  HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32)
+  HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32)
+  HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32)
+  HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32)
+  HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32)
+  HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32)
+  HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32)
+  HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32)
+  HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32)
+  HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32)
+  HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32)
+  HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32)
+  II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32)
+  II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32)
+  II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32)
+  II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32)
+  II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32)
+  II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32)
+  II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32)
+  II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32)
+  II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32)
+  II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32)
+  II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32)
+  II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32)
+  II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32)
+  II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32)
+  II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32)
+  II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32)
+  state[0] = state[0] + a
+  state[1] = state[1] + b
+  state[2] = state[2] + c
+  state[3] = state[3] + d
+
+proc md5Init*(c: var MD5Context) =
+  ## initializes a MD5Context
+  c.state[0] = 0x67452301'u32
+  c.state[1] = 0xEFCDAB89'u32
+  c.state[2] = 0x98BADCFE'u32
+  c.state[3] = 0x10325476'u32
+  c.count[0] = 0'u32
+  c.count[1] = 0'u32
   zeroMem(addr(c.buffer), sizeof(MD5buffer))
 
-proc md5Update*(c: var MD5Context, input: cstring, len: int) = 
+proc md5Update*(c: var MD5Context, input: cstring, len: int) =
   ## updates the MD5Context with the `input` data of length `len`
   var input = input
-  var Index = (c.count[0] shr 3) and 0x3F
-  c.count[0] = c.count[0] +% toU32(len shl 3)
-  if c.count[0] < (len shl 3): c.count[1] = c.count[1] +% 1'i32
-  c.count[1] = c.count[1] +% toU32(len shr 29)
+  var Index = int((c.count[0] shr 3) and 0x3F)
+  c.count[0] = c.count[0] + (uint32(len) shl 3)
+  if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32
+  c.count[1] = c.count[1] + (uint32(len) shr 29)
   var PartLen = 64 - Index
-  if len >= PartLen: 
+  if len >= PartLen:
     copyMem(addr(c.buffer[Index]), input, PartLen)
     transform(addr(c.buffer), c.state)
     var i = PartLen
-    while i + 63 < len: 
+    while i + 63 < len:
       transform(addr(input[i]), c.state)
       inc(i, 64)
     copyMem(addr(c.buffer[0]), addr(input[i]), len-i)
   else:
     copyMem(addr(c.buffer[Index]), addr(input[0]), len)
 
-proc md5Final*(c: var MD5Context, digest: var MD5Digest) = 
+proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
   ## finishes the MD5Context and stores the result in `digest`
   var
     Bits: MD5CBits
     PadLen: int
   decode(Bits, c.count)
-  var Index = (c.count[0] shr 3) and 0x3F
+  var Index = int((c.count[0] shr 3) and 0x3F)
   if Index < 56: PadLen = 56 - Index
   else: PadLen = 120 - Index
   md5Update(c, padding, PadLen)
@@ -204,34 +206,34 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) =
   decode(digest, c.state)
   zeroMem(addr(c), sizeof(MD5Context))
 
-proc toMD5*(s: string): MD5Digest = 
+proc toMD5*(s: string): MD5Digest =
   ## computes the MD5Digest value for a string `s`
   var c: MD5Context
   md5Init(c)
   md5Update(c, cstring(s), len(s))
   md5Final(c, result)
-  
-proc `$`*(D: MD5Digest): string = 
+
+proc `$`*(D: MD5Digest): string =
   ## converts a MD5Digest value into its string representation
   const digits = "0123456789abcdef"
   result = ""
-  for i in 0..15: 
+  for i in 0..15:
     add(result, digits[(D[i] shr 4) and 0xF])
     add(result, digits[D[i] and 0xF])
 
-proc getMD5*(s: string): string =  
+proc getMD5*(s: string): string =
   ## computes an MD5 value of `s` and returns its string representation
-  var 
+  var
     c: MD5Context
     d: MD5Digest
   md5Init(c)
   md5Update(c, cstring(s), len(s))
   md5Final(c, d)
   result = $d
-  
-proc `==`*(D1, D2: MD5Digest): bool =  
+
+proc `==`*(D1, D2: MD5Digest): bool =
   ## checks if two MD5Digest values are identical
-  for i in 0..15: 
+  for i in 0..15:
     if D1[i] != D2[i]: return false
   return true
 
@@ -241,5 +243,3 @@ when isMainModule:
   assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") ==
     "7e716d0e702df0505fc72e2b89467910")
   assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e")
-
-
diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim
index 1c988c609..f17c6d317 100644
--- a/lib/pure/selectors.nim
+++ b/lib/pure/selectors.nim
@@ -23,7 +23,7 @@ proc `$`*(x: SocketHandle): string {.borrow.}
 
 type
   Event* = enum
-    EvRead, EvWrite
+    EvRead, EvWrite, EvError
 
   SelectorKey* = ref object
     fd*: SocketHandle
@@ -146,12 +146,19 @@ elif defined(linux):
     ## on the ``fd``.
     result = @[]
     let evNum = epoll_wait(s.epollFD, addr s.events[0], 64.cint, timeout.cint)
-    if evNum < 0: raiseOSError(osLastError())
+    if evNum < 0:
+      let err = osLastError()
+      if err.cint == EINTR:
+        return @[]
+      raiseOSError(osLastError())
     if evNum == 0: return @[]
     for i in 0 .. <evNum:
       let fd = s.events[i].data.fd.SocketHandle
     
       var evSet: set[Event] = {}
+      if (s.events[i].events and EPOLLERR) != 0: evSet = evSet + {EvError}
+      if (s.events[i].events and EPOLLHUP) != 0: evSet = evSet + {EvError}
+      if (s.events[i].events and EPOLLRDHUP) != 0: evSet = evSet + {EvError}
       if (s.events[i].events and EPOLLIN) != 0: evSet = evSet + {EvRead}
       if (s.events[i].events and EPOLLOUT) != 0: evSet = evSet + {EvWrite}
       let selectorKey = s.fds[fd]
diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim
index 21efea3bc..8e7c8efe6 100644
--- a/lib/pure/unittest.nim
+++ b/lib/pure/unittest.nim
@@ -1,7 +1,7 @@
 #
 #
 #            Nim's Runtime Library
-#        (c) Copyright 2012 Nim Contributors
+#        (c) Copyright 2015 Nim Contributors
 #
 #    See the file "copying.txt", included in this
 #    distribution, for details about the copyright.
@@ -9,12 +9,27 @@
 
 ## :Author: Zahary Karadjov
 ##
-## This module implements the standard unit testing facilities such as
-## suites, fixtures and test cases as well as facilities for combinatorial 
-## and randomzied test case generation (not yet available) 
-## and object mocking (not yet available)
+## This module implements boilerplate to make testing easy.
 ##
-## It is loosely based on C++'s boost.test and Haskell's QuickTest
+## Example:
+##
+## .. code:: nim
+##
+##   suite "description for this stuff":
+##     test "essential truths":
+##       # give up and stop if this fails
+##       require(true)
+##
+##     test "slightly less obvious stuff":
+##       # print a nasty message and move on, skipping
+##       # the remainder of this block
+##       check(1 != 1)
+##       check("asd"[2] == 'd')
+##
+##     test "out of bounds error is thrown on bad access":
+##       let v = @[1, 2, 3]  # you can do initialization here
+##       expect(IndexError):
+##         discard v[4]
 
 import
   macros
@@ -209,3 +224,5 @@ if envOutLvl.len > 0:
     if $opt == envOutLvl:
       outputLevel = opt
       break
+
+system.addQuitProc(resetAttributes)
diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim
index 368802dc2..edc690aec 100644
--- a/lib/pure/uri.nim
+++ b/lib/pure/uri.nim
@@ -16,6 +16,7 @@ type
   Uri* = object
     scheme*, username*, password*: string 
     hostname*, port*, path*, query*, anchor*: string
+    opaque*: bool
 
 {.deprecated: [TUrl: Url, TUri: Uri].}
 
@@ -115,6 +116,8 @@ proc parseUri*(uri: string): Uri =
     if authority == "":
       raise newException(ValueError, "Expected authority got nothing.")
     parseAuthority(authority, result)
+  else:
+    result.opaque = true
 
   # Path
   parsePath(uri, i, result)
@@ -256,7 +259,10 @@ proc `$`*(u: Uri): string =
   result = ""
   if u.scheme.len > 0:
     result.add(u.scheme)
-    result.add("://")
+    if u.opaque:
+      result.add(":")
+    else:
+      result.add("://")
   if u.username.len > 0:
     result.add(u.username)
     if u.password.len > 0:
@@ -268,22 +274,28 @@ proc `$`*(u: Uri): string =
     result.add(":")
     result.add(u.port)
   if u.path.len > 0:
-    if u.path[0] != '/': result.add("/")
     result.add(u.path)
-  result.add(u.query)
-  result.add(u.anchor)
+  if u.query.len > 0:
+    result.add("?")
+    result.add(u.query)
+  if u.anchor.len > 0:
+    result.add("#")
+    result.add(u.anchor)
 
 when isMainModule:
   block:
-    let test = parseUri("http://localhost:8080/test")
+    let str = "http://localhost:8080/test"
+    let test = parseUri(str)
     doAssert test.scheme == "http"
     doAssert test.port == "8080"
     doAssert test.path == "/test"
     doAssert test.hostname == "localhost"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("foo://username:password@example.com:8042/over/there" &
-                        "/index.dtb?type=animal&name=narwhal#nose")
+    let str = "foo://username:password@example.com:8042/over/there" &
+              "/index.dtb?type=animal&name=narwhal#nose"
+    let test = parseUri(str)
     doAssert test.scheme == "foo"
     doAssert test.username == "username"
     doAssert test.password == "password"
@@ -292,34 +304,45 @@ when isMainModule:
     doAssert test.path == "/over/there/index.dtb"
     doAssert test.query == "type=animal&name=narwhal"
     doAssert test.anchor == "nose"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("urn:example:animal:ferret:nose")
+    let str = "urn:example:animal:ferret:nose"
+    let test = parseUri(str)
     doAssert test.scheme == "urn"
     doAssert test.path == "example:animal:ferret:nose"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("mailto:username@example.com?subject=Topic")
+    let str = "mailto:username@example.com?subject=Topic"
+    let test = parseUri(str)
     doAssert test.scheme == "mailto"
     doAssert test.username == "username"
     doAssert test.hostname == "example.com"
     doAssert test.query == "subject=Topic"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar")
+    let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+    let test = parseUri(str)
     doAssert test.scheme == "magnet"
     doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("/test/foo/bar?q=2#asdf")
+    let str = "/test/foo/bar?q=2#asdf"
+    let test = parseUri(str)
     doAssert test.scheme == ""
     doAssert test.path == "/test/foo/bar"
     doAssert test.query == "q=2"
     doAssert test.anchor == "asdf"
+    doAssert($test == str)
 
   block:
-    let test = parseUri("test/no/slash")
+    let str = "test/no/slash"
+    let test = parseUri(str)
     doAssert test.path == "test/no/slash"
+    doAssert($test == str)
 
   # Remove dot segments tests
   block:
@@ -371,5 +394,3 @@ when isMainModule:
   block:
     let test = parseUri("http://example.com/foo/") / "/bar/asd"
     doAssert test.path == "/foo/bar/asd"
-
-  
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index e0db3fba4..58587cf7f 100644
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -49,7 +49,7 @@ type
     waMarkGlobal,    # part of the backup/debug mark&sweep
     waMarkPrecise,   # part of the backup/debug mark&sweep
     waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, 
-    waCollectWhite,
+    waCollectWhite #, waDebug
 
   TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
     # A ref type can have a finalizer that is called before the object's
@@ -595,9 +595,15 @@ proc scan(s: PCell) =
     else:
       s.setColor(rcWhite)
       forAllChildren(s, waScan)
-  
+
 proc collectWhite(s: PCell) =
-  if s.color == rcWhite and s notin gch.cycleRoots:
+  # This is a hacky way to deal with the following problem (bug #1796)
+  # Consider this content in cycleRoots:
+  #   x -> a; y -> a  where 'a' is an acyclic object so not included in
+  # cycleRoots itself. Then 'collectWhite' used to free 'a' twice. The
+  # 'isAllocatedPtr' check prevents this. This also means we do not need
+  # to query 's notin gch.cycleRoots' at all.
+  if isAllocatedPtr(gch.region, s) and s.color == rcWhite:
     s.setColor(rcBlack)
     forAllChildren(s, waCollectWhite)
     freeCyclicCell(gch, s)
@@ -648,6 +654,28 @@ when useMarkForDebug or useBackupGc:
       if objStart != nil:
         markS(gch, objStart)
 
+when logGC:
+  var
+    cycleCheckA: array[100, PCell]
+    cycleCheckALen = 0
+
+  proc alreadySeen(c: PCell): bool =
+    for i in 0 .. <cycleCheckALen:
+      if cycleCheckA[i] == c: return true
+    if cycleCheckALen == len(cycleCheckA):
+      gcAssert(false, "cycle detection overflow")
+      quit 1
+    cycleCheckA[cycleCheckALen] = c
+    inc cycleCheckALen
+
+  proc debugGraph(s: PCell) =
+    if alreadySeen(s):
+      writeCell("child cell (already seen) ", s)
+    else:
+      writeCell("cell {", s)
+      forAllChildren(s, waDebug)
+      c_fprintf(c_stdout, "}\n")
+
 proc doOperation(p: pointer, op: TWalkOp) =
   if p == nil: return
   var c: PCell = usrToCell(p)
@@ -690,6 +718,7 @@ proc doOperation(p: pointer, op: TWalkOp) =
   of waMarkPrecise:
     when useMarkForDebug or useBackupGc:
       add(gch.tempStack, c)
+  #of waDebug: debugGraph(c)
 
 proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
   doOperation(d, TWalkOp(op))
@@ -702,7 +731,6 @@ when useMarkForDebug or useBackupGc:
 
 proc collectRoots(gch: var TGcHeap) =
   for s in elements(gch.cycleRoots):
-    excl(gch.cycleRoots, s)
     collectWhite(s)
 
 proc collectCycles(gch: var TGcHeap) =
diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim
index ee52b54f5..b0173b78f 100644
--- a/lib/system/gc2.nim
+++ b/lib/system/gc2.nim
@@ -1344,7 +1344,7 @@ when not defined(useNimRtl):
       else:
         dec(gch.recGcLock)
 
-  proc GC_setStrategy(strategy: TGC_Strategy) =
+  proc GC_setStrategy(strategy: GC_Strategy) =
     case strategy
     of gcThroughput: nil
     of gcResponsiveness: nil
diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim
index e091c0889..a1a0353ca 100644
--- a/lib/system/mmdisp.nim
+++ b/lib/system/mmdisp.nim
@@ -144,7 +144,7 @@ when defined(boehmgc):
     proc GC_disable() = boehmGC_disable()
     proc GC_enable() = boehmGC_enable()
     proc GC_fullCollect() = boehmGCfullCollect()
-    proc GC_setStrategy(strategy: TGC_Strategy) = discard
+    proc GC_setStrategy(strategy: GC_Strategy) = discard
     proc GC_enableMarkAndSweep() = discard
     proc GC_disableMarkAndSweep() = discard
     proc GC_getStatistics(): string = return ""
@@ -221,7 +221,7 @@ elif defined(nogc) and defined(useMalloc):
     proc GC_disable() = discard
     proc GC_enable() = discard
     proc GC_fullCollect() = discard
-    proc GC_setStrategy(strategy: TGC_Strategy) = discard
+    proc GC_setStrategy(strategy: GC_Strategy) = discard
     proc GC_enableMarkAndSweep() = discard
     proc GC_disableMarkAndSweep() = discard
     proc GC_getStatistics(): string = return ""
@@ -281,7 +281,7 @@ elif defined(nogc):
   proc GC_disable() = discard
   proc GC_enable() = discard
   proc GC_fullCollect() = discard
-  proc GC_setStrategy(strategy: TGC_Strategy) = discard
+  proc GC_setStrategy(strategy: GC_Strategy) = discard
   proc GC_enableMarkAndSweep() = discard
   proc GC_disableMarkAndSweep() = discard
   proc GC_getStatistics(): string = return ""
diff --git a/lib/wrappers/mysql.nim b/lib/wrappers/mysql.nim
index 945e09ecf..5deceb6e2 100644
--- a/lib/wrappers/mysql.nim
+++ b/lib/wrappers/mysql.nim
@@ -12,7 +12,7 @@
 
 when defined(Unix): 
   const 
-    lib = "libmysqlclient.so.15"
+    lib = "libmysqlclient.so.(15|16|17|18)"
 when defined(Windows): 
   const 
     lib = "libmysql.dll"
diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim
index f091d8f46..29fe3a921 100644
--- a/lib/wrappers/openssl.nim
+++ b/lib/wrappers/openssl.nim
@@ -50,7 +50,7 @@ when useWinVersion:
   from winlean import SocketHandle
 else:
   const
-    versions = "(|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)"
+    versions = "(.10|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.0.9.7|.0.9.6|.0.9.5|.0.9.4)"
   when defined(macosx):
     const
       DLLSSLName = "libssl" & versions & ".dylib"
diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim
index 38ee1250a..1c39f43d5 100644
--- a/tests/gc/closureleak.nim
+++ b/tests/gc/closureleak.nim
@@ -7,7 +7,7 @@ from strutils import join
 type
   TFoo * = object
     id: int
-    func: proc(){.closure.}
+    fn: proc(){.closure.}
 var foo_counter = 0
 var alive_foos = newseq[int](0)
 
@@ -26,7 +26,7 @@ for i in 0 .. <10:
 
 for i in 0 .. <10:
   let f = newFoo()
-  f.func = proc = 
+  f.fn = proc = 
     echo f.id
 
 GC_fullcollect()
diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim
new file mode 100644
index 000000000..46fed6c45
--- /dev/null
+++ b/tests/gc/cyclecollector.nim
@@ -0,0 +1,21 @@
+
+# Program to detect bug #1796 reliably
+
+type
+  Node = ref object
+    a, b: Node
+    leaf: string
+
+proc createCycle(leaf: string): Node =
+  new result
+  result.a = result
+  shallowCopy result.leaf, leaf
+
+proc main =
+  for i in 0 .. 100_000:
+    var leaf = "this is the leaf. it allocates"
+    let x = createCycle(leaf)
+    let y = createCycle(leaf)
+  echo "done ", getOccupiedMem()
+
+main()
diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim
index 27134d7dd..2213a83ac 100644
--- a/tests/gc/gctest.nim
+++ b/tests/gc/gctest.nim
@@ -196,7 +196,8 @@ write(stdout, "starting main...\n")
 main()
 
 GC_fullCollect()
+# the M&S GC fails with this call and it's unclear why. Definitely something
+# we need to fix!
 GC_fullCollect()
 writeln(stdout, GC_getStatistics())
 write(stdout, "finished\n")
-
diff --git a/tests/generics/t1050.nim b/tests/generics/t1050.nim
new file mode 100644
index 000000000..a6f9a2482
--- /dev/null
+++ b/tests/generics/t1050.nim
@@ -0,0 +1,16 @@
+discard """
+  msg: "int"
+  output: "4"
+"""
+
+import typetraits
+
+type ArrayType[T] = distinct T
+
+proc arrayItem(a: ArrayType): auto =
+  static: echo(name(type(a).T))
+  result = (type(a).T)(4)
+
+var arr: ArrayType[int]
+echo arrayItem(arr)
+
diff --git a/tests/metatype/tsemistatic.nim b/tests/metatype/tsemistatic.nim
index 0a003be03..a13175ba8 100644
--- a/tests/metatype/tsemistatic.nim
+++ b/tests/metatype/tsemistatic.nim
@@ -1,9 +1,15 @@
 discard """
   msg: "static 10\ndynamic\nstatic 20\n"
   output: "s\nd\nd\ns"
-  disabled: "true"
 """
 
+type
+  semistatic[T] =
+    static[T] or T
+
+template isStatic*(x): expr =
+  compiles(static(x))
+
 proc foo(x: semistatic[int]) =
   when isStatic(x):
     static: echo "static ", x
diff --git a/tests/metatype/tstaticparams.nim b/tests/metatype/tstaticparams.nim
index 6d7c569e0..7fc5f479b 100644
--- a/tests/metatype/tstaticparams.nim
+++ b/tests/metatype/tstaticparams.nim
@@ -1,6 +1,6 @@
 discard """
   file: "tstaticparams.nim"
-  output: "abracadabra\ntest\n3\n15\n4\n2"
+  output: "abracadabra\ntest\n3\n15\n4\n2\nfloat\n3\nfloat\nyin\nyang"
 """
 
 type 
@@ -56,3 +56,87 @@ type TTestSub[N: static[int]] = TTest[1, N]
 
 var z: TTestSub[2]
 echo z.high
+
+# issue 1049
+proc matrix_1*[M, N, T](mat: Matrix[M,N,T], a: array[N, int]) = discard
+proc matrix_2*[M, N, T](mat: Matrix[M,N,T], a: array[N+1, int]) = discard
+
+proc matrix_3*[M, N: static[int]; T](mat: Matrix[M,N,T], a: array[N, int]) = discard
+proc matrix_4*[M, N: static[int]; T](mat: Matrix[M,N,T], a: array[N+1, int]) = discard
+
+var
+  tmat: Matrix[4,4,int]
+  ar1: array[4, int]
+  ar2: array[5, int]
+
+matrix_1(tmat, ar1)
+matrix_2(tmat, ar2)
+matrix_3(tmat, ar1)
+matrix_4(tmat, ar2)
+
+template reject(x): stmt =
+  static: assert(not compiles(x))
+
+# test with arrays of wrong size
+reject matrix_1(tmat, ar2)
+reject matrix_2(tmat, ar1)
+reject matrix_3(tmat, ar2)
+reject matrix_4(tmat, ar1)
+
+# bug 1820
+
+type
+  T1820_1[T; Y: static[int]] = object
+    bar: T
+
+proc intOrFloat*[Y](f: T1820_1[int, Y]) = echo "int"
+proc intOrFloat*[Y](f: T1820_1[float, Y]) = echo "float"
+proc threeOrFour*[T](f: T1820_1[T, 3]) =  echo "3"
+proc threeOrFour*[T](f: T1820_1[T, 4]) = echo "4"
+
+var foo_1: T1820_1[float, 3]
+
+foo_1.intOrFloat
+foo_1.threeOrFour
+
+type
+  YinAndYang = enum
+    Yin,
+    Yang
+
+  T1820_2[T; Y: static[YinAndYang]] = object
+    bar: T
+
+proc intOrFloat*[Y](f: T1820_2[int, Y]) = echo "int"
+proc intOrFloat*[Y](f: T1820_2[float, Y]) = echo "float"
+proc yinOrYang*[T](f: T1820_2[T, YinAndYang.Yin]) = echo "yin"
+proc yinOrYang*[T](f: T1820_2[T, Yang]) = echo "yang"
+
+var foo_2: T1820_2[float, Yin]
+var foo_3: T1820_2[float, YinAndYang.Yang]
+
+foo_2.intOrFloat
+foo_2.yinOrYang
+foo_3.yinOrYang
+
+# bug 1859
+
+type 
+  TypeWith2Params[N, M: static[int]] = object
+
+proc bindBothParams[N](x: TypeWith2Params[N, N]) = discard
+proc dontBind1[N,M](x: TypeWith2Params[N, M]) = discard
+proc dontBind2(x: TypeWith2Params) = discard
+
+var bb_1: TypeWith2Params[2, 2]
+var bb_2: TypeWith2Params[2, 3]
+
+bindBothParams(bb_1)
+reject bindBothParams(bb_2)
+
+dontBind1 bb_1
+dontBind1 bb_2
+
+dontBind2 bb_1
+dontBind2 bb_2
+
diff --git a/tests/metatype/tusertypeclasses.nim b/tests/metatype/tusertypeclasses.nim
index 6e9e4934b..4e5e6221c 100644
--- a/tests/metatype/tusertypeclasses.nim
+++ b/tests/metatype/tusertypeclasses.nim
@@ -1,5 +1,13 @@
 discard """
-  output: "Sortable\nSortable\nContainer"
+  output: '''Sortable
+Sortable
+Container
+true
+true
+false
+false
+false
+'''
 """
 
 import typetraits
@@ -41,3 +49,20 @@ proc y(x: TObj): int = 10
 proc testFoo(x: TFoo) = discard
 testFoo(TObj(x: 10))
 
+type
+  Matrix[Rows, Cols: static[int]; T] = generic M
+    M.M == Rows
+    M.N == Cols
+    M.T is T
+
+  MyMatrix[M, N: static[int]; T] = object
+    data: array[M*N, T]
+
+var x: MyMatrix[3, 3, int]
+
+echo x is Matrix
+echo x is Matrix[3, 3, int]
+echo x is Matrix[3, 3, float]
+echo x is Matrix[4, 3, int]
+echo x is Matrix[3, 4, int]
+
diff --git a/tests/misc/tvarious.nim b/tests/misc/tvarious.nim
index 434d25e48..8124b3fc7 100644
--- a/tests/misc/tvarious.nim
+++ b/tests/misc/tvarious.nim
@@ -1,67 +1,67 @@
-# Test various aspects

+# Test various aspects
 
 # bug #572
 var a=12345678901'u64
-

+
 var x = (x: 42, y: (a: 8, z: 10))
 echo x.y
-

-import

-  mvarious

-

-type

-  PA = ref TA

-  PB = ref TB

-

-  TB = object

-    a: PA

-

-  TA = object

-    b: TB

-    x: int

-

-proc getPA(): PA =

-  var

-    b: bool

-  b = not false

-  return nil

+
+import
+  mvarious
+
+type
+  PA = ref TA
+  PB = ref TB
+
+  TB = object
+    a: PA
+
+  TA = object
+    b: TB
+    x: int
+
+proc getPA(): PA =
+  var
+    b: bool
+  b = not false
+  return nil
 
 # bug #501
 proc f(): int = 54
-

-var

-  global: int

-

-var

-  s: string

-  i: int

-  r: TA

-

-r.b.a.x = 0

-global = global + 1

-exportme()

-write(stdout, "Hallo wie heißt du? ")

-write(stdout, getPA().x)

-s = readLine(stdin)

-i = 0

-while i < s.len:

-  if s[i] == 'c': write(stdout, "'c' in deinem Namen gefunden\n")

-  i = i + 1

-

-write(stdout, "Du heißt " & s)

+
+var
+  global: int
+
+var
+  s: string
+  i: int
+  r: TA
+
+r.b.a.x = 0
+global = global + 1
+exportme()
+write(stdout, "Hallo wie heißt du? ")
+write(stdout, getPA().x)
+s = readLine(stdin)
+i = 0
+while i < s.len:
+  if s[i] == 'c': write(stdout, "'c' in deinem Namen gefunden\n")
+  i = i + 1
+
+write(stdout, "Du heißt " & s)
 
 # bug #544
-when false:
-  # yay, fails again
-  type Bar [T; I:range] = array[I, T]
-  proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] =
-    when len(a) != 3: 
-      # Error: constant expression expected
-      {.fatal:"Dimensions have to be 3".}
-    #...
-  block:
-    var a, b: Bar[int, 0..2]
-    discard foo(a, b)
+
+# yay, fails again
+type Bar [T; I:range] = array[I, T]
+proc foo*[T; I:range](a, b: Bar[T, I]): Bar[T, I] =
+  when len(a) != 3:
+    # Error: constant expression expected
+    {.fatal:"Dimensions have to be 3".}
+  #...
+block:
+  var a, b: Bar[int, range[0..2]]
+  discard foo(a, b)
 
 # bug #1788
 
diff --git a/tests/static/tstaticparammacro.nim b/tests/static/tstaticparammacro.nim
index 7fb9e2014..ebd6caa47 100644
--- a/tests/static/tstaticparammacro.nim
+++ b/tests/static/tstaticparammacro.nim
@@ -10,6 +10,9 @@ AST a
 AST b 
 (e: [55, 66], f: [77, 88])
 55
+10
+20Test
+20
 '''
 """
 
@@ -50,3 +53,22 @@ macro mB(data: static[Tb]): stmt =
 mA(a)
 mB(b)
 
+type
+  Foo[N: static[int], Z: static[string]] = object
+
+macro staticIntMacro(f: static[int]): stmt = echo f
+staticIntMacro 10
+
+var
+  x: Foo[20, "Test"]
+
+macro genericMacro[N; Z: static[string]](f: Foo[N, Z], ll = 3, zz = 12): stmt =
+  echo N, Z
+
+genericMacro x
+
+template genericTemplate[N, Z](f: Foo[N, Z], ll = 3, zz = 12): int = N
+
+static:
+  echo genericTemplate(x) # Error: internal error: (filename: compiler/evaltempl.nim, line: 39)
+
diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim
index ae9905cde..7c7d71aa2 100644
--- a/tests/testament/categories.nim
+++ b/tests/testament/categories.nim
@@ -107,12 +107,15 @@ proc dllTests(r: var TResults, cat: Category, options: string) =
 # ------------------------------ GC tests -------------------------------------
 
 proc gcTests(r: var TResults, cat: Category, options: string) =
-  template test(filename: expr): stmt =
+  template testWithoutMs(filename: expr): stmt =
     testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun)
     testSpec r, makeTest("tests/gc" / filename, options &
                   " -d:release", cat, actionRun)
     testSpec r, makeTest("tests/gc" / filename, options &
                   " -d:release -d:useRealtimeGC", cat, actionRun)
+
+  template test(filename: expr): stmt =
+    testWithoutMs filename
     testSpec r, makeTest("tests/gc" / filename, options &
                   " --gc:markAndSweep", cat, actionRun)
     testSpec r, makeTest("tests/gc" / filename, options &
@@ -124,13 +127,15 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
   test "gctest"
   test "gcleak3"
   test "gcleak4"
-  test "gcleak5"
+  # Disabled because it works and takes too long to run:
+  #test "gcleak5"
   test "weakrefs"
   test "cycleleak"
   test "closureleak"
-  test "refarrayleak"
-  test "stackrefleak"
+  testWithoutMs "refarrayleak"
   
+  test "stackrefleak"
+  test "cyclecollector"
 
 # ------------------------- threading tests -----------------------------------
 
diff --git a/tools/niminst/nsis.tmpl b/tools/niminst/nsis.tmpl
index 40e171d41..23bbf3ac9 100644
--- a/tools/niminst/nsis.tmpl
+++ b/tools/niminst/nsis.tmpl
@@ -128,7 +128,7 @@
     ; Write application registry keys
     WriteRegStr HKCU "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\bin\?{c.name}.exe"
     WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
-    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
+    WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninstaller.exe"
     WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\?{c.name}.exe"
     WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
     WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
diff --git a/tools/nimweb.nim b/tools/nimweb.nim
index 0596076b3..91aa17e7e 100644
--- a/tools/nimweb.nim
+++ b/tools/nimweb.nim
@@ -23,6 +23,7 @@ type
     gitCommit: string
     quotations: Table[string, tuple[quote, author: string]]
     numProcessors: int # Set by parallelBuild:n, only works for values > 0.
+    gaId: string  # google analytics ID, nil means analytics are disabled
   TRssItem = object
     year, month, day, title: string
   TAction = enum
@@ -144,7 +145,12 @@ proc parseCmdLine(c: var TConfigData) =
         c.vars[substr(val, 0, idx-1)] = substr(val, idx+1)
       of "website": action = actOnlyWebsite
       of "pdf": action = actPdf
-      else: quit(usage)
+      of "googleanalytics":
+        c.gaId = val
+        c.nimArgs.add("--doc.googleAnalytics:" & val & " ")
+      else:
+        echo("Invalid argument $1" % [key])
+        quit(usage)
     of cmdEnd: break
   if c.infile.len == 0: quit(usage)
 
diff --git a/tools/website.tmpl b/tools/website.tmpl
index fed52ac15..0c059fb87 100644
--- a/tools/website.tmpl
+++ b/tools/website.tmpl
@@ -192,13 +192,15 @@ View at: localhost:5000
 		</footer>

 

   <script>
+#  if c.gaId != nil:
     (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
     (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
     m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
     })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
 
-    ga('create', 'UA-48159761-1', 'nim-lang.org');
+    ga('create', '${c.gaId}', 'nim-lang.org');
     ga('send', 'pageview');
+#  end if
 
 var timer;
 var prevIndex = 0;
diff --git a/web/index.txt b/web/index.txt
index b6d4f8e8f..95cac9316 100644
--- a/web/index.txt
+++ b/web/index.txt
@@ -5,16 +5,16 @@ Home
 Welcome to Nim
 --------------
 
-**Nim** (formerly known as "Nimrod") is a statically typed, imperative 
-programming language that tries to give the programmer ultimate power without 
+**Nim** (formerly known as "Nimrod") is a statically typed, imperative
+programming language that tries to give the programmer ultimate power without
 compromises on runtime efficiency. This means it focuses on compile-time
 mechanisms in all their various forms.
 
-Beneath a nice infix/indentation based syntax with a 
-powerful (AST based, hygienic) macro system lies a semantic model that supports 
-a soft realtime GC on thread local heaps. Asynchronous message passing is used 
-between threads, so no "stop the world" mechanism is necessary. An unsafe 
-shared memory heap is also provided for the increased efficiency that results 
+Beneath a nice infix/indentation based syntax with a
+powerful (AST based, hygienic) macro system lies a semantic model that supports
+a soft realtime GC on thread local heaps. Asynchronous message passing is used
+between threads, so no "stop the world" mechanism is necessary. An unsafe
+shared memory heap is also provided for the increased efficiency that results
 from that model.
 
 
@@ -24,7 +24,7 @@ Nim is efficient
 * Native code generation (currently via compilation to C), not dependent on a
   virtual machine: **Nim produces small executables without dependencies
   for easy redistribution.**
-* A fast **non-tracing** garbage collector that supports soft 
+* A fast **non-tracing** garbage collector that supports soft
   real-time systems (like games).
 * System programming features: Ability to manage your own memory and access the
   hardware directly. Pointers to garbage collected memory are distinguished
@@ -33,22 +33,22 @@ Nim is efficient
 * Cross-module inlining.
 * Dynamic method binding with inlining and without virtual method table.
 * Compile time evaluation of user-defined functions.
-* Whole program dead code elimination: Only *used functions* are included in 
+* Whole program dead code elimination: Only *used functions* are included in
   the executable.
-* Value-based datatypes: For instance, objects and arrays can be allocated on 
+* Value-based datatypes: For instance, objects and arrays can be allocated on
   the stack.
 
 
 Nim is expressive
 =================
 
-* **The Nim compiler and all of the standard library are implemented in 
+* **The Nim compiler and all of the standard libraries are implemented in
   Nim.**
 * Built-in high level datatypes: strings, sets, sequences, etc.
-* Modern type system with local type inference, tuples, variants, 
+* Modern type system with local type inference, tuples, variants,
   generics, etc.
 * User-defineable operators; code with new operators is often easier to read
-  than code which overloads built-in operators. For example, a 
+  than code which overloads built-in operators. For example, a
   ``=~`` operator is defined in the ``re`` module.
 * Macros can modify the abstract syntax tree at compile time.
 
@@ -58,7 +58,7 @@ Nim is elegant
 
 * Macros can use the imperative paradigm to construct parse trees. Nim
   does not require a different coding style for meta programming.
-* Macros cannot change Nim's syntax because there is no need for it. 
+* Macros cannot change Nim's syntax because there is no need for it.
   Nim's syntax is flexible enough.
 * Statements are grouped by indentation but can span multiple lines.
   Indentation must not contain tabulators so the compiler always sees
@@ -72,12 +72,12 @@ Nim plays nice with others
   Porting to other platforms is easy.
 * **The Nim Compiler can also generate C++ or Objective C for easier
   interfacing.**
-* There are lots of bindings: for example, bindings to GTK2, the Windows API, 
-  the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE, 
+* There are lots of bindings: for example, bindings to GTK2, the Windows API,
+  the POSIX API, OpenGL, SDL, Cairo, Python, Lua, TCL, X11, libzip, PCRE,
   libcurl, mySQL and SQLite are included in the standard distribution or
   can easily be obtained via the
   `Nimble package manager <https://github.com/nim-lang/nimble>`_.
-* A C to Nim conversion utility: New bindings to C libraries are easily 
+* A C to Nim conversion utility: New bindings to C libraries are easily
   generated by ``c2nim``.
 
 
diff --git a/web/question.txt b/web/question.txt
index d3a2dd5c0..c27155858 100644
--- a/web/question.txt
+++ b/web/question.txt
@@ -110,6 +110,7 @@ General
   - Scite: Included
   - Gedit: The `Aporia .lang file <https://github.com/nimrod-code/Aporia/blob/master/share/gtksourceview-2.0/language-specs/nimrod.lang>`_
   - jEdit: https://github.com/exhu/nimrod-misc/tree/master/jedit
+  - TextMate: Available in bundle installer (`Repository <https://github.com/textmate/nim.tmbundle>`_)
 
 
 .. container:: standout