summary refs log tree commit diff stats
path: root/compiler/sigmatch.nim
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/sigmatch.nim')
-rw-r--r--compiler/sigmatch.nim733
1 files changed, 440 insertions, 293 deletions
diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim
index 123d1df2e..5c8a3bc58 100644
--- a/compiler/sigmatch.nim
+++ b/compiler/sigmatch.nim
@@ -10,7 +10,7 @@
 ## This module implements the signature matching for resolving
 ## the call to overloaded procs, generic procs and operators.
 
-import 
+import
   intsets, ast, astalgo, semdata, types, msgs, renderer, lookups, semtypinst,
   magicsys, condsyms, idents, lexer, options, parampatterns, strutils, trees,
   nimfix.pretty
@@ -19,7 +19,7 @@ when not defined(noDocgen):
   import docgen
 
 type
-  TCandidateState* = enum 
+  TCandidateState* = enum
     csEmpty, csMatch, csNoMatch
 
   CandidateErrors* = seq[PSym]
@@ -39,12 +39,14 @@ type
     bindings*: TIdTable      # maps types to types
     baseTypeMatch: bool      # needed for conversions from T to openarray[T]
                              # for example
-    proxyMatch*: bool        # to prevent instantiations
+    fauxMatch*: TTypeKind    # the match was successful only due to the use
+                             # of error or wildcard (unknown) types.
+                             # this is used to prevent instantiations.
     genericConverter*: bool  # true if a generic converter needs to
                              # be instantiated
     coerceDistincts*: bool   # this is an explicit coercion that can strip away
                              # a distrinct type
-    typedescMatched: bool
+    typedescMatched*: bool
     inheritancePenalty: int  # to prefer closest father object type
     errors*: CandidateErrors # additional clarifications to be displayed to the
                              # user if overload resolution fails
@@ -60,12 +62,14 @@ type
     isGeneric,
     isFromIntLit,            # conversion *from* int literal; proven safe
     isEqual
-  
+
 const
   isNilConversion = isConvertible # maybe 'isIntConv' fits better?
-    
+
 proc markUsed*(info: TLineInfo, s: PSym)
 
+template hasFauxMatch*(c: TCandidate): bool = c.fauxMatch != tyNone
+
 proc initCandidateAux(ctx: PContext,
                       c: var TCandidate, callee: PType) {.inline.} =
   c.c = ctx
@@ -87,7 +91,7 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PType) =
   initIdTable(c.bindings)
 
 proc put(t: var TIdTable, key, val: PType) {.inline.} =
-  idTablePut(t, key, val)
+  idTablePut(t, key, val.skipIntLit)
 
 proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
                     binding: PNode, calleeScope = -1) =
@@ -95,9 +99,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
   c.calleeSym = callee
   if callee.kind in skProcKinds and calleeScope == -1:
     if callee.originatingModule == ctx.module:
-      let rootSym = if sfFromGeneric notin callee.flags: callee
-                    else: callee.owner
-      c.calleeScope = rootSym.scope.depthLevel
+      c.calleeScope = 2
+      var owner = callee
+      while true:
+        owner = owner.skipGenericOwner
+        if owner.kind == skModule: break
+        inc c.calleeScope
     else:
       c.calleeScope = 1
   else:
@@ -109,9 +116,12 @@ proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym,
     for i in 1..min(sonsLen(typeParams), sonsLen(binding)-1):
       var formalTypeParam = typeParams.sons[i-1].typ
       var bound = binding[i].typ
-      if bound != nil and formalTypeParam.kind != tyTypeDesc:
+      internalAssert bound != nil
+      if formalTypeParam.kind == tyTypeDesc:
+        if bound.kind != tyTypeDesc:
+          bound = makeTypeDesc(ctx, bound)
+      else:
         bound = bound.skipTypes({tyTypeDesc})
