summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2017-05-17 22:13:57 +0200
committerAraq <rumpf_a@web.de>2017-05-17 22:13:57 +0200
commit8f67b90997d1c0416c4991ecec666a63339103c2 (patch)
tree45053004503995b050d04620705df209df8f2af7
parent68573630340f60f452d5f206da37017e47bd8f6e (diff)
parentc8cec27c0a503d6703536d54b9cc48684dc69086 (diff)
downloadNim-8f67b90997d1c0416c4991ecec666a63339103c2.tar.gz
Merge branch 'araq2' into devel
-rw-r--r--compiler/ast.nim22
-rw-r--r--compiler/astalgo.nim4
-rw-r--r--compiler/ccgexprs.nim6
-rw-r--r--compiler/ccgtypes.nim7
-rw-r--r--compiler/condsyms.nim1
-rw-r--r--compiler/evaltempl.nim2
-rw-r--r--compiler/msgs.nim2
-rw-r--r--compiler/parser.nim172
-rw-r--r--compiler/pbraces.nim5
-rw-r--r--compiler/sem.nim27
-rw-r--r--compiler/semcall.nim2
-rw-r--r--compiler/semdata.nim4
-rw-r--r--compiler/semexprs.nim66
-rw-r--r--compiler/semfields.nim5
-rw-r--r--compiler/seminst.nim3
-rw-r--r--compiler/semmagic.nim48
-rw-r--r--compiler/semstmts.nim126
-rw-r--r--compiler/semtypes.nim38
-rw-r--r--compiler/semtypinst.nim70
-rw-r--r--compiler/sighashes.nim12
-rw-r--r--compiler/sigmatch.nim334
-rw-r--r--compiler/types.nim6
-rw-r--r--compiler/vm.nim7
-rw-r--r--doc/manual/type_rel.txt101
-rw-r--r--lib/js/jsffi.nim67
-rw-r--r--lib/system.nim17
-rw-r--r--tests/clearmsg/tmacroerrorproc.nim6
-rw-r--r--tests/closure/tdonotation.nim45
-rw-r--r--tests/concepts/t5642.nim25
-rw-r--r--tests/concepts/tconceptinclosure.nim53
-rw-r--r--tests/concepts/tmisc_issues.nim2
-rw-r--r--tests/concepts/trandom_vars.nim42
-rw-r--r--tests/cpp/tcovariancerules.nim424
-rw-r--r--tests/errmsgs/tinvalidinout.nim26
-rw-r--r--tests/generics/t5570.nim27
-rw-r--r--tests/generics/t5602_inheritence.nim18
-rw-r--r--tests/generics/t5643.nim30
-rw-r--r--tests/generics/t5683.nim31
-rw-r--r--tests/generics/tbindoncevsbindmany.nim68
-rw-r--r--tests/generics/tfakecovariance.nim78
-rw-r--r--tests/generics/tgenericconst.nim39
-rw-r--r--tests/generics/tmapping_generic_alias.nim28
-rw-r--r--tests/generics/tparam_binding.nim28
-rw-r--r--tests/generics/tptrinheritance.nim20
-rw-r--r--tests/js/tjsffi.nim62
-rw-r--r--tests/macros/tlexerex.nim5
-rw-r--r--tests/parser/tpostexprblocks.nim513
-rw-r--r--tests/stdlib/tmarshal.nim24
-rw-r--r--tests/template/tgenerictemplates.nim13
-rw-r--r--tests/template/tgensymregression.nim3
-rw-r--r--tests/types/t5640.nim6
-rw-r--r--tests/types/t5648.nim21
-rw-r--r--tests/types/thard_tyforward.nim22
-rw-r--r--web/bountysource.nim4
54 files changed, 2478 insertions, 339 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 49ca1c5e0..63b656715 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -224,7 +224,7 @@ type
   TNodeKinds* = set[TNodeKind]
 
 type
-  TSymFlag* = enum    # already 32 flags!
+  TSymFlag* = enum    # already 33 flags!
     sfUsed,           # read access of sym (for warnings) or simply used
     sfExported,       # symbol is exported from module
     sfFromGeneric,    # symbol is instantiation of a generic; this is needed
@@ -452,10 +452,12 @@ type
     nfExprCall  # this is an attempt to call a regular expression
     nfIsRef     # this node is a 'ref' node; used for the VM
     nfPreventCg # this node should be ignored by the codegen
+    nfBlockArg  # this a stmtlist appearing in a call (e.g. a do block)
 
   TNodeFlags* = set[TNodeFlag]
-  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: 30)
+  TTypeFlag* = enum   # keep below 32 for efficiency reasons (now: beyond that)
     tfVarargs,        # procedure has C styled varargs
+                      # tyArray type represeting a varargs list
     tfNoSideEffect,   # procedure type does not allow side effects
     tfFinal,          # is the object final?
     tfInheritable,    # is the object inheritable?
@@ -506,6 +508,9 @@ type
     tfTriggersCompileTime # uses the NimNode type which make the proc
                           # implicitly '.compiletime'
     tfRefsAnonObj     # used for 'ref object' and 'ptr object'
+    tfCovariant       # covariant generic param mimicing a ptr type
+    tfWeakCovariant   # covariant generic param mimicing a seq/array type
+    tfContravariant   # contravariant generic param
 
   TTypeFlags* = set[TTypeFlag]
 
@@ -1002,15 +1007,17 @@ proc add*(father, son: PNode) =
   if isNil(father.sons): father.sons = @[]
   add(father.sons, son)
 
-proc `[]`*(n: PNode, i: int): PNode {.inline.} =
-  result = n.sons[i]
+type Indexable = PNode | PType
+
+template `[]`*(n: Indexable, i: int): Indexable =
+  n.sons[i]
 
 template `-|`*(b, s: untyped): untyped =
   (if b >= 0: b else: s.len + b)
 
 # son access operators with support for negative indices
-template `{}`*(n: PNode, i: int): untyped = n[i -| n]
-template `{}=`*(n: PNode, i: int, s: PNode) =
+template `{}`*(n: Indexable, i: int): untyped = n[i -| n]
+template `{}=`*(n: Indexable, i: int, s: Indexable) =
   n.sons[i -| n] = s
 
 when defined(useNodeIds):
@@ -1346,6 +1353,9 @@ proc initIdTable*(x: var TIdTable) =
   x.counter = 0
   newSeq(x.data, StartSize)
 
+proc newIdTable*: TIdTable =
+  initIdTable(result)
+
 proc resetIdTable*(x: var TIdTable) =
   x.counter = 0
   # clear and set to old initial size:
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim
index 77108eb7b..1fc8b7cea 100644
--- a/compiler/astalgo.nim
+++ b/compiler/astalgo.nim
@@ -70,6 +70,8 @@ proc debug*(n: PNode) {.deprecated.}
 template mdbg*: bool {.dirty.} =
   when compiles(c.module):
     c.module.fileIdx == gProjectMainIdx
+  elif compiles(c.c.module):
+    c.c.module.fileIdx == gProjectMainIdx
   elif compiles(m.c.module):
     m.c.module.fileIdx == gProjectMainIdx
   elif compiles(cl.c.module):
@@ -79,6 +81,8 @@ template mdbg*: bool {.dirty.} =
       p.lex.fileIdx == gProjectMainIdx
     else:
       p.module.module.fileIdx == gProjectMainIdx
+  elif compiles(m.module.fileIdx):
+    m.module.fileIdx == gProjectMainIdx
   elif compiles(L.fileIdx):
     L.fileIdx == gProjectMainIdx
   else:
diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index d244153db..f5b99c405 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -269,7 +269,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) =
     # little HACK to support the new 'var T' as return type:
     linefmt(p, cpsStmts, "$1 = $2;$n", rdLoc(dest), rdLoc(src))
     return
-  let ty = skipTypes(dest.t, abstractRange)
+  let ty = skipTypes(dest.t, abstractRange + tyUserTypeClasses)
   case ty.kind
   of tyRef:
     genRefAssign(p, dest, src, flags)
@@ -758,7 +758,7 @@ proc genRecordField(p: BProc, e: PNode, d: var TLoc) =
   genRecordFieldAux(p, e, d, a)
   var r = rdLoc(a)
   var f = e.sons[1].sym
-  let ty = skipTypes(a.t, abstractInst)
+  let ty = skipTypes(a.t, abstractInst + tyUserTypeClasses)
   if ty.kind == tyTuple:
     # we found a unique tuple type which lacks field information
     # so we use Field$i
@@ -2205,7 +2205,7 @@ proc getNullValueAux(p: BProc; obj, cons: PNode, result: var Rope) =
     let field = obj.sym
     for i in 1..<cons.len:
       if cons[i].kind == nkExprColonExpr:
-        if cons[i][0].sym == field:
+        if cons[i][0].sym.name == field.name:
           result.add genConstExpr(p, cons[i][1])
           return
       elif i == field.position:
diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim
index 2b7cfaea1..02eb5ba82 100644
--- a/compiler/ccgtypes.nim
+++ b/compiler/ccgtypes.nim
@@ -164,7 +164,7 @@ proc mapType(typ: PType): TCTypeKind =
   of tySet: result = mapSetType(typ)
   of tyOpenArray, tyArray, tyVarargs: result = ctArray
   of tyObject, tyTuple: result = ctStruct
-  of tyUserTypeClass, tyUserTypeClassInst:
+  of tyUserTypeClasses:
     internalAssert typ.isResolvedUserTypeClass
     return mapType(typ.lastSon)
   of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal,
@@ -1094,7 +1094,7 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) =
 
 proc genTypeInfo(m: BModule, t: PType): Rope =
   let origType = t
-  var t = skipTypes(origType, irrelevantForBackend)
+  var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses)
 
   let sig = hashType(origType)
   result = m.typeInfoMarker.getOrDefault(sig)
@@ -1131,6 +1131,9 @@ proc genTypeInfo(m: BModule, t: PType): Rope =
   of tyStatic:
     if t.n != nil: result = genTypeInfo(m, lastSon t)
     else: internalError("genTypeInfo(" & $t.kind & ')')
+  of tyUserTypeClasses:
+    internalAssert t.isResolvedUserTypeClass
+    return genTypeInfo(m, t.lastSon)
   of tyProc:
     if t.callConv != ccClosure:
       genTypeInfoAuxBase(m, t, t, result, rope"0")
diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim
index 4303fd6c8..a4f47ac72 100644
--- a/compiler/condsyms.nim
+++ b/compiler/condsyms.nim
@@ -103,3 +103,4 @@ proc initDefines*() =
   defineSymbol("nimNewShiftOps")
   defineSymbol("nimDistros")
   defineSymbol("nimHasCppDefine")
+  defineSymbol("nimGenericInOutFlags")
diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim
index 5bd274a3e..fda0b79dd 100644
--- a/compiler/evaltempl.nim
+++ b/compiler/evaltempl.nim
@@ -90,7 +90,7 @@ proc evalTemplateArgs(n: PNode, s: PSym; fromHlo: bool): PNode =
 
   result = newNodeI(nkArgList, n.info)
   for i in 1 .. givenRegularParams:
-    result.addSon n.sons[i]
+    result.addSon n[i]
 
   # handle parameters with default values, which were
   # not supplied by the user
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 3a97f1ed2..e416a3826 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -113,6 +113,7 @@ type
     errGenericLambdaNotAllowed,
     errProcHasNoConcreteType,
     errCompilerDoesntSupportTarget,
+    errInOutFlagNotExtern,
     errUser,
     warnCannotOpenFile,
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit,
@@ -380,6 +381,7 @@ const
                                 "of the generic paramers can be inferred from the expected signature.",
     errProcHasNoConcreteType: "'$1' doesn't have a concrete type, due to unspecified generic parameters.",
     errCompilerDoesntSupportTarget: "The current compiler \'$1\' doesn't support the requested compilation target",
+    errInOutFlagNotExtern: "The `$1` modifier can be used only with imported types",
     errUser: "$1",
     warnCannotOpenFile: "cannot open \'$1\'",
     warnOctalEscape: "octal escape sequences do not exist; leading zero is ignored",
diff --git a/compiler/parser.nim b/compiler/parser.nim
index fabe4bcc8..d4c44788e 100644
--- a/compiler/parser.nim
+++ b/compiler/parser.nim
@@ -67,6 +67,8 @@ proc parseTry(p: var TParser; isExpr: bool): PNode
 proc parseCase(p: var TParser): PNode
 proc parseStmtPragma(p: var TParser): PNode
 proc parsePragma(p: var TParser): PNode
+proc postExprBlocks(p: var TParser, x: PNode): PNode
+proc parseExprStmt(p: var TParser): PNode
 # implementation
 
 proc getTok(p: var TParser) =
@@ -194,7 +196,6 @@ proc newIdentNodeP(ident: PIdent, p: TParser): PNode =
 proc parseExpr(p: var TParser): PNode
 proc parseStmt(p: var TParser): PNode
 proc parseTypeDesc(p: var TParser): PNode
-proc parseDoBlocks(p: var TParser, call: PNode)
 proc parseParamList(p: var TParser, retColon = true): PNode
 
 proc isSigilLike(tok: TToken): bool {.inline.} =
@@ -365,7 +366,10 @@ proc colonOrEquals(p: var TParser, a: PNode): PNode =
 proc exprColonEqExpr(p: var TParser): PNode =
   #| exprColonEqExpr = expr (':'|'=' expr)?
   var a = parseExpr(p)
-  result = colonOrEquals(p, a)
+  if p.tok.tokType == tkDo:
+    result = postExprBlocks(p, a)
+  else:
+    result = colonOrEquals(p, a)
 
 proc exprList(p: var TParser, endTok: TTokType, result: PNode) =
   #| exprList = expr ^+ comma
@@ -520,7 +524,9 @@ proc parsePar(p: var TParser): PNode =
     result.add(parseStmtPragma(p))
   elif p.tok.tokType != tkParRi:
     var a = simpleExpr(p)
-    if p.tok.tokType == tkEquals:
+    if p.tok.tokType == tkDo:
+      result = postExprBlocks(p, a)
+    elif p.tok.tokType == tkEquals:
       # special case: allow assignments
       let asgn = newNodeP(nkAsgn, p)
       getTok(p)
@@ -669,7 +675,6 @@ proc namedParams(p: var TParser, callee: PNode,
   # progress guaranteed
   exprColonEqExprListAux(p, endTok, result)
 
-proc parseMacroColon(p: var TParser, x: PNode): PNode
 proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
   #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' doBlocks?
   #|       | doBlocks
@@ -696,14 +701,6 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
       result = namedParams(p, result, nkCall, tkParRi)
       if result.len > 1 and result.sons[1].kind == nkExprColonExpr:
         result.kind = nkObjConstr
-      elif p.tok.tokType == tkDo:
-        parseDoBlocks(p, result)
-    of tkDo:
-      # progress guaranteed
-      var a = result
-      result = newNodeP(nkCall, p)
-      addSon(result, a)
-      parseDoBlocks(p, result)
     of tkDot:
       # progress guaranteed
       result = dotExpr(p, result)
@@ -735,10 +732,7 @@ proc primarySuffix(p: var TParser, r: PNode, baseIndent: int): PNode =
             if p.tok.tokType != tkComma: break
             getTok(p)
             optInd(p, x)
-          if p.tok.tokType == tkDo:
-            parseDoBlocks(p, result)
-          else:
-            result = parseMacroColon(p, result)
+          result = postExprBlocks(p, result)
       break
     else:
       break
@@ -977,16 +971,9 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode =
   let params = parseParamList(p, retColon=false)
   let pragmas = optPragmas(p)
   colcom(p, result)
-  result = newProcNode(nkDo, info, parseStmt(p),
-                       params = params,
-                       pragmas = pragmas)
-
-proc parseDoBlocks(p: var TParser, call: PNode) =
-  #| doBlocks = doBlock ^* IND{=}
-  while sameOrNoInd(p) and p.tok.tokType == tkDo:
-    let info = parLineInfo(p)
-    getTok(p)
-    addSon(call, parseDoBlock(p, info))
+  result = parseStmt(p)
+  if params.kind != nkEmpty:
+    result = newProcNode(nkDo, info, result, params = params, pragmas = pragmas)
 
 proc parseProcExpr(p: var TParser, isExpr: bool): PNode =
   #| procExpr = 'proc' paramListColon pragmas? ('=' COMMENT? stmt)?
@@ -1162,49 +1149,75 @@ proc makeCall(n: PNode): PNode =
     result = newNodeI(nkCall, n.info)
     result.add n
 
-proc parseMacroColon(p: var TParser, x: PNode): PNode =
-  #| macroColon = ':' stmt? ( IND{=} 'of' exprList ':' stmt
-  #|                        | IND{=} 'elif' expr ':' stmt
-  #|                        | IND{=} 'except' exprList ':' stmt
-  #|                        | IND{=} 'else' ':' stmt )*
+proc postExprBlocks(p: var TParser, x: PNode): PNode =
+  #| postExprBlocks = ':' stmt? ( IND{=} doBlock
+  #|                            | IND{=} 'of' exprList ':' stmt
+  #|                            | IND{=} 'elif' expr ':' stmt
+  #|                            | IND{=} 'except' exprList ':' stmt
+  #|                            | IND{=} 'else' ':' stmt )*
   result = x
-  if p.tok.tokType == tkColon and p.tok.indent < 0:
+  if p.tok.indent >= 0: return
+
+  var
+    openingParams = emptyNode
+    openingPragmas = emptyNode
+
+  if p.tok.tokType == tkDo:
+    getTok(p)
+    openingParams = parseParamList(p, retColon=false)
+    openingPragmas = optPragmas(p)
+
+  if p.tok.tokType == tkColon:
     result = makeCall(result)
     getTok(p)
     skipComment(p, result)
-    let stmtList = newNodeP(nkStmtList, p)
     if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}:
