summary refs log tree commit diff stats
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-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
23 files changed, 656 insertions, 313 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,