-      assert bound != nil
       put(c.bindings, formalTypeParam, bound)
 
 proc newCandidate*(ctx: PContext, callee: PSym,
@@ -121,7 +131,7 @@ proc newCandidate*(ctx: PContext, callee: PSym,
 proc newCandidate*(ctx: PContext, callee: PType): TCandidate =
   initCandidate(ctx, result, callee)
 
-proc copyCandidate(a: var TCandidate, b: TCandidate) = 
+proc copyCandidate(a: var TCandidate, b: TCandidate) =
   a.c = b.c
   a.exactMatches = b.exactMatches
   a.subtypeMatches = b.subtypeMatches
@@ -146,36 +156,44 @@ proc sumGeneric(t: PType): int =
     of tyVar:
       # but do not make 'var T' more specific than 'T'!
       t = t.sons[0]
-    of tyGenericInvokation, tyTuple:
-      result = ord(t.kind == tyGenericInvokation)
+    of tyGenericInvocation, tyTuple:
+      result = ord(t.kind == tyGenericInvocation)
       for i in 0 .. <t.len: result += t.sons[i].sumGeneric
       break
-    of tyProc:
-      # proc matche proc better than 'stmt' to disambiguate 'spawn'
-      return 1
     of tyGenericParam, tyExpr, tyStatic, tyStmt, tyTypeDesc: break
+    of tyBool, tyChar, tyEnum, tyObject, tyProc, tyPointer,
+        tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128,
+        tyUInt..tyUInt64:
+      return 1
     else: return 0
 
+#var ggDebug: bool
+
 proc complexDisambiguation(a, b: PType): int =
-  var x, y: int
-  for i in 1 .. <a.len: x += a.sons[i].sumGeneric
-  for i in 1 .. <b.len: y += b.sons[i].sumGeneric
-  result = x - y
+  # 'a' matches better if *every* argument matches better or equal than 'b'.
+  var winner = 0
+  for i in 1 .. <min(a.len, b.len):
+    let x = a.sons[i].sumGeneric
+    let y = b.sons[i].sumGeneric
+    #if ggDebug:
+    #  echo "came her ", typeToString(a.sons[i]), " ", typeToString(b.sons[i])
+    if x != y:
+      if winner == 0:
+        if x > y: winner = 1
+        else: winner = -1
+      elif x > y:
+        if winner != 1:
+          # contradiction
+          return 0
+      else:
+        if winner != -1:
+          return 0
+  result = winner
   when false:
-    proc betterThan(a, b: PType): bool {.inline.} = a.sumGeneric > b.sumGeneric
-
-    if a.len > 1 and b.len > 1:
-      let aa = a.sons[1].sumGeneric
-      let bb = b.sons[1].sumGeneric
-      var a = a
-      var b = b
-      
-      if aa < bb: swap(a, b)
-      # all must be better
-      for i in 2 .. <min(a.len, b.len):
-        if not a.sons[i].betterThan(b.sons[i]): return 0
-      # a must be longer or of the same length as b:
-      result = a.len - b.len
+    var x, y: int
+    for i in 1 .. <a.len: x += a.sons[i].sumGeneric
+    for i in 1 .. <b.len: y += b.sons[i].sumGeneric
+    result = x - y
 
 proc cmpCandidates*(a, b: TCandidate): int =
   result = a.exactMatches - b.exactMatches
@@ -196,12 +214,13 @@ proc cmpCandidates*(a, b: TCandidate): int =
   # prefer more specialized generic over more general generic:
   result = complexDisambiguation(a.callee, b.callee)
 
-proc writeMatches*(c: TCandidate) = 
+proc writeMatches*(c: TCandidate) =
   writeln(stdout, "exact matches: " & $c.exactMatches)
+  writeln(stdout, "generic matches: " & $c.genericMatches)
   writeln(stdout, "subtype matches: " & $c.subtypeMatches)
-  writeln(stdout, "conv matches: " & $c.convMatches)
   writeln(stdout, "intconv matches: " & $c.intConvMatches)
-  writeln(stdout, "generic matches: " & $c.genericMatches)
+  writeln(stdout, "conv matches: " & $c.convMatches)
+  writeln(stdout, "inheritance: " & $c.inheritancePenalty)
 
 proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string =
   if arg.kind in nkSymChoices:
@@ -217,7 +236,7 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
   result = ""
   for i in countup(startIdx, n.len - 1):
     var arg = n.sons[i]
-    if n.sons[i].kind == nkExprEqExpr: 
+    if n.sons[i].kind == nkExprEqExpr:
       add(result, renderTree(n.sons[i].sons[0]))
       add(result, ": ")
       if arg.typ.isNil:
@@ -233,9 +252,9 @@ proc describeArgs*(c: PContext, n: PNode, startIdx = 1;
     if i != sonsLen(n) - 1: add(result, ", ")
 
 proc typeRel*(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation
-proc concreteType(c: TCandidate, t: PType): PType = 
+proc concreteType(c: TCandidate, t: PType): PType =
   case t.kind
-  of tyArrayConstr: 
+  of tyArrayConstr:
     # make it an array
     result = newType(tyArray, t.owner)
     addSonSkipIntLit(result, t.sons[0]) # XXX: t.owner is wrong for ID!
@@ -247,27 +266,27 @@ proc concreteType(c: TCandidate, t: PType): PType =
     else: result = t
   of tyGenericParam, tyAnything:
     result = t
-    while true: 
+    while true:
       result = PType(idTableGet(c.bindings, t))
       if result == nil:
         break # it's ok, no match
         # example code that triggers it:
         # proc sort[T](cmp: proc(a, b: T): int = cmp)
       if result.kind != tyGenericParam: break
-  of tyGenericInvokation:
+  of tyGenericInvocation:
     internalError("cannot resolve type: " & typeToString(t))
     result = t
   else:
     result = t                # Note: empty is valid here
-  
-proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = 
-  if a.kind == f.kind: 
+
+proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
+  if a.kind == f.kind:
     result = isEqual
   else:
     let ab = skipTypes(a, {tyRange})
     let k = ab.kind
     if k == f.kind: result = isSubrange
-    elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64, 
+    elif k == tyInt and f.kind in {tyRange, tyInt8..tyInt64,
                                    tyUInt..tyUInt64} and
         isIntLit(ab) and ab.n.intVal >= firstOrd(f) and
                          ab.n.intVal <= lastOrd(f):
@@ -276,9 +295,9 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation =
       result = isFromIntLit
     elif f.kind == tyInt and k in {tyInt8..tyInt32}:
       result = isIntConv
-    elif k >= min and k <= max: 
+    elif k >= min and k <= max:
       result = isConvertible
-    elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64, 
+    elif a.kind == tyRange and a.sons[0].kind in {tyInt..tyInt64,
                                                   tyUInt8..tyUInt32} and
                          a.n[0].intVal >= firstOrd(f) and
                          a.n[1].intVal <= lastOrd(f):
@@ -310,12 +329,12 @@ proc handleFloatRange(f, a: PType): TTypeRelation =
       if f.kind == tyFloat32: result = isConvertible
       else: result = isIntConv
     else: result = isNone
-  
+
 proc isObjectSubtype(a, f: PType): int =
   var t = a
   assert t.kind == tyObject
   var depth = 0
-  while t != nil and not sameObjectTypes(f, t): 
+  while t != nil and not sameObjectTypes(f, t):
     assert t.kind == tyObject
     t = t.sons[0]
     if t == nil: break
@@ -324,17 +343,18 @@ proc isObjectSubtype(a, f: PType): int =
   if t != nil:
     result = depth
 
-proc minRel(a, b: TTypeRelation): TTypeRelation = 
+proc minRel(a, b: TTypeRelation): TTypeRelation =
   if a <= b: result = a
   else: result = b
-  
+
 proc recordRel(c: var TCandidate, f, a: PType): TTypeRelation =
   result = isNone
-  if sameType(f, a): result = isEqual
+  if sameType(f, a):
+    result = isEqual
   elif sonsLen(a) == sonsLen(f):
     result = isEqual
     let firstField = if f.kind == tyTuple: 0
-                     else: 1 
+                     else: 1
     for i in countup(firstField, sonsLen(f) - 1):
       var m = typeRel(c, f.sons[i], a.sons[i])
       if m < isSubtype: return isNone
@@ -365,32 +385,32 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
       # We are matching a generic proc (as proc param)
       # to another generic type appearing in the proc
       # signature. There is a change that the target
-      # type is already fully-determined, so we are 
+      # type is already fully-determined, so we are
       # going to try resolve it
       f = generateTypeInstance(c.c, c.bindings, c.call.info, f)
       if f == nil or f.isMetaType:
         # no luck resolving the type, so the inference fails
         return isNone
     let reverseRel = typeRel(c, a, f)
-    if reverseRel == isGeneric:
+    if reverseRel >= isGeneric:
       result = isInferred
-      inc c.genericMatches
+      #inc c.genericMatches
   else:
     result = typeRel(c, f, a)
 
   if result <= isSubtype or inconsistentVarTypes(f, a):
     result = isNone
- 
-  if result == isEqual:
-    inc c.exactMatches
-    
+
+  #if result == isEqual:
+  #  inc c.exactMatches
+
 proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
   case a.kind
   of tyProc:
     if sonsLen(f) != sonsLen(a): return
     result = isEqual      # start with maximum; also correct for no
                           # params at all
-    
+
     template checkParam(f, a) =
       result = minRel(result, procParamTypeRel(c, f, a))
       if result == isNone: return
@@ -399,7 +419,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
     # return type!
     for i in 1 .. <f.sonsLen:
       checkParam(f.sons[i], a.sons[i])
-    
+
     if f.sons[0] != nil:
       if a.sons[0] != nil:
         checkParam(f.sons[0], a.sons[0])
@@ -410,7 +430,8 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
 
     if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags:
       return isNone
-    elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {}:
+    elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and
+        optThreadAnalysis in gGlobalOptions:
       # noSideEffect implies ``tfThread``!
       return isNone
     elif f.flags * {tfIterator} != a.flags * {tfIterator}:
@@ -424,6 +445,7 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation =
         return isNone
     when useEffectSystem:
       if not compatibleEffects(f, a): return isNone
+
   of tyNil:
     result = f.allowsNil
   of tyIter:
@@ -453,7 +475,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
   openScope(c)
   inc c.inTypeClass
 
-  finally:
+  defer:
     dec c.inTypeClass
     closeScope(c)
 
@@ -462,42 +484,46 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
       var
         typeParamName = ff.base.sons[i-1].sym.name
         typ = ff.sons[i]
-        param = newSym(skType, typeParamName, body.sym, body.sym.info)
-        
-      param.typ = makeTypeDesc(c, typ)
+        param: PSym
+
+      template paramSym(kind): expr =
+        newSym(kind, typeParamName, body.sym, body.sym.info)
+
+      case typ.kind
+      of tyStatic:
+        param = paramSym skConst
+        param.typ = typ.base
+        param.ast = typ.n
+      of tyUnknown:
+        param = paramSym skVar
+        param.typ = typ
+      else:
+        param = paramSym skType
+        param.typ = makeTypeDesc(c, typ)
+
       addDecl(c, param)
+      #echo "A ", param.name.s, " ", typeToString(param.typ), " ", param.kind
 
   for param in body.n[0]:
     var
       dummyName: PNode
       dummyType: PType
-    
+
     if param.kind == nkVarTy:
       dummyName = param[0]
-      dummyType = if a.kind != tyVar: makeVarType(c, a)
-                  else: a
+      dummyType = if a.kind != tyVar: makeVarType(c, a) else: a
     else:
       dummyName = param
       dummyType = a
 
     internalAssert dummyName.kind == nkIdent
-    var dummyParam = newSym(skType, dummyName.ident, body.sym, body.sym.info)
+    var dummyParam = newSym(skVar, dummyName.ident, body.sym, body.sym.info)
     dummyParam.typ = dummyType
     addDecl(c, dummyParam)
+    #echo "B ", dummyName.ident.s, " ", typeToString(dummyType), " ", dummyparam.kind
 
   var checkedBody = c.semTryExpr(c, body.n[3].copyTree)
-  #m.errors = bufferedMsgs
-  clearBufferedMsgs()
   if checkedBody == nil: return isNone
-
-  if checkedBody.kind == nkStmtList:
-    for stmt in checkedBody:
-      case stmt.kind
-      of nkReturnStmt: discard
-      of nkTypeSection: discard
-      of nkConstDef: discard
-      else: discard
-    
   return isGeneric
 
 proc shouldSkipDistinct(rules: PNode, callIdent: PIdent): bool =
@@ -517,11 +543,21 @@ proc maybeSkipDistinct(t: PType, callee: PSym): PType =
   else:
     result = t
 
+proc tryResolvingStaticExpr(c: var TCandidate, n: PNode): PNode =
+  # Consider this example:
+  #   type Value[N: static[int]] = object
+  #   proc foo[N](a: Value[N], r: range[0..(N-1)])
+  # Here, N-1 will be initially nkStaticExpr that can be evaluated only after
+  # N is bound to a concrete value during the matching of the first param.
+  # This proc is used to evaluate such static expressions.
+  let instantiated = replaceTypesInBody(c.c, c.bindings, n)
+  result = c.c.semExpr(c.c, instantiated)
+
 proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   # typeRel can be used to establish various relationships between types:
   #
   # 1) When used with concrete types, it will check for type equivalence
-  # or a subtype relationship. 
+  # or a subtype relationship.
   #
   # 2) When used with a concrete type against a type class (such as generic
   # signature of a proc), it will check whether the concrete type is a member
@@ -536,9 +572,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   result = isNone
   assert(f != nil)
-  
+
   if f.kind == tyExpr:
-    put(c.bindings, f, aOrig)
+    if aOrig != nil: put(c.bindings, f, aOrig)
     return isGeneric
 
   assert(aOrig != nil)
@@ -549,18 +585,17 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   # start the param matching process. This could be done in `prepareOperand`
   # for example, but unfortunately `prepareOperand` is not called in certain
   # situation when nkDotExpr are rotated to nkDotCalls
-  
+
   if a.kind == tyGenericInst and
       skipTypes(f, {tyVar}).kind notin {
-        tyGenericBody, tyGenericInvokation,
+        tyGenericBody, tyGenericInvocation,
         tyGenericInst, tyGenericParam} + tyTypeClasses:
     return typeRel(c, f, lastSon(a))
 
   template bindingRet(res) =
-    when res == isGeneric:
-      if doBind:
-        let bound = aOrig.skipTypes({tyRange}).skipIntLit
-        if doBind: put(c.bindings, f, bound)
+    if doBind:
+      let bound = aOrig.skipTypes({tyRange}).skipIntLit
+      if doBind: put(c.bindings, f, bound)
     return res
 
   template considerPreviousT(body: stmt) {.immediate.} =
@@ -572,20 +607,21 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyOr:
     # 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:
-      if typeRel(c, f, branch, false) == isNone:
-        return isNone
-
-    return isGeneric
+      let x = typeRel(c, f, branch, false)
+      if x == isNone: return isNone
+      if x < result: result = x
 
   of tyAnd:
     # seq[Sortable and Iterable] vs seq[Sortable]
     # only one match is enough
     for branch in a.sons:
-      if typeRel(c, f, branch, false) != isNone:
-        return isGeneric
-
-    return isNone
+      let x = typeRel(c, f, branch, false)
+      if x != isNone:
+        return if x >= isGeneric: isGeneric else: x
+    result = isNone
 
   of tyNot:
     case f.kind
@@ -593,9 +629,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       # seq[!int] vs seq[!number]
       # seq[float] matches the first, but not the second
       # we must turn the problem around:
-      # is number a subset of int? 
+      # is number a subset of int?
       return typeRel(c, a.lastSon, f.lastSon)
- 
+
     else:
       # negative type classes are essentially infinite,
       # so only the `any` type class is their superset
@@ -616,10 +652,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     elif skipTypes(a, {tyRange}).kind == f.kind: result = isSubtype
   of tyRange:
     if a.kind == f.kind:
+      if f.base.kind == tyNone: return isGeneric
       result = typeRel(c, base(f), base(a))
       # bugfix: accept integer conversions here
       #if result < isGeneric: result = isNone
       if result notin {isNone, isGeneric}:
+        # resolve any late-bound static expressions
+        # that may appear in the range:
+        for i in 0..1:
+          if f.n[i].kind == nkStaticExpr:
+            f.n.sons[i] = tryResolvingStaticExpr(c, f.n[i])
         result = typeRangeRel(f, a)
     else:
       if skipTypes(f, {tyRange}).kind == a.kind:
@@ -657,22 +699,27 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         else:
           fRange = prev
       result = typeRel(c, f.sons[1], a.sons[1])
-      if result < isGeneric:
-        result = isNone
-      elif tfUnresolved in fRange.flags and
-           rangeHasStaticIf(fRange):
-        # This is a range from an array instantiated with a generic
-        # static param. We must extract the static param here and bind
-        # it to the size of the currently supplied array.
-        var
-          rangeStaticT = fRange.getStaticTypeFromRange
-          replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
-          inputUpperBound = a.sons[0].n[1].intVal
-        # we must correct for the off-by-one discrepancy between
-        # ranges and static params:
-        replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
-        put(c.bindings, rangeStaticT, replacementT)
-        result = isGeneric
+      if result < isGeneric: return isNone
+      if rangeHasStaticIf(fRange):
+        if tfUnresolved in fRange.flags:
+          # This is a range from an array instantiated with a generic
+          # static param. We must extract the static param here and bind
+          # it to the size of the currently supplied array.
+          var
+            rangeStaticT = fRange.getStaticTypeFromRange
+            replacementT = newTypeWithSons(c.c, tyStatic, @[tyInt.getSysType])
+            inputUpperBound = a.sons[0].n[1].intVal
+          # we must correct for the off-by-one discrepancy between
+          # ranges and static params:
+          replacementT.n = newIntNode(nkIntLit, inputUpperBound + 1)
+          put(c.bindings, rangeStaticT, replacementT)
+          return isGeneric
+
+        let len = tryResolvingStaticExpr(c, fRange.n[1])
+        if len.kind == nkIntLit and len.intVal+1 == lengthOrd(a):
+          return # if we get this far, the result is already good
+        else:
+          return isNone
       elif lengthOrd(fRange) != lengthOrd(a):
         result = isNone
     else: discard
@@ -683,20 +730,20 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     of tyOpenArray, tyVarargs:
       result = typeRel(c, base(f), base(a))
       if result < isGeneric: result = isNone
-    of tyArrayConstr: 
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): 
+    of tyArrayConstr:
+      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty):
         result = isSubtype    # [] is allowed here
-      elif typeRel(c, base(f), a.sons[1]) >= isGeneric: 
+      elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
         result = isSubtype
-    of tyArray: 
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[1].kind == tyEmpty): 
+    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: 
+      elif typeRel(c, base(f), a.sons[1]) >= isGeneric:
         result = isConvertible
-    of tySequence: 
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 
+    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: 
+      elif typeRel(c, base(f), a.sons[0]) >= isGeneric:
         result = isConvertible
     else: discard
   of tySequence:
@@ -724,7 +771,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
   of tyForward: internalError("forward type in typeRel()")
   of tyNil:
     if a.kind == f.kind: result = isEqual
-  of tyTuple: 
+  of tyTuple:
     if a.kind == tyTuple: result = recordRel(c, f, a)
   of tyObject:
     if a.kind == tyObject:
@@ -737,15 +784,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           inc(c.inheritancePenalty, depth)
           result = isSubtype
   of tyDistinct:
-    if (a.kind == tyDistinct) and sameDistinctTypes(f, a): result = isEqual
+    if a.kind == tyDistinct and sameDistinctTypes(f, a): result = isEqual
     elif c.coerceDistincts: result = typeRel(c, f.base, a)
-  of tySet: 
-    if a.kind == tySet: 
-      if (f.sons[0].kind != tyGenericParam) and (a.sons[0].kind == tyEmpty): 
+  of tySet:
+    if a.kind == tySet:
+      if f.sons[0].kind != tyGenericParam and a.sons[0].kind == tyEmpty:
         result = isSubtype
-      else: 
+      else:
         result = typeRel(c, f.sons[0], a.sons[0])
-        if result <= isConvertible: 
+        if result <= isConvertible:
           result = isNone     # BUGFIX!
   of tyPtr, tyRef:
     if a.kind == f.kind:
@@ -779,9 +826,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       if a.len == 1: result = isConvertible
     of tyCString: result = isConvertible
     else: discard
-  of tyString: 
+  of tyString:
     case a.kind
-    of tyString: 
+    of tyString:
       if tfNotNil in f.flags and tfNotNil notin a.flags:
         result = isNilConversion
       else:
@@ -804,7 +851,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     of tyArray:
       if (firstOrd(a.sons[0]) == 0) and
           (skipTypes(a.sons[0], {tyRange}).kind in {tyInt..tyInt64}) and
-          (a.sons[1].kind == tyChar): 
+          (a.sons[1].kind == tyChar):
         result = isConvertible
     else: discard
 
@@ -819,10 +866,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         let ff = rootf.sons[i]
         let aa = roota.sons[i]
         result = typeRel(c, ff, aa)
-        if result == isNone: return        
+        if result == isNone: return
         if ff.kind == tyRange and result != isEqual: return isNone
-
-      result = isGeneric
+      #result = isGeneric
+      # XXX See bug #2220. A[int] should match A[int] better than some generic X
     else:
       result = typeRel(c, lastSon(f), a)
 
@@ -833,52 +880,70 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       let ff = lastSon(f)
       if ff != nil: result = typeRel(c, ff, a)
 
-  of tyGenericInvokation:
+  of tyGenericInvocation:
     var x = a.skipGenericAlias
-    if x.kind == tyGenericInvokation or f.sons[0].kind != tyGenericBody:
-      #InternalError("typeRel: tyGenericInvokation -> tyGenericInvokation")
+    if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
+      #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
       # simply no match for now:
       discard
-    elif x.kind == tyGenericInst and 
+    elif x.kind == tyGenericInst and
           (f.sons[0] == x.sons[0]) and
           (sonsLen(x) - 1 == sonsLen(f)):
       for i in countup(1, sonsLen(f) - 1):
         if x.sons[i].kind == tyGenericParam:
           internalError("wrong instantiated type!")
-        elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return 
+        elif typeRel(c, f.sons[i], x.sons[i]) <= isSubtype: return
       result = isGeneric
     else:
-      result = typeRel(c, f.sons[0], x)
+      let genericBody = f.sons[0]
+      result = typeRel(c, genericBody, x)
       if result != isNone:
+        # see tests/generics/tgeneric3.nim for an example that triggers this
+        # piece of code:
+        #
+        # proc internalFind[T,D](n: PNode[T,D], key: T): ref TItem[T,D]
+        # proc internalPut[T,D](ANode: ref TNode[T,D], Akey: T, Avalue: D,
+        #                       Oldvalue: var D): ref TNode[T,D]
+        # var root = internalPut[int, int](nil, 312, 312, oldvalue)
+        # var it1 = internalFind(root, 312) # cannot instantiate: 'D'
+        #
         # we steal the generic parameters from the tyGenericBody:
         for i in countup(1, sonsLen(f) - 1):
-          var x = PType(idTableGet(c.bindings, f.sons[0].sons[i - 1]))
-          if x == nil or x.kind in {tyGenericInvokation, tyGenericParam}:
+          var x = PType(idTableGet(c.bindings, genericBody.sons[i-1]))
+          if x == nil:
+            discard "maybe fine (for eg. a==tyNil)"
+          elif x.kind in {tyGenericInvocation, tyGenericParam}:
             internalError("wrong instantiated type!")
-          put(c.bindings, f.sons[i], x)
-  
+          else:
+            put(c.bindings, f.sons[i], x)
+
   of tyAnd:
     considerPreviousT:
       for branch in f.sons:
-        if typeRel(c, branch, aOrig) < isSubtype:
-          return isNone
-
-      bindingRet isGeneric
+        let x = typeRel(c, branch, aOrig)
+        if x < isSubtype: return isNone
+        # 'and' implies minimum matching result:
+        if x < result: result = x
+      bindingRet result
 
   of tyOr:
     considerPreviousT:
+      result = isNone
       for branch in f.sons:
-        if typeRel(c, branch, aOrig) >= isSubtype:
-          bindingRet isGeneric
-       
-      return isNone
+        let x = typeRel(c, branch, aOrig)
+        # 'or' implies maximum matching result:
+        if x > result: result = x
+      if result >= isSubtype:
+        bindingRet result
+      else:
+        result = isNone
 
   of tyNot:
     considerPreviousT:
       for branch in f.sons:
         if typeRel(c, branch, aOrig) != isNone:
           return isNone
-      
+
       bindingRet isGeneric
 
   of tyAnything:
@@ -917,7 +982,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
     if x == nil:
       if c.callee.kind == tyGenericBody and
          f.kind == tyGenericParam and not c.typedescMatched:
-        # XXX: The fact that generic types currently use tyGenericParam for 
+        # XXX: The fact that generic types currently use tyGenericParam for
         # their parameters is really a misnomer. tyGenericParam means "match
         # any value" and what we need is "match any type", which can be encoded
         # by a tyTypeDesc params. Unfortunately, this requires more substantial
@@ -930,7 +995,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
           else:
             internalAssert a.sons != nil and a.sons.len > 0
             c.typedescMatched = true
-            result = typeRel(c, f.base, a.base)
+            var aa = a
+            while aa.kind in {tyTypeDesc, tyGenericParam} and
+                aa.len > 0:
+              aa = lastSon(aa)
+            result = typeRel(c, f.base, aa)
+            if result > isGeneric: result = isGeneric
         else:
           result = isNone
       else:
@@ -954,20 +1024,38 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
             return isNone
         if doBind:
           put(c.bindings, f, concrete)
+      elif result > isGeneric:
+        result = isGeneric
     elif a.kind == tyEmpty:
       result = isGeneric
     elif x.kind == tyGenericParam:
       result = isGeneric
     else:
       result = typeRel(c, x, a) # check if it fits
-  
+      if result > isGeneric: result = isGeneric
+
   of tyStatic:
-    if aOrig.kind == tyStatic:
-      result = typeRel(c, f.lastSon, a)
-      if result != isNone: put(c.bindings, f, aOrig)
+    let prev = PType(idTableGet(c.bindings, f))
+    if prev == nil:
+      if aOrig.kind == tyStatic:
+        result = typeRel(c, f.lastSon, a)
+        if result != isNone and f.n != nil:
+          if not exprStructuralEquivalent(f.n, aOrig.n):
+            result = isNone
+        if result != isNone: put(c.bindings, f, aOrig)
+      else:
+        result = isNone
+    elif prev.kind == tyStatic:
+      if aOrig.kind == tyStatic:
+        result = typeRel(c, prev.lastSon, a)
+        if result != isNone and prev.n != nil:
+          if not exprStructuralEquivalent(prev.n, aOrig.n):
+            result = isNone
+      else: result = isNone
     else:
+      # XXX endless recursion?
+      #result = typeRel(c, prev, aOrig)
       result = isNone
-
   of tyTypeDesc:
     var prev = PType(idTableGet(c.bindings, f))
     if prev == nil:
@@ -975,12 +1063,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
       # when `f` is an unresolved typedesc, `a` could be any
       # type, so we should not perform this check earlier
       if a.kind != tyTypeDesc: return isNone
-    
+
       if f.base.kind == tyNone:
         result = isGeneric
       else:
         result = typeRel(c, f.base, a.base)
-      
+
       if result != isNone:
         put(c.bindings, f, a)
     else:
@@ -990,9 +1078,9 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
         result = typeRel(c, prev.base, a.base)
       else:
         result = isNone
- 
+
   of tyIter:
-    if a.kind == tyIter or 
+    if a.kind == tyIter or
       (a.kind == tyProc and tfIterator in a.flags):
       result = typeRel(c, f.base, a.base)
     else:
@@ -1000,48 +1088,50 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
 
   of tyStmt:
     result = isGeneric
-  
+
   of tyProxy:
     result = isEqual
 
   of tyFromExpr:
     # fix the expression, so it contains the already instantiated types
-    let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
-    let reevaluted = c.c.semExpr(c.c, instantiated)
-    case reevaluted.typ.kind
+    if f.n == nil or f.n.kind == nkEmpty: return isGeneric
+    let reevaluated = tryResolvingStaticExpr(c, f.n)
+    case reevaluated.typ.kind
     of tyTypeDesc:
-      result = typeRel(c, a, reevaluted.typ.base)
+      result = typeRel(c, a, reevaluated.typ.base)
     of tyStatic:
-      result = typeRel(c, a, reevaluted.typ.base)
-      if result != isNone and reevaluted.typ.n != nil:
-        if not exprStructuralEquivalent(aOrig.n, reevaluted.typ.n):
+      result = typeRel(c, a, reevaluated.typ.base)
+      if result != isNone and reevaluated.typ.n != nil:
+        if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n):
           result = isNone
     else:
       localError(f.n.info, errTypeExpected)
       result = isNone