-      let body = parseStmt(p)
-      stmtList.add body
-      #addSon(result, makeStmtList(body))
-    # progress guaranteed
+      var stmtList = newNodeP(nkStmtList, p)
+      stmtList.add parseStmt(p)
+      # to keep backwards compatibility (see tests/vm/tstringnil)
+      if stmtList[0].kind == nkStmtList: stmtList = stmtList[0]
+
+      stmtList.flags.incl nfBlockArg
+      if openingParams.kind != nkEmpty:
+        result.add newProcNode(nkDo, stmtList.info, stmtList,
+                               params = openingParams, pragmas = openingPragmas)
+      else:
+        result.add stmtList
+
     while sameInd(p):
-      var b: PNode
-      case p.tok.tokType
-      of tkOf:
-        b = newNodeP(nkOfBranch, p)
-        exprList(p, tkColon, b)
-      of tkElif:
-        b = newNodeP(nkElifBranch, p)
-        getTok(p)
-        optInd(p, b)
-        addSon(b, parseExpr(p))
-      of tkExcept:
-        b = newNodeP(nkExceptBranch, p)
-        exprList(p, tkColon, b)
-      of tkElse:
-        b = newNodeP(nkElse, p)
+      var nextBlock: PNode
+      let nextToken = p.tok.tokType
+      if nextToken == tkDo:
+        let info = parLineInfo(p)
         getTok(p)
-      else: break
-      eat(p, tkColon)
-      addSon(b, parseStmt(p))
-      addSon(stmtList, b)
-      if b.kind == nkElse: break
-    if stmtList.len == 1 and stmtList[0].kind == nkStmtList:
-      # to keep backwards compatibility (see tests/vm/tstringnil)
-      result.add stmtList[0]
-    else:
-      result.add stmtList
+        nextBlock = parseDoBlock(p, info)
+      else:
+        case nextToken:
+        of tkOf:
+          nextBlock = newNodeP(nkOfBranch, p)
+          exprList(p, tkColon, nextBlock)
+        of tkElif:
+          nextBlock = newNodeP(nkElifBranch, p)
+          getTok(p)
+          optInd(p, nextBlock)
+          nextBlock.addSon parseExpr(p)
+        of tkExcept:
+          nextBlock = newNodeP(nkExceptBranch, p)
+          exprList(p, tkColon, nextBlock)
+        of tkElse:
+          nextBlock = newNodeP(nkElse, p)
+          getTok(p)
+        else: break
+        eat(p, tkColon)
+        nextBlock.addSon parseStmt(p)
+
+      nextBlock.flags.incl nfBlockArg
+      result.add nextBlock
+
+      if nextBlock.kind == nkElse: break
+  else:
+    if openingParams.kind != nkEmpty:
+      parMessage(p, errTokenExpected, ":")
 
 proc parseExprStmt(p: var TParser): PNode =
   #| exprStmt = simpleExpr
@@ -1219,12 +1232,7 @@ proc parseExprStmt(p: var TParser): PNode =
     getTok(p)
     optInd(p, result)
     var b = parseExpr(p)
-    if p.tok.tokType == tkColon and p.tok.indent < 0:
-      if b.kind != nkEmpty:
-        let call = makeCall(b)
-        call.add parseDoBlock(p, parLineInfo(p))
-        parseDoBlocks(p, call)
-        b = call
+    b = postExprBlocks(p, b)
     addSon(result, a)
     addSon(result, b)
   else:
@@ -1247,11 +1255,7 @@ proc parseExprStmt(p: var TParser): PNode =
         optInd(p, result)
     else:
       result = a
-    if p.tok.tokType == tkDo and p.tok.indent < 0:
-      result = makeCall(result)
-      parseDoBlocks(p, result)
-      return result
-    result = parseMacroColon(p, result)
+    result = postExprBlocks(p, result)
 
 proc parseModuleName(p: var TParser, kind: TNodeKind): PNode =
   result = parseExpr(p)
@@ -1341,7 +1345,9 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode =
     # NL terminates:
     addSon(result, ast.emptyNode)
   else:
-    addSon(result, parseExpr(p))
+    var e = parseExpr(p)
+    e = postExprBlocks(p, e)
+    addSon(result, e)
 
 proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode =
   #| condStmt = expr colcom stmt COMMENT?
@@ -1514,6 +1520,13 @@ proc parseGenericParam(p: var TParser): PNode =
   # progress guaranteed
   while true:
     case p.tok.tokType
+    of tkIn, tkOut:
+      let x = p.lex.cache.getIdent(if p.tok.tokType == tkIn: "in" else: "out")
+      a = newNodeP(nkPrefix, p)
+      a.addSon newIdentNodeP(x, p)
+      getTok(p)
+      expectIdent(p)
+      a.addSon(parseSymbol(p))
     of tkSymbol, tkAccent:
       a = parseSymbol(p)
       if a.kind == nkEmpty: return
@@ -1542,7 +1555,7 @@ proc parseGenericParamList(p: var TParser): PNode =
   getTok(p)
   optInd(p, result)
   # progress guaranteed
-  while p.tok.tokType in {tkSymbol, tkAccent}:
+  while p.tok.tokType in {tkSymbol, tkAccent, tkIn, tkOut}:
     var a = parseGenericParam(p)
     addSon(result, a)
     if p.tok.tokType notin {tkComma, tkSemiColon}: break
@@ -1893,14 +1906,7 @@ proc parseVariable(p: var TParser): PNode =
   #| variable = (varTuple / identColonEquals) colonBody? indAndComment
   if p.tok.tokType == tkParLe: result = parseVarTuple(p)
   else: result = parseIdentColonEquals(p, {withPragma})
-  if p.tok.tokType == tkColon and p.tok.indent < 0:
-    let last = result.len-1
-    let ex = result.sons[last]
-    if ex.kind != nkEmpty:
-      let call = makeCall(ex)
-      call.add parseDoBlock(p, parLineInfo(p))
-      parseDoBlocks(p, call)
-      result.sons[last] = call
+  result{-1} = postExprBlocks(p, result{-1})
   indAndComment(p, result)
 
 proc parseBind(p: var TParser, k: TNodeKind): PNode =
diff --git a/compiler/pbraces.nim b/compiler/pbraces.nim
index b59fbc6cf..fa4ccc139 100644
--- a/compiler/pbraces.nim
+++ b/compiler/pbraces.nim
@@ -1336,6 +1336,11 @@ proc parseGenericParam(p: var TParser): PNode =
   result = newNodeP(nkIdentDefs, p)
   while true:
     case p.tok.tokType
+    of tkIn, tkOut:
+      let t = p.tok.tokType
+      getTok(p)
+      expectIdent(p)
+      a = parseSymbol(p)
     of tkSymbol, tkAccent:
       a = parseSymbol(p)
       if a.kind == nkEmpty: return
diff --git a/compiler/sem.nim b/compiler/sem.nim
index ab3c5cec8..9f80e1399 100644
--- a/compiler/sem.nim
+++ b/compiler/sem.nim
@@ -351,8 +351,8 @@ when false:
     for i in 0 ..< n.safeLen:
       resetSemFlag(n[i])
 
-proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
-                       flags: TExprFlags): PNode =
+proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
+                       s: PSym, flags: TExprFlags): PNode =
   ## Semantically check the output of a macro.
   ## This involves processes such as re-checking the macro output for type
   ## coherence, making sure that variables declared with 'let' aren't
@@ -363,8 +363,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
     globalError(s.info, errTemplateInstantiationTooNested)
   c.friendModules.add(s.owner.getModule)
 
-  result = n
-  excl(n.flags, nfSem)
+  result = macroResult
+  excl(result.flags, nfSem)
   #resetSemFlag n
   if s.typ.sons[0] == nil:
     result = semStmt(c, result)
@@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
     of tyStmt:
       result = semStmt(c, result)
     of tyTypeDesc:
-      if n.kind == nkStmtList: result.kind = nkStmtListType
+      if result.kind == nkStmtList: result.kind = nkStmtListType
       var typ = semTypeNode(c, result, nil)
       result.typ = makeTypeDesc(c, typ)
       #result = symNodeFromType(c, typ, n.info)
     else:
+      var retType = s.typ.sons[0]
+      if s.ast[genericParamsPos] != nil and retType.isMetaType:
+        # The return type may depend on the Macro arguments
+        # e.g. template foo(T: typedesc): seq[T]
+        # We will instantiate the return type here, because
+        # we now know the supplied arguments
+        var paramTypes = newIdTable()
+        for param, value in genericParamsInMacroCall(s, call):
+          idTablePut(paramTypes, param.typ, value.typ)
+
+        retType = generateTypeInstance(c, paramTypes,
+                                       macroResult.info, retType)
+
       result = semExpr(c, result, flags)
-      result = fitNode(c, s.typ.sons[0], result, result.info)
+      result = fitNode(c, retType, result, result.info)
       #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
   dec(evalTemplateCounter)
   discard c.friendModules.pop()
@@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
   #  c.evalContext = c.createEvalContext(emStatic)
   result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
   if efNoSemCheck notin flags:
-    result = semAfterMacroCall(c, result, sym, flags)
+    result = semAfterMacroCall(c, n, result, sym, flags)
   result = wrapInComesFrom(nOrig.info, result)
   popInfoContext()
 
diff --git a/compiler/semcall.nim b/compiler/semcall.nim
index 1089ab7db..4fa4f7f32 100644
--- a/compiler/semcall.nim
+++ b/compiler/semcall.nim
@@ -429,7 +429,7 @@ proc explicitGenericSym(c: PContext, n: PNode, s: PSym): PNode =
   for i in 1..sonsLen(n)-1:
     let formal = s.ast.sons[genericParamsPos].sons[i-1].typ
     let arg = n[i].typ
-    let tm = typeRel(m, formal, arg, true)
+    let tm = typeRel(m, formal, arg)
     if tm in {isNone, isConvertible}: return nil
   var newInst = generateInstance(c, s, m.bindings, n.info)
   newInst.typ.flags.excl tfUnresolved
diff --git a/compiler/semdata.nim b/compiler/semdata.nim
index 8f2c802de..5da2b70fa 100644
--- a/compiler/semdata.nim
+++ b/compiler/semdata.nim
@@ -277,6 +277,10 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType =
   assert n != nil
   result.n = n
 
+proc newTypeWithSons2*(kind: TTypeKind, owner: PSym, sons: seq[PType]): PType =
+  result = newType(kind, owner)
+  result.sons = sons
+
 proc newTypeWithSons*(c: PContext, kind: TTypeKind,
                       sons: seq[PType]): PType =
   result = newType(kind, getCurrOwner(c))
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 25f62983d..59fa208d2 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
   styleCheckUse(n.info, s)
   pushInfoContext(n.info)
   result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
-  if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags)
+  if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
   popInfoContext()
 
 proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode
@@ -47,10 +47,11 @@ proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
     #raiseRecoverableError("")
     result = errorNode(c, n)
   if result.typ == nil or result.typ == enforceVoidContext:
-    # we cannot check for 'void' in macros ...
-    localError(n.info, errExprXHasNoType,
-               renderTree(result, {renderNoComments}))
-    result.typ = errorType(c)
+    if n.kind != nkStmtList:
+      # we cannot check for 'void' in macros ...
+      localError(n.info, errExprXHasNoType,
+                 renderTree(result, {renderNoComments}))
+      result.typ = errorType(c)
   else:
     if efNoProcvarCheck notin flags: semProcvarCheck(c, result)
     if result.typ.kind == tyVar: result = newDeref(result)
@@ -682,27 +683,9 @@ proc bracketedMacro(n: PNode): PSym =
     if result.kind notin {skMacro, skTemplate}:
       result = nil
 
-proc semBracketedMacro(c: PContext; outer, inner: PNode; s: PSym;
-                       flags: TExprFlags): PNode =
-  # We received untransformed bracket expression coming from macroOrTmpl[].
-  # Transform it to macro or template call, where first come normal
-  # arguments, next come generic template arguments.
-  var sons = newSeq[PNode]()
-  sons.add inner.sons[0]
-  # Normal arguments:
-  for i in 1..<outer.len:
-    sons.add outer.sons[i]
-  # Generic template arguments from bracket expression:
-  for i in 1..<inner.len:
-    sons.add inner.sons[i]
-  shallowCopy(outer.sons, sons)
-  # FIXME: Shouldn't we check sfImmediate and call semDirectOp?
-  # However passing to semDirectOp doesn't work here.
-  case s.kind
-  of skMacro: result = semMacroExpr(c, outer, outer, s, flags)
-  of skTemplate: result = semTemplateExpr(c, outer, s, flags)
-  else: assert(false)
-  return
+proc setGenericParams(c: PContext, n: PNode) =
+  for i in 1 .. <n.len:
+    n[i].typ = semTypeNode(c, n[i], nil)
 
 proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags): PNode =
   result = n
@@ -744,7 +727,8 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode =
     elif n.sons[0].kind == nkBracketExpr:
       let s = bracketedMacro(n.sons[0])
       if s != nil:
-        return semBracketedMacro(c, n, n.sons[0], s, flags)
+        setGenericParams(c, n[0])
+        return semDirectOp(c, n, flags)
 
   let nOrig = n.copyTree
   semOpAux(c, n)
@@ -1155,6 +1139,8 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
     ty = n.sons[0].typ
     return nil
   ty = skipTypes(ty, {tyGenericInst, tyVar, tyPtr, tyRef, tyAlias})
+  if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass:
+    ty = ty.lastSon
   while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct})
   var check: PNode = nil
   if ty.kind == tyObject:
@@ -1713,7 +1699,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
   # We transform the do block into a template with a param for
   # each interpolation. We'll pass this template to getAst.
   var
-    doBlk = n{-1}
+    quotedBlock = n{-1}
     op = if n.len == 3: expectString(c, n[1]) else: "``"
     quotes = newSeq[PNode](1)
       # the quotes will be added to a nkCall statement
@@ -1721,20 +1707,23 @@ proc semQuoteAst(c: PContext, n: PNode): PNode =
     ids = newSeq[PNode]()
       # this will store the generated param names
 
-  if doBlk.kind != nkDo:
+  if quotedBlock.kind != nkStmtList:
     localError(n.info, errXExpected, "block")
 
-  processQuotations(doBlk.sons[bodyPos], op, quotes, ids)
+  processQuotations(quotedBlock, op, quotes, ids)
+
+  var dummyTemplate = newProcNode(
+    nkTemplateDef, quotedBlock.info, quotedBlock,
+    name = newAnonSym(c, skTemplate, n.info).newSymNode)
 
-  doBlk.sons[namePos] = newAnonSym(c, skTemplate, n.info).newSymNode
   if ids.len > 0:
-    doBlk.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
-    doBlk[paramsPos].add getSysSym("typed").newSymNode # return type
+    dummyTemplate.sons[paramsPos] = newNodeI(nkFormalParams, n.info)
+    dummyTemplate[paramsPos].add getSysSym("typed").newSymNode # return type
     ids.add getSysSym("untyped").newSymNode # params type
     ids.add emptyNode # no default value
-    doBlk[paramsPos].add newNode(nkIdentDefs, n.info, ids)
+    dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids)
 
-  var tmpl = semTemplateDef(c, doBlk)
+  var tmpl = semTemplateDef(c, dummyTemplate)
   quotes[0] = tmpl[namePos]
   result = newNode(nkCall, n.info, @[
     getMagicSym(mExpandToAst).newSymNode,
@@ -2158,10 +2147,6 @@ proc shouldBeBracketExpr(n: PNode): bool =
           n.sons[0] = be
           return true
 
-proc setGenericParams(c: PContext, n: PNode) =
-  for i in 1 .. <n.len:
-    n[i].typ = semTypeNode(c, n[i], nil)
-
 proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   result = n
   if gCmd == cmdIdeTools: suggestExpr(c, n)
@@ -2325,8 +2310,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
   of nkCurly: result = semSetConstr(c, n)
   of nkBracket: result = semArrayConstr(c, n, flags)
   of nkObjConstr: result = semObjConstr(c, n, flags)
-  of nkLambda: result = semLambda(c, n, flags)
-  of nkDo: result = semDo(c, n, flags)
+  of nkLambdaKinds: result = semLambda(c, n, flags)
   of nkDerefExpr: result = semDeref(c, n)
   of nkAddr:
     result = n
diff --git a/compiler/semfields.nim b/compiler/semfields.nim
index 06826ef75..6002705b3 100644
--- a/compiler/semfields.nim
+++ b/compiler/semfields.nim
@@ -121,12 +121,13 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode =
     localError(n.info, errWrongNumberOfVariables)
     return result
 
-  var tupleTypeA = skipTypes(call.sons[1].typ, abstractVar-{tyTypeDesc})
+  const skippedTypesForFields = abstractVar - {tyTypeDesc} + tyUserTypeClasses
+  var tupleTypeA = skipTypes(call.sons[1].typ, skippedTypesForFields)
   if tupleTypeA.kind notin {tyTuple, tyObject}:
     localError(n.info, errGenerated, "no object or tuple type")
     return result
   for i in 1..call.len-1:
-    var tupleTypeB = skipTypes(call.sons[i].typ, abstractVar-{tyTypeDesc})
+    var tupleTypeB = skipTypes(call.sons[i].typ, skippedTypesForFields)
     if not sameType(tupleTypeA, tupleTypeB):
       typeMismatch(call.sons[i].info, tupleTypeA, tupleTypeB)
 
diff --git a/compiler/seminst.nim b/compiler/seminst.nim
index 874be8dd6..b5dca4c1b 100644
--- a/compiler/seminst.nim
+++ b/compiler/seminst.nim
@@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
   c.p = x
 
 proc rawHandleSelf(c: PContext; owner: PSym) =
-  if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil:
+  const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
+  if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
     let params = owner.typ.n
     if params.len > 1:
       let arg = params[1].sym
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 3e1989eaf..eb6259df0 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -89,9 +89,9 @@ proc semInstantiationInfo(c: PContext, n: PNode): PNode =
 proc toNode(t: PType, i: TLineInfo): PNode =
   result = newNodeIT(nkType, i, t)
 
-const 
+const
   # these are types that use the bracket syntax for instantiation
-  # they can be subjected to the type traits `genericHead` and 
+  # they can be subjected to the type traits `genericHead` and
   # `Uninstantiated`
   tyUserDefinedGenerics* = {tyGenericInst, tyGenericInvocation,
                             tyUserTypeClassInst}
@@ -109,27 +109,43 @@ proc uninstantiate(t: PType): PType =
     of tyCompositeTypeClass: uninstantiate t.sons[1]
     else: t
 
-proc evalTypeTrait(trait: PNode, operand: PType, context: PSym): PNode =
-  var typ = operand.skipTypes({tyTypeDesc})
+proc evalTypeTrait(traitCall: PNode, operand: PType, context: PSym): PNode =
+  const skippedTypes = {tyTypeDesc}
+  let trait = traitCall[0]
+  internalAssert trait.kind == nkSym
+  var operand = operand.skipTypes(skippedTypes)
+
+  template operand2: PType =
+    traitCall.sons[2].typ.skipTypes({tyTypeDesc})
+
+  template typeWithSonsResult(kind, sons): PNode =
+    newTypeWithSons2(kind, context, sons).toNode(traitCall.info)
+
   case trait.sym.name.s
+  of "or", "|":
+    return typeWithSonsResult(tyOr, @[operand, operand2])
+  of "and":
+    return typeWithSonsResult(tyAnd, @[operand, operand2])
+  of "not":
+    return typeWithSonsResult(tyNot, @[operand])
   of "name":
-    result = newStrNode(nkStrLit, typ.typeToString(preferName))
+    result = newStrNode(nkStrLit, operand.typeToString(preferName))
     result.typ = newType(tyString, context)
-    result.info = trait.info
+    result.info = traitCall.info
   of "arity":
-    result = newIntNode(nkIntLit, typ.len - ord(typ.kind==tyProc))
+    result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc))
     result.typ = newType(tyInt, context)