-  
+
+  of tyNone:
+    if a.kind == tyNone: result = isEqual
   else:
-    internalAssert false
-  
-proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation = 
+    internalError " unknown type kind " & $f.kind
+
+proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
   var m: TCandidate
   initCandidate(c, m, f)
   result = typeRel(m, f, a)
 
-proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate, 
-                         f: PType): PType = 
+proc getInstantiatedType(c: PContext, arg: PNode, m: TCandidate,
+                         f: PType): PType =
   result = PType(idTableGet(m.bindings, f))
-  if result == nil: 
+  if result == nil:
     result = generateTypeInstance(c, m.bindings, arg, f)
   if result == nil:
     internalError(arg.info, "getInstantiatedType")
     result = errorType(c)
-  
-proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate, 
-                  c: PContext): PNode = 
+
+proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
+                  c: PContext): PNode =
   result = newNodeI(kind, arg.info)
   if containsGenericType(f):
-    if not m.proxyMatch:
+    if not m.hasFauxMatch:
       result.typ = getInstantiatedType(c, arg, m, f)
     else:
       result.typ = errorType(c)
@@ -1051,10 +1141,10 @@ proc implicitConv(kind: TNodeKind, f: PType, arg: PNode, m: TCandidate,
   addSon(result, ast.emptyNode)
   addSon(result, arg)
 
-proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, 
-                   arg: PNode): PNode = 
+proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
+                   arg: PNode): PNode =
   result = nil
-  for i in countup(0, len(c.converters) - 1): 
+  for i in countup(0, len(c.converters) - 1):
     var src = c.converters[i].typ.sons[1]
     var dest = c.converters[i].typ.sons[0]
     # for generic type converters we need to check 'src <- a' before
@@ -1062,12 +1152,12 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
     # see tests/tgenericconverter:
     let srca = typeRel(m, src, a)
     if srca notin {isEqual, isGeneric}: continue
-    
+
     let destIsGeneric = containsGenericType(dest)
     if destIsGeneric:
       dest = generateTypeInstance(c, m.bindings, arg, dest)
     let fdest = typeRel(m, f, dest)
-    if fdest in {isEqual, isGeneric}: 
+    if fdest in {isEqual, isGeneric}:
       markUsed(arg.info, c.converters[i])
       var s = newSymNode(c.converters[i])
       s.typ = c.converters[i].typ
@@ -1079,10 +1169,15 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType,
       m.genericConverter = srca == isGeneric or destIsGeneric
       return result
 
-proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType, 
-                    arg: PNode): PNode = 
+proc localConvMatch(c: PContext, m: var TCandidate, f, a: PType,
+                    arg: PNode): PNode =
   # arg.typ can be nil in 'suggest':
   if isNil(arg.typ): return nil