-    result.info = trait.info
+    result.info = traitCall.info
   of "genericHead":
-    var res = uninstantiate(typ)
-    if res == typ and res.kind notin tyMagicGenerics:
-      localError(trait.info,
+    var res = uninstantiate(operand)
+    if res == operand and res.kind notin tyMagicGenerics:
+      localError(traitCall.info,
         "genericHead expects a generic type. The given type was " &
-        typeToString(typ))
-      return newType(tyError, context).toNode(trait.info)
-    result = res.base.toNode(trait.info)
+        typeToString(operand))
+      return newType(tyError, context).toNode(traitCall.info)
+    result = res.base.toNode(traitCall.info)
   of "stripGenericParams":
-    result = uninstantiate(typ).toNode(trait.info)
+    result = uninstantiate(operand).toNode(traitCall.info)
   else:
     internalAssert false
 
@@ -140,7 +156,7 @@ proc semTypeTraits(c: PContext, n: PNode): PNode =
   if t.sonsLen > 0:
     # This is either a type known to sem or a typedesc
     # param to a regular proc (again, known at instantiation)
-    result = evalTypeTrait(n[0], t, getCurrOwner(c))
+    result = evalTypeTrait(n, t, getCurrOwner(c))
   else:
     # a typedesc variable, pass unmodified to evals
     result = n
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index be0d90c0f..8f522ccc2 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -730,7 +730,9 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
     var s: PSym
     if name.kind == nkDotExpr:
       s = qualifiedLookUp(c, name, {checkUndeclared, checkModule})
-      if s.kind != skType or s.typ.skipTypes(abstractPtrs).kind != tyObject or tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
+      if s.kind != skType or
+         s.typ.skipTypes(abstractPtrs).kind != tyObject or
+         tfPartial notin s.typ.skipTypes(abstractPtrs).flags:
         localError(name.info, "only .partial objects can be extended")
     else:
       s = semIdentDef(c, name, skType)
@@ -742,6 +744,87 @@ proc typeSectionLeftSidePass(c: PContext, n: PNode) =
       if sfGenSym notin s.flags: addInterfaceDecl(c, s)
     a.sons[0] = newSymNode(s)
 
+proc checkCovariantParamsUsages(genericType: PType) =
+  var body = genericType{-1}
+
+  proc traverseSubTypes(t: PType): bool =
+    template error(msg) = localError(genericType.sym.info, msg)
+
+    result = false
+
+    template subresult(r) =
+      let sub = r
+      result = result or sub
+
+    case t.kind
+    of tyGenericParam:
+      t.flags.incl tfWeakCovariant
+      return true
+
+    of tyObject:
+      for field in t.n:
+        subresult traverseSubTypes(field.typ)
+
+    of tyArray:
+      return traverseSubTypes(t[1])
+
+    of tyProc:
+      for subType in t.sons:
+        if subType != nil:
+          subresult traverseSubTypes(subType)
+      if result:
+        error("non-invariant type param used in a proc type: " &  $t)
+
+    of tySequence:
+      return traverseSubTypes(t[0])
+
+    of tyGenericInvocation:
+      let targetBody = t[0]
+      for i in 1 .. <t.len:
+        let param = t[i]
+        if param.kind == tyGenericParam:
+          if tfCovariant in param.flags:
+            let formalFlags = targetBody[i-1].flags
+            if tfCovariant notin formalFlags:
+              error("covariant param '" & param.sym.name.s &
+                    "' used in a non-covariant position")
+            elif tfWeakCovariant in formalFlags:
+              param.flags.incl tfWeakCovariant
+            result = true
+          elif tfContravariant in param.flags:
+            let formalParam = targetBody[i-1].sym
+            if tfContravariant notin formalParam.typ.flags:
+              error("contravariant param '" & param.sym.name.s &
+                    "' used in a non-contravariant position")
+            result = true
+        else:
+          subresult traverseSubTypes(param)
+
+    of tyAnd, tyOr, tyNot, tyStatic, tyBuiltInTypeClass, tyCompositeTypeClass:
+      error("non-invariant type parameters cannot be used with types such '" & $t & "'")
+
+    of tyUserTypeClass, tyUserTypeClassInst:
+      error("non-invariant type parameters are not supported in concepts")
+
+    of tyTuple:
+      for fieldType in t.sons:
+        subresult traverseSubTypes(fieldType)
+
+    of tyPtr, tyRef, tyVar:
+      if t.base.kind == tyGenericParam: return true
+      return traverseSubTypes(t.base)
+
+    of tyDistinct, tyAlias:
+      return traverseSubTypes(t.lastSon)
+
+    of tyGenericInst:
+      internalAssert false
+
+    else:
+      discard
+
+  discard traverseSubTypes(body)
+
 proc typeSectionRightSidePass(c: PContext, n: PNode) =
   for i in countup(0, sonsLen(n) - 1):
     var a = n.sons[i]
@@ -782,6 +865,22 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) =
         body.sym = s
         body.size = -1 # could not be computed properly
         s.typ.sons[sonsLen(s.typ) - 1] = body
+        if tfCovariant in s.typ.flags:
+          checkCovariantParamsUsages(s.typ)
+          # XXX: This is a temporary limitation:
+          # The codegen currently produces various failures with
+          # generic imported types that have fields, but we need
+          # the fields specified in order to detect weak covariance.
+          # The proper solution is to teach the codegen how to handle
+          # such types, because this would offer various interesting
+          # possibilities such as instantiating C++ generic types with
+          # garbage collected Nim types.
+          if sfImportc in s.flags:
+            var body = s.typ.lastSon
+            if body.kind == tyObject:
+              # erases all declared fields
+              body.n.sons = nil
+
       popOwner(c)
       closeScope(c)
     elif a.sons[2].kind != nkEmpty:
@@ -851,9 +950,12 @@ proc typeSectionFinalPass(c: PContext, n: PNode) =
         # type aliases are hard:
         var t = semTypeNode(c, x, nil)
         assert t != nil
-        if t.kind in {tyObject, tyEnum, tyDistinct}:
-          assert s.typ != nil
-          if s.typ.kind != tyAlias:
+        if s.typ != nil and s.typ.kind != tyAlias:
+          if t.kind in {tyProc, tyGenericInst} and not t.isMetaType:
+            assignType(s.typ, t)
+            s.typ.id = t.id
+          elif t.kind in {tyObject, tyEnum, tyDistinct}:
+            assert s.typ != nil
             assignType(s.typ, t)
             s.typ.id = t.id     # same id
       checkConstructedType(s.info, s.typ)
@@ -1066,13 +1168,6 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode =
   popOwner(c)
   result.typ = s.typ
 
-proc semDo(c: PContext, n: PNode, flags: TExprFlags): PNode =
-  # 'do' without params produces a stmt:
-  if n[genericParamsPos].kind == nkEmpty and n[paramsPos].kind == nkEmpty:
-    result = semStmt(c, n[bodyPos])
-  else:
-    result = semLambda(c, n, flags)
-
 proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode =
   var n = n
 
@@ -1177,7 +1272,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
       var objB = t.sons[2]
       while true:
         if objB.kind == tyGenericBody: objB = objB.lastSon
-        elif objB.kind == tyGenericInvocation: objB = objB.sons[0]
+        elif objB.kind in {tyGenericInvocation, tyGenericInst}:
+          objB = objB.sons[0]
         else: break
       if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
         if obj.assignment.isNil:
@@ -1666,7 +1762,11 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode =
       else: discard
 
   if result.len == 1 and
-     c.inTypeClass == 0 and # concept bodies should be preserved as a stmt list
+     # concept bodies should be preserved as a stmt list:
+     c.inTypeClass == 0 and
+     # also, don't make life complicated for macros.
+     # they will always expect a proper stmtlist:
+     nfBlockArg notin n.flags and
      result.sons[0].kind != nkDefer:
     result = result.sons[0]
 
diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim
index 422d2f0fa..09f90d8d0 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -135,7 +135,9 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
     let isCall = ord(n.kind in nkCallKinds+{nkBracketExpr})
     let n = if n[0].kind == nkBracket: n[0] else: n
     checkMinSonsLen(n, 1)
-    var base = semTypeNode(c, n.lastSon, nil).skipTypes({tyTypeDesc})
+    var t = semTypeNode(c, n.lastSon, nil)
+    if t.kind == tyTypeDesc and tfUnresolved notin t.flags:
+      t = t.base
     result = newOrPrevType(kind, prev, c)
     var isNilable = false
     # check every except the last is an object:
@@ -149,7 +151,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType =
               tyError, tyObject}:
           message n[i].info, errGenerated, "region needs to be an object type"
         addSonSkipIntLit(result, region)
-    addSonSkipIntLit(result, base)
+    addSonSkipIntLit(result, t)
     #if not isNilable: result.flags.incl tfNotNil
 
 proc semVarType(c: PContext, n: PNode, prev: PType): PType =
@@ -891,13 +893,20 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
       let lifted = liftingWalk(paramType.sons[i])
       if lifted != nil: paramType.sons[i] = lifted
 
-    if paramType.base.lastSon.kind == tyUserTypeClass:
+    let body = paramType.base
+    if body.kind == tyForward:
+      # this may happen for proc type appearing in a type section
+      # before one of its param types
+      return
+
+    if body.lastSon.kind == tyUserTypeClass:
       let expanded = instGenericContainer(c, info, paramType,
                                           allowMetaTypes = true)
       result = liftingWalk(expanded, true)
 
-  of tyUserTypeClasses, tyBuiltInTypeClass, tyAnd, tyOr, tyNot:
-    result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), true))
+  of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass,
+     tyAnd, tyOr, tyNot:
+    result = addImplicitGeneric(copyType(paramType, getCurrOwner(c), false))
 
   of tyGenericParam:
     markUsed(info, paramType.sym, c.graph.usageSym)
@@ -1065,6 +1074,7 @@ proc semBlockType(c: PContext, n: PNode, prev: PType): PType =
 
 proc semGenericParamInInvocation(c: PContext, n: PNode): PType =
   result = semTypeNode(c, n, nil)
+  n.typ = makeTypeDesc(c, result)
 
 proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) =
   var
@@ -1574,10 +1584,24 @@ proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode =
                       # type for each generic param. the index
                       # of the parameter will be stored in the
                       # attached symbol.
+      var paramName = a.sons[j]
+      var covarianceFlag = tfUnresolved
+
+      if paramName.safeLen == 2:
+        if not nimEnableCovariance or paramName[0].ident.s == "in":
+          if father == nil or sfImportc notin father.sym.flags:
+            localError(paramName.info, errInOutFlagNotExtern, paramName[0].ident.s)
+        covarianceFlag = if paramName[0].ident.s == "in": tfContravariant
+                         else: tfCovariant
+        if father != nil: father.flags.incl tfCovariant
+        paramName = paramName[1]
+
       var s = if finalType.kind == tyStatic or tfWildcard in typ.flags:
-          newSymG(skGenericParam, a.sons[j], c).linkTo(finalType)
+          newSymG(skGenericParam, paramName, c).linkTo(finalType)
         else:
-          newSymG(skType, a.sons[j], c).linkTo(finalType)
+          newSymG(skType, paramName, c).linkTo(finalType)
+
+      if covarianceFlag != tfUnresolved: s.typ.flags.incl(covarianceFlag)
       if def.kind != nkEmpty: s.ast = def
       if father != nil: addSonSkipIntLit(father, s.typ)
       s.position = result.len
diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim
index 9ff0b7e78..80fb9168b 100644
--- a/compiler/semtypinst.nim
+++ b/compiler/semtypinst.nim
@@ -9,10 +9,10 @@
 
 # This module does the instantiation of generic types.
 
-import ast, astalgo, msgs, types, magicsys, semdata, renderer
+import ast, astalgo, msgs, types, magicsys, semdata, renderer, options
 
 const
-  tfInstClearedFlags = {tfHasMeta}
+  tfInstClearedFlags = {tfHasMeta, tfUnresolved}
 
 proc checkPartialConstructedType(info: TLineInfo, t: PType) =
   if tfAcyclic in t.flags and skipTypes(t, abstractInst).kind != tyObject:
@@ -50,6 +50,9 @@ proc searchInstTypes*(key: PType): PType =
       # types such as Channel[empty]. Why?
       # See the notes for PActor in handleGenericInvocation
       return
+    if not sameFlags(inst, key):
+      continue
+
     block matchType:
       for j in 1 .. high(key.sons):
         # XXX sameType is not really correct for nested generics?
@@ -231,6 +234,7 @@ proc lookupTypeVar(cl: var TReplTypeVars, t: PType): PType =
 proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
   # XXX: relying on allowMetaTypes is a kludge
   result = copyType(t, t.owner, cl.allowMetaTypes)
+  if cl.allowMetaTypes: return
   result.flags.incl tfFromGeneric
   if not (t.kind in tyMetaTypes or
          (t.kind == tyStatic and t.n == nil)):
@@ -247,10 +251,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
     result = PType(idTableGet(cl.localCache, t))
   else:
     result = searchInstTypes(t)
+
   if result != nil and eqTypeFlags*result.flags == eqTypeFlags*t.flags: return
   for i in countup(1, sonsLen(t) - 1):
     var x = t.sons[i]
-    if x.kind == tyGenericParam:
+    if x.kind in {tyGenericParam}:
       x = lookupTypeVar(cl, x)
       if x != nil:
         if header == t: header = instCopyType(cl, t)
@@ -307,31 +312,32 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
   rawAddSon(result, newbody)
   checkPartialConstructedType(cl.info, newbody)
   let dc = newbody.deepCopy
-  if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
-    # 'deepCopy' needs to be instantiated for
-    # generics *when the type is constructed*:
-    newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
-                                            attachedDeepCopy, 1)
-  if bodyIsNew and newbody.typeInst == nil:
-    #doassert newbody.typeInst == nil
-    newbody.typeInst = result
-    if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
-      # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
-      # need to look into this issue later
-      assert newbody.kind in {tyRef, tyPtr}
-      assert newbody.lastSon.typeInst == nil
-      newbody.lastSon.typeInst = result
-  let asgn = newbody.assignment
-  if asgn != nil and sfFromGeneric notin asgn.flags:
-    # '=' needs to be instantiated for generics when the type is constructed:
-    newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
-                                              attachedAsgn, 1)
-  let methods = skipTypes(bbody, abstractPtrs).methods
-  for col, meth in items(methods):
-    # we instantiate the known methods belonging to that type, this causes
-    # them to be registered and that's enough, so we 'discard' the result.
-    discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
-      attachedAsgn, col)
+  if cl.allowMetaTypes == false:
+    if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
+      # 'deepCopy' needs to be instantiated for
+      # generics *when the type is constructed*:
+      newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
+                                              attachedDeepCopy, 1)
+    if bodyIsNew and newbody.typeInst == nil:
+      #doassert newbody.typeInst == nil
+      newbody.typeInst = result
+      if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
+        # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
+        # need to look into this issue later
+        assert newbody.kind in {tyRef, tyPtr}
+        assert newbody.lastSon.typeInst == nil
+        newbody.lastSon.typeInst = result
+    let asgn = newbody.assignment
+    if asgn != nil and sfFromGeneric notin asgn.flags:
+      # '=' needs to be instantiated for generics when the type is constructed:
+      newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
+                                                attachedAsgn, 1)
+    let methods = skipTypes(bbody, abstractPtrs).methods
+    for col, meth in items(methods):
+      # we instantiate the known methods belonging to that type, this causes
+      # them to be registered and that's enough, so we 'discard' the result.
+      discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
+        attachedAsgn, col)
 
 proc eraseVoidParams*(t: PType) =
   # transform '(): void' into '()' because old parts of the compiler really
@@ -526,6 +532,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
   result = replaceTypeVarsT(cl, t)
   popInfoContext()
 
+proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
+                                 t: PType): PType =
+  var cl = initTypeVars(p, pt, info, nil)
+  cl.allowMetaTypes = true
+  pushInfoContext(info)
+  result = replaceTypeVarsT(cl, t)
+  popInfoContext()
+
 template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
                                t: PType): untyped =
   generateTypeInstance(p, pt, arg.info, t)
diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim
index fd703a433..6eecf98d8 100644
--- a/compiler/sighashes.nim
+++ b/compiler/sighashes.nim
@@ -154,7 +154,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
     else:
       c.hashSym(t.sym)
     return
-  of tyAlias, tyGenericInst:
+  of tyAlias, tyGenericInst, tyUserTypeClasses:
     c.hashType t.lastSon, flags
     return
   else:
@@ -201,16 +201,6 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) =
   of tyRef, tyPtr, tyGenericBody, tyVar:
     c.hashType t.lastSon, flags
     if tfVarIsPtr in t.flags: c &= ".varisptr"
-  of tyUserTypeClass:
-    if t.sym != nil and t.sym.owner != nil:
-      c &= t.sym.owner.name.s
-    else:
-      c &= "unknown typeclass"
-  of tyUserTypeClassInst:
-    let body = t.sons[0]
-    c.hashSym body.sym
-    for i in countup(1, sonsLen(t) - 2):
-      c.hashType t.sons[i], flags
   of tyFromExpr, tyFieldAccessor:
     c.hashTree(t.n)
   of tyTuple:
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 4661abda0..cb526947e 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -26,7 +26,7 @@ type
     sym*: PSym
     unmatchedVarParam*: int
     diagnostics*: seq[string]
-  
+
   CandidateErrors* = seq[CandidateError]
 
   TCandidate* = object
@@ -68,7 +68,13 @@ type
                               # future.
     mutabilityProblem*: uint8 # tyVar mismatch
     inheritancePenalty: int  # to prefer closest father object type
-  
+
+  TTypeRelFlag* = enum
+    trDontBind
+    trNoCovariance
+
+  TTypeRelFlags* = set[TTypeRelFlag]
+
   TTypeRelation* = enum      # order is important!
     isNone, isConvertible,
     isIntConv,
@@ -177,6 +183,13 @@ proc sumGeneric(t: PType): int =
         tyOpenArray, tyVarargs, tySet, tyRange, tySequence, tyGenericBody:
       t = t.lastSon
       inc result
+    of tyOr:
+      var maxBranch = 0
+      for branch in t.sons:
+        let branchSum = branch.sumGeneric
+        if branchSum > maxBranch: maxBranch = branchSum
+      inc result, maxBranch + 1
+      break
     of tyVar:
       t = t.sons[0]
       inc result
@@ -185,8 +198,8 @@ proc sumGeneric(t: PType): int =
       t = t.lastSon
       if t.kind == tyEmpty: break
       inc result
-    of tyGenericInvocation, tyTuple, tyProc:
-      result += ord(t.kind == tyGenericInvocation)
+    of tyGenericInvocation, tyTuple, tyProc, tyAnd:
+      result += ord(t.kind in {tyGenericInvocation, tyAnd})
       for i in 0 .. <t.len:
         if t.sons[i] != nil:
           result += t.sons[i].sumGeneric
@@ -228,6 +241,15 @@ proc complexDisambiguation(a, b: PType): int =
     for i in 1 .. <b.len: y += b.sons[i].sumGeneric
     result = x - y
 
+proc writeMatches*(c: TCandidate) =
+  echo "Candidate '", c.calleeSym.name.s, "' at ", c.calleeSym.info
+  echo "  exact matches: ", c.exactMatches
+  echo "  generic matches: ", c.genericMatches
+  echo "  subtype matches: ", c.subtypeMatches
+  echo "  intconv matches: ", c.intConvMatches
+  echo "  conv matches: ", c.convMatches
+  echo "  inheritance: ", c.inheritancePenalty
+
 proc cmpCandidates*(a, b: TCandidate): int =
   result = a.exactMatches - b.exactMatches
   if result != 0: return
@@ -248,14 +270,6 @@ proc cmpCandidates*(a, b: TCandidate): int =
   if result != 0: return
   result = a.calleeScope - b.calleeScope
 
-proc writeMatches*(c: TCandidate) =
-  writeLine(stdout, "exact matches: " & $c.exactMatches)
-  writeLine(stdout, "generic matches: " & $c.genericMatches)
-  writeLine(stdout, "subtype matches: " & $c.subtypeMatches)
-  writeLine(stdout, "intconv matches: " & $c.intConvMatches)
-  writeLine(stdout, "conv matches: " & $c.convMatches)
-  writeLine(stdout, "inheritance: " & $c.inheritancePenalty)
-
 proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   if arg.kind in nkSymChoices:
     result = typeToString(arg[0].typ, prefer)
@@ -288,7 +302,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
     add(result, argTypeToString(arg, prefer))
     if i != sonsLen(n) - 1: add(result, ", ")
 
-proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation
+proc typeRel*(c: var TCandidate, f, aOrig: PType,
+              flags: TTypeRelFlags = {}): TTypeRelation
+
 proc concreteType(c: TCandidate, t: PType): PType =
   case t.kind
   of tyNil:
@@ -639,7 +655,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
                         makeTypeDesc(c, typ)
 
         typeParams.safeAdd((param, typ))
-      
+
       addDecl(c, param)
 
   for param in typeClass.n[0]:
@@ -676,7 +692,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
     flags: TExprFlags = {}
     collectDiagnostics = m.diagnostics != nil or
                          sfExplain in typeClass.sym.flags
-  
+
   if collectDiagnostics:
     oldWriteHook = writelnHook
     # XXX: we can't write to m.diagnostics directly, because
@@ -688,13 +704,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
       let msg = s.replace("Error:", errorPrefix)
       if oldWriteHook != nil: oldWriteHook msg
       diagnostics.add msg
-   
+
   var checkedBody = c.semTryExpr(c, body.copyTree, flags)
 
   if collectDiagnostics:
     writelnHook = oldWriteHook
     for msg in diagnostics: m.diagnostics.safeAdd msg
-  
+
   if checkedBody == nil: return nil
 
   # The inferrable type params have been identified during the semTryExpr above.
@@ -739,14 +755,14 @@ proc tryResolvingStaticExpr(c: var TCandidate, n: PNode,
                                         allowMetaTypes = allowUnresolved)
   result = c.c.semExpr(c.c, instantiated)
 
-proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
+proc inferStaticParam*(c: var TCandidate, lhs: PNode, rhs: BiggestInt): bool =
   # This is a simple integer arithimetic equation solver,
   # capable of deriving the value of a static parameter in
   # expressions such as (N + 5) / 2 = rhs
   #
   # Preconditions:
   #
-  #   * The input of this proc must be semantized 
+  #   * The input of this proc must be semantized
   #     - all templates should be expanded
   #     - aby constant folding possible should already be performed
   #
@@ -754,64 +770,69 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
   #
   # Result:
   #
-  #   The proc will return the inferred static type with the `n` field
-  #   populated with the inferred value.
-  #
-  #   `nil` will be returned if the inference was not possible
+  #   The proc will return true if the static types was successfully
+  #   inferred. The result will be bound to the original static type
+  #   in the TCandidate.
   #
   if lhs.kind in nkCallKinds and lhs[0].kind == nkSym:
     case lhs[0].sym.magic
     of mUnaryLt:
-      return inferStaticParam(lhs[1], rhs + 1)
+      return inferStaticParam(c, lhs[1], rhs + 1)
 
     of mAddI, mAddU, mInc, mSucc:
       if lhs[1].kind == nkIntLit:
-        return inferStaticParam(lhs[2], rhs - lhs[1].intVal)
+        return inferStaticParam(c, lhs[2], rhs - lhs[1].intVal)
       elif lhs[2].kind == nkIntLit:
-        return inferStaticParam(lhs[1], rhs - lhs[2].intVal)
-    
+        return inferStaticParam(c, lhs[1], rhs - lhs[2].intVal)
+
     of mDec, mSubI, mSubU, mPred:
       if lhs[1].kind == nkIntLit:
-        return inferStaticParam(lhs[2], lhs[1].intVal - rhs)
+        return inferStaticParam(c, lhs[2], lhs[1].intVal - rhs)
       elif lhs[2].kind == nkIntLit:
-        return inferStaticParam(lhs[1], rhs + lhs[2].intVal)
-    
+        return inferStaticParam(c, lhs[1], rhs + lhs[2].intVal)
+
     of mMulI, mMulU:
       if lhs[1].kind == nkIntLit:
         if rhs mod lhs[1].intVal == 0:
-          return inferStaticParam(lhs[2], rhs div lhs[1].intVal)
+          return inferStaticParam(c, lhs[2], rhs div lhs[1].intVal)
       elif lhs[2].kind == nkIntLit:
         if rhs mod lhs[2].intVal == 0:
-          return inferStaticParam(lhs[1], rhs div lhs[2].intVal)
-    
+          return inferStaticParam(c, lhs[1], rhs div lhs[2].intVal)
+
     of mDivI, mDivU:
       if lhs[1].kind == nkIntLit:
         if lhs[1].intVal mod rhs == 0:
-          return inferStaticParam(lhs[2], lhs[1].intVal div rhs)
+          return inferStaticParam(c, lhs[2], lhs[1].intVal div rhs)
       elif lhs[2].kind == nkIntLit:
-        return inferStaticParam(lhs[1], lhs[2].intVal * rhs)
-    
+        return inferStaticParam(c, lhs[1], lhs[2].intVal * rhs)
+
     of mShlI:
       if lhs[2].kind == nkIntLit:
-        return inferStaticParam(lhs[1], rhs shr lhs[2].intVal)
-    
+        return inferStaticParam(c, lhs[1], rhs shr lhs[2].intVal)
+
     of mShrI:
       if lhs[2].kind == nkIntLit:
-        return inferStaticParam(lhs[1], rhs shl lhs[2].intVal)
-    
+        return inferStaticParam(c, lhs[1], rhs shl lhs[2].intVal)
+
     of mUnaryMinusI:
-      return inferStaticParam(lhs[1], -rhs)
-    
+      return inferStaticParam(c, lhs[1], -rhs)
+
     of mUnaryPlusI, mToInt, mToBiggestInt:
-      return inferStaticParam(lhs[1], rhs)
-    
+      return inferStaticParam(c, lhs[1], rhs)
+
     else: discard
-  
+
   elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
-    lhs.typ.n = newIntNode(nkIntLit, rhs)
-    return lhs.typ
-  
-  return nil
+    var inferred = newTypeWithSons(c.c, tyStatic, lhs.typ.sons)
+    inferred.n = newIntNode(nkIntLit, rhs)
+    put(c, lhs.typ, inferred)
+    if c.c.inTypeClass > 0:
+      # inside concepts, binding is currently done with
+      # direct mutation of the involved types:
+      lhs.typ.n = inferred.n
+    return true
+
+  return false
 
 proc failureToInferStaticParam(n: PNode) =
   let staticParam = n.findUnresolvedStatic
@@ -825,13 +846,10 @@ proc inferStaticsInRange(c: var TCandidate,
                                           allowUnresolved = true)
   let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
                                           allowUnresolved = true)
-  
-  template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) =
+  template doInferStatic(e: PNode, r: BiggestInt) =
     var exp = e
     var rhs = r
-    var inferred = inferStaticParam(exp, rhs)
-    if inferred != nil:
-      put(c, inferred, inferred)
+    if inferStaticParam(c, exp, rhs):
       return isGeneric
     else:
       failureToInferStaticParam exp
@@ -842,15 +860,36 @@ proc inferStaticsInRange(c: var TCandidate,
         return isGeneric
       else:
         return isNone
-    doInferStatic(c, upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
+    doInferStatic(upperBound, lengthOrd(concrete) + lowerBound.intVal - 1)
   elif upperBound.kind == nkIntLit:
-    doInferStatic(c, lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
+    doInferStatic(lowerBound, upperBound.intVal + 1 - lengthOrd(concrete))
 
 template subtypeCheck() =
   if result <= isSubrange and f.lastSon.skipTypes(abstractInst).kind in {tyRef, tyPtr, tyVar}:
     result = isNone
 
-proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
+proc isCovariantPtr(c: var TCandidate, f, a: PType): bool =
+  # this proc is always called for a pair of matching types
+  assert f.kind == a.kind
+
+  template baseTypesCheck(lhs, rhs: PType): bool =
+    lhs.kind notin {tyPtr, tyRef, tyVar} and
+      typeRel(c, lhs, rhs, {trNoCovariance}) == isSubtype
+
+  case f.kind
+  of tyRef, tyPtr:
+    return baseTypesCheck(f.base, a.base)
+  of tyGenericInst:
+    let body = f.base
+    return body == a.base and
+           a.sonsLen == 3 and
+           tfWeakCovariant notin body.sons[0].flags and
+           baseTypesCheck(f.sons[1], a.sons[1])
+  else:
+    return false
+
+proc typeRel(c: var TCandidate, f, aOrig: PType,
+             flags: TTypeRelFlags = {}): TTypeRelation =
   # typeRel can be used to establish various relationships between types:
   #
   # 1) When used with concrete types, it will check for type equivalence
@@ -917,6 +956,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = isEqual
       return
 
+  template doBind: bool = trDontBind notin flags
+
   # var and static arguments match regular modifier-free types
   var a = aOrig.skipTypes({tyStatic, tyVar}).maybeSkipDistinct(c.calleeSym)
   # XXX: Theoretically, maybeSkipDistinct could be called before we even
@@ -946,23 +987,28 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   case a.kind
   of tyOr:
+    # XXX: deal with the current dual meaning of tyGenericParam
+    c.typedescMatched = true
     # seq[int|string] vs seq[number]
     # both int and string must match against number
     # but ensure that '[T: A|A]' matches as good as '[T: A]' (bug #2219):
     result = isGeneric
     for branch in a.sons:
-      let x = typeRel(c, f, branch, false)
+      let x = typeRel(c, f, branch, flags + {trDontBind})
       if x == isNone: return isNone
       if x < result: result = x
+    return
 
   of tyAnd:
+    # XXX: deal with the current dual meaning of tyGenericParam
+    c.typedescMatched = true
     # seq[Sortable and Iterable] vs seq[Sortable]
     # only one match is enough
     for branch in a.sons:
-      let x = typeRel(c, f, branch, false)
+      let x = typeRel(c, f, branch, flags + {trDontBind})
       if x != isNone:
         return if x >= isGeneric: isGeneric else: x
-    result = isNone
+    return isNone
 
   of tyNot:
     case f.kind
@@ -986,7 +1032,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyUserTypeClass, tyUserTypeClassInst:
     # consider this: 'var g: Node' *within* a concept where 'Node'
     # is a concept too (tgraph)
-    let x = typeRel(c, a, f, false)
+    let x = typeRel(c, a, f, flags + {trDontBind})
     if x >= isGeneric:
       return isGeneric
   else: discard
@@ -1032,7 +1078,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyFloat128: result = handleFloatRange(f, a)
   of tyVar:
     if aOrig.kind == tyVar: result = typeRel(c, f.base, aOrig.base)
-    else: result = typeRel(c, f.base, aOrig)
+    else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance})
     subtypeCheck()
   of tyArray:
     case a.kind
@@ -1046,38 +1092,60 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           fRange = a
         else:
           fRange = prev
-      result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
-                          a.sons[1].skipTypes({tyTypeDesc}))
-      if result < isGeneric: return isNone
-      
+      let ff = f.sons[1].skipTypes({tyTypeDesc})
+      let aa = a.sons[1].skipTypes({tyTypeDesc})
+      result = typeRel(c, ff, aa)
+      if result < isGeneric:
+        if nimEnableCovariance and
+           trNoCovariance notin flags and
+           ff.kind == aa.kind and
+           isCovariantPtr(c, ff, aa):
+          result = isSubtype
+        else:
+          return isNone
+
       if fRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, fRange, a)
       elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
         return inferStaticsInRange(c, aRange, f)
-      elif lengthOrd(fRange) != lengthOrd(a):
-        result = isNone
+      else:
+        if lengthOrd(fRange) != lengthOrd(aRange):
+          result = isNone
     else: discard
   of tyOpenArray, tyVarargs:
     # varargs[expr] is special too but handled earlier. So we only need to
     # handle varargs[stmt] which is the same as varargs[typed]:
     if f.kind == tyVarargs:
+      if tfVarargs in a.flags:
+        return typeRel(c, f.base, a.lastSon)
       if tfOldSchoolExprStmt in f.sons[0].flags:
         if f.sons[0].kind == tyExpr: return
       elif f.sons[0].kind == tyStmt: return
+
+    template matchArrayOrSeq(aBase: PType) =
+      let ff = f.base
+      let aa = aBase
+      let baseRel = typeRel(c, ff, aa)
+      if baseRel >= isGeneric:
+        result = isConvertible
+      elif nimEnableCovariance and
+           trNoCovariance notin flags and
+           ff.kind == aa.kind and
+           isCovariantPtr(c, ff, aa):
+        result = isConvertible
+
     case a.kind
     of tyOpenArray, tyVarargs:
       result = typeRel(c, base(f), base(a))
       if result < isGeneric: result = isNone
     of tyArray:
       if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
-        result = isSubtype
-      elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
-        result = isConvertible
+        return isSubtype
+      matchArrayOrSeq(a.sons[1])
     of tySequence:
       if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
-        result = isConvertible
-      elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
-        result = isConvertible
+        return isConvertible
+      matchArrayOrSeq(a.sons[0])
     of tyString:
       if f.kind == tyOpenArray:
         if f.sons[0].kind == tyChar:
@@ -1092,8 +1160,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty):
         result = isSubtype
       else:
-        result = typeRel(c, f.sons[0], a.sons[0])
-        if result < isGeneric: result = isNone
+        let ff = f.sons[0]
+        let aa = a.sons[0]
+        result = typeRel(c, ff, aa)
+        if result < isGeneric:
+          if nimEnableCovariance and
+             trNoCovariance notin flags and
+             ff.kind == aa.kind and
+             isCovariantPtr(c, ff, aa):
+            result = isSubtype
+          else:
+            result = isNone
         elif tfNotNil in f.flags and tfNotNil notin a.flags:
           result = isNilConversion
     of tyNil: result = f.allowsNil