+
+  # sem'checking for 'echo' needs to be re-entrant:
+  # XXX we will revisit this issue after 0.10.2 is released
+  if f == arg.typ and arg.kind == nkHiddenStdConv: return arg
+
   var call = newNodeI(nkCall, arg.info)
   call.add(f.n.copyTree)
   call.add(arg.copyTree)
@@ -1101,6 +1196,26 @@ proc isInlineIterator*(t: PType): bool =
   result = t.kind == tyIter or
           (t.kind == tyBuiltInTypeClass and t.base.kind == tyIter)
 
+proc isEmptyContainer*(t: PType): bool =
+  case t.kind
+  of tyExpr, tyNil: result = true
+  of tyArray, tyArrayConstr: result = t.sons[1].kind == tyEmpty
+  of tySet, tySequence, tyOpenArray, tyVarargs:
+    result = t.sons[0].kind == tyEmpty
+  of tyGenericInst: result = isEmptyContainer(t.lastSon)
+  else: result = false
+
+proc incMatches(m: var TCandidate; r: TTypeRelation; convMatch = 1) =
+  case r
+  of isConvertible, isIntConv: inc(m.convMatches, convMatch)
+  of isSubtype, isSubrange: inc(m.subtypeMatches)
+  of isGeneric, isInferred: inc(m.genericMatches)
+  of isFromIntLit: inc(m.intConvMatches, 256)
+  of isInferredConvertible:
+    inc(m.convMatches)
+  of isEqual: inc(m.exactMatches)
+  of isNone: discard
+
 proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
                         argSemantized, argOrig: PNode): PNode =
   var