@@ -1145,7 +1222,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if a.len < f.len: return isNone
       for i in 0..f.len-2:
         if typeRel(c, f.sons[i], a.sons[i]) == isNone: return isNone
-      result = typeRel(c, f.lastSon, a.lastSon)
+      result = typeRel(c, f.lastSon, a.lastSon, flags + {trNoCovariance})
       subtypeCheck()
       if result <= isConvertible: result = isNone
       elif tfNotNil in f.flags and tfNotNil notin a.flags:
@@ -1204,9 +1281,66 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyEmpty, tyVoid:
     if a.kind == f.kind: result = isEqual
 
-  of tyGenericInst, tyAlias:
+  of tyAlias:
     result = typeRel(c, lastSon(f), a)
 
+  of tyGenericInst:
+    var prev = PType(idTableGet(c.bindings, f))
+    var f = if prev == nil: f else: prev
+
+    let roota = a.skipGenericAlias
+    let rootf = f.skipGenericAlias
+
+    var m = c
+    if a.kind == tyGenericInst:
+      if roota.base == rootf.base:
+        let nextFlags = flags + {trNoCovariance}
+        var hasCovariance = false
+        for i in 1 .. rootf.sonsLen-2:
+          let ff = rootf.sons[i]
+          let aa = roota.sons[i]
+          result = typeRel(c, ff, aa, nextFlags)
+          if result notin {isEqual, isGeneric}:
+            if trNoCovariance notin flags and ff.kind == aa.kind:
+              let paramFlags = rootf.base.sons[i-1].flags
+              hasCovariance =
+                if tfCovariant in paramFlags:
+                  if tfWeakCovariant in paramFlags:
+                    isCovariantPtr(c, ff, aa)
+                  else:
+                    ff.kind notin {tyRef, tyPtr} and result == isSubtype
+                else:
+                  tfContravariant in paramFlags and
+                    typeRel(c, aa, ff) == isSubtype
+              if hasCovariance:
+                continue
+
+            return isNone
+        if prev == nil: put(c, f, a)
+        result = isGeneric
+      else:
+        let fKind = rootf.lastSon.kind
+        if fKind in {tyAnd, tyOr}:
+          result = typeRel(c, lastSon(f), a)
+          if result != isNone: put(c, f, a)
+          return
+
+        var aAsObject = roota.lastSon
+
+        if fKind in {tyRef, tyPtr} and aAsObject.kind == fKind:
+          aAsObject = aAsObject.base
+
+        if aAsObject.kind == tyObject:
+          let baseType = aAsObject.base
+          if baseType != nil:
+            c.inheritancePenalty += 1
+            return typeRel(c, f, baseType)
+
+        result = isNone
+    else:
+      result = typeRel(c, lastSon(f), a)
+      if result != isNone: put(c, f, a)
+
   of tyGenericBody:
     considerPreviousT:
       if a.kind == tyGenericInst and a.sons[0] == f:
@@ -1217,6 +1351,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyGenericInvocation:
     var x = a.skipGenericAlias
+    # XXX: This is very hacky. It should be moved back into liftTypeParam
+    if x.kind in {tyGenericInst, tyArray} and
+       c.calleeSym != nil and
+       c.calleeSym.kind == skProc:
+      let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
+      return typeRel(c, inst, a)
+
     var depth = 0
     if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
       #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
@@ -1333,13 +1474,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     if f.isResolvedUserTypeClass:
       result = typeRel(c, f.lastSon, a)
     else:
-      var matched = matchUserTypeClass(c.c, c, f, aOrig)
-      if matched != nil:
-        bindConcreteTypeToUserTypeClass(matched, a)
-        put(c, f, matched)
-        result = isGeneric
-      else:
-        result = isNone
+      considerPreviousT:
+        var matched = matchUserTypeClass(c.c, c, f, aOrig)
+        if matched != nil:
+          bindConcreteTypeToUserTypeClass(matched, a)
+          if doBind: put(c, f, matched)
+          result = isGeneric
+        else:
+          result = isNone
 
   of tyCompositeTypeClass:
     considerPreviousT:
@@ -1357,6 +1499,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if result != isNone:
         put(c, f, a)
         result = isGeneric
+
   of tyGenericParam:
     var x = PType(idTableGet(c.bindings, f))
     if x == nil:
@@ -1376,16 +1519,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
             internalAssert a.sons != nil and a.sons.len > 0
             c.typedescMatched = true
             var aa = a
-            while aa.kind in {tyTypeDesc, tyGenericParam} and
-                aa.len > 0:
+            while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0:
               aa = lastSon(aa)
+            if aa.kind == tyGenericParam:
+              return isGeneric
             result = typeRel(c, f.base, aa)
             if result > isGeneric: result = isGeneric
         else:
           result = isNone
       else:
         if f.sonsLen > 0 and f.sons[0].kind != tyNone:
-          result = typeRel(c, f.lastSon, a)
+          result = typeRel(c, f.lastSon, a, flags + {trDontBind})
           if doBind and result notin {isNone, isGeneric}:
             let concrete = concreteType(c, a)
             if concrete == nil: return isNone
@@ -1590,6 +1734,10 @@ proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
   of isEqual: inc(m.exactMatches)
   of isNone: discard
 
+template matchesVoidProc(t: PType): bool =
+  (t.kind == tyProc and t.len == 1 and t.sons[0] == nil) or
+    (t.kind == tyBuiltInTypeClass and t.sons[0].kind == tyProc)
+
 proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
                         argSemantized, argOrig: PNode): PNode =
   var
@@ -1726,6 +1874,14 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType,
       inc(m.genericMatches)
       m.fauxMatch = a.kind
       return arg
+    elif a.kind == tyVoid and f.matchesVoidProc and argOrig.kind == nkStmtList:
+      # lift do blocks without params to lambdas
+      let lifted = c.semExpr(c, newProcNode(nkDo, argOrig.info, argOrig), {})
+      if f.kind == tyBuiltInTypeClass:
+        inc m.genericMatches
+        put(m, f, lifted.typ)
+      inc m.convMatches
+      return implicitConv(nkHiddenStdConv, f, lifted, m, c)
     result = userConvMatch(c, m, f, a, arg)
     # check for a base type match, which supports varargs[T] without []
     # constructor in a call:
@@ -2008,6 +2164,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
             #assert(container == nil)
             if container.isNil:
               container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+              container.typ.flags.incl tfVarargs
             else:
               incrIndexType(container.typ)
             addSon(container, arg)
@@ -2086,6 +2243,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
     localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
     return nil
   var f = dc.typ.sons[col]
+
   if op == attachedDeepCopy:
     if f.kind in {tyRef, tyPtr}: f = f.lastSon
   else:
diff --git a/compiler/types.nim b/compiler/types.nim
index 3f84548a1..2886ac619 100644
--- a/compiler/types.nim
+++ b/compiler/types.nim
@@ -886,6 +886,9 @@ proc isGenericAlias*(t: PType): bool =
 proc skipGenericAlias*(t: PType): PType =
   return if t.isGenericAlias: t.lastSon else: t
 
+proc sameFlags*(a, b: PType): bool {.inline.} =
+  result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
+
 proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
   template cycleCheck() =
     # believe it or not, the direct check for ``containsOrIncl(c, a, b)``
@@ -898,9 +901,6 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool =
     else:
       if containsOrIncl(c, a, b): return true
 
-  proc sameFlags(a, b: PType): bool {.inline.} =
-    result = eqTypeFlags*a.flags == eqTypeFlags*b.flags
-
   if x == y: return true
   var a = skipTypes(x, {tyGenericInst, tyAlias})
   var b = skipTypes(y, {tyGenericInst, tyAlias})
diff --git a/compiler/vm.nim b/compiler/vm.nim
index 3c475cf57..e201e98dc 100644
--- a/compiler/vm.nim
+++ b/compiler/vm.nim
@@ -1606,6 +1606,13 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
     n.typ = x.typ
     result.node = n
 
+iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
+  let gp = macroSym.ast[genericParamsPos]
+  for i in 0 .. <gp.len:
+    let genericParam = gp[i].sym
+    let posInCall = macroSym.typ.len + i
+    yield (genericParam, call[posInCall])
+
 var evalMacroCounter: int
 
 proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,
diff --git a/doc/manual/type_rel.txt b/doc/manual/type_rel.txt
index 5b68f73aa..1d1425934 100644
--- a/doc/manual/type_rel.txt
+++ b/doc/manual/type_rel.txt
@@ -111,6 +111,105 @@ relation is extended to the types ``var``, ``ref``, ``ptr``:
 .. XXX nil is a special value!
 
 
+Covariance
+----------
+
+Covariance in Nim can be introduced only though pointer-like types such
+as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated
+with pointer-like types will be considered covariant if and only if they
+are also immutable. The introduction of a ``var`` modifier or additional
+``ptr`` or ``ref`` indirections would result in invariant treatment of
+these types.
+
+``proc`` types are currently always invariant, but future versions of Nim
+may relax this rule.
+
+User-defined generic types may also be covariant with respect to some of
+their parameters. By default, all generic params are considered invariant,
+but you may choose the apply the prefix modifier ``in`` to a parameter to
+make it contravariant or ``out`` to make it covariant:
+
+.. code-block:: nim
+  type
+    AnnotatedPtr[out T] =
+      metadata: MyTypeInfo
+      p: ref T
+
+    RingBuffer[out T] =
+      startPos: int
+      data: seq[T]
+
+    Action {.importcpp: "std::function<void ('0)>".} [in T] = object
+
+When the designated generic parameter is used to instantiate a pointer-like
+type as in the case of `AnnotatedPtr` above, the resulting generic type will
+also have pointer-like covariance:
+
+.. code-block:: nim
+  type
+    GuiWidget = object of RootObj
+    Button = object of GuiWidget
+    ComboBox = object of GuiWidget
+
+  var
+    widgetPtr: AnnotatedPtr[GuiWidget]
+    buttonPtr: AnnotatedPtr[Button]
+
+  ...
+
+  proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ...
+
+  # you can call procs expecting base types by supplying a derived type
+  drawWidget(buttonPtr)
+
+  # and you can convert more-specific pointer types to more general ones
+  widgetPtr = buttonPtr
+
+Just like with regular pointers, covariance will be enabled only for immutable
+values:
+
+.. code-block:: nim
+  proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) =
+    x.p = new(ComboBox)
+
+  makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified
+                          # to point to a ComboBox
+
+On the other hand, in the `RingBuffer` example above, the designated generic
+param is used to instantiate the non-pointer ``seq`` type, which means that
+the resulting generic type will have covariance that mimics an array or
+sequence (i.e. it will be covariant only when instantiated with ``ptr`` and
+``ref`` types):
+
+.. code-block:: nim
+
+  type
+    Base = object of RootObj
+    Derived = object of Base
+
+  proc consumeBaseValues(b: RingBuffer[Base]) = ...
+
+  var derivedValues: RingBuffer[Derived]
+
+  consumeBaseValues(derivedValues) # Error, Base and Derived values may differ
+                                   # in size
+
+  proc consumeBasePointers(b: RingBuffer[ptr Base]) = ...
+
+  var derivedPointers: RingBuffer[ptr Derived]
+
+  consumeBaseValues(derivedPointers) # This is legal
+
+Please note that Nim will treat the user-defined pointer-like types as
+proper alternatives to the built-in pointer types. That is, types such
+as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be
+considered covariant and you can create new pointer-like types by instantiating
+other user-defined pointer-like types.
+
+The contravariant parameters introduced with the ``in`` modifier are currently
+useful only when interfacing with imported types having such semantics.
+
+
 Convertible relation
 --------------------
 A type ``a`` is **implicitly** convertible to type ``b`` iff the following
@@ -119,6 +218,8 @@ algorithm returns true:
 .. code-block:: nim
   # XXX range types?
   proc isImplicitlyConvertible(a, b: PType): bool =
+    if isSubtype(a, b) or isCovariant(a, b):
+      return true
     case a.kind
     of int:     result = b in {int8, int16, int32, int64, uint, uint8, uint16,
                                uint32, uint64, float, float32, float64}
diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim
index 646b79fe5..3b3f38e41 100644
--- a/lib/js/jsffi.nim
+++ b/lib/js/jsffi.nim
@@ -78,6 +78,10 @@ type
     ## Statically typed wrapper around a JavaScript object.
   NotString = concept c
     c isnot string
+  js* = JsObject
+
+var jsarguments* {.importc: "arguments", nodecl}: JsObject
+  ## JavaScript's arguments pseudo-variable
 
 # New
 proc newJsObject*: JsObject {. importcpp: "{@}" .}
@@ -93,18 +97,64 @@ proc hasOwnProperty*(x: JsObject, prop: cstring): bool
 proc jsTypeOf*(x: JsObject): cstring {. importcpp: "typeof(#)" .}
   ## Returns the name of the JsObject's JavaScript type as a cstring.
 
+proc jsnew*(x: auto): JsObject {.importcpp: "(new #)".}
+  ## Turns a regular function call into an invocation of the
+  ## JavaScript's `new` operator
+
+proc jsdelete*(x: auto): JsObject {.importcpp: "(delete #)".}
+  ## JavaScript's `delete` operator
+
 # Conversion to and from JsObject
 proc to*(x: JsObject, T: typedesc): T {. importcpp: "(#)" .}
   ## Converts a JsObject `x` to type `T`.
+
 proc toJs*[T](val: T): JsObject {. importcpp: "(#)" .}
   ## Converts a value of any type to type JsObject
 
+template toJs*(s: string): JsObject = cstring(s).toJs
+
+macro jsFromAst*(n: untyped): untyped =
+  result = n
+  if n.kind == nnkStmtList:
+    result = newProc(procType = nnkDo, body = result)
+  return quote: toJs(`result`)
+
+proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".}
+  ## Concatenation operator for JavaScript strings
+
+proc `+`  *(x, y: JsObject): JsObject {. importcpp: "(# + #)" .}
+proc `-`  *(x, y: JsObject): JsObject {. importcpp: "(# - #)" .}
+proc `*`  *(x, y: JsObject): JsObject {. importcpp: "(# * #)" .}
+proc `/`  *(x, y: JsObject): JsObject {. importcpp: "(# / #)" .}
+proc `%`  *(x, y: JsObject): JsObject {. importcpp: "(# % #)" .}
+proc `+=` *(x, y: JsObject): JsObject {. importcpp: "(# += #)", discardable .}
+proc `-=` *(x, y: JsObject): JsObject {. importcpp: "(# -= #)", discardable .}
+proc `*=` *(x, y: JsObject): JsObject {. importcpp: "(# *= #)", discardable .}
+proc `/=` *(x, y: JsObject): JsObject {. importcpp: "(# /= #)", discardable .}
+proc `%=` *(x, y: JsObject): JsObject {. importcpp: "(# %= #)", discardable .}
+proc `++` *(x: JsObject): JsObject    {. importcpp: "(++#)" .}
+proc `--` *(x: JsObject): JsObject    {. importcpp: "(--#)" .}
+proc `>`  *(x, y: JsObject): JsObject {. importcpp: "(# > #)" .}
+proc `<`  *(x, y: JsObject): JsObject {. importcpp: "(# < #)" .}
+proc `>=` *(x, y: JsObject): JsObject {. importcpp: "(# >= #)" .}
+proc `<=` *(x, y: JsObject): JsObject {. importcpp: "(# <= #)" .}
+proc `and`*(x, y: JsObject): JsObject {. importcpp: "(# && #)" .}
+proc `or` *(x, y: JsObject): JsObject {. importcpp: "(# || #)" .}
+proc `not`*(x: JsObject): JsObject    {. importcpp: "(!#)" .}
+proc `in` *(x, y: JsObject): JsObject {. importcpp: "(# in #)" .}
+
 proc `[]`*(obj: JsObject, field: cstring): JsObject {. importcpp: getImpl .}
   ## Return the value of a property of name `field` from a JsObject `obj`.
 
+proc `[]`*(obj: JsObject, field: int): JsObject {. importcpp: getImpl .}
+  ## Return the value of a property of name `field` from a JsObject `obj`.
+
 proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {. importcpp: setImpl .}
   ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
 
+proc `[]=`*[T](obj: JsObject, field: int, val: T) {. importcpp: setImpl .}
+  ## Set the value of a property of name `field` in a JsObject `obj` to `v`.
+
 proc `[]`*[K: NotString, V](obj: JsAssoc[K, V], field: K): V
   {. importcpp: getImpl .}
   ## Return the value of a property of name `field` from a JsAssoc `obj`.
@@ -171,8 +221,9 @@ macro `.=`*(obj: JsObject, field: static[cstring], value: untyped): untyped =
         {. importcpp: `importString`, gensym .}
       helper(`obj`, `value`)
 
-macro `.()`*(obj: JsObject, field: static[cstring],
-    args: varargs[JsObject, toJs]): JsObject =
+macro `.()`*(obj: JsObject,
+             field: static[cstring],
+             args: varargs[JsObject, jsFromAst]): JsObject =
   ## Experimental "method call" operator for type JsObject.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
   ## it with `args` as arguments, returning a JsObject (which may be discarded,
@@ -196,9 +247,9 @@ macro `.()`*(obj: JsObject, field: static[cstring],
     if not mangledNames.hasKey($field):
       mangledNames[$field] = $mangleJsName(field)
     importString = "#." & mangledNames[$field] & "(@)"
-  result = quote do:
+  result = quote:
     proc helper(o: JsObject): JsObject
-      {. importcpp: `importString`, gensym .}
+      {. importcpp: `importString`, gensym, discardable .}
     helper(`obj`)
   for idx in 0 ..< args.len:
     let paramName = newIdentNode(!("param" & $idx))
@@ -206,7 +257,7 @@ macro `.()`*(obj: JsObject, field: static[cstring],
     result[1].add args[idx].copyNimTree
 
 macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
-  field: static[cstring]): V =
+                                   field: static[cstring]): V =
   ## Experimental dot accessor (get) for type JsAssoc.
   ## Returns the value of a property of name `field` from a JsObject `x`.
   var importString: string
@@ -222,7 +273,8 @@ macro `.`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`)
 
 macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
-  field: static[cstring], value: V): untyped =
+                                    field: static[cstring],
+                                    value: V): untyped =
   ## Experimental dot accessor (set) for type JsAssoc.
   ## Sets the value of a property of name `field` in a JsObject `x` to `value`.
   var importString: string
@@ -238,7 +290,8 @@ macro `.=`*[K: string | cstring, V](obj: JsAssoc[K, V],
     helper(`obj`, `value`)
 
 macro `.()`*[K: string | cstring, V: proc](obj: JsAssoc[K, V],
-  field: static[cstring], args: varargs[untyped]): auto =
+                                           field: static[cstring],
+                                           args: varargs[untyped]): auto =
   ## Experimental "method call" operator for type JsAssoc.
   ## Takes the name of a method of the JavaScript object (`field`) and calls
   ## it with `args` as arguments. Here, everything is typechecked, so you do not
diff --git a/lib/system.nim b/lib/system.nim
index 9b41253cc..05b4b88f2 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -49,6 +49,9 @@ type
   cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
   pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
                               ## operator to get a pointer to a variable
+
+  typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
+
 const
   on* = true    ## alias for ``true``
   off* = false  ## alias for ``false``
@@ -56,6 +59,15 @@ const
 {.push warning[GcMem]: off, warning[Uninit]: off.}
 {.push hints: off.}
 
+proc `or` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `or` meta class
+
+proc `and` *(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `and` meta class
+
+proc `not` *(a: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.}
+  ## Constructs an `not` meta class
+
 type
   Ordinal* {.magic: Ordinal.}[T] ## Generic ordinal type. Includes integer,
                                  ## bool, character, and enumeration types
@@ -66,11 +78,11 @@ type
   `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
 
   `nil` {.magic: "Nil".}
+
   expr* {.magic: Expr, deprecated.} ## meta type to denote an expression (for templates)
-                        ## **Deprecated** since version 0.15. Use ``untyped`` instead.
+    ## **Deprecated** since version 0.15. Use ``untyped`` instead.
   stmt* {.magic: Stmt, deprecated.} ## meta type to denote a statement (for templates)
     ## **Deprecated** since version 0.15. Use ``typed`` instead.
-  typedesc* {.magic: TypeDesc.} ## meta type to denote a type description
   void* {.magic: "VoidType".}   ## meta type to denote the absence of any type
   auto* {.magic: Expr.} ## meta type for automatic type determination
   any* = distinct auto ## meta type for any supported type
@@ -1332,6 +1344,7 @@ const
   hasThreadSupport = compileOption("threads") and not defined(nimscript)
   hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own
   taintMode = compileOption("taintmode")
+  nimEnableCovariance* = defined(nimEnableCovariance) # or true
 
 when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"):
   # tcc doesn't support TLS