@@ -1108,12 +1223,12 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     arg = argSemantized
     argType = argType
     c = m.c
- 
+
   if tfHasStatic in fMaybeStatic.flags:
     # XXX: When implicit statics are the default
     # this will be done earlier - we just have to
     # make sure that static types enter here
-     
+
     # XXX: weaken tyGenericParam and call it tyGenericPlaceholder
     # and finally start using tyTypedesc for generic types properly.
     if argType.kind == tyGenericParam and tfWildcard in argType.flags:
@@ -1122,9 +1237,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
       return argSemantized
 
     if argType.kind == tyStatic:
-      if m.callee.kind == tyGenericBody:
-        result = newNodeI(nkType, argOrig.info)
-        result.typ = makeTypeFromExpr(c, arg)
+      if m.callee.kind == tyGenericBody and tfGenericTypeParam notin argType.flags:
+        result = newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg))
         return
     else:
       var evaluated = c.semTryConstExpr(c, arg)
@@ -1133,34 +1247,26 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
         arg.typ.sons = @[evaluated.typ]
         arg.typ.n = evaluated
         argType = arg.typ
- 
+
   var
     a = if c.inTypeClass > 0: argType.skipTypes({tyTypeDesc, tyFieldAccessor})
         else: argType
- 
+
     r = typeRel(m, f, a)
 
   if r != isNone and m.calleeSym != nil and
      m.calleeSym.kind in {skMacro, skTemplate}:
-    # XXX: duplicating this is ugly, maybe we should move this
+    # XXX: duplicating this is ugly, but we cannot (!) move this
     # directly into typeRel using return-like templates
-    case r
-    of isConvertible, isIntConv: inc(m.convMatches)
-    of isSubtype, isSubrange: inc(m.subtypeMatches)
-    of isGeneric, isInferred: inc(m.genericMatches)
-    of isInferredConvertible: inc(m.genericMatches); inc(m.convMatches)
-    of isFromIntLit: inc(m.intConvMatches, 256)
-    of isEqual: inc(m.exactMatches)
-    of isNone: discard
-
-    if f.kind == tyStmt and argOrig.kind == nkDo:
-      return argOrig[bodyPos]
+    incMatches(m, r)
+    if f.kind == tyStmt:
+      return arg
     elif f.kind == tyTypeDesc:
       return arg
     elif f.kind == tyStatic:
       return arg.typ.n
     else:
-      return argOrig
+      return argSemantized # argOrig
 
   if r != isNone and f.isInlineIterator:
     var inlined = newTypeS(tyStatic, c)
@@ -1172,21 +1278,22 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
   case r
   of isConvertible:
     inc(m.convMatches)
-    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isIntConv:
     # I'm too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
     inc(m.intConvMatches)
-    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
-  of isSubtype: 
+    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
+  of isSubtype:
     inc(m.subtypeMatches)
-    result = implicitConv(nkHiddenSubConv, f, copyTree(arg), m, c)
+    result = implicitConv(nkHiddenSubConv, f, arg, m, c)
   of isSubrange:
     inc(m.subtypeMatches)
-    #result = copyTree(arg)
-    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+    if f.kind == tyVar:
+      result = arg
+    else:
+      result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isInferred, isInferredConvertible:
-    inc(m.genericMatches)
     if arg.kind in {nkProcDef, nkIteratorDef} + nkLambdaKinds:
       result = c.semInferredLambda(c, m.bindings, arg)
     else:
@@ -1195,31 +1302,36 @@ proc paramTypesMatchAux(m: var TCandidate, f, argType: PType,
     if r == isInferredConvertible:
       inc(m.convMatches)
       result = implicitConv(nkHiddenStdConv, f, result, m, c)
+    else:
+      inc(m.genericMatches)
   of isGeneric:
     inc(m.genericMatches)
-    result = copyTree(arg)
-    result.typ = getInstantiatedType(c, arg, m, f)
-    # BUG: f may not be the right key!
-    if skipTypes(result.typ, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
+    if arg.typ == nil:
+      result = arg
+    elif skipTypes(arg.typ, abstractVar-{tyTypeDesc}).kind == tyTuple:
       result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
-      # BUGFIX: use ``result.typ`` and not `f` here
+    elif arg.typ.isEmptyContainer:
+      result = arg.copyTree
+      result.typ = getInstantiatedType(c, arg, m, f)
+    else:
+      result = arg
   of isFromIntLit:
     # too lazy to introduce another ``*matches`` field, so we conflate
     # ``isIntConv`` and ``isIntLit`` here:
     inc(m.intConvMatches, 256)
-    result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
-  of isEqual: 
+    result = implicitConv(nkHiddenStdConv, f, arg, m, c)
+  of isEqual:
     inc(m.exactMatches)
-    result = copyTree(arg)
+    result = arg
     if skipTypes(f, abstractVar-{tyTypeDesc}).kind in {tyTuple}:
-      result = implicitConv(nkHiddenStdConv, f, copyTree(arg), m, c)
+      result = implicitConv(nkHiddenStdConv, f, arg, m, c)
   of isNone:
     # do not do this in ``typeRel`` as it then can't infere T in ``ref T``:
-    if a.kind == tyProxy:
+    if a.kind in {tyProxy, tyUnknown}:
       inc(m.genericMatches)
-      m.proxyMatch = true
-      return copyTree(arg)
-    result = userConvMatch(c, m, f, a, arg) 
+      m.fauxMatch = a.kind
+      return arg
+    result = userConvMatch(c, m, f, a, arg)
     # check for a base type match, which supports varargs[T] without []
     # constructor in a call:
     if result == nil and f.kind == tyVarargs:
@@ -1240,12 +1352,11 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
                       arg, argOrig: PNode): PNode =
   if arg == nil or arg.kind notin nkSymChoices:
     result = paramTypesMatchAux(m, f, a, arg, argOrig)
-  else: 
+  else:
     # CAUTION: The order depends on the used hashing scheme. Thus it is
     # incorrect to simply use the first fitting match. However, to implement
     # this correctly is inefficient. We have to copy `m` here to be able to
     # roll back the side effects of the unification algorithm.
-
     let c = m.c
     var x, y, z: TCandidate
     initCandidate(c, x, m.callee)
@@ -1255,38 +1366,54 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType,
     y.calleeSym = m.calleeSym
     z.calleeSym = m.calleeSym
     var best = -1
-    for i in countup(0, sonsLen(arg) - 1): 
+    for i in countup(0, sonsLen(arg) - 1):
       if arg.sons[i].sym.kind in {skProc, skMethod, skConverter}+skIterators:
         copyCandidate(z, m)
-        var r = typeRel(z, f, arg.sons[i].typ)
-        if r != isNone: 
+        z.callee = arg.sons[i].typ
+        z.calleeSym = arg.sons[i].sym
+        #if arg.sons[i].sym.name.s == "cmp":
+        #  ggDebug = true
+        #  echo "CALLLEEEEEEEE A ", typeToString(z.callee)
+        # XXX this is still all wrong: (T, T) should be 2 generic matches
+        # and  (int, int) 2 exact matches, etc. Essentially you cannot call
+        # typeRel here and expect things to work!
+        let r = typeRel(z, f, arg.sons[i].typ)
+        incMatches(z, r, 2)
+        #if arg.sons[i].sym.name.s == "cmp": # and arg.info.line == 606:
+        #  echo "M ", r, " ", arg.info, " ", typeToString(arg.sons[i].sym.typ)
+        #  writeMatches(z)
+        if r != isNone:
+          z.state = csMatch
           case x.state
-          of csEmpty, csNoMatch: 
+          of csEmpty, csNoMatch:
             x = z
             best = i
-            x.state = csMatch
-          of csMatch: 
-            var cmp = cmpCandidates(x, z)
+          of csMatch:
+            let cmp = cmpCandidates(x, z)
             if cmp < 0:
               best = i
               x = z
             elif cmp == 0:
               y = z           # z is as good as x
-    if x.state == csEmpty: 
-      result = nil
-    elif (y.state == csMatch) and (cmpCandidates(x, y) == 0): 
-      if x.state != csMatch: 
-        internalError(arg.info, "x.state is not csMatch") 
-      # ambiguous: more than one symbol fits
+    if x.state == csEmpty:
       result = nil
-    else: 
+    elif y.state == csMatch and cmpCandidates(x, y) == 0:
+      if x.state != csMatch:
+        internalError(arg.info, "x.state is not csMatch")
+      # ambiguous: more than one symbol fits!
+      # See tsymchoice_for_expr as an example. 'f.kind == tyExpr' should match
+      # anyway:
+      if f.kind == tyExpr: result = arg
+      else: result = nil
+    else:
       # only one valid interpretation found:
       markUsed(arg.info, arg.sons[best].sym)
       styleCheckUse(arg.info, arg.sons[best].sym)
       result = paramTypesMatchAux(m, f, arg.sons[best].typ, arg.sons[best],
                                   argOrig)
 
-proc setSon(father: PNode, at: int, son: PNode) = 
+
+proc setSon(father: PNode, at: int, son: PNode) =
   if sonsLen(father) <= at: setLen(father.sons, at + 1)
   father.sons[at] = son
 
@@ -1297,9 +1424,12 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode =
     # a.typ == nil is valid
     result = a
   elif a.typ.isNil:
+    # XXX This is unsound! 'formal' can differ from overloaded routine to
+    # overloaded routine!
     let flags = if formal.kind == tyIter: {efDetermineType, efWantIterator}
-                elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
-                else: {efDetermineType}
+                else: {efDetermineType, efAllowStmt}
+                #elif formal.kind == tyStmt: {efDetermineType, efWantStmt}
+                #else: {efDetermineType}
     result = c.semOperand(c, a, flags)
   else:
     result = a
@@ -1330,7 +1460,7 @@ proc incrIndexType(t: PType) =
   inc t.sons[0].n.sons[1].intVal
 
 proc matchesAux(c: PContext, n, nOrig: PNode,
-                m: var TCandidate, marker: var IntSet) = 
+                m: var TCandidate, marker: var IntSet) =
   template checkConstraint(n: expr) {.immediate, dirty.} =
     if not formal.constraint.isNil:
       if matchNodeKinds(formal.constraint, n):
@@ -1339,6 +1469,12 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
       else:
         m.state = csNoMatch
         return
+    if formal.typ.kind == tyVar:
+      if n.isLValue:
+        inc(m.genericMatches, 100)
+      else:
+        m.state = csNoMatch
+        return
 
   var
     # iterates over formal parameters
@@ -1360,20 +1496,20 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
       # named param
       # check if m.callee has such a param:
       prepareNamedParam(n.sons[a])
-      if n.sons[a].sons[0].kind != nkIdent: 
+      if n.sons[a].sons[0].kind != nkIdent:
         localError(n.sons[a].info, errNamedParamHasToBeIdent)
         m.state = csNoMatch
-        return 
+        return
       formal = getSymFromList(m.callee.n, n.sons[a].sons[0].ident, 1)
-      if formal == nil: 
+      if formal == nil:
         # no error message!
         m.state = csNoMatch
-        return 
-      if containsOrIncl(marker, formal.position): 
+        return
+      if containsOrIncl(marker, formal.position):
         # already in namedParams:
         localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
         m.state = csNoMatch
-        return 
+        return
       m.baseTypeMatch = false
       n.sons[a].sons[1] = prepareOperand(c, formal.typ, n.sons[a].sons[1])
       n.sons[a].typ = n.sons[a].sons[1].typ
@@ -1383,14 +1519,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
         m.state = csNoMatch
         return
       checkConstraint(n.sons[a].sons[1])
-      if m.baseTypeMatch: 
-        assert(container == nil)
+      if m.baseTypeMatch:
+        #assert(container == nil)
         container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
         addSon(container, arg)
         setSon(m.call, formal.position + 1, container)
         if f != formalLen - 1: container = nil
-      else: 
+      else:
         setSon(m.call, formal.position + 1, arg)
+      inc f
     else:
       # unnamed param
       if f >= formalLen:
@@ -1404,12 +1541,14 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
                                         copyTree(n.sons[a]), m, c))
           else:
             addSon(m.call, copyTree(n.sons[a]))
-        elif formal != nil:
+        elif formal != nil and formal.typ.kind == tyVarargs:
+          # beware of the side-effects in 'prepareOperand'! So only do it for
+          # varags matching. See tests/metatype/tstatic_overloading.
           m.baseTypeMatch = false
           n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
           var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
                                     n.sons[a], nOrig.sons[a])
-          if (arg != nil) and m.baseTypeMatch and (container != nil):
+          if arg != nil and m.baseTypeMatch and container != nil:
             addSon(container, arg)
             incrIndexType(container.typ)
           else:
@@ -1419,15 +1558,15 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           m.state = csNoMatch
           return
       else:
-        if m.callee.n.sons[f].kind != nkSym: 
+        if m.callee.n.sons[f].kind != nkSym:
           internalError(n.sons[a].info, "matches")
           return
         formal = m.callee.n.sons[f].sym
-        if containsOrIncl(marker, formal.position): 
+        if containsOrIncl(marker, formal.position) and container.isNil:
           # already in namedParams:
           localError(n.sons[a].info, errCannotBindXTwice, formal.name.s)
           m.state = csNoMatch
-          return 
+          return
         m.baseTypeMatch = false
         n.sons[a] = prepareOperand(c, formal.typ, n.sons[a])
         var arg = paramTypesMatch(m, formal.typ, n.sons[a].typ,
@@ -1436,17 +1575,22 @@ proc matchesAux(c: PContext, n, nOrig: PNode,
           m.state = csNoMatch
           return
         if m.baseTypeMatch:
-          assert(container == nil)
-          container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
+          #assert(container == nil)
+          if container.isNil:
+            container = newNodeIT(nkBracket, n.sons[a].info, arrayConstr(c, arg))
           addSon(container, arg)
-          setSon(m.call, formal.position + 1, 
+          setSon(m.call, formal.position + 1,
                  implicitConv(nkHiddenStdConv, formal.typ, container, m, c))
-          if f != formalLen - 1: container = nil
+          #if f != formalLen - 1: container = nil
+
+          # pick the formal from the end, so that 'x, y, varargs, z' works:
+          f = max(f, formalLen - n.len + a + 1)
         else:
           setSon(m.call, formal.position + 1, arg)
+          inc(f)
+          container = nil
       checkConstraint(n.sons[a])
     inc(a)
-    inc(f)
 
 proc semFinishOperands*(c: PContext, n: PNode) =
   # this needs to be called to ensure that after overloading resolution every
@@ -1491,12 +1635,15 @@ proc argtypeMatches*(c: PContext, f, a: PType): bool =
   # instantiate generic converters for that
   result = res != nil
 
-proc instDeepCopy*(c: PContext; dc: PSym; t: PType; info: TLineInfo): PSym {.
-                    procvar.} =
+proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
+                      op: TTypeAttachedOp): PSym {.procvar.} =
   var m: TCandidate
   initCandidate(c, m, dc.typ)
   var f = dc.typ.sons[1]
-  if f.kind in {tyRef, tyPtr}: f = f.lastSon
+  if op == attachedDeepCopy:
+    if f.kind in {tyRef, tyPtr}: f = f.lastSon
+  else:
+    if f.kind == tyVar: f = f.lastSon
   if typeRel(m, f, t) == isNone:
     localError(info, errGenerated, "cannot instantiate 'deepCopy'")
   else:
@@ -1510,7 +1657,7 @@ when not declared(tests):
 
 tests:
   var dummyOwner = newSym(skModule, getIdent("test_module"), nil, UnknownLineInfo())
-  
+
   proc `|` (t1, t2: PType): PType =
     result = newType(tyOr, dummyOwner)
     result.rawAddSon(t1)
@@ -1531,12 +1678,12 @@ tests:
 
   proc array(x: int, t: PType): PType =
     result = newType(tyArray, dummyOwner)
-    
+
     var n = newNodeI(nkRange, UnknownLineInfo())
     addSon(n, newIntNode(nkIntLit, 0))
     addSon(n, newIntNode(nkIntLit, x))
     let range = newType(tyRange, dummyOwner)
-    
+
     result.rawAddSon(range)
     result.rawAddSon(t)
 
@@ -1562,7 +1709,7 @@ tests:
 
     setup:
       var c: TCandidate
-      InitCandidate(nil, c, nil)
+      initCandidate(nil, c, nil)
 
     template yes(x, y) =
       test astToStr(x) & " is " & astToStr(y):
@@ -1571,7 +1718,7 @@ tests:
     template no(x, y) =
       test astToStr(x) & " is not " & astToStr(y):
         check typeRel(c, y, x) == isNone
-    
+
     yes seq(any), array(10, int) | seq(any)
     # Sure, seq[any] is directly included
 
@@ -1579,16 +1726,16 @@ tests:
     yes seq(int), seq(number)
     # Sure, the int sequence is certainly
     # part of the number sequences (and all sequences)
-    
+
     no seq(any), seq(float)
     # Nope, seq[any] includes types that are not seq[float] (e.g. seq[int])
 
     yes seq(int|string), seq(any)
     # Sure
- 
+
     yes seq(int&string), seq(any)
     # Again
-    
+
     yes seq(int&string), seq(int)
     # A bit more complicated
     # seq[int&string] is not a real type, but it's analogous to
@@ -1597,23 +1744,23 @@ tests:
     no seq(int|string), seq(int|float)
     # Nope, seq[string] is not included in not included in
     # the seq[int|float] set
-    
+
     no seq(!(int|string)), seq(string)
     # A sequence that is neither seq[int] or seq[string]
     # is obviously not seq[string]
-     
+
     no seq(!int), seq(number)
     # Now your head should start to hurt a bit
     # A sequence that is not seq[int] is not necessarily a number sequence
     # it could well be seq[string] for example
-    
+
     yes seq(!(int|string)), seq(!string)
     # all sequnece types besides seq[int] and seq[string]
     # are subset of all sequence types that are not seq[string]
 
     no seq(!(int|string)), seq(!(string|TFoo))
     # Nope, seq[TFoo] is included in the first set, but not in the second
-    
+
     no seq(!string), seq(!number)
     # Nope, seq[int] in included in the first set, but not in the second
 
@@ -1621,7 +1768,7 @@ tests:
     yes seq(!int), seq(any)
     no seq(any), seq(!any)
     no seq(!int), seq(!any)
-    
+
     yes int, ordinal
     no  string, ordinal