diff --git a/tests/clearmsg/tmacroerrorproc.nim b/tests/clearmsg/tmacroerrorproc.nim
index 9a6ff6a06..cd9b15e25 100644
--- a/tests/clearmsg/tmacroerrorproc.nim
+++ b/tests/clearmsg/tmacroerrorproc.nim
@@ -7,7 +7,7 @@ discard """
 import macros
 
 macro mixer(n: typed): untyped =
-  expectKind(n, nnkCharLit)
-  
+  expectKind(n[0], nnkCharLit)
+
 mixer:
-  echo "owh"
\ No newline at end of file
+  echo "owh"
diff --git a/tests/closure/tdonotation.nim b/tests/closure/tdonotation.nim
new file mode 100644
index 000000000..94eba8ddb
--- /dev/null
+++ b/tests/closure/tdonotation.nim
@@ -0,0 +1,45 @@
+discard """
+output: '''
+click at 10,20
+lost focus 1
+lost focus 2
+registered handler for UserEvent 1
+registered handler for UserEvent 2
+registered handler for UserEvent 3'''
+"""
+
+import future
+
+type
+  Button = object
+  Event = object
+    x, y: int
+
+proc onClick(x: Button, handler: proc(x: Event)) =
+  handler(Event(x: 10, y: 20))
+
+proc onFocusLost(x: Button, handler: proc()) =
+  handler()
+
+proc onUserEvent(x: Button, eventName: string, handler: proc) =
+  echo "registered handler for ", eventName
+
+var b = Button()
+
+b.onClick do (e: Event):
+  echo "click at ", e.x, ",", e.y
+
+b.onFocusLost:
+  echo "lost focus 1"
+
+b.onFocusLost do:
+  echo "lost focus 2"
+
+b.onUserEvent "UserEvent 1" do:
+  discard
+
+b.onUserEvent "UserEvent 2":
+  discard
+
+b.onUserEvent("UserEvent 3", () => echo "event 3")
+
diff --git a/tests/concepts/t5642.nim b/tests/concepts/t5642.nim
new file mode 100644
index 000000000..d1e7bd1dd
--- /dev/null
+++ b/tests/concepts/t5642.nim
@@ -0,0 +1,25 @@
+discard """
+  output: 9
+"""
+
+type DataTable = concept x
+  x is object
+  for f in fields(x):
+    f is seq
+
+type Students = object
+   id : seq[int]
+   name : seq[string]
+   age: seq[int]
+
+proc nrow*(dt: DataTable) : Natural =
+  var totalLen = 0
+  for f in fields(dt):
+    totalLen += f.len
+  return totalLen
+
+let
+ stud = Students (id : @[1,2,3], name : @["Vas", "Pas", "NafNaf"], age : @[10,16,32])
+
+echo nrow(stud)
+
diff --git a/tests/concepts/tconceptinclosure.nim b/tests/concepts/tconceptinclosure.nim
new file mode 100644
index 000000000..23c1bf293
--- /dev/null
+++ b/tests/concepts/tconceptinclosure.nim
@@ -0,0 +1,53 @@
+discard """
+  output: '''
+10
+20
+int
+20
+3
+'''
+"""
+
+import typetraits
+
+type
+  FonConcept = concept x
+    x.x is int
+
+  GenericConcept[T] = concept x
+    x.x is T
+    const L = T.name.len
+
+  Implementation = object
+    x: int
+
+  Closure = object
+    f: proc()
+
+proc f1(x: FonConcept): Closure =
+  result.f = proc () =
+    echo x.x
+
+proc f2(x: GenericConcept): Closure =
+  result.f = proc () =
+    echo x.x
+    echo GenericConcept.T.name
+
+proc f3[T](x: GenericConcept[T]): Closure =
+  result.f = proc () =
+    echo x.x
+    echo x.L
+
+let x = Implementation(x: 10)
+let y = Implementation(x: 20)
+
+let a = x.f1
+let b = x.f2
+let c = x.f1
+let d = y.f2
+let e = y.f3
+
+a.f()
+d.f()
+e.f()
+
diff --git a/tests/concepts/tmisc_issues.nim b/tests/concepts/tmisc_issues.nim
index 10e072521..d9bb84a2f 100644
--- a/tests/concepts/tmisc_issues.nim
+++ b/tests/concepts/tmisc_issues.nim
@@ -42,7 +42,7 @@ echo p2 is AbstractPointOfFloat      # true
 echo p2.x is float and p2.y is float # true
 
 # https://github.com/nim-lang/Nim/issues/2018
-type ProtocolFollower = generic
+type ProtocolFollower = concept
   true # not a particularly involved protocol
 
 type ImplementorA = object
diff --git a/tests/concepts/trandom_vars.nim b/tests/concepts/trandom_vars.nim
new file mode 100644
index 000000000..a236cebad
--- /dev/null
+++ b/tests/concepts/trandom_vars.nim
@@ -0,0 +1,42 @@
+discard """
+output: "11.0"
+"""
+
+type
+  # A random number generator
+  Random = object
+    random: proc(): float
+  # A generic typeclass for a random var
+  RandomVar[A] = concept x
+    var rng: Random
+    rng.sample(x) is A
+  # A few concrete instances
+  Uniform = object
+    a, b: float
+  ClosureVar[A] = object
+    f: proc(rng: var Random): A
+
+# How to sample from various concrete instances
+proc sample(rng: var Random, u: Uniform): float = u.a + (u.b - u.a) * rng.random()
+
+proc sample[A](rng: var Random, c: ClosureVar[A]): A = c.f(rng)
+
+proc uniform(a, b: float): Uniform = Uniform(a: a, b: b)
+
+# How to lift a function on values to a function on random variables
+proc map[A, B](x: RandomVar[A], f: proc(a: A): B): ClosureVar[B] =
+  proc inner(rng: var Random): B =
+    f(rng.sample(x))
+
+  result.f = inner
+
+import future
+
+proc fakeRandom(): Random =
+  result.random = () => 0.5
+
+let x = uniform(1, 10).map((x: float) => 2 * x)
+
+var rng = fakeRandom()
+
+echo rng.sample(x)
diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim
new file mode 100644
index 000000000..dfe4cb941
--- /dev/null
+++ b/tests/cpp/tcovariancerules.nim
@@ -0,0 +1,424 @@
+discard """
+cmd: "nim cpp $file"
+output: '''
+cat
+cat
+dog
+dog
+cat
+cat
+dog
+dog X
+cat
+cat
+dog
+dog
+dog value
+cat value
+dog value
+cat value
+dog
+dog
+dog value
+cat value
+dog 1
+dog 2
+'''
+"""
+
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+import macros
+
+macro skipElse(n: untyped): typed = n[0]
+
+template acceptWithCovariance(x, otherwise): typed =
+  when nimEnableCovariance:
+    x
+  else:
+    reject(x)
+    skipElse(otherwise)
+
+type
+  Animal = object of RootObj
+    x: string
+
+  Dog = object of Animal
+    y: int
+
+  Cat = object of Animal
+    z: int
+
+  AnimalRef = ref Animal
+  AnimalPtr = ptr Animal
+
+  RefAlias[T] = ref T
+
+var dog = new(Dog)
+dog.x = "dog"
+
+var cat = new(Cat)
+cat.x = "cat"
+
+proc makeDerivedRef(x: string): ref Dog =
+  new(result)
+  result.x = x
+
+proc makeDerived(x: string): Dog =
+  result.x = x
+
+var covariantSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
+var nonCovariantSeq = @[makeDerived("dog 1"), makeDerived("dog 2")]
+var covariantArr = [makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
+var nonCovariantArr = [makeDerived("dog 1"), makeDerived("dog 2")]
+
+proc wantsCovariantSeq1(s: seq[ref Animal]) =
+  for a in s: echo a.x
+
+proc wantsCovariantSeq2(s: seq[AnimalRef]) =
+  for a in s: echo a.x
+
+proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) =
+  for a in s: echo a.x
+
+proc wantsCovariantOperArray(s: openarray[ref Animal]) =
+  for a in s: echo a.x
+
+proc modifiesCovariantOperArray(s: var openarray[ref Animal]) =
+  for a in s: echo a.x
+
+proc modifiesDerivedOperArray(s: var openarray[ref Dog]) =
+  for a in s: echo a.x
+
+proc wantsNonCovariantOperArray(s: openarray[Animal]) =
+  for a in s: echo a.x
+
+proc wantsCovariantArray(s: array[2, ref Animal]) =
+  for a in s: echo a.x
+
+proc wantsNonCovariantSeq(s: seq[Animal]) =
+  for a in s: echo a.x
+
+proc wantsNonCovariantArray(s: array[2, Animal]) =
+  for a in s: echo a.x
+
+proc modifiesCovariantSeq(s: var seq[ref Animal]) =
+  for a in s: echo a.x
+
+proc modifiesCovariantArray(s: var array[2, ref Animal]) =
+  for a in s: echo a.x
+
+proc modifiesCovariantSeq(s: ptr seq[ref Animal]) =
+  for a in s[]: echo a.x
+
+proc modifiesCovariantArray(s: ptr array[2, ref Animal]) =
+  for a in s[]: echo a.x
+
+proc modifiesDerivedSeq(s: var seq[ref Dog]) =
+  for a in s: echo a.x
+
+proc modifiesDerivedArray(s: var array[2, ref Dog]) =
+  for a in s: echo a.x
+
+proc modifiesDerivedSeq(s: ptr seq[ref Dog]) =
+  for a in s[]: echo a.x
+
+proc modifiesDerivedArray(s: ptr array[2, ref Dog]) =
+  for a in s[]: echo a.x
+
+accept:
+  wantsCovariantArray([AnimalRef(dog), AnimalRef(dog)])
+  wantsCovariantArray([AnimalRef(cat), AnimalRef(dog)])
+  wantsCovariantArray([AnimalRef(cat), dog])
+
+  # there is a special rule that detects the base
+  # type of polymorphic arrays
+  wantsCovariantArray([cat, dog])
+
+acceptWithCovariance:
+  wantsCovariantArray([cat, cat])
+else:
+  echo "cat"
+  echo "cat"
+
+var animalRefArray: array[2, ref Animal]
+
+accept:
+  animalRefArray = [AnimalRef(dog), AnimalRef(dog)]
+  animalRefArray = [AnimalRef(cat), dog]
+
+acceptWithCovariance:
+  animalRefArray = [dog, dog]
+  wantsCovariantArray animalRefArray
+else:
+  echo "dog"
+  echo "dog"
+
+accept:
+  var animal: AnimalRef = dog
+  animal = cat
+
+var vdog: Dog
+vdog.x = "dog value"
+var vcat: Cat
+vcat.x = "cat value"
+
+reject:
+  vcat = vdog
+
+# XXX: The next two cases seem incosistent, perhaps we should change the rules
+accept:
+  # truncating copies are allowed
+  var vanimal: Animal = vdog
+  vanimal = vdog
+
+reject:
+  # truncating copies are not allowed with arrays
+  var vanimalArray: array[2, Animal]
+  var vdogArray = [vdog, vdog]
+  vanimalArray = vdogArray
+
+accept:
+  # a more explicit version of a truncating copy that
+  # should probably always remain allowed
+  var vnextnimal: Animal = Animal(vdog)
+
+proc wantsRefSeq(x: seq[AnimalRef]) = discard
+
+accept:
+  wantsCovariantSeq1(@[AnimalRef(dog), AnimalRef(dog)])
+  wantsCovariantSeq1(@[AnimalRef(cat), AnimalRef(dog)])
+  wantsCovariantSeq1(@[AnimalRef(cat), dog])
+  wantsCovariantSeq1(@[cat, dog])
+
+  wantsCovariantSeq2(@[AnimalRef(dog), AnimalRef(dog)])
+  wantsCovariantSeq2(@[AnimalRef(cat), AnimalRef(dog)])
+  wantsCovariantSeq2(@[AnimalRef(cat), dog])
+  wantsCovariantSeq2(@[cat, dog])
+
+  wantsCovariantSeq3(@[AnimalRef(dog), AnimalRef(dog)])
+  wantsCovariantSeq3(@[AnimalRef(cat), AnimalRef(dog)])
+  wantsCovariantSeq3(@[AnimalRef(cat), dog])
+  wantsCovariantSeq3(@[cat, dog])
+
+  wantsCovariantOperArray([cat, dog])
+
+acceptWithCovariance:
+  wantsCovariantSeq1(@[cat, cat])
+  wantsCovariantSeq2(@[dog, makeDerivedRef("dog X")])
+  # XXX: wantsCovariantSeq3(@[cat, cat])
+
+  wantsCovariantOperArray(@[cat, cat])
+  wantsCovariantOperArray([dog, dog])
+else:
+  echo "cat"
+  echo "cat"
+  echo "dog"
+  echo "dog X"
+  echo "cat"
+  echo "cat"
+  echo "dog"
+  echo "dog"
+
+var dogRefs = @[dog, dog]
+var dogRefsArray = [dog, dog]
+var animalRefs = @[dog, cat]
+
+accept:
+  modifiesDerivedArray(dogRefsArray)
+  modifiesDerivedSeq(dogRefs)
+
+reject modifiesCovariantSeq(dogRefs)
+reject modifiesCovariantSeq(addr(dogRefs))
+reject modifiesCovariantSeq(dogRefs.addr)
+
+reject modifiesCovariantArray([dog, dog])
+reject modifiesCovariantArray(dogRefsArray)
+reject modifiesCovariantArray(addr(dogRefsArray))
+reject modifiesCovariantArray(dogRefsArray.addr)
+
+var dogValues = @[vdog, vdog]
+var dogValuesArray = [vdog, vdog]
+var animalValues = @[Animal(vdog), Animal(vcat)]
+var animalValuesArray = [Animal(vdog), Animal(vcat)]
+
+wantsNonCovariantSeq animalValues
+wantsNonCovariantArray animalValuesArray
+
+reject wantsNonCovariantSeq(dogRefs)
+reject modifiesCovariantOperArray(dogRefs)
+reject wantsNonCovariantArray(dogRefsArray)
+reject wantsNonCovariantSeq(dogValues)
+reject wantsNonCovariantArray(dogValuesArray)
+reject modifiesValueArray()
+
+modifiesDerivedOperArray dogRefs
+reject modifiesDerivedOperArray(dogValues)
+reject modifiesDerivedOperArray(animalRefs)
+
+wantsNonCovariantOperArray animalValues
+reject wantsNonCovariantOperArray(animalRefs)
+reject wantsNonCovariantOperArray(dogRefs)
+reject wantsNonCovariantOperArray(dogValues)
+
+var animalRefSeq: seq[ref Animal]
+
+accept:
+  animalRefSeq = @[AnimalRef(dog), AnimalRef(dog)]
+  animalRefSeq = @[AnimalRef(cat), dog]
+
+acceptWithCovariance:
+  animalRefSeq = @[makeDerivedRef("dog 1"), makeDerivedRef("dog 2")]
+  wantsCovariantSeq1(animalRefSeq)
+else:
+  echo "dog 1"
+  echo "dog 2"
+
+var pdog: ptr Dog
+var pcat: ptr Cat
+
+proc wantsPointer(x: ptr Animal) =
+  discard
+
+accept:
+  wantsPointer pdog
+  wantsPointer pcat
+
+# covariance should be disabled when var is involved
+proc wantsVarPointer1(x: var ptr Animal) =
+  discard
+
+proc wantsVarPointer2(x: var AnimalPtr) =
+  discard
+
+reject wantsVarPointer1(pdog)
+reject wantsVarPointer2(pcat)
+
+# covariance may be allowed for certain extern types
+
+{.emit: """
+template <class T> struct FN { typedef void (*type)(T); };
+template <class T> struct ARR { typedef T DataType[2]; DataType data; };
+""".}
+
+type
+  MyPtr {.importcpp: "'0 *"} [out T] = object
+
+  MySeq {.importcpp: "ARR<'0>", nodecl} [out T] = object
+    data: array[2, T]
+
+  MyAction {.importcpp: "FN<'0>::type"} [in T] = object
+
+var
+  cAnimal: MyPtr[Animal]
+  cDog: MyPtr[Dog]
+  cCat: MyPtr[Cat]
+
+  cAnimalFn: MyAction[Animal]
+  cCatFn: MyAction[Cat]
+  cDogFn: MyAction[Dog]
+
+  cRefAnimalFn: MyAction[ref Animal]
+  cRefCatFn: MyAction[ref Cat]
+  cRefDogFn: MyAction[ref Dog]
+
+accept:
+  cAnimal = cDog
+  cAnimal = cCat
+
+  cDogFn = cAnimalFn
+  cCatFn = cAnimalFn
+
+  cRefDogFn = cRefAnimalFn
+  cRefCatFn = cRefAnimalFn
+
+reject: cDogFn = cRefAnimalFn
+reject: cCatFn = cRefAnimalFn
+
+reject: cCat = cDog
+reject: cAnimalFn = cDogFn
+reject: cAnimalFn = cCatFn
+reject: cRefAnimalFn = cRefDogFn
+reject: cRefAnimalFn = cRefCatFn
+reject: cRefAnimalFn = cDogFn
+
+var
+  ptrPtrDog: ptr ptr Dog
+  ptrPtrAnimal: ptr ptr Animal
+
+reject: ptrPtrDog = ptrPtrAnimal
+
+# Try to break the rules by introducing some tricky
+# double indirection types:
+var
+  cPtrRefAnimal: MyPtr[ref Animal]
+  cPtrRefDog: MyPtr[ref Dog]
+
+  cPtrAliasRefAnimal: MyPtr[RefAlias[Animal]]
+  cPtrAliasRefDog: MyPtr[RefAlias[Dog]]
+
+  cDoublePtrAnimal: MyPtr[MyPtr[Animal]]
+  cDoublePtrDog: MyPtr[MyPtr[Dog]]
+
+reject: cPtrRefAnimal = cPtrRefDog
+reject: cDoublePtrAnimal = cDoublePtrDog
+reject: cRefAliasPtrAnimal = cRefAliasPtrDog
+reject: cPtrRefAnimal = cRefAliasPtrDog
+reject: cPtrAliasRefAnimal = cPtrRefDog
+
+var
+  # Array and Sequence types are covariant only
+  # when instantiated with ref or ptr types:
+  cAnimals: MySeq[ref Animal]
+  cDogs: MySeq[ref Dog]
+
+  # "User-defined" pointer types should be OK too:
+  cAnimalPtrSeq: MySeq[MyPtr[Animal]]
+  cDogPtrSeq: MySeq[MyPtr[Dog]]
+
+  # Value types shouldn't work:
+  cAnimalValues: MySeq[Animal]
+  cDogValues: MySeq[Dog]
+
+  # Double pointer types should not work either:
+  cAnimalRefPtrSeq: MySeq[ref MyPtr[Animal]]
+  cDogRefPtrSeq: MySeq[ref MyPtr[Dog]]
+  cAnimalPtrPtrSeq: MySeq[ptr ptr Animal]
+  cDogPtrPtrSeq: MySeq[ptr ptr Dog]
+
+accept:
+  cAnimals = cDogs
+  cAnimalPtrSeq = cDogPtrSeq
+
+reject: cAnimalValues = cDogValues
+reject: cAnimalRefPtrSeq = cDogRefPtrSeq
+reject: cAnimalPtrPtrSeq = cDogPtrPtrSeq
+
+proc wantsAnimalSeq(x: MySeq[Animal]) = discard
+proc wantsAnimalRefSeq(x: MySeq[ref Animal]) = discard
+proc modifiesAnimalRefSeq(x: var MySeq[ref Animal]) = discard
+proc usesAddressOfAnimalRefSeq(x: ptr MySeq[ref Animal]) = discard
+
+accept wantsAnimalSeq(cAnimalValues)
+reject wantsAnimalSeq(cDogValues)
+reject wantsAnimalSeq(cAnimals)
+
+reject wantsAnimalRefSeq(cAnimalValues)
+reject wantsAnimalRefSeq(cDogValues)
+accept wantsAnimalRefSeq(cAnimals)
+accept wantsAnimalRefSeq(cDogs)
+
+reject modifiesAnimalRefSeq(cAnimalValues)
+reject modifiesAnimalRefSeq(cDogValues)
+accept modifiesAnimalRefSeq(cAnimals)
+reject modifiesAnimalRefSeq(cDogs)
+
+reject usesAddressOfAnimalRefSeq(addr cAnimalValues)
+reject usesAddressOfAnimalRefSeq(addr cDogValues)
+accept usesAddressOfAnimalRefSeq(addr cAnimals)
+reject usesAddressOfAnimalRefSeq(addr cDogs)
+
diff --git a/tests/errmsgs/tinvalidinout.nim b/tests/errmsgs/tinvalidinout.nim
new file mode 100644
index 000000000..ce7eb6022
--- /dev/null
+++ b/tests/errmsgs/tinvalidinout.nim
@@ -0,0 +1,26 @@
+discard """
+cmd: "nim check $file"
+errormsg: "The `in` modifier can be used only with imported types"
+nimout: '''
+tinvalidinout.nim(14, 7) Error: The `out` modifier can be used only with imported types
+tinvalidinout.nim(17, 9) Error: The `in` modifier can be used only with imported types
+tinvalidinout.nim(18, 9) Error: The `in` modifier can be used only with imported types
+'''
+"""
+
+type
+  Foo {.header: "foo.h", importcpp.} [in T] = object
+
+  Bar[out X] = object
+    x: int
+
+proc f1[in T](x: T) = discard
+proc f2[in T](x: T) {.importc: "f", header: "foo.h"}
+
+var
+  f: Foo[int]
+  b: Bar[string]
+
+f1 f
+f2 b
+
diff --git a/tests/generics/t5570.nim b/tests/generics/t5570.nim
new file mode 100644
index 000000000..e3f9ff415
--- /dev/null
+++ b/tests/generics/t5570.nim
@@ -0,0 +1,27 @@
+discard """
+  nimout: "type uint32\ntype uint32"
+  output: "(weight: 17.0, color: 100)"
+"""
+
+import macros
+
+type
+  BaseFruit[T] = object of RootObj
+    color: T
+
+  Banana[T] = object of BaseFruit[uint32]
+    weight: T
+
+macro printTypeName(typ: typed): untyped =
+  echo "type ", getType(typ).repr
+
+proc setColor[K](self: var BaseFruit[K], c: int) =
+  printTypeName(self.color)
+  self.color = uint32(c)
+
+var x: Banana[float64]
+x.weight = 17
+printTypeName(x.color)
+x.setColor(100)
+echo x
+
diff --git a/tests/generics/t5602_inheritence.nim b/tests/generics/t5602_inheritence.nim
new file mode 100644
index 000000000..6d48c796e
--- /dev/null
+++ b/tests/generics/t5602_inheritence.nim
@@ -0,0 +1,18 @@
+discard """
+  output: "seq[float]\n0"
+"""
+
+# https://github.com/nim-lang/Nim/issues/5602
+
+import typetraits
+
+type
+  Foo[T] = object of RootObj
+  Bar[T] = object of Foo[seq[T]]
+
+proc p[T](f: Foo[T]): T =
+  echo T.name
+
+var s: Bar[float]
+echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead
+
diff --git a/tests/generics/t5643.nim b/tests/generics/t5643.nim
new file mode 100644
index 000000000..962d5cef5
--- /dev/null
+++ b/tests/generics/t5643.nim
@@ -0,0 +1,30 @@
+type
+  Matrix*[M, N: static[int], T: SomeReal] = object
+    data: ref array[N * M, T]
+
+  Matrix64*[M, N: static[int]] = Matrix[M, N, float64]
+
+proc zeros64(M,N: static[int]): Matrix64[M,N] =
+  new result.data
+  for i in 0 .. < (M * N):
+    result.data[i] = 0'f64
+
+proc bar*[M,N: static[int], T](a: Matrix[M,N,T], b: Matrix[M,N,T]) =
+  discard
+
+let a = zeros64(2,2)
+bar(a,a)
+  # https://github.com/nim-lang/Nim/issues/5643
+  #
+  # The test case was failing here, because the compiler failed to
+  # detect the two matrix instantiations as the same type.
+  #
+  # The root cause was that the `T` type variable is a different
+  # type after the first Matrix type has been matched.
+  #
+  # Sigmatch was failing to match the second version of `T`, but
+  # due to some complex interplay between tyOr, tyTypeDesc and
+  # tyGenericParam this was allowed to went through. The generic
+  # instantiation of the second matrix was incomplete and the
+  # generic cache lookup failed, producing two separate types.
+
diff --git a/tests/generics/t5683.nim b/tests/generics/t5683.nim
new file mode 100644
index 000000000..38da52ec2
--- /dev/null
+++ b/tests/generics/t5683.nim
@@ -0,0 +1,31 @@
+discard """
+output: "perm: 22 det: 22"
+"""
+
+type Matrix[M,N: static[int]] = array[M, array[N, float]]
+
+proc det[M,N](a: Matrix[M,N]): int = N*10 + M
+proc perm[M,N](a: Matrix[M,N]): int = M*10 + N
+
+const
+  a = [ [1.0, 2.0]
+      , [3.0, 4.0]
+      ]
+
+echo "perm: ", a.perm, " det: ", a.det
+
+# This tests multiple instantiations of a generic
+# proc involving static params:
+type
+  Vector64*[N: static[int]] = ref array[N, float64]
+  Array64[N: static[int]] = array[N, float64]
+
+proc vector*[N: static[int]](xs: Array64[N]): Vector64[N] =
+  new result
+  for i in 0 .. < N:
+    result[i] = xs[i]
+
+let v1 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+let v2 = vector([1.0, 2.0, 3.0, 4.0, 5.0])
+let v3 = vector([1.0, 2.0, 3.0, 4.0])
+
diff --git a/tests/generics/tbindoncevsbindmany.nim b/tests/generics/tbindoncevsbindmany.nim
new file mode 100644
index 000000000..01e801f0e
--- /dev/null
+++ b/tests/generics/tbindoncevsbindmany.nim
@@ -0,0 +1,68 @@
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+type
+  ObjectWithNumber = concept obj
+    obj.number is int
+
+  Foo[T] = object
+    x: T
+
+type A = object
+  anumber: int
+
+type B = object
+  bnumber: int
+
+proc number(a: A): int = a.anumber
+proc number(b: B): int = b.bnumber
+
+proc notDistincConcept1(a: ObjectWithNumber, b: ObjectWithNumber) = discard
+proc notDistincConcept2(a, b: ObjectWithNumber) = discard
+proc distinctConcept1(a, b: distinct ObjectWithNumber) = discard
+proc distinctConcept2(a: ObjectWithNumber, b: distinct ObjectWithNumber) = discard
+proc distinctConcept3(a: distinct ObjectWithNumber, b: ObjectWithNumber) = discard
+proc distinctConcept4(a: distinct ObjectWithNumber, b: distinct ObjectWithNumber) = discard
+
+var a = A(anumber: 5)
+var b = B(bnumber: 6)
+
+accept notDistincConcept1(a, a)
+accept notDistincConcept1(b, b)
+reject notDistincConcept2(a, b)
+
+accept notDistincConcept2(a, a)
+accept notDistincConcept2(b, b)
+reject notDistincConcept2(a, b)
+
+accept distinctConcept1(a, b)
+accept distinctConcept2(a, b)
+accept distinctConcept3(a, b)
+accept distinctConcept4(a, b)
+
+proc nonDistincGeneric1(a: Foo, b: Foo) = discard
+proc nonDistincGeneric2(a, b: Foo) = discard
+proc distinctGeneric1(a, b: distinct Foo) = discard
+proc distinctGeneric2(a: distinct Foo, b: Foo) = discard
+proc distinctGeneric3(a: Foo, b: distinct Foo) = discard
+proc distinctGeneric4(a: distinct Foo, b: distinct Foo) = discard
+
+var f1 = Foo[int](x: 10)
+var f2 = Foo[string](x: "x")
+
+accept nonDistincGeneric1(f1, f1)
+accept nonDistincGeneric1(f2, f2)
+reject nonDistincGeneric1(f1, f2)
+
+accept nonDistincGeneric2(f1, f1)
+accept nonDistincGeneric2(f2, f2)
+reject nonDistincGeneric2(f1, f2)
+
+accept distinctGeneric1(f1, f1)
+accept distinctGeneric2(f1, f1)
+accept distinctGeneric3(f1, f1)
+accept distinctGeneric4(f1, f1)
+
diff --git a/tests/generics/tfakecovariance.nim b/tests/generics/tfakecovariance.nim
new file mode 100644
index 000000000..0920cb504
--- /dev/null
+++ b/tests/generics/tfakecovariance.nim
@@ -0,0 +1,78 @@
+template accept(x) =
+  static: assert(compiles(x))
+
+template reject(x) =
+  static: assert(not compiles(x))
+
+type
+  BaseObj = object of RootObj
+  DerivedObj = object of BaseObj
+  NonDerivedObj = object
+
+  Container[T] = object
+
+var base: BaseObj
+var derived: DerivedObj
+var nonDerived: NonDerivedObj
+
+var baseContainer: Container[BaseObj]
+var derivedContainer: Container[DerivedObj]
+var nonDerivedContainer: Container[NonDerivedObj]
+
+# We can fake covariance by listing some specific derived types that
+# will be allowed with our overload. This is not a real covariance,
+# because there will be multiple instantiations of the proc, but for
+# many purposes, it will suffice:
+
+proc wantsSpecificContainers(c: Container[BaseObj or DerivedObj]) = discard
+
+accept wantsSpecificContainers(baseContainer)
+accept wantsSpecificContainers(derivedContainer)
+
+reject wantsSpecificContainers(nonDerivedContainer)
+reject wantsSpecificContainers(derived)
+
+# Now, let's make a more general solution able to catch all derived types:
+
+type
+  DerivedFrom[T] = concept type D
+    var derived: ref D
+    var base: ref T = derived
+
+proc wantsDerived(x: DerivedFrom[BaseObj]) = discard
+
+accept wantsDerived(base)
+accept wantsDerived(derived)
+
+reject wantsDerived(nonDerived)
+reject wantsDerived(baseContainer)
+
+proc wantsDerivedContainer(c: Container[DerivedFrom[BaseObj]]) = discard
+
+accept wantsDerivedContainer(baseContainer)
+accept wantsDerivedContainer(derivedContainer)
+
+reject wantsDerivedContainer(nonDerivedContainer)
+
+# The previous solutions were solving the problem for a single overload.
+# Let's solve it for multiple overloads by introducing a converter:
+
+type
+  OtherContainer[T] = object
+
+proc wantsBaseContainer1(c: OtherContainer[BaseObj]) = discard
+proc wantsBaseContainer2(c: OtherContainer[BaseObj]) = discard
+
+converter derivedToBase(c: OtherContainer[DerivedFrom[BaseObj]]): OtherContainer[BaseObj] = discard
+
+block:
+  var baseContainer: OtherContainer[BaseObj]
+  var derivedContainer: OtherContainer[DerivedObj]
+  var nonDerivedContainer: OtherContainer[NonDerivedObj]
+
+  accept wantsBaseContainer1(derivedContainer)
+  reject wantsBaseContainer1(nonDerivedContainer)
+
+  accept wantsBaseContainer2(derivedContainer)
+  reject wantsBaseContainer2(nonDerivedContainer)
+
diff --git a/tests/generics/tgenericconst.nim b/tests/generics/tgenericconst.nim
new file mode 100644
index 000000000..3c86888df
--- /dev/null
+++ b/tests/generics/tgenericconst.nim
@@ -0,0 +1,39 @@
+discard """
+output: '''
+@[1, 2]
+@[3, 4]
+1
+'''
+"""
+
+# https://github.com/nim-lang/Nim/issues/5756
+
+type
+  Vec*[N : static[int]] = object
+    x: int
+    arr*: array[N, int32]
+
+  Mat*[M,N: static[int]] = object
+    x: int
+    arr*: array[M, Vec[N]]
+
+proc vec2*(x,y:int32) : Vec[2] =
+  result.arr = [x,y]
+  result.x = 10
+
+proc mat2*(a,b: Vec[2]): Mat[2,2] =
+  result.arr = [a,b]
+  result.x = 20
+
+const M = mat2(vec2(1, 2), vec2(3, 4))
+
+let m1 = M
+echo @(m1.arr[0].arr)
+echo @(m1.arr[1].arr)
+
+proc foo =
+  let m2 = M
+  echo m1.arr[0].arr[0]
+
+foo()
+
diff --git a/tests/generics/tmapping_generic_alias.nim b/tests/generics/tmapping_generic_alias.nim
new file mode 100644
index 000000000..efdf32ead
--- /dev/null
+++ b/tests/generics/tmapping_generic_alias.nim
@@ -0,0 +1,28 @@
+discard """
+output: '''type(c) = GenAlias[system.int]
+T = int
+seq[int]
+'''
+"""
+
+import typetraits
+
+type
+  Gen[T] = object
+    x: T
+
+  GenAlias[T] = Gen[seq[T]]
+
+proc f1[T](x: Gen[T]) =
+  echo T.name
+
+proc f2[T](x: GenAlias[T]) =
+  echo "type(c) = ", type(x).name
+  echo "T = ", T.name
+  f1 x
+
+let
+  y = Gen[seq[int]](x: @[10])
+
+f2 y
+
diff --git a/tests/generics/tparam_binding.nim b/tests/generics/tparam_binding.nim
new file mode 100644
index 000000000..643e9b226
--- /dev/null
+++ b/tests/generics/tparam_binding.nim
@@ -0,0 +1,28 @@
+discard """
+  errormsg: "got (ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float])"
+  line: 27
+"""
+
+type
+  Matrix[M,N: static[int]; T: SomeReal] = distinct array[0..(M*N - 1), T]
+
+let a = new Matrix[2,2,float]
+let b = new Matrix[2,1,float]
+
+proc foo[M,N: static[int],T](a: ref Matrix[M, N, T], b: ref Matrix[M, N, T])=
+  discard
+
+foo(a, a)
+
+proc bar[M,N: static[int],T](a: ref Matrix[M, M, T], b: ref Matrix[M, N, T])=
+  discard
+
+bar(a, b)
+bar(a, a)
+
+proc baz[M,N: static[int],T](a: ref Matrix[N, N, T], b: ref Matrix[M, N, T])=
+  discard
+
+baz(a, a)
+baz(a, b)
+
diff --git a/tests/generics/tptrinheritance.nim b/tests/generics/tptrinheritance.nim
new file mode 100644
index 000000000..221b8777b
--- /dev/null
+++ b/tests/generics/tptrinheritance.nim
@@ -0,0 +1,20 @@
+type NSPasteboardItem* = ptr object
+type NSPasteboard* = ptr object
+type NSArrayAbstract = ptr object {.inheritable.}
+type NSMutableArrayAbstract = ptr object of NSArrayAbstract
+type NSArray*[T] = ptr object of NSArrayAbstract
+type NSMutableArray*[T] = ptr object of NSArray[T]
+
+proc newMutableArrayAbstract*(): NSMutableArrayAbstract = discard
+
+template newMutableArray*(T: typedesc): NSMutableArray[T] =
+  cast[NSMutableArray[T]](newMutableArrayAbstract())
+
+proc writeObjects*(p: NSPasteboard, o: NSArray[NSPasteboardItem]) = discard
+
+let a = newMutableArray NSPasteboardItem
+var x: NSMutableArray[NSPasteboardItem]
+var y: NSArray[NSPasteboardItem] = x
+
+writeObjects(nil, a)
+
diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim
index 71eb211e3..e4aad4b99 100644
--- a/tests/js/tjsffi.nim
+++ b/tests/js/tjsffi.nim
@@ -1,5 +1,5 @@
 discard """
-  output: '''true
+output: '''
 true
 true
 true
@@ -14,10 +14,18 @@ true
 true
 true
 true
-true'''
+true
+true
+3
+2
+12
+Event { name: 'click: test' }
+Event { name: 'reloaded: test' }
+Event { name: 'updates: test' }
+'''
 """
 
-import macros, jsffi
+import macros, jsffi, jsconsole
 
 # Tests for JsObject
 # Test JsObject []= and []
@@ -55,8 +63,8 @@ block:
 block:
   proc test(): bool =
     let obj = newJsObject()
-    obj.`?!$` = proc(x, y, z: int, t: string): string = t & $(x + y + z)
-    obj.`?!$`(1, 2, 3, "Result is: ").to(string) == "Result is: 6"
+    obj.`?!$` = proc(x, y, z: int, t: cstring): cstring = t & $(x + y + z)
+    obj.`?!$`(1, 2, 3, "Result is: ").to(cstring) == "Result is: 6"
   echo test()
 
 # Test JsObject []()
@@ -265,3 +273,47 @@ block:
     let obj = TestObject(a: 9, onWhatever: bindMethod(handleWhatever))
     obj.onWhatever(1) == 10
   echo test()
+
+block:
+  {.emit: "function jsProc(n) { return n; }" .}
+  proc jsProc(x: int32): JsObject {.importc: "jsProc".}
+
+  proc test() =
+    var x = jsProc(1)
+    var y = jsProc(2)
+    console.log x + y
+    console.log ++x
+
+    x += jsProc(10)
+    console.log x
+
+  test()
+
+import macros
+
+block:
+  {.emit:
+  """
+  function Event(name) { this.name = name; }
+  function on(eventName, eventHandler) { eventHandler(new Event(eventName + ": test")); }
+  var jslib = { "on": on, "subscribe": on };
+  """
+  .}
+
+  type Event = object
+    name: cstring
+
+  proc on(event: cstring, handler: proc) {.importc: "on".}
+  var jslib {.importc: "jslib", nodecl.}: JsObject
+
+  on("click") do (e: Event):
+    console.log e
+
+  jslib.on "reloaded" do:
+    console.log jsarguments[0]
+
+  # this test case is different from the above, because
+  # `subscribe` is not overloaded in the current scope
+  jslib.subscribe "updates":
+    console.log jsarguments[0]
+
diff --git a/tests/macros/tlexerex.nim b/tests/macros/tlexerex.nim
index d348a4bcc..db2c38ef6 100644
--- a/tests/macros/tlexerex.nim
+++ b/tests/macros/tlexerex.nim
@@ -1,8 +1,7 @@
-
 import macros
 
-macro match*(s: cstring|string; pos: int; sections: untyped): untyped =
-  for sec in sections.children:
+macro match*(s: cstring|string; pos: int; sections: varargs[untyped]): untyped =
+  for sec in sections:
     expectKind sec, nnkOfBranch
     expectLen sec, 2
   result = newStmtList()
diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim
new file mode 100644
index 000000000..85f2628bf
--- /dev/null
+++ b/tests/parser/tpostexprblocks.nim
@@ -0,0 +1,513 @@
+discard """
+nimout: '''
+StmtList
+  Ident !"foo"
+  Call
+    Ident !"foo"
+  Call
+    Ident !"foo"
+    Ident !"x"
+  Command
+    Ident !"foo"
+    Ident !"x"
+  Call
+    Ident !"foo"
+    StmtList
+      DiscardStmt
+        Empty
+  Call
+    Ident !"foo"
+    StmtList
+      DiscardStmt
+        Empty
+  Call
+    Ident !"foo"
+    StrLit test
+    StmtList
+      DiscardStmt
+        Empty
+  Call
+    Ident !"foo"
+    StrLit test
+    StmtList
+      DiscardStmt
+        Empty
+  Command
+    Ident !"foo"
+    StrLit test
+    StmtList
+      DiscardStmt
+        Empty
+  Command
+    Ident !"foo"
+    StrLit test
+    StmtList
+      DiscardStmt
+        Empty
+  Command
+    Ident !"foo"
+    IntLit 1
+    Par
+      Infix
+        Ident !"+"
+        IntLit 2
+        IntLit 3
+    StmtList
+      DiscardStmt
+        Empty
+  Command
+    Ident !"foo"
+    IntLit 1
+    Par
+      Infix
+        Ident !"+"
+        IntLit 2
+        IntLit 3
+    StmtList
+      DiscardStmt
+        Empty
+  Call
+    Ident !"foo"
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Empty
+        IdentDefs
+          Ident !"x"
+          Empty
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+  Call
+    Ident !"foo"
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Empty
+        IdentDefs
+          Ident !"x"
+          Ident !"int"
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+  Call
+    Ident !"foo"
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Ident !"int"
+        IdentDefs
+          Ident !"x"
+          Ident !"int"
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+  Command
+    Ident !"foo"
+    Ident !"x"
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Empty
+        IdentDefs
+          Ident !"y"
+          Empty
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+  Call
+    Ident !"foo"
+    StmtList
+      DiscardStmt
+        Empty
+    Else
+      StmtList
+        DiscardStmt
+          Empty
+  Call
+    Ident !"foo"
+    StmtList
+      DiscardStmt
+        Empty
+    StmtList
+      DiscardStmt
+        Empty
+    Else
+      StmtList
+        DiscardStmt
+          Empty
+  Command
+    Ident !"foo"
+    Ident !"x"
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Empty
+        IdentDefs
+          Ident !"y"
+          Empty
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Ident !"int"
+        IdentDefs
+          Ident !"z"
+          Empty
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+    Do
+      Empty
+      Empty
+      Empty
+      FormalParams
+        Ident !"int"
+        IdentDefs
+          Ident !"w"
+          Ident !"int"
+          Empty
+      Empty
+      Empty
+      StmtList
+        DiscardStmt
+          Empty
+    StmtList
+      DiscardStmt
+        Empty
+    Else
+      StmtList
+        DiscardStmt
+          Empty
+  Call
+    Ident !"foo"
+    Ident !"x"
+    Call
+      Ident !"bar"
+      StmtList
+        DiscardStmt
+          Empty
+      Else
+        StmtList
+          DiscardStmt
+            Empty
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Ident !"foo"
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Call
+        Ident !"foo"
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Call
+        Ident !"foo"
+        Ident !"x"
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Command
+        Ident !"foo"
+        Ident !"x"
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Call
+        Ident !"foo"
+        StmtList
+          DiscardStmt
+            Empty
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Call
+        Ident !"foo"
+        StmtList
+          DiscardStmt
+            Empty
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Call
+        Ident !"foo"
+        StmtList
+          DiscardStmt
+            Empty
+        Else
+          StmtList
+            DiscardStmt
+              Empty
+  VarSection
+    IdentDefs
+      Ident !"a"
+      Empty
+      Command
+        Ident !"foo"
+        Ident !"x"
+        Do
+          Empty
+          Empty
+          Empty
+          FormalParams
+            Empty
+            IdentDefs
+              Ident !"y"
+              Empty
+              Empty
+          Empty
+          Empty
+          StmtList
+            DiscardStmt
+              Empty
+        Else
+          StmtList
+            DiscardStmt
+              Empty
+  Asgn
+    Ident !"a"
+    Ident !"foo"
+  Asgn
+    Ident !"a"
+    Call
+      Ident !"foo"
+  Asgn
+    Ident !"a"
+    Call
+      Ident !"foo"
+      Ident !"x"
+  Asgn
+    Ident !"a"
+    Command
+      Ident !"foo"
+      Ident !"x"
+  Asgn
+    Ident !"a"
+    Call
+      Ident !"foo"
+      StmtList
+        DiscardStmt
+          Empty
+  Asgn
+    Ident !"a"
+    Call
+      Ident !"foo"
+      StmtList
+        DiscardStmt
+          Empty
+  Asgn
+    Ident !"a"
+    Call
+      Ident !"foo"
+      StmtList
+        DiscardStmt
+          Empty
+      Else
+        StmtList
+          DiscardStmt
+            Empty
+  Asgn
+    Ident !"a"
+    Command
+      Ident !"foo"
+      Ident !"x"
+      Do
+        Empty
+        Empty
+        Empty
+        FormalParams
+          Empty
+          IdentDefs
+            Ident !"y"
+            Empty
+            Empty
+        Empty
+        Empty
+        StmtList
+          DiscardStmt
+            Empty
+      Else
+        StmtList
+          DiscardStmt
+            Empty
+  Call
+    DotExpr
+      Ident !"result"
+      Ident !"add"
+    BracketExpr
+      Call
+        Ident !"quote"
+        StmtList
+          DiscardStmt
+            Empty
+      IntLit 0
+'''
+"""
+
+import macros
+
+dumpTree:
+  # simple calls
+  foo
+  foo()
+  foo(x)
+  foo x
+
+  foo:
+    discard
+
+  foo do:
+    discard
+
+  foo("test"):
+    discard
+
+  foo("test") do:
+    discard
+
+  foo "test":
+    discard
+
+  foo "test" do:
+    discard
+
+  # more complicated calls
+  foo 1, (2+3):
+    discard
+
+  foo 1, (2+3) do:
+    discard
+
+  foo do (x):
+    discard
+
+  foo do (x: int):
+    discard
+
+  foo do (x: int) -> int:
+    discard
+
+  foo x do (y):
+    discard
+
+  # extra blocks
+  foo:
+    discard
+  else:
+    discard
+
+  foo do:
+    discard
+  do:
+    discard
+  else:
+    discard
+
+  foo x do (y):
+    discard
+  do (z) -> int:
+    discard
+  do (w: int) -> int:
+    discard
+  do:
+    discard
+  else:
+    discard
+
+  # call with blocks as a param
+  foo(x, bar do:
+    discard
+  else:
+    discard
+  )
+
+  # introduce a variable
+  var a = foo
+  var a = foo()
+  var a = foo(x)
+  var a = foo x
+
+  var a = foo:
+    discard
+
+  var a = foo do:
+    discard
+
+  var a = foo do:
+    discard
+  else:
+    discard
+
+  var a = foo x do (y):
+    discard
+  else:
+    discard
+
+  # assignments
+  a = foo
+  a = foo()
+  a = foo(x)
+  a = foo x
+
+  a = foo:
+    discard
+
+  a = foo do:
+    discard
+
+  a = foo do:
+    discard
+  else:
+    discard
+
+  a = foo x do (y):
+    discard
+  else:
+    discard
+
+  # some edge cases
+  result.add((quote do:
+    discard
+  )[0])
+
diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim
index b05cb5a10..6a53a2964 100644
--- a/tests/stdlib/tmarshal.nim
+++ b/tests/stdlib/tmarshal.nim
@@ -1,7 +1,10 @@
 discard """
   output: '''{"age": 12, "bio": "\u042F Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"}
 true
-true'''
+true
+alpha 100
+omega 200
+'''
 """
 
 import marshal
@@ -83,3 +86,22 @@ var instance1 = Person(name: "Cletus", age: 12,
 echo($$instance1)
 echo(to[Person]($$instance1).bio == instance1.bio)
 echo(to[Person]($$instance1).blob == instance1.blob)
+
+# bug 5757
+
+type
+  Something = object
+    x: string
+    y: int
+
+var data1 = """{"x": "alpha", "y": 100}"""
+var data2 = """{"x": "omega", "y": 200}"""
+
+var r = to[Something](data1)
+
+echo r.x, " ", r.y
+
+r = to[Something](data2)
+
+echo r.x, " ", r.y
+
diff --git a/tests/template/tgenerictemplates.nim b/tests/template/tgenerictemplates.nim
new file mode 100644
index 000000000..2c83bc0ec
--- /dev/null
+++ b/tests/template/tgenerictemplates.nim
@@ -0,0 +1,13 @@
+type
+  SomeObj = object of RootObj
+
+  Foo[T, U] = object
+    x: T
+    y: U
+
+template someTemplate[T](): tuple[id: int32, obj: T] =
+  var result: tuple[id: int32, obj: T] = (0'i32, T())
+  result
+
+let ret = someTemplate[SomeObj]()
+
diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim
index 0fadbde41..4cc64a831 100644
--- a/tests/template/tgensymregression.nim
+++ b/tests/template/tgensymregression.nim
@@ -59,14 +59,13 @@ template wrap(body: typed): untyped =
 
 macro makeProc(): typed =
   # Make a template tree
-  result = (quote do:
+  result = quote do:
     proc someProc* =
       wrap do:
         let x = 123
         # Implicit conversion here
         let s: string = x
         echo s
-  )
 
 makeProc()
 
diff --git a/tests/types/t5640.nim b/tests/types/t5640.nim
new file mode 100644
index 000000000..5e1c99c4d
--- /dev/null
+++ b/tests/types/t5640.nim
@@ -0,0 +1,6 @@
+type
+  vecBase[I: static[int]] = distinct array[I, float32]
+  vec2* = vecBase[2]
+
+var v = vec2([0.0'f32, 0.0'f32])
+
diff --git a/tests/types/t5648.nim b/tests/types/t5648.nim
new file mode 100644
index 000000000..c230cc12c
--- /dev/null
+++ b/tests/types/t5648.nim
@@ -0,0 +1,21 @@
+discard """
+output: "ptr Foo"
+"""
+
+import typetraits
+
+type Foo = object
+  bar*: int
+
+proc main() =
+  var f = create(Foo)
+  f.bar = 3
+  echo f.type.name
+
+  discard realloc(f, 0)
+
+  var g = Foo()
+  g.bar = 3
+
+main()
+
diff --git a/tests/types/thard_tyforward.nim b/tests/types/thard_tyforward.nim
new file mode 100644
index 000000000..7131cd64b
--- /dev/null
+++ b/tests/types/thard_tyforward.nim
@@ -0,0 +1,22 @@
+type
+  Bar[T] = Foo[T, T]
+  Baz[T] = proc (x: Foo[T, T])
+  
+  GenericAlias[T] = Foo[T]
+  GenericAlias2[T] = Foo[Baz[T]]
+  
+  Concrete1 = Foo[int, float]
+  Concrete2 = proc(x: proc(a: Foo[int, float]))
+  
+  Foo[T, U] = object
+    x: T
+    y: U
+
+var
+  x1: Bar[float]
+  x2: Baz[int]
+  x3: Concrete1
+  x4: Concrete2
+  x5: GenericAlias[int]
+  x6: GenericAlias2[string]
+
diff --git a/web/bountysource.nim b/web/bountysource.nim
index 1d47cea56..5dfdb4497 100644
--- a/web/bountysource.nim
+++ b/web/bountysource.nim
@@ -33,13 +33,13 @@ proc getSupporters(self: BountySource): Future[JsonNode] {.async.} =
   let response = await self.client.get(apiUrl &
     "/supporters?order=monthly&per_page=200&team_slug=" & self.team)
   doAssert response.status.startsWith($Http200)
-  return parseJson(response.body)
+  return parseJson(await response.body)
 
 proc getGithubUser(username: string): Future[JsonNode] {.async.} =
   let client = newAsyncHttpClient()
   let response = await client.get(githubApiUrl & "/users/" & username)
   if response.status.startsWith($Http200):
-    return parseJson(response.body)
+    return parseJson(await response.body)
   else:
     echo("Could not get Github user: ", username, ". ", response.status)
     return